root / pykota / trunk / bin / pkbanner @ 2703

Revision 2631, 13.5 kB (checked in by jerome, 19 years ago)

Added support for the CANCEL command in subprocesses launched from the
overwrite_jobticket directive : this will allow end users to be asked
if they really want to proceed to printing once the new version
of pykoticon will be ready.
Several minor fixes.

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