root / pkpgcounter / trunk / pkpgpdls / postscript.py @ 273

Revision 273, 7.4 kB (checked in by jerome, 19 years ago)

Better heuristic to detect when gs has to be used for parsing

  • Property svn:eol-style set to native
  • Property svn:keywords set to Auth Date Id Rev
Line 
1#! /usr/bin/env python
2# -*- coding: ISO-8859-15 -*-
3#
4# pkpgcounter : a generic Page Description Language parser
5#
6# (c) 2003, 2004, 2005 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 2 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, write to the Free Software
19# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20#
21# $Id$
22#
23
24import sys
25import popen2
26
27import pdlparser
28
29class Parser(pdlparser.PDLParser) :
30    """A parser for PostScript documents."""
31    def isValid(self) :   
32        """Returns 1 if data is PostScript, else 0."""
33        if self.firstblock.startswith("%!") or \
34           self.firstblock.startswith("\004%!") or \
35           self.firstblock.startswith("\033%-12345X%!PS") or \
36           ((self.firstblock[:128].find("\033%-12345X") != -1) and \
37             ((self.firstblock.find("LANGUAGE=POSTSCRIPT") != -1) or \
38              (self.firstblock.find("LANGUAGE = POSTSCRIPT") != -1) or \
39              (self.firstblock.find("LANGUAGE = Postscript") != -1))) or \
40              (self.firstblock.find("%!PS-Adobe") != -1) :
41            self.logdebug("DEBUG: Input file is in the PostScript format.")
42            return 1
43        else :   
44            return 0
45       
46    def throughGhostScript(self) :
47        """Get the count through GhostScript, useful for non-DSC compliant PS files."""
48        self.logdebug("Internal parser sucks, using GhostScript instead...")
49        self.infile.seek(0)
50        command = 'gs -sDEVICE=bbox -dNOPAUSE -dBATCH -dQUIET - 2>&1 | grep -c "%%HiResBoundingBox:" 2>/dev/null'
51        child = popen2.Popen4(command)
52        try :
53            data = self.infile.read(pdlparser.MEGABYTE)   
54            while data :
55                child.tochild.write(data)
56                data = self.infile.read(pdlparser.MEGABYTE)
57            child.tochild.flush()
58            child.tochild.close()   
59        except (IOError, OSError), msg :   
60            raise pdlparser.PDLParserError, "Problem during analysis of Binary PostScript document : %s" % msg
61           
62        pagecount = 0
63        try :
64            pagecount = int(child.fromchild.readline().strip())
65        except (IOError, OSError, AttributeError, ValueError), msg :
66            raise pdlparser.PDLParserError, "Problem during analysis of Binary PostScript document : %s" % msg
67        child.fromchild.close()
68       
69        try :
70            child.wait()
71        except OSError, msg :   
72            raise pdlparser.PDLParserError, "Problem during analysis of Binary PostScript document : %s" % msg
73        self.logdebug("GhostScript said : %s pages" % pagecount)   
74        return pagecount * self.copies
75       
76    def natively(self) :
77        """Count pages in a DSC compliant PostScript document."""
78        self.infile.seek(0)
79        pagecount = 0
80        self.pages = { 0 : { "copies" : 1 } }
81        oldpagenum = None
82        previousline = ""
83        notrust = 0
84        for line in self.infile.xreadlines() : 
85            if line.startswith(r"%%BeginResource: procset pdf") :
86                notrust = 1 # Let this stuff be managed by GhostScript, but we still extract number of copies
87            elif line.startswith(r"%%Page: ") or line.startswith(r"(%%[Page: ") :
88                proceed = 1
89                try :
90                    newpagenum = int(line.split(']')[0].split()[1])
91                except :   
92                    pass
93                else :   
94                    if newpagenum == oldpagenum :
95                        proceed = 0
96                    else :
97                        oldpagenum = newpagenum
98                if proceed :       
99                    pagecount += 1
100                    self.pages[pagecount] = { "copies" : self.pages[pagecount-1]["copies"] }
101            elif line.startswith(r"%%Requirements: numcopies(") :   
102                try :
103                    number = int(line.strip().split('(')[1].split(')')[0])
104                except :     
105                    pass
106                else :   
107                    if number > self.pages[pagecount]["copies"] :
108                        self.pages[pagecount]["copies"] = number
109            elif line.startswith(r"%%BeginNonPPDFeature: NumCopies ") :
110                # handle # of copies set by some Windows printer driver
111                try :
112                    number = int(line.strip().split()[2])
113                except :     
114                    pass
115                else :   
116                    if number > self.pages[pagecount]["copies"] :
117                        self.pages[pagecount]["copies"] = number
118            elif line.startswith("1 dict dup /NumCopies ") :
119                # handle # of copies set by mozilla/kprinter
120                try :
121                    number = int(line.strip().split()[4])
122                except :     
123                    pass
124                else :   
125                    if number > self.pages[pagecount]["copies"] :
126                        self.pages[pagecount]["copies"] = number
127            elif line.startswith("/languagelevel where{pop languagelevel}{1}ifelse 2 ge{1 dict dup/NumCopies") :
128                try :
129                    number = int(previousline.strip()[2:])
130                except :
131                    pass
132                else :
133                    if number > self.pages[pagecount]["copies"] :
134                        self.pages[pagecount]["copies"] = number
135            previousline = line
136           
137        # extract max number of copies to please the ghostscript parser, just   
138        # in case we will use it later
139        self.copies = max([ v["copies"] for (k, v) in self.pages.items() ])
140       
141        # now apply the number of copies to each page
142        for pnum in range(1, pagecount + 1) :
143            page = self.pages.get(pnum, self.pages.get(1, { "copies" : 1 }))
144            copies = page["copies"]
145            pagecount += (copies - 1)
146            self.logdebug("%s * page #%s" % (copies, pnum))
147        self.logdebug("Internal parser said : %s pages" % pagecount)
148       
149        if notrust :   
150            pagecount = 0 # Let gs do counting
151        return pagecount
152       
153    def getJobSize(self) :   
154        """Count pages in PostScript document."""
155        self.copies = 1
156        return self.natively() or self.throughGhostScript()
157           
158       
159def test() :       
160    """Test function."""
161    if (len(sys.argv) < 2) or ((not sys.stdin.isatty()) and ("-" not in sys.argv[1:])) :
162        sys.argv.append("-")
163    totalsize = 0   
164    for arg in sys.argv[1:] :
165        if arg == "-" :
166            infile = sys.stdin
167            mustclose = 0
168        else :   
169            infile = open(arg, "rb")
170            mustclose = 1
171        try :
172            parser = Parser(infile, debug=1)
173            totalsize += parser.getJobSize()
174        except pdlparser.PDLParserError, msg :   
175            sys.stderr.write("ERROR: %s\n" % msg)
176            sys.stderr.flush()
177        if mustclose :   
178            infile.close()
179    print "%s" % totalsize
180   
181if __name__ == "__main__" :   
182    test()
Note: See TracBrowser for help on using the browser.