root / pykota / trunk / bin / pkbanner @ 3305

Revision 3305, 12.2 kB (checked in by jerome, 16 years ago)

Now pkbanner uses the new command line parser. A bit rough around
the edges, and requires some refactoring, but it seems to work fine.

  • 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: UTF-8 -*-
3#
4# PyKota : Print Quotas for CUPS
5#
6# (c) 2003, 2004, 2005, 2006, 2007, 2008 Jerome Alet <alet@librelogiciel.com>
7# This program is free software: you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation, either version 3 of the License, or
10# (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program.  If not, see <http://www.gnu.org/licenses/>.
19#
20# $Id$
21#
22#
23
24"""A banner generator for PyKota"""       
25
26import sys
27import os
28import time
29import cStringIO
30import popen2
31
32try :
33    from reportlab.pdfgen import canvas
34    from reportlab.lib import pagesizes
35    from reportlab.lib.units import cm
36except ImportError :   
37    hasRL = False
38else :   
39    hasRL = True
40   
41try :
42    import PIL.Image 
43except ImportError :   
44    hasPIL = False
45else :   
46    hasPIL = True
47   
48import pykota.appinit
49from pykota.utils import *
50from pykota.commandline import parser
51
52from pykota.errors import PyKotaToolError, PyKotaCommandLineError
53from pykota.tool import Tool
54from pykota import version
55
56class PyKotaBanner(Tool) :       
57    """A class for pkbanner."""
58    def getPageSize(self, pgsize) :
59        """Returns the correct page size or None if not found."""
60        try :
61            return getattr(pagesizes, pgsize.upper())
62        except AttributeError :   
63            try :
64                return getattr(pagesizes, pgsize.lower())
65            except AttributeError :
66                pass
67               
68    def getVar(self, varname) :           
69        """Extracts a variable from the environment and returns its value or 'Unknown' in the current locale."""
70        return os.environ.get(varname) or _("Unknown")
71       
72    def printVar(self, canvas, x, y, label, value, size, savetoner) :
73        """Outputs a variable onto the PDF canvas.
74       
75           Returns the number of points to substract to current Y coordinate.
76        """   
77        canvas.saveState()
78        canvas.setFont("Helvetica-Bold", size)
79        (r, g, b) =  [ color + (savetoner * (1.0 - color)) for color in (0, 0, 0) ] # Black * savetoner
80        canvas.setFillColorRGB(r, g, b)
81        message = "%s :" % _(label)
82        canvas.drawRightString(x, y, message)
83        canvas.setFont("Courier-Bold", size)
84        (r, g, b) =  [ color + (savetoner * (1.0 - color)) for color in (1, 0, 0) ] # Red * savetoner
85        canvas.setFillColorRGB(r, g, b)
86        canvas.drawString(x + 0.5*cm, y, value)
87        canvas.restoreState()
88        return (size + 4)
89   
90    def genPDF(self, pagesize, logo, url, text, savetoner) :
91        """Generates the banner in PDF format, return the PDF document as a string."""
92        document = cStringIO.StringIO()
93        c = canvas.Canvas(document, pagesize=pagesize, pageCompression=1)
94       
95        c.setAuthor(self.effectiveUserName)
96        c.setTitle(_("PyKota generated Banner"))
97        c.setSubject(_("This is a print banner generated with PyKota"))
98       
99        xcenter = pagesize[0] / 2.0
100        ycenter = pagesize[1] / 2.0
101                   
102        ypos = pagesize[1] - (2 * cm)           
103       
104        if logo :
105            try :   
106                imglogo = PIL.Image.open(logo)
107            except :   
108                self.printInfo("Unable to open image %s" % logo, "warn")
109            else :
110                (width, height) = imglogo.size
111                multi = float(width) / (8 * cm) 
112                width = float(width) / multi
113                height = float(height) / multi
114                xpos = xcenter - (width / 2.0)
115                ypos -= height
116                c.drawImage(logo, xpos, ypos, width, height)
117       
118        # New top
119        xpos = pagesize[0] / 5.0
120        ypos -= (1 * cm) + 20
121       
122        printername = self.getVar("PYKOTAPRINTERNAME")
123        username = self.getVar("PYKOTAUSERNAME")
124        accountbanner = self.config.getAccountBanner(printername)
125       
126        # Outputs the username
127        ypos -= self.printVar(c, xcenter, ypos, _("Username"), username, 20, savetoner) 
128       
129        # Text   
130        if text :
131            ypos -= self.printVar(c, xcenter, ypos, _("More Info"), text, 20, savetoner) 
132       
133        # Printer and Job Id
134        job = "%s - %s" % (printername, self.getVar("PYKOTAJOBID"))
135        ypos -= self.printVar(c, xcenter, ypos, _("Job"), job, 14, savetoner) 
136       
137        # Current date (TODO : at the time the banner was printed ! Change this to job's submission date)
138        datetime = time.strftime("%c", time.localtime())
139        ypos -= self.printVar(c, xcenter, ypos, _("Date"), datetime, 14, savetoner) 
140       
141        # Result of the print job
142        action = self.getVar("PYKOTAACTION")
143        if action == "ALLOW" :
144            action = _("Allowed")
145        elif action == "DENY" :   
146            action = _("Denied")
147        elif action == "WARN" :   
148            action = _("Allowed with Warning")
149        elif action == "PROBLEM" :   
150            # should never occur
151            action = _("Problem")
152        elif action == "CANCEL" :   
153            # should never occur
154            action = _("Cancelled")
155        ypos -= self.printVar(c, xcenter, ypos, _("Result"), action, 14, savetoner) 
156       
157        # skip some space
158        ypos -= 20
159       
160        # Outputs title and filename
161        # We put them at x=0.25*pagewidth so that the line is long enough to hold them
162        title = self.getVar("PYKOTATITLE")
163        ypos -= self.printVar(c, xcenter / 2.0, ypos, _("Title"), title, 10, savetoner) 
164       
165        filename = self.getVar("PYKOTAFILENAME")
166        ypos -= self.printVar(c, xcenter / 2.0, ypos, _("Filename"), filename, 10, savetoner) 
167       
168        # skip some space
169        ypos -= 20
170       
171        # Now outputs the user's account balance or page counter
172        ypos -= self.printVar(c, xcenter, ypos, _("Pages printed so far on %s") % printername, self.getVar("PYKOTAPAGECOUNTER"), 14, savetoner) 
173        limitby = self.getVar("PYKOTALIMITBY")
174        if limitby == "balance" : 
175            ypos -= self.printVar(c, xcenter, ypos, _("Account balance"), self.getVar("PYKOTABALANCE"), 14, savetoner) 
176        elif limitby == "quota" :
177            ypos -= self.printVar(c, xcenter, ypos, _("Soft Limit"), self.getVar("PYKOTASOFTLIMIT"), 14, savetoner) 
178            ypos -= self.printVar(c, xcenter, ypos, _("Hard Limit"), self.getVar("PYKOTAHARDLIMIT"), 14, savetoner) 
179            ypos -= self.printVar(c, xcenter, ypos, _("Date Limit"), self.getVar("PYKOTADATELIMIT"), 14, savetoner) 
180        else :
181            if limitby == "noquota" :
182                msg = _("No Limit")
183            elif limitby == "nochange" :   
184                msg = _("No Accounting")
185            elif limitby == "noprint" :   
186                msg = _("Forbidden")
187            else :   
188                msg = _("Unknown")
189            ypos -= self.printVar(c, xcenter, ypos, _("Printing Mode"), msg, 14, savetoner)
190           
191        # URL
192        if url :
193            c.saveState()
194            c.setFont("Courier-Bold", 16)
195            (r, g, b) =  [ color + (savetoner * (1.0 - color)) for color in (0, 0, 1) ] # Blue * savetoner
196            c.setFillColorRGB(r, g, b)
197            c.drawCentredString(xcenter, 2 * cm, url)
198            c.restoreState()
199       
200        c.showPage()
201        c.save()
202        return document.getvalue()
203       
204    def main(self, arguments, options) :
205        """Generates a banner."""
206        if not hasRL :
207            raise PyKotaToolError, "The ReportLab module is missing. Download it from http://www.reportlab.org"
208        if not hasPIL :
209            raise PyKotaToolError, "The Python Imaging Library is missing. Download it from http://www.pythonware.com/downloads"
210           
211        try :
212            if (options.savetoner < 0) or (options.savetoner > 99) :
213                raise ValueError, _("Allowed range is (0..99)")
214        except ValueError, msg :
215            self.printInfo(_("Invalid 'savetoner' option %s : %s") % (options.savetoner, msg), "warn")
216            savetoner = 0.0
217        else :   
218            savetoner = options.savetoner / 100.0   
219           
220        pagesize = self.getPageSize(options.pagesize)
221        if pagesize is None :
222            pagesize = self.getPageSize("a4")
223            self.printInfo(_("Invalid 'pagesize' option %s, defaulting to A4.") % options.pagesize, "warn")
224           
225        self.logdebug("Generating the banner in PDF format...")   
226        doc = self.genPDF(pagesize, 
227                          options.logo.strip().encode(sys.getfilesystemencoding(), "replace"), 
228                          options.url.strip(), 
229                          " ".join(arguments).strip(), 
230                          savetoner)
231       
232        self.logdebug("Converting the banner to PostScript...")   
233        os.environ["PATH"] = "%s:/bin:/usr/bin:/usr/local/bin:/opt/bin:/sbin:/usr/sbin" % os.environ.get("PATH", "")
234        child = popen2.Popen3("gs -q -dNOPAUSE -dBATCH -dPARANOIDSAFER -sDEVICE=pswrite -sOutputFile=- -")
235        try :
236            child.tochild.write(doc)
237            child.tochild.close()
238            sys.stdout.write(child.fromchild.read())
239            sys.stdout.flush()
240            child.fromchild.close()
241        except IOError, msg :   
242            self.printInfo("I/O Error %s" % msg, "error")
243        status = child.wait()
244        if os.WIFEXITED(status) :
245            status = os.WEXITSTATUS(status)
246        self.logdebug("PDF to PostScript converter exit code is %s" % str(status))
247        self.logdebug("Banner completed.")
248        return status
249
250if __name__ == "__main__" :
251    # TODO : --papertray : to print banners on a different paper (colored for example)
252    parser = parser.PyKotaOptionParser(description=_("Banner generator for PyKota"))
253    parser.add_option("-l", "--logo",
254                            dest="logo",
255                            default=u"/usr/share/pykota/logos/pykota.jpeg",
256                            help=_("The image to use as the banner's logo. The logo will be drawn at the center top of the page. The default logo is %default"))
257    parser.add_option("-p", "--pagesize",
258                            dest="pagesize",
259                            default=u"A4",
260                            help=_("Sets the size of the page. Most well known page sizes are recognized, like 'A4' or 'Letter' to name a few. The default page size is %default"))
261    parser.add_option("-s", "--savetoner",
262                            dest="savetoner",
263                            type="int",
264                            default=0,
265                            help=_("Sets the text luminosity to this percent. This can be used to save toner. The default value is %default, which means that no toner saving will be done."))
266    parser.add_option("-u", "--url",
267                            dest="url",
268                            default=u"http://www.pykota.com",
269                            help=_("Sets the url to write at the bottom of the banner page. The default url is %default"))
270    parser.add_example('--logo="" --savetoner=75',
271                       _("This would generate an A4 banner with no logo, and text luminosity would be increased by 75%%."))
272    (options, arguments) = parser.parse_args()                   
273    if options.version :
274        from pykota import version
275        sys.stdout.write("%s\n" % version.__version__)
276        sys.exit(0)
277   
278    retcode = 0
279    try :
280        # Initializes the command line tool
281        banner = PyKotaBanner()
282        banner.deferredInit()
283        retcode = banner.main(arguments, options)
284    except KeyboardInterrupt :       
285        logerr("\nInterrupted with Ctrl+C !\n")
286        retcode = -3
287    except PyKotaCommandLineError, msg :   
288        logerr("%s : %s\n" % (sys.argv[0], msg))
289        retcode = -2
290    except SystemExit :       
291        pass
292    except :
293        try :
294            banner.crashed("pkbanner failed")
295        except :   
296            crashed("pkbanner failed")
297        retcode = -1
298       
299    sys.exit(retcode)   
Note: See TracBrowser for help on using the browser.