root / pkpgcounter / trunk / pkpgpdls / analyzer.py @ 358

Revision 358, 8.4 kB (checked in by jerome, 18 years ago)

Ensures that the resolution argument is comprised between 72 and 1200 dpi.

  • 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, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18#
19# $Id$
20#
21
22"""This is the main module of pkpgcounter.
23
24It defines the PDLAnalyzer class, which provides a generic way to parse
25input files, by automatically detecting the best parser to use."""
26
27import sys
28import tempfile
29
30import version, pdlparser, postscript, pdf, pcl345, pclxl, \
31       escp2, dvi, tiff, ooo, zjstream
32
33
34class AnalyzerOptions :
35    """A class for use as the options parameter to PDLAnalyzer's constructor."""
36    def __init__(self, debug=None,
37                       colorspace=None,
38                       resolution=None) :
39        """Sets initial attributes."""
40        self.debug = debug
41        self.colorspace = colorspace
42        self.resolution = resolution
43   
44   
45class PDLAnalyzer :   
46    """Class for PDL autodetection."""
47    def __init__(self, filename, options=AnalyzerOptions()) :
48        """Initializes the PDL analyzer.
49       
50           filename is the name of the file or '-' for stdin.
51           filename can also be a file-like object which
52           supports read() and seek().
53        """
54        self.options = options
55        self.filename = filename
56        self.infile = None
57        self.mustclose = None
58       
59    def getJobSize(self) :   
60        """Returns the job's size."""
61        self.openFile()
62        try :
63            pdlhandler = self.detectPDLHandler()
64        except pdlparser.PDLParserError, msg :   
65            self.closeFile()
66            raise pdlparser.PDLParserError, "Unknown file format for %s (%s)" % (self.filename, msg)
67        else :
68            try :
69                size = pdlhandler.getJobSize()
70            finally :   
71                self.closeFile()
72            return size
73       
74    def openFile(self) :   
75        """Opens the job's data stream for reading."""
76        self.mustclose = 0  # by default we don't want to close the file when finished
77        if hasattr(self.filename, "read") and hasattr(self.filename, "seek") :
78            # filename is in fact a file-like object
79            infile = self.filename
80        elif self.filename == "-" :
81            # we must read from stdin
82            infile = sys.stdin
83        else :   
84            # normal file
85            self.infile = open(self.filename, "rb")
86            self.mustclose = 1
87            return
88           
89        # Use a temporary file, always seekable contrary to standard input.
90        self.infile = tempfile.TemporaryFile(mode="w+b")
91        while 1 :
92            data = infile.read(pdlparser.MEGABYTE) 
93            if not data :
94                break
95            self.infile.write(data)
96        self.infile.flush()   
97        self.infile.seek(0)
98           
99    def closeFile(self) :       
100        """Closes the job's data stream if we can close it."""
101        if self.mustclose :
102            self.infile.close()   
103        else :   
104            # if we don't have to close the file, then
105            # ensure the file pointer is reset to the
106            # start of the file in case the process wants
107            # to read the file again.
108            try :
109                self.infile.seek(0)
110            except IOError :   
111                pass    # probably stdin, which is not seekable
112       
113    def detectPDLHandler(self) :   
114        """Tries to autodetect the document format.
115       
116           Returns the correct PDL handler class or None if format is unknown
117        """   
118        # Try to detect file type by reading first and last blocks of datas   
119        # Each parser can read them automatically, but here we do this only once.
120        self.infile.seek(0)
121        firstblock = self.infile.read(pdlparser.FIRSTBLOCKSIZE)
122        try :
123            self.infile.seek(-pdlparser.LASTBLOCKSIZE, 2)
124            lastblock = self.infile.read(pdlparser.LASTBLOCKSIZE)
125        except IOError :   
126            lastblock = ""
127        self.infile.seek(0)
128        if not firstblock :
129            raise pdlparser.PDLParserError, "input file %s is empty !" % str(self.filename)
130        else :   
131            for module in (postscript, \
132                           pclxl, \
133                           pdf, \
134                           pcl345, \
135                           escp2, \
136                           dvi, \
137                           tiff, \
138                           zjstream, \
139                           ooo) :
140                try :               
141                    return module.Parser(self.infile, self.options.debug, firstblock, lastblock)
142                except pdlparser.PDLParserError :
143                    pass # try next parser
144        raise pdlparser.PDLParserError, "Analysis of first data block failed."
145           
146def main() :   
147    """Entry point for PDL Analyzer."""
148    import optparse
149    from copy import copy
150   
151    def check_cichoice(option, opt, value) :
152        """To add a CaseIgnore Choice option type."""
153        valower = value.lower()
154        if valower in [v.lower() for v in option.cichoices] :
155            return valower
156        else :   
157            choices = ", ".join([repr(o) for o in option.cichoices])
158            raise optparse.OptionValueError(
159                "option %s: invalid choice: %r (choose from %s)"
160                % (opt, value, choices))
161   
162    class MyOption(optparse.Option) :
163        """New Option class, with CaseIgnore Choice type."""
164        TYPES = optparse.Option.TYPES + ("cichoice",)
165        ATTRS = optparse.Option.ATTRS + ["cichoices"]
166        TYPE_CHECKER = copy(optparse.Option.TYPE_CHECKER)
167        TYPE_CHECKER["cichoice"] = check_cichoice
168       
169    parser = optparse.OptionParser(option_class=MyOption, 
170                                   usage="python analyzer.py [options] file1 [file2 ...]")
171    parser.add_option("-v", "--version", 
172                            action="store_true", 
173                            dest="version",
174                            help="Show pkpgcounter's version number and exit.")
175    parser.add_option("-d", "--debug", 
176                            action="store_true", 
177                            dest="debug",
178                            help="Activate debug mode.")
179    parser.add_option("-c", "--colorspace", 
180                            dest="colorspace",
181                            type="cichoice",
182                            cichoices=["bw", "cmyk", "cmy", "all"],
183                            help="Activate the computation of ink usage, and defines the colorspace to use. Supported values are 'BW', 'CMYK', 'CMY' and 'ALL'.")
184    parser.add_option("-r", "--resolution", 
185                            type="int", 
186                            default=150, 
187                            dest="resolution",
188                            help="The resolution in DPI to use when checking ink usage. Lower resolution is faster. Default is 150.")
189    (options, arguments) = parser.parse_args()
190    if options.version :
191        print "%s" % version.__version__
192    elif not (72 <= options.resolution <= 1200) :   
193        sys.stderr.write("ERROR: the argument to the --resolution command line switch must be between 72 and 1200.\n")
194        sys.stderr.flush()
195    else :
196        if (not arguments) or ((not sys.stdin.isatty()) and ("-" not in arguments)) :
197            arguments.append("-")
198        totalsize = 0   
199        try :
200            for arg in arguments :
201                try :
202                    parser = PDLAnalyzer(arg, options)
203                    totalsize += parser.getJobSize()
204                except (IOError, pdlparser.PDLParserError), msg :   
205                    sys.stderr.write("ERROR: %s\n" % msg)
206                    sys.stderr.flush()
207        except KeyboardInterrupt :           
208            sys.stderr.write("WARN: Aborted at user's request.\n")
209            sys.stderr.flush()
210        print "%s" % totalsize
211   
212if __name__ == "__main__" :   
213    main()
Note: See TracBrowser for help on using the browser.