root / pykota / trunk / bin / pkbanner @ 2627

Revision 2627, 13.3 kB (checked in by jerome, 18 years ago)

Now pkbanner handles new print (un-)limiting modes correctly.

  • 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, 2006 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        elif limitby == "quota" :
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        else :
216            if limitby == "noquota" :
217                msg = _("No Limit")
218            elif limitby == "nochange" :   
219                msg = _("No Accounting")
220            elif limitby == "noprint" :   
221                msg = _("Forbidden")
222            else :   
223                msg = _("Unknown")
224            ypos -= self.printVar(c, xcenter, ypos, _("Printing Mode"), msg, 14, savetoner)
225           
226        # URL
227        if url :
228            c.saveState()
229            c.setFont("Courier-Bold", 16)
230            (r, g, b) =  [ color + (savetoner * (1.0 - color)) for color in (0, 0, 1) ] # Blue * savetoner
231            c.setFillColorRGB(r, g, b)
232            c.drawCentredString(xcenter, 2 * cm, url)
233            c.restoreState()
234       
235        c.showPage()
236        c.save()
237        return document.getvalue()
238       
239    def main(self, arguments, options) :
240        """Generates a banner."""
241        if not hasRL :
242            raise PyKotaToolError, "The ReportLab module is missing. Download it from http://www.reportlab.org"
243        if not hasPIL :
244            raise PyKotaToolError, "The Python Imaging Library is missing. Download it from http://www.pythonware.com/downloads"
245           
246        try :
247            savetoner = int(options["savetoner"])
248            if (savetoner < 0) or (savetoner > 99) :
249                raise ValueError, _("Allowed range is (0..99)")
250            savetoner /= 100.0   
251        except (TypeError, ValueError), msg :
252            self.printInfo(_("Invalid 'savetoner' option %s : %s") % (options["savetoner"], msg), "warn")
253            savetoner = 0.0
254           
255        pagesize = self.getPageSize(options["pagesize"])
256        if pagesize is None :
257            pagesize = self.getPageSize("a4")
258            self.printInfo(_("Invalid 'pagesize' option %s, defaulting to A4.") % options["pagesize"], "warn")
259           
260        self.logdebug("Generating the banner in PDF format...")   
261        doc = self.genPDF(pagesize, 
262                          options["logo"].strip(), 
263                          options["url"].strip(), 
264                          " ".join(arguments).strip(), 
265                          savetoner)
266       
267        self.logdebug("Converting the banner to PostScript...")   
268        os.environ["PATH"] = "%s:/bin:/usr/bin:/usr/local/bin:/opt/bin:/sbin:/usr/sbin" % os.environ.get("PATH", "")
269        child = popen2.Popen3("gs -q -dNOPAUSE -dBATCH -dPARANOIDSAFER -sDEVICE=pswrite -sOutputFile=- -")
270        try :
271            child.tochild.write(doc)
272            child.tochild.close()
273            sys.stdout.write(child.fromchild.read())
274            sys.stdout.flush()
275            child.fromchild.close()
276        except IOError, msg :   
277            self.printInfo("I/O Error %s" % msg, "error")
278        status = child.wait()
279        if os.WIFEXITED(status) :
280            status = os.WEXITSTATUS(status)
281        self.logdebug("PDF to PostScript converter exit code is %s" % str(status))
282        self.logdebug("Banner completed.")
283        return status
284
285if __name__ == "__main__" :
286    # TODO : --papertray : to print banners on a different paper (colored for example)
287    retcode = 0
288    try :
289        defaults = { \
290                     "savetoner" : "0", \
291                     "pagesize" : "a4", \
292                     "logo" : "/usr/share/pykota/logos/pykota.jpeg",
293                     "url" : "http://www.librelogiciel.com/software/",
294                   }
295        short_options = "vhs:l:p:u:"
296        long_options = ["help", "version", "savetoner=", "pagesize=", "logo=", "url="]
297       
298        # Initializes the command line tool
299        banner = PyKotaBanner(doc=__doc__)
300        banner.deferredInit()
301       
302        # parse and checks the command line
303        (options, args) = banner.parseCommandline(sys.argv[1:], short_options, long_options, allownothing=1)
304       
305        # sets long options
306        options["help"] = options["h"] or options["help"]
307        options["version"] = options["v"] or options["version"]
308        options["savetoner"] = options["s"] or options["savetoner"] or defaults["savetoner"]
309        options["pagesize"] = options["p"] or options["pagesize"] or defaults["pagesize"]
310        options["url"] = options["u"] or options["url"] or defaults["url"]
311       
312        options["logo"] = options["l"] or options["logo"]
313        if options["logo"] is None : # Allows --logo="" to disable the logo entirely
314            options["logo"] = defaults["logo"] 
315       
316        if options["help"] :
317            banner.display_usage_and_quit()
318        elif options["version"] :
319            banner.display_version_and_quit()
320        else :
321            retcode = banner.main(args, options)
322    except KeyboardInterrupt :       
323        sys.stderr.write("\nInterrupted with Ctrl+C !\n")
324        retcode = -3
325    except PyKotaCommandLineError, msg :   
326        sys.stderr.write("%s : %s\n" % (sys.argv[0], msg))
327        retcode = -2
328    except SystemExit :       
329        pass
330    except :
331        try :
332            banner.crashed("pkbanner failed")
333        except :   
334            crashed("pkbanner failed")
335        retcode = -1
336       
337    sys.exit(retcode)   
Note: See TracBrowser for help on using the browser.