root / pykota / trunk / bin / pkbanner @ 1923

Revision 1923, 13.7 kB (checked in by jalet, 19 years ago)

Improved banner handling.
Fix for raw printing and banners.

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
Line 
1#! /usr/bin/env python
2# -*- coding: ISO-8859-15 -*-
3
4# A banner generator for PyKota
5#
6# PyKota - Print Quotas for CUPS and LPRng
7#
8# (c) 2003-2004 Jerome Alet <alet@librelogiciel.com>
9# This program is free software; you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation; either version 2 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program; if not, write to the Free Software
21# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
22#
23# $Id$
24#
25# $Log$
26# Revision 1.5  2004/11/15 22:01:34  jalet
27# Improved banner handling.
28# Fix for raw printing and banners.
29#
30# Revision 1.4  2004/11/15 19:59:34  jalet
31# PyKota banners now basically work !
32#
33# Revision 1.3  2004/11/12 23:46:44  jalet
34# Heavy work on pkbanner. Not finished yet though, but mostly works.
35#
36# Revision 1.2  2004/11/11 14:25:48  jalet
37# Added some TODO comments
38#
39# Revision 1.1  2004/11/10 22:48:47  jalet
40# Banner generator's skeleton added
41#
42#
43#
44
45import sys
46import os
47import time
48import cStringIO
49import popen2
50
51try :
52    from reportlab.pdfgen import canvas
53    from reportlab.lib import pagesizes
54    from reportlab.lib.units import cm
55except ImportError :   
56    hasRL = 0
57else :   
58    hasRL = 1
59   
60try :
61    import PIL.Image 
62except ImportError :   
63    hasPIL = 0
64else :   
65    hasPIL = 1
66   
67from pykota.tool import Tool, PyKotaToolError, crashed, N_
68
69__doc__ = N_("""pkbanner v%s (c) 2003-2004 C@LL - Conseil Internet & Logiciels Libres
70
71Generates banners.
72
73command line usage :
74
75  pkbanner  [options]  [files]
76
77options :
78
79  -v | --version       Prints pkbanner's version number then exits.
80  -h | --help          Prints this message then exits.
81 
82  -d | --darkness d    Sets the darkness to d%%. This can be used to
83                       save toner. The default value is 100.
84                       NOT IMPLEMENTED YET.
85 
86  -p | --pagesize sz   Sets sz as the page size. Most well known
87                       page sizes are recognized, like 'A4' or 'Letter'
88                       to name a few.
89 
90  -l | --logo img      Use the image as the banner's logo. The logo will
91                       be drawn at the top center of the page. The default
92                       logo is /usr/share/pykota/logos/pykota.jpeg
93                       
94  -u | --url u         Uses u as an url to be written at the bottom of
95                       the banner page. The default url is :
96                       http://www.librelogiciel.com/software/
97 
98examples :                             
99
100  Using pkbanner directly from the command line is not recommended,
101  excepted for testing purposes. You should use pkbanner in the
102  'startingbanner' or 'endingbanner' directives in pykota.conf
103 
104  For this reason, there's no example.
105
106This program is free software; you can redistribute it and/or modify
107it under the terms of the GNU General Public License as published by
108the Free Software Foundation; either version 2 of the License, or
109(at your option) any later version.
110
111This program is distributed in the hope that it will be useful,
112but WITHOUT ANY WARRANTY; without even the implied warranty of
113MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
114GNU General Public License for more details.
115
116You should have received a copy of the GNU General Public License
117along with this program; if not, write to the Free Software
118Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
119
120Please e-mail bugs to: %s""")
121       
122class PyKotaBanner(Tool) :       
123    """A class for pkbanner."""
124    primaryfields = [ 
125                ("PRINTERNAME", N_("Printer")),
126                ("JOBID", N_("JobId")),
127                ("JOBORIGINATINGHOSTNAME", N_("Client host")),
128                ("TITLE", N_("Title")),
129                ("FILENAME", N_("Filename")),
130                ("COPIES", N_("Copies")),
131                ("JOBSIZEBYTES", N_("Job size in bytes")),
132                ("PRECOMPUTEDJOBSIZE", N_("Estimated job size")),
133                ("PRECOMPUTEDJOBPRICE", N_("Estimated job price")),
134             ]   
135             
136    secondaryfields = [
137                ("LIMITBY", N_("Account limited by")),
138                ("BALANCE", N_("Account balance")),
139                ("PAGECOUNTER", N_("Number of pages on this printer")),
140                ("SOFTLIMIT", N_("Soft limit")),
141                ("HARDLIMIT", N_("Hard limit")),
142                ("DATELIMIT", N_("Date limit")),
143             ]                           
144   
145    tertiaryfields = [
146                ("ACTION", N_("Action taken for current job")),
147             ]                           
148   
149    def getPageSize(self, pgsize) :
150        """Returns the correct page size or None if not found."""
151        try :
152            return getattr(pagesizes, pgsize.upper())
153        except AttributeError :   
154            try :
155                return getattr(pagesizes, pgsize.lower())
156            except AttributeError :
157                pass
158               
159    def getVar(self, varname) :           
160        """Extracts a variable from the environment and returns its value or 'Unknown' in the current locale."""
161        return os.environ.get(varname) or _("Unknown")
162       
163    def printVar(self, canvas, x, y, label, value, size, darkness) :
164        """Outputs a variable onto the PDF canvas.
165       
166           Returns the number of points to substract to current Y coordinate.
167        """   
168        canvas.saveState()
169        canvas.setFont("Helvetica-Bold", size)
170        (r, g, b) =  (0, 0, 0)  # Black : TODO : darkness
171        canvas.setFillColorRGB(r, g, b)
172        message = "%s :" % _(label).title()
173        canvas.drawRightString(x, y, message)
174        canvas.setFont("Courier-Bold", size)
175        (r, g, b) = (1, 0, 0)   # Red : TODO : darkness
176        canvas.setFillColorRGB(r, g, b)
177        canvas.drawString(x + 0.5*cm, y, value)
178        canvas.restoreState()
179        return (size + 4)
180   
181    def genPDF(self, pagesize, logo, url, darkness) :
182        """Generates the banner in PDF format, return the PDF document as a string."""
183       
184        document = cStringIO.StringIO()
185        c = canvas.Canvas(document, pagesize=pagesize, pageCompression=1)
186       
187        c.setAuthor("Jerome Alet")
188        c.setTitle("PyKota generated Banner")
189        c.setSubject("This is a print banner generated with PyKota")
190       
191        xcenter = pagesize[0] / 2.0
192        ycenter = pagesize[1] / 2.0
193                   
194        ypos = pagesize[1] - (2 * cm)           
195       
196        if logo :
197            try :   
198                imglogo = PIL.Image.open(logo)
199            except :   
200                self.printInfo("Unable to open image %s" % logo, "warn")
201            else :
202                (width, height) = imglogo.size
203                multi = float(width) / (8 * cm) 
204                width = float(width) / multi
205                height = float(height) / multi
206                xpos = xcenter - (width / 2.0)
207                ypos -= height
208                c.drawImage(logo, xpos, ypos, width, height)
209       
210        # New top
211        xpos = pagesize[0] / 5.0
212        ypos -= (1 * cm) + 20
213       
214        printername = self.getVar("PYKOTAPRINTERNAME")
215        username = self.getVar("PYKOTAUSERNAME")
216        accountbanner = self.config.getAccountBanner(printername)
217       
218        # Outputs the username
219        ypos -= self.printVar(c, xcenter, ypos, _("Username"), username, 20, darkness) 
220       
221        # Printer and Job Id
222        job = "%s - %s" % (printername, self.getVar("PYKOTAJOBID"))
223        ypos -= self.printVar(c, xcenter, ypos, _("Job"), job, 14, darkness) 
224       
225        # Current date (TODO : at the time the banner was printed ! Change this to job's submission date)
226        datetime = time.strftime("%c", time.localtime())
227        ypos -= self.printVar(c, xcenter, ypos, _("Date"), datetime, 14, darkness) 
228       
229        # Result of the print job
230        action = self.getVar("PYKOTAACTION")
231        if action == "ALLOW" :
232            action = _("Allowed")
233        elif action == "DENY" :   
234            action = _("Denied")
235        elif action == "WARN" :   
236            action = _("Allowed with Warning")
237        ypos -= self.printVar(c, xcenter, ypos, _("Result"), action, 14, darkness) 
238       
239        # skip some space
240        ypos -= 20
241       
242        # Outputs title and filename
243        title = self.getVar("PYKOTATITLE")
244        ypos -= self.printVar(c, xcenter, ypos, _("Title"), title, 10, darkness) 
245       
246        filename = self.getVar("PYKOTAFILENAME")
247        ypos -= self.printVar(c, xcenter, ypos, _("Filename"), filename, 10, darkness) 
248       
249        # skip some space
250        ypos -= 20
251       
252        # Now outputs the user's account balance or page counter
253        ypos -= self.printVar(c, xcenter, ypos, _("Pages printed on %s") % printername, self.getVar("PYKOTAPAGECOUNTER"), 14, darkness) 
254        limitby = self.getVar("PYKOTALIMITBY")
255        if limitby == "balance" : 
256            ypos -= self.printVar(c, xcenter, ypos, _("Account balance"), self.getVar("PYKOTABALANCE"), 14, darkness) 
257        else :
258            ypos -= self.printVar(c, xcenter, ypos, _("Soft Limit"), self.getVar("PYKOTASOFTLIMIT"), 14, darkness) 
259            ypos -= self.printVar(c, xcenter, ypos, _("Hard Limit"), self.getVar("PYKOTAHARDLIMIT"), 14, darkness) 
260            ypos -= self.printVar(c, xcenter, ypos, _("Date Limit"), self.getVar("PYKOTADATELIMIT"), 14, darkness) 
261           
262        # URL
263        if url :
264            c.saveState()
265            c.setFont("Courier-Bold", 16)
266            (r, g, b) = (0, 0, 1)   # Blue : TODO : darkness
267            c.setFillColorRGB(r, g, b)
268            c.drawCentredString(xcenter, 2 * cm, url)
269            c.restoreState()
270       
271        c.showPage()
272        c.save()
273        return document.getvalue()
274       
275    def main(self, files, options) :
276        """Generates a banner."""
277        if not hasRL :
278            raise PyKotaToolError, "The ReportLab module is missing. Download it from http://www.reportlab.org"
279        if not hasPIL :
280            raise PyKotaToolError, "The Python Imaging Library is missing. Download it from http://www.pythonware.com/downloads"
281           
282        try :
283            darkness = int(options["darkness"])
284            if (darkness <= 0) or (darkness > 100) :
285                raise ValueError, "Allowed range is (1..100)"
286            darkness /= 100.0   
287        except (TypeError, ValueError), msg :
288            self.printInfo("Invalid darkness value %s : %s" % (options["darkness"], msg), "warn")
289            darkness = 1.0
290           
291        pagesize = self.getPageSize(options["pagesize"])
292        if pagesize is None :
293            pagesize = self.getPageSize("a4")
294            self.printInfo("Unknown page size %s, defaulting to A4." % options["pagesize"], "warn")
295           
296        self.logdebug("Generating the banner in PDF format...")   
297        doc = self.genPDF(pagesize, options["logo"].strip(), options["url"].strip(), darkness)
298       
299        self.logdebug("Converting the banner to PostScript...")   
300        os.environ["PATH"] = "%s:/bin:/usr/bin:/usr/local/bin:/opt/bin:/sbin:/usr/sbin" % os.environ.get("PATH", "")
301        child = popen2.Popen3("gs -q -dNOPAUSE -dBATCH -dPARANOIDSAFER -sDEVICE=pswrite -sOutputFile=- - 2>/tmp/errgs")
302        child.tochild.write(doc)
303        child.tochild.close()
304        sys.stdout.write(child.fromchild.read())
305        sys.stdout.flush()
306        child.fromchild.close()
307        status = child.wait()
308        if os.WIFEXITED(status) :
309            status = os.WEXITSTATUS(status)
310        self.logdebug("PDF to PostScript converter exit code is %s" % str(status))
311        self.logdebug("Banner completed.")
312        return status
313
314def getInfo(name) :
315    """Extracts some information from the environment."""
316    return os.environ.get(name, _("Unknown"))
317
318if __name__ == "__main__" :
319    # TODO : --papertray : to print banners on a different paper (colored for example)
320    retcode = 0
321    try :
322        defaults = { \
323                     "darkness" : "100", \
324                     "pagesize" : "a4", \
325                     "logo" : "/usr/share/pykota/logos/pykota.jpeg",
326                     "url" : "http://www.librelogiciel.com/software/",
327                   }
328        short_options = "vhd:l:p:u:"
329        long_options = ["help", "version", "darkness=", "pagesize=", "logo=", "url="]
330       
331        # Initializes the command line tool
332        banner = PyKotaBanner(doc=__doc__)
333       
334        # parse and checks the command line
335        (options, args) = banner.parseCommandline(sys.argv[1:], short_options, long_options, allownothing=1)
336       
337        # sets long options
338        options["help"] = options["h"] or options["help"]
339        options["version"] = options["v"] or options["version"]
340        options["darkness"] = options["d"] or options["darkness"] or defaults["darkness"]
341        options["pagesize"] = options["p"] or options["pagesize"] or defaults["pagesize"]
342        options["logo"] = options["l"] or options["logo"] or defaults["logo"]
343        options["url"] = options["u"] or options["url"] or defaults["url"]
344       
345        if options["help"] :
346            banner.display_usage_and_quit()
347        elif options["version"] :
348            banner.display_version_and_quit()
349        else :
350            retcode = banner.main(args, options)
351    except SystemExit :       
352        pass
353    except :
354        try :
355            banner.crashed("pkbanner failed")
356        except :   
357            crashed("pkbanner failed")
358        retcode = -1
359       
360    sys.exit(retcode)   
Note: See TracBrowser for help on using the browser.