root / pykota / trunk / bin / pkpgcounter @ 1451

Revision 1451, 7.4 kB (checked in by jalet, 20 years ago)

pkpgcounter : comments
pkprinters : when --add is used, existing printers are now skipped.

  • 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# pkpgcounter, a smart software page counter
5#
6# PyKota - Print Quotas for CUPS and LPRng
7#
8# (c) 2003-2004 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
22#
23# $Id$
24#
25# $Log$
26# Revision 1.5  2004/05/06 12:37:29  jalet
27# pkpgcounter : comments
28# pkprinters : when --add is used, existing printers are now skipped.
29#
30# Revision 1.4  2004/05/04 12:21:55  jalet
31# Now uses mmap in PCL mode
32#
33# Revision 1.3  2004/05/04 04:39:26  jalet
34# Better PCL support
35#
36# Revision 1.2  2004/05/04 03:14:26  jalet
37# fixed copy&paste problem in pkpgcounter
38#
39# Revision 1.1  2004/04/08 17:07:42  jalet
40# pkpgcounter added
41#
42#
43
44import sys
45import os
46import mmap
47import tempfile
48
49def ispostscript(data) :   
50    """Returns 1 if data is PostScript, else 0."""
51    if data.startswith("%!") or \
52       data.startswith("\004%!") or \
53       data.startswith("\033%-12345X%!PS") or \
54       ((data[:128].find("\033%-12345X") != -1) and \
55         ((data.find("LANGUAGE=POSTSCRIPT") != -1) or \
56          (data.find("LANGUAGE = POSTSCRIPT") != -1) or \
57          (data.find("LANGUAGE = Postscript") != -1))) :
58        return 1
59    else :   
60        return 0
61   
62def postscript(infile) :
63    """Count pages in a PostScript document."""
64    pagecount = 0
65    pagenum = None
66    while 1 :
67        line = infile.readline()
68        if not line :
69            break
70        if line.startswith("%%Page: ") :
71            pagecount += 1
72    return pagecount
73   
74def ispcl(data) :   
75    """Returns 1 if data is PCL, else 0."""
76    if data.startswith("\033E\033") or \
77       ((data[:128].find("\033%-12345X") != -1) and \
78         ((data.find("LANGUAGE=PCL") != -1) or \
79          (data.find("LANGUAGE = PCL") != -1) or \
80          (data.find("LANGUAGE = Pcl") != -1))) :
81        return 1
82    else :   
83        return 0
84   
85def pcl(infile) :   
86    """Count pages in a PostScript document."""
87    #
88    # Algorithm from pclcount
89    # (c) 2003, by Eduardo Gielamo Oliveira & Rodolfo Broco Manin
90    # published under the terms of the GNU General Public Licence v2.
91    #
92    # Backported from C to Python by Jerome Alet, then enhanced
93    # with more PCL tags detected. I think all the necessary PCL tags
94    # are recognized to correctly handle PCL5 files wrt their number
95    # of pages. The documentation used for this was :
96    #
97    # HP PCL/PJL Reference Set
98    # PCL5 Printer Language Technical Quick Reference Guide
99    # http://h20000.www2.hp.com/bc/docs/support/SupportManual/bpl13205/bpl13205.pdf
100    #
101    infileno = infile.fileno()
102    infile = mmap.mmap(infileno, os.fstat(infileno).st_size, access=mmap.ACCESS_READ)
103    tagsends = { "&n" : "W", 
104                 "&b" : "W", 
105                 "*i" : "W", 
106                 "*l" : "W", 
107                 "*m" : "W", 
108                 "*v" : "W", 
109                 "*c" : "W", 
110                 "(f" : "W", 
111                 "*b" : "VW",
112                 "(s" : "W", 
113                 ")s" : "W", 
114                 "&p" : "X", 
115                 "&l" : "X" } 
116    copies = 1
117    pagecount = 0
118    tag = None
119    position = 0
120    while 1 :
121        try :
122            char = infile[position]
123        except IndexError :     # EOF   
124            break
125        position += 1
126        if not char :
127            break
128        if char == "\014" :   
129            pagecount += 1
130        elif char == "\033" :   
131            #
132            #     <ESC>*b###W -> Start of a raster data row/block
133            #     <ESC>*b###V -> Start of a raster data plane
134            #     <ESC>*c###W -> Start of a user defined pattern
135            #     <ESC>*i###W -> Start of a viewing illuminant block
136            #     <ESC>*l###W -> Start of a color lookup table
137            #     <ESC>*m###W -> Start of a download dither matrix block
138            #     <ESC>*v###W -> Start of a configure image data block
139            #     <ESC>(s###W -> Start of a characters description block
140            #     <ESC>)s###W -> Start of a fonts description block
141            #     <ESC>(f###W -> Start of a symbol set block
142            #     <ESC>&b###W -> Start of configuration data block
143            #     <ESC>&l###X -> Number of copies
144            #     <ESC>&n###W -> Starts an alphanumeric string ID block
145            #     <ESC>&p###X -> Start of a non printable characters block
146            #
147            tagstart = infile[position]
148            position += 1
149            if tagstart in "E9=YZ" : # one byte PCL tag
150                continue             # skip to next tag
151            tag = tagstart + infile[position]
152            position += 1
153            try :
154                tagend = tagsends[tag]
155            except KeyError :   
156                pass    # Unsupported PCL tag
157            else :   
158                # Now read the numeric argument
159                size = 0
160                while 1 :
161                    char = infile[position]
162                    position += 1
163                    if not char.isdigit() :
164                        break
165                    size = (size * 10) + int(char)   
166                if char in tagend :   
167                    if tag == "&l" :
168                        copies = size
169                    else :   
170                        # doing a read will prevent the seek
171                        # for unseekable streams.
172                        # we just ignore the block anyway.
173                        if tag == "&n" : 
174                            # we have to take care of the operation id byte
175                            # which is before the string itself
176                            size += 1
177                        position += size   
178    return copies * pagecount       
179
180def smartpagecounter(filename) :
181    """Autodetects file format and returns number of pages."""
182    if filename == "-" :
183        # we must read from stdin
184        # but since stdin is not seekable, we have to use a temporary
185        # file instead.
186        infile = tempfile.TemporaryFile()
187        while 1 :
188            data = sys.stdin.read(256 * 1024) 
189            if not data :
190                break
191            infile.write(data)
192        infile.flush()   
193        infile.seek(0)
194    else :   
195        # normal file
196        infile = open(filename, "rb")
197       
198    # Try to detect file type by reading first block of datas   
199    firstblock = infile.read(1024)
200    infile.seek(0)
201    if ispostscript(firstblock) :
202        size = postscript(infile)
203    elif ispcl(firstblock) :   
204        size = pcl(infile)
205    else :   
206        sys.stderr.write("ERROR : Unknown file format for %s\n" % filename)
207        size = 0
208    infile.close()   
209    return size
210   
211if __name__ == "__main__" :   
212    if len(sys.argv) < 2 :
213        sys.argv.append("-")
214       
215    totalsize = 0   
216    for arg in sys.argv[1:] :
217        totalsize += smartpagecounter(arg)
218    print "%s" % totalsize
Note: See TracBrowser for help on using the browser.