root / pykota / trunk / bin / pkbanner @ 2512

Revision 2512, 12.8 kB (checked in by jerome, 19 years ago)

Ensure that human made errors (like incorrect command line options)
don't produce a traceback anymore. No need to frighten users with
such complete tracebacks and email reporting each time they mistype
some command.
Makes pykosd check more carefully the values of its command line options.

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
RevLine 
[1908]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#
[2028]8# (c) 2003, 2004, 2005 Jerome Alet <alet@librelogiciel.com>
[1908]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
[2303]21# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
[1908]22#
23# $Id$
24#
[2028]25#
[1908]26
27import sys
28import os
[1923]29import time
[1911]30import cStringIO
[1918]31import popen2
[1908]32
[1911]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   
[2512]49from pykota.tool import Tool, PyKotaToolError, PyKotaCommandLineError, crashed, N_
[1911]50
[2344]51__doc__ = N_("""pkbanner v%(__version__)s (c) %(__years__)s %(__author__)s
[1911]52
53Generates banners.
54
55command line usage :
56
[2193]57  pkbanner  [options]  [more info]
[1911]58
59options :
60
61  -v | --version       Prints pkbanner's version number then exits.
62  -h | --help          Prints this message then exits.
63 
[1923]64  -l | --logo img      Use the image as the banner's logo. The logo will
[1938]65                       be drawn at the center top of the page. The default
[1923]66                       logo is /usr/share/pykota/logos/pykota.jpeg
67                       
[1934]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 
[1938]72  -s | --savetoner s   Sets the text luminosity factor to s%%. This can be
[1934]73                       used to save toner. The default value is 0, which
74                       means that no toner saving will be done.
75 
[1923]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 
[1911]80examples :                             
81
[1923]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 
[1936]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.
[2344]95""")
[1911]96       
97class PyKotaBanner(Tool) :       
98    """A class for pkbanner."""
99    def getPageSize(self, pgsize) :
100        """Returns the correct page size or None if not found."""
101        try :
102            return getattr(pagesizes, pgsize.upper())
103        except AttributeError :   
104            try :
105                return getattr(pagesizes, pgsize.lower())
106            except AttributeError :
107                pass
108               
[1923]109    def getVar(self, varname) :           
110        """Extracts a variable from the environment and returns its value or 'Unknown' in the current locale."""
111        return os.environ.get(varname) or _("Unknown")
112       
[1934]113    def printVar(self, canvas, x, y, label, value, size, savetoner) :
[1923]114        """Outputs a variable onto the PDF canvas.
115       
116           Returns the number of points to substract to current Y coordinate.
117        """   
118        canvas.saveState()
119        canvas.setFont("Helvetica-Bold", size)
[1934]120        (r, g, b) =  [ color + (savetoner * (1.0 - color)) for color in (0, 0, 0) ] # Black * savetoner
[1923]121        canvas.setFillColorRGB(r, g, b)
[1925]122        message = "%s :" % _(label)
[1923]123        canvas.drawRightString(x, y, message)
124        canvas.setFont("Courier-Bold", size)
[1934]125        (r, g, b) =  [ color + (savetoner * (1.0 - color)) for color in (1, 0, 0) ] # Red * savetoner
[1923]126        canvas.setFillColorRGB(r, g, b)
127        canvas.drawString(x + 0.5*cm, y, value)
128        canvas.restoreState()
129        return (size + 4)
130   
[2193]131    def genPDF(self, pagesize, logo, url, text, savetoner) :
[1911]132        """Generates the banner in PDF format, return the PDF document as a string."""
133        document = cStringIO.StringIO()
134        c = canvas.Canvas(document, pagesize=pagesize, pageCompression=1)
135       
136        c.setAuthor("Jerome Alet")
137        c.setTitle("PyKota generated Banner")
138        c.setSubject("This is a print banner generated with PyKota")
139       
140        xcenter = pagesize[0] / 2.0
141        ycenter = pagesize[1] / 2.0
142                   
143        ypos = pagesize[1] - (2 * cm)           
144       
[1923]145        if logo :
146            try :   
147                imglogo = PIL.Image.open(logo)
148            except :   
149                self.printInfo("Unable to open image %s" % logo, "warn")
150            else :
151                (width, height) = imglogo.size
152                multi = float(width) / (8 * cm) 
153                width = float(width) / multi
154                height = float(height) / multi
155                xpos = xcenter - (width / 2.0)
156                ypos -= height
157                c.drawImage(logo, xpos, ypos, width, height)
158       
[1911]159        # New top
160        xpos = pagesize[0] / 5.0
161        ypos -= (1 * cm) + 20
162       
[1923]163        printername = self.getVar("PYKOTAPRINTERNAME")
164        username = self.getVar("PYKOTAUSERNAME")
165        accountbanner = self.config.getAccountBanner(printername)
166       
167        # Outputs the username
[1934]168        ypos -= self.printVar(c, xcenter, ypos, _("Username"), username, 20, savetoner) 
[1923]169       
[2193]170        # Text   
171        if text :
172            ypos -= self.printVar(c, xcenter, ypos, _("More Info"), text, 20, savetoner) 
173       
[1923]174        # Printer and Job Id
175        job = "%s - %s" % (printername, self.getVar("PYKOTAJOBID"))
[1934]176        ypos -= self.printVar(c, xcenter, ypos, _("Job"), job, 14, savetoner) 
[1923]177       
178        # Current date (TODO : at the time the banner was printed ! Change this to job's submission date)
179        datetime = time.strftime("%c", time.localtime())
[1934]180        ypos -= self.printVar(c, xcenter, ypos, _("Date"), datetime, 14, savetoner) 
[1923]181       
182        # Result of the print job
183        action = self.getVar("PYKOTAACTION")
184        if action == "ALLOW" :
185            action = _("Allowed")
186        elif action == "DENY" :   
187            action = _("Denied")
188        elif action == "WARN" :   
189            action = _("Allowed with Warning")
[1934]190        ypos -= self.printVar(c, xcenter, ypos, _("Result"), action, 14, savetoner) 
[1923]191       
192        # skip some space
193        ypos -= 20
194       
195        # Outputs title and filename
[1926]196        # We put them at x=0.25*pagewidth so that the line is long enough to hold them
[1923]197        title = self.getVar("PYKOTATITLE")
[1934]198        ypos -= self.printVar(c, xcenter / 2.0, ypos, _("Title"), title, 10, savetoner) 
[1923]199       
200        filename = self.getVar("PYKOTAFILENAME")
[1934]201        ypos -= self.printVar(c, xcenter / 2.0, ypos, _("Filename"), filename, 10, savetoner) 
[1923]202       
203        # skip some space
204        ypos -= 20
205       
206        # Now outputs the user's account balance or page counter
[1934]207        ypos -= self.printVar(c, xcenter, ypos, _("Pages printed so far on %s") % printername, self.getVar("PYKOTAPAGECOUNTER"), 14, savetoner) 
[1923]208        limitby = self.getVar("PYKOTALIMITBY")
209        if limitby == "balance" : 
[1934]210            ypos -= self.printVar(c, xcenter, ypos, _("Account balance"), self.getVar("PYKOTABALANCE"), 14, savetoner) 
[1923]211        else :
[1934]212            ypos -= self.printVar(c, xcenter, ypos, _("Soft Limit"), self.getVar("PYKOTASOFTLIMIT"), 14, savetoner) 
213            ypos -= self.printVar(c, xcenter, ypos, _("Hard Limit"), self.getVar("PYKOTAHARDLIMIT"), 14, savetoner) 
214            ypos -= self.printVar(c, xcenter, ypos, _("Date Limit"), self.getVar("PYKOTADATELIMIT"), 14, savetoner) 
[1923]215           
216        # URL
217        if url :
[1911]218            c.saveState()
[1923]219            c.setFont("Courier-Bold", 16)
[1934]220            (r, g, b) =  [ color + (savetoner * (1.0 - color)) for color in (0, 0, 1) ] # Blue * savetoner
[1923]221            c.setFillColorRGB(r, g, b)
222            c.drawCentredString(xcenter, 2 * cm, url)
[1911]223            c.restoreState()
224       
225        c.showPage()
226        c.save()
227        return document.getvalue()
[1923]228       
[2193]229    def main(self, arguments, options) :
[1911]230        """Generates a banner."""
231        if not hasRL :
232            raise PyKotaToolError, "The ReportLab module is missing. Download it from http://www.reportlab.org"
233        if not hasPIL :
234            raise PyKotaToolError, "The Python Imaging Library is missing. Download it from http://www.pythonware.com/downloads"
235           
[1923]236        try :
[1934]237            savetoner = int(options["savetoner"])
238            if (savetoner < 0) or (savetoner > 99) :
239                raise ValueError, _("Allowed range is (0..99)")
240            savetoner /= 100.0   
[1923]241        except (TypeError, ValueError), msg :
[1934]242            self.printInfo(_("Invalid 'savetoner' option %s : %s") % (options["savetoner"], msg), "warn")
243            savetoner = 0.0
[1923]244           
[1911]245        pagesize = self.getPageSize(options["pagesize"])
246        if pagesize is None :
247            pagesize = self.getPageSize("a4")
[1934]248            self.printInfo(_("Invalid 'pagesize' option %s, defaulting to A4.") % options["pagesize"], "warn")
[1911]249           
[1918]250        self.logdebug("Generating the banner in PDF format...")   
[2193]251        doc = self.genPDF(pagesize, 
252                          options["logo"].strip(), 
253                          options["url"].strip(), 
254                          " ".join(arguments).strip(), 
255                          savetoner)
[1918]256       
257        self.logdebug("Converting the banner to PostScript...")   
258        os.environ["PATH"] = "%s:/bin:/usr/bin:/usr/local/bin:/opt/bin:/sbin:/usr/sbin" % os.environ.get("PATH", "")
[2193]259        child = popen2.Popen3("gs -q -dNOPAUSE -dBATCH -dPARANOIDSAFER -sDEVICE=pswrite -sOutputFile=- -")
260        try :
261            child.tochild.write(doc)
262            child.tochild.close()
263            sys.stdout.write(child.fromchild.read())
264            sys.stdout.flush()
265            child.fromchild.close()
266        except IOError, msg :   
267            self.printInfo("I/O Error %s" % msg, "error")
[1918]268        status = child.wait()
269        if os.WIFEXITED(status) :
270            status = os.WEXITSTATUS(status)
271        self.logdebug("PDF to PostScript converter exit code is %s" % str(status))
272        self.logdebug("Banner completed.")
273        return status
[1911]274
[1908]275if __name__ == "__main__" :
[1909]276    # TODO : --papertray : to print banners on a different paper (colored for example)
[1911]277    retcode = 0
278    try :
279        defaults = { \
[1971]280                     "savetoner" : "0", \
[1911]281                     "pagesize" : "a4", \
282                     "logo" : "/usr/share/pykota/logos/pykota.jpeg",
283                     "url" : "http://www.librelogiciel.com/software/",
284                   }
[1934]285        short_options = "vhs:l:p:u:"
286        long_options = ["help", "version", "savetoner=", "pagesize=", "logo=", "url="]
[1911]287       
288        # Initializes the command line tool
289        banner = PyKotaBanner(doc=__doc__)
[2210]290        banner.deferredInit()
[1911]291       
292        # parse and checks the command line
293        (options, args) = banner.parseCommandline(sys.argv[1:], short_options, long_options, allownothing=1)
294       
295        # sets long options
296        options["help"] = options["h"] or options["help"]
297        options["version"] = options["v"] or options["version"]
[1934]298        options["savetoner"] = options["s"] or options["savetoner"] or defaults["savetoner"]
[1911]299        options["pagesize"] = options["p"] or options["pagesize"] or defaults["pagesize"]
300        options["url"] = options["u"] or options["url"] or defaults["url"]
301       
[1934]302        options["logo"] = options["l"] or options["logo"]
303        if options["logo"] is None : # Allows --logo="" to disable the logo entirely
304            options["logo"] = defaults["logo"] 
305       
[1911]306        if options["help"] :
307            banner.display_usage_and_quit()
308        elif options["version"] :
309            banner.display_version_and_quit()
310        else :
311            retcode = banner.main(args, options)
[2216]312    except KeyboardInterrupt :       
313        sys.stderr.write("\nInterrupted with Ctrl+C !\n")
[2512]314    except PyKotaCommandLineError, msg :   
315        sys.stderr.write("%s : %s\n" % (sys.argv[0], msg))
[1911]316    except SystemExit :       
317        pass
318    except :
319        try :
320            banner.crashed("pkbanner failed")
321        except :   
322            crashed("pkbanner failed")
323        retcode = -1
324       
325    sys.exit(retcode)   
Note: See TracBrowser for help on using the browser.