root / pkpgcounter / trunk / pdlanalyzer / pdlparser.py @ 199

Revision 199, 9.5 kB (checked in by jerome, 19 years ago)

More work done on moving stuff around

  • Property svn:eol-style set to native
  • Property svn:keywords set to Auth Date Id Rev
Line 
1#
2# pkpgcounter : a generic Page Description Language parser
3#
4# (c) 2003,2004,2005 Jerome Alet <alet@librelogiciel.com>
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
18#
19# $Id$
20#
21
22import sys
23import tempfile
24
25from pdlanalyzer.postscript import PostScriptParser
26from pdlanalyzer.pdf import PDFParser
27from pdlanalyzer.pcl345 import PCL345Parser
28from pdlanalyzer.pclxl import PCLXLParser
29from pdlanalyzer.escp2 import ESCP2Parser
30
31KILOBYTE = 1024   
32MEGABYTE = 1024 * KILOBYTE   
33LASTBLOCKSIZE = int(KILOBYTE / 4)
34
35class PDLParserError(Exception):
36    """An exception for PDLParser related stuff."""
37    def __init__(self, message = ""):
38        self.message = message
39        Exception.__init__(self, message)
40    def __repr__(self):
41        return self.message
42    __str__ = __repr__
43       
44class PDLParser :
45    """Generic PDL parser."""
46    def __init__(self, infile, debug=0) :
47        """Initialize the generic parser."""
48        self.debug = debug
49        self.infile = infile
50               
51    def getJobSize(self) :           
52        """Counts pages in the document."""
53        raise RuntimeError, "Not implemented !"
54       
55class PDLAnalyzer :   
56    """Class for PDL autodetection."""
57    def __init__(self, filename, debug=0) :
58        """Initializes the PDL analyzer.
59       
60           filename is the name of the file or '-' for stdin.
61           filename can also be a file-like object which
62           supports read() and seek().
63        """
64        self.debug = debug
65        self.filename = filename
66        try :
67            import psyco 
68        except ImportError :   
69            sys.stderr.write("pkpgcounter : you should install psyco if possible, this would greatly speedup parsing.\n")
70            pass # Psyco is not installed
71        else :   
72            # Psyco is installed, tell it to compile
73            # the CPU intensive methods : PCL and PCLXL
74            # parsing will greatly benefit from this,
75            # for PostScript and PDF the difference is
76            # barely noticeable since they are already
77            # almost optimal, and much more speedy anyway.
78            psyco.bind(PostScriptParser.getJobSize)
79            psyco.bind(PDFParser.getJobSize)
80            psyco.bind(ESCP2Parser.getJobSize)
81            psyco.bind(PCLParser.getJobSize)
82            psyco.bind(PCLXLParser.getJobSize)
83       
84    def getJobSize(self) :   
85        """Returns the job's size."""
86        self.openFile()
87        try :
88            pdlhandler = self.detectPDLHandler()
89        except PDLParserError, msg :   
90            self.closeFile()
91            raise PDLParserError, "ERROR : Unknown file format for %s (%s)" % (self.filename, msg)
92        else :
93            try :
94                size = pdlhandler(self.infile, self.debug).getJobSize()
95            finally :   
96                self.closeFile()
97            return size
98       
99    def openFile(self) :   
100        """Opens the job's data stream for reading."""
101        self.mustclose = 0  # by default we don't want to close the file when finished
102        if hasattr(self.filename, "read") and hasattr(self.filename, "seek") :
103            # filename is in fact a file-like object
104            infile = self.filename
105        elif self.filename == "-" :
106            # we must read from stdin
107            infile = sys.stdin
108        else :   
109            # normal file
110            self.infile = open(self.filename, "rb")
111            self.mustclose = 1
112            return
113           
114        # Use a temporary file, always seekable contrary to standard input.
115        self.infile = tempfile.TemporaryFile(mode="w+b")
116        while 1 :
117            data = infile.read(MEGABYTE) 
118            if not data :
119                break
120            self.infile.write(data)
121        self.infile.flush()   
122        self.infile.seek(0)
123           
124    def closeFile(self) :       
125        """Closes the job's data stream if we can close it."""
126        if self.mustclose :
127            self.infile.close()   
128        else :   
129            # if we don't have to close the file, then
130            # ensure the file pointer is reset to the
131            # start of the file in case the process wants
132            # to read the file again.
133            try :
134                self.infile.seek(0)
135            except :   
136                pass    # probably stdin, which is not seekable
137       
138    def isPostScript(self, sdata, edata) :   
139        """Returns 1 if data is PostScript, else 0."""
140        if sdata.startswith("%!") or \
141           sdata.startswith("\004%!") or \
142           sdata.startswith("\033%-12345X%!PS") or \
143           ((sdata[:128].find("\033%-12345X") != -1) and \
144             ((sdata.find("LANGUAGE=POSTSCRIPT") != -1) or \
145              (sdata.find("LANGUAGE = POSTSCRIPT") != -1) or \
146              (sdata.find("LANGUAGE = Postscript") != -1))) or \
147              (sdata.find("%!PS-Adobe") != -1) :
148            if self.debug : 
149                sys.stderr.write("%s is a PostScript file\n" % str(self.filename))
150            return 1
151        else :   
152            return 0
153       
154    def isPDF(self, sdata, edata) :   
155        """Returns 1 if data is PDF, else 0."""
156        if sdata.startswith("%PDF-") or \
157           sdata.startswith("\033%-12345X%PDF-") or \
158           ((sdata[:128].find("\033%-12345X") != -1) and (sdata.upper().find("LANGUAGE=PDF") != -1)) or \
159           (sdata.find("%PDF-") != -1) :
160            if self.debug : 
161                sys.stderr.write("%s is a PDF file\n" % str(self.filename))
162            return 1
163        else :   
164            return 0
165       
166    def isPCL(self, sdata, edata) :   
167        """Returns 1 if data is PCL, else 0."""
168        if sdata.startswith("\033E\033") or \
169           (sdata.startswith("\033*rbC") and (not edata[-3:] == "\f\033@")) or \
170           sdata.startswith("\033%8\033") or \
171           (sdata.find("\033%-12345X") != -1) :
172            if self.debug : 
173                sys.stderr.write("%s is a PCL3/4/5 file\n" % str(self.filename))
174            return 1
175        else :   
176            return 0
177       
178    def isPCLXL(self, sdata, edata) :   
179        """Returns 1 if data is PCLXL aka PCL6, else 0."""
180        if ((sdata[:128].find("\033%-12345X") != -1) and \
181             (sdata.find(" HP-PCL XL;") != -1) and \
182             ((sdata.find("LANGUAGE=PCLXL") != -1) or \
183              (sdata.find("LANGUAGE = PCLXL") != -1))) :
184            if self.debug : 
185                sys.stderr.write("%s is a PCLXL (aka PCL6) file\n" % str(self.filename))
186            return 1
187        else :   
188            return 0
189           
190    def isESCP2(self, sdata, edata) :       
191        """Returns 1 if data is ESC/P2, else 0."""
192        if sdata.startswith("\033@") or \
193           sdata.startswith("\033*") or \
194           sdata.startswith("\n\033@") or \
195           sdata.startswith("\0\0\0\033\1@EJL") : # ESC/P Raster ??? Seen on Stylus Photo 1284
196            if self.debug : 
197                sys.stderr.write("%s is an ESC/P2 file\n" % str(self.filename))
198            return 1
199        else :   
200            return 0
201   
202    def detectPDLHandler(self) :   
203        """Tries to autodetect the document format.
204       
205           Returns the correct PDL handler class or None if format is unknown
206        """   
207        # Try to detect file type by reading first block of datas   
208        self.infile.seek(0)
209        firstblock = self.infile.read(4 * KILOBYTE)
210        try :
211            self.infile.seek(-LASTBLOCKSIZE, 2)
212            lastblock = self.infile.read(LASTBLOCKSIZE)
213        except IOError :   
214            lastblock = ""
215        self.infile.seek(0)
216        if self.isPostScript(firstblock, lastblock) :
217            return PostScriptParser
218        elif self.isPCLXL(firstblock, lastblock) :   
219            return PCLXLParser
220        elif self.isPDF(firstblock, lastblock) :   
221            return PDFParser
222        elif self.isPCL(firstblock, lastblock) :   
223            return PCLParser
224        elif self.isESCP2(firstblock, lastblock) :   
225            return ESCP2Parser
226        else :   
227            raise PDLParserError, "Analysis of first data block failed."
228           
229def main() :   
230    """Entry point for PDL Analyzer."""
231    if (len(sys.argv) < 2) or ((not sys.stdin.isatty()) and ("-" not in sys.argv[1:])) :
232        sys.argv.append("-")
233       
234    if ("-h" in sys.argv[1:]) or ("--help" in sys.argv[1:]) :
235        print "usage : pkpgcounter file1 file2 ... fileN"
236    elif ("-v" in sys.argv[1:]) or ("--version" in sys.argv[1:]) :
237        print "%s" % version.__version__
238    else :
239        totalsize = 0   
240        debug = 0
241        minindex = 1
242        if sys.argv[1] == "--debug" :
243            minindex = 2
244            debug = 1
245        for arg in sys.argv[minindex:] :
246            try :
247                parser = PDLAnalyzer(arg, debug)
248                totalsize += parser.getJobSize()
249            except PDLParserError, msg :   
250                sys.stderr.write("ERROR: %s\n" % msg)
251                sys.stderr.flush()
252        print "%s" % totalsize
253   
254if __name__ == "__main__" :   
255    main()
Note: See TracBrowser for help on using the browser.