root / pykota / trunk / bin / pkpgcounter @ 1456

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

Doesn't exit anymore on the first nul byte

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