root / pykota / trunk / bin / pkbanner @ 2515

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
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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, PyKotaCommandLineError, crashed, N_
50
51__doc__ = N_("""pkbanner v%(__version__)s (c) %(__years__)s %(__author__)s
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""")
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               
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       
113    def printVar(self, canvas, x, y, label, value, size, savetoner) :
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)
120        (r, g, b) =  [ color + (savetoner * (1.0 - color)) for color in (0, 0, 0) ] # Black * savetoner
121        canvas.setFillColorRGB(r, g, b)
122        message = "%s :" % _(label)
123        canvas.drawRightString(x, y, message)
124        canvas.setFont("Courier-Bold", size)
125        (r, g, b) =  [ color + (savetoner * (1.0 - color)) for color in (1, 0, 0) ] # Red * savetoner
126        canvas.setFillColorRGB(r, g, b)
127        canvas.drawString(x + 0.5*cm, y, value)
128        canvas.restoreState()
129        return (size + 4)
130   
131    def genPDF(self, pagesize, logo, url, text, savetoner) :
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       
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       
159        # New top
160        xpos = pagesize[0] / 5.0
161        ypos -= (1 * cm) + 20
162       
163        printername = self.getVar("PYKOTAPRINTERNAME")
164        username = self.getVar("PYKOTAUSERNAME")
165        accountbanner = self.config.getAccountBanner(printername)
166       
167        # Outputs the username
168        ypos -= self.printVar(c, xcenter, ypos, _("Username"), username, 20, savetoner) 
169       
170        # Text   
171        if text :
172            ypos -= self.printVar(c, xcenter, ypos, _("More Info"), text, 20, savetoner) 
173       
174        # Printer and Job Id
175        job = "%s - %s" % (printername, self.getVar("PYKOTAJOBID"))
176        ypos -= self.printVar(c, xcenter, ypos, _("Job"), job, 14, savetoner) 
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())
180        ypos -= self.printVar(c, xcenter, ypos, _("Date"), datetime, 14, savetoner) 
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")
190        ypos -= self.printVar(c, xcenter, ypos, _("Result"), action, 14, savetoner) 
191       
192        # skip some space
193        ypos -= 20
194       
195        # Outputs title and filename
196        # We put them at x=0.25*pagewidth so that the line is long enough to hold them
197        title = self.getVar("PYKOTATITLE")
198        ypos -= self.printVar(c, xcenter / 2.0, ypos, _("Title"), title, 10, savetoner) 
199       
200        filename = self.getVar("PYKOTAFILENAME")
201        ypos -= self.printVar(c, xcenter / 2.0, ypos, _("Filename"), filename, 10, savetoner) 
202       
203        # skip some space
204        ypos -= 20
205       
206        # Now outputs the user's account balance or page counter
207        ypos -= self.printVar(c, xcenter, ypos, _("Pages printed so far on %s") % printername, self.getVar("PYKOTAPAGECOUNTER"), 14, savetoner) 
208        limitby = self.getVar("PYKOTALIMITBY")
209        if limitby == "balance" : 
210            ypos -= self.printVar(c, xcenter, ypos, _("Account balance"), self.getVar("PYKOTABALANCE"), 14, savetoner) 
211        else :
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) 
215           
216        # URL
217        if url :
218            c.saveState()
219            c.setFont("Courier-Bold", 16)
220            (r, g, b) =  [ color + (savetoner * (1.0 - color)) for color in (0, 0, 1) ] # Blue * savetoner
221            c.setFillColorRGB(r, g, b)
222            c.drawCentredString(xcenter, 2 * cm, url)
223            c.restoreState()
224       
225        c.showPage()
226        c.save()
227        return document.getvalue()
228       
229    def main(self, arguments, options) :
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           
236        try :
237            savetoner = int(options["savetoner"])
238            if (savetoner < 0) or (savetoner > 99) :
239                raise ValueError, _("Allowed range is (0..99)")
240            savetoner /= 100.0   
241        except (TypeError, ValueError), msg :
242            self.printInfo(_("Invalid 'savetoner' option %s : %s") % (options["savetoner"], msg), "warn")
243            savetoner = 0.0
244           
245        pagesize = self.getPageSize(options["pagesize"])
246        if pagesize is None :
247            pagesize = self.getPageSize("a4")
248            self.printInfo(_("Invalid 'pagesize' option %s, defaulting to A4.") % options["pagesize"], "warn")
249           
250        self.logdebug("Generating the banner in PDF format...")   
251        doc = self.genPDF(pagesize, 
252                          options["logo"].strip(), 
253                          options["url"].strip(), 
254                          " ".join(arguments).strip(), 
255                          savetoner)
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", "")
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")
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
274
275if __name__ == "__main__" :
276    # TODO : --papertray : to print banners on a different paper (colored for example)
277    retcode = 0
278    try :
279        defaults = { \
280                     "savetoner" : "0", \
281                     "pagesize" : "a4", \
282                     "logo" : "/usr/share/pykota/logos/pykota.jpeg",
283                     "url" : "http://www.librelogiciel.com/software/",
284                   }
285        short_options = "vhs:l:p:u:"
286        long_options = ["help", "version", "savetoner=", "pagesize=", "logo=", "url="]
287       
288        # Initializes the command line tool
289        banner = PyKotaBanner(doc=__doc__)
290        banner.deferredInit()
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"]
298        options["savetoner"] = options["s"] or options["savetoner"] or defaults["savetoner"]
299        options["pagesize"] = options["p"] or options["pagesize"] or defaults["pagesize"]
300        options["url"] = options["u"] or options["url"] or defaults["url"]
301       
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       
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)
312    except KeyboardInterrupt :       
313        sys.stderr.write("\nInterrupted with Ctrl+C !\n")
314    except PyKotaCommandLineError, msg :   
315        sys.stderr.write("%s : %s\n" % (sys.argv[0], msg))
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.