root / pykota / trunk / bin / pkbanner @ 2250

Revision 2216, 13.4 kB (checked in by jerome, 20 years ago)

Now exits with no traceback in case of Ctrl+C

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