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

Revision 450, 9.2 kB (checked in by jerome, 18 years ago)

Now uses Python's universal newline detection to read input files,
and also uses file objects directly instead of calling their xreadlines()
method.
Fixed an accounting problem in the PDF parser for some type of files.

  • 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, 2006, 2007 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
24"""This modules implements a page counter for PostScript documents."""
25
26import sys
27import os
28import tempfile
29import popen2
30
31import pdlparser
32import inkcoverage
33
34class Parser(pdlparser.PDLParser) :
35    """A parser for PostScript documents."""
36    totiffcommands = [ 'gs -sDEVICE=tiff24nc -dPARANOIDSAFER -dNOPAUSE -dBATCH -dQUIET -r%(dpi)i -sOutputFile="%(fname)s" -' ]
37    def isValid(self) :   
38        """Returns True if data is PostScript, else False."""
39        if self.firstblock.startswith("%!") or \
40           self.firstblock.startswith("\004%!") or \
41           self.firstblock.startswith("\033%-12345X%!PS") or \
42           ((self.firstblock[:128].find("\033%-12345X") != -1) and \
43             ((self.firstblock.find("LANGUAGE=POSTSCRIPT") != -1) or \
44              (self.firstblock.find("LANGUAGE = POSTSCRIPT") != -1) or \
45              (self.firstblock.find("LANGUAGE = Postscript") != -1))) or \
46              (self.firstblock.find("%!PS-Adobe") != -1) :
47            self.logdebug("DEBUG: Input file is in the PostScript format.")
48            return True
49        else :   
50            return False
51       
52    def throughGhostScript(self) :
53        """Get the count through GhostScript, useful for non-DSC compliant PS files."""
54        self.logdebug("Internal parser sucks, using GhostScript instead...")
55        self.infile.seek(0)
56        command = 'gs -sDEVICE=bbox -dPARANOIDSAFER -dNOPAUSE -dBATCH -dQUIET - 2>&1 | grep -c "%%HiResBoundingBox:" 2>/dev/null'
57        child = popen2.Popen4(command)
58        try :
59            data = self.infile.read(pdlparser.MEGABYTE)   
60            while data :
61                child.tochild.write(data)
62                data = self.infile.read(pdlparser.MEGABYTE)
63            child.tochild.flush()
64            child.tochild.close()   
65        except (IOError, OSError), msg :   
66            raise pdlparser.PDLParserError, "Problem during analysis of Binary PostScript document : %s" % msg
67           
68        pagecount = 0
69        try :
70            pagecount = int(child.fromchild.readline().strip())
71        except (IOError, OSError, AttributeError, ValueError), msg :
72            raise pdlparser.PDLParserError, "Problem during analysis of Binary PostScript document : %s" % msg
73        child.fromchild.close()
74       
75        try :
76            child.wait()
77        except OSError, msg :   
78            raise pdlparser.PDLParserError, "Problem during analysis of Binary PostScript document : %s" % msg
79        self.logdebug("GhostScript said : %s pages" % pagecount)   
80        return pagecount * self.copies
81       
82    def natively(self) :
83        """Count pages in a DSC compliant PostScript document."""
84        self.infile.seek(0)
85        pagecount = 0
86        self.pages = { 0 : { "copies" : 1 } }
87        oldpagenum = None
88        previousline = ""
89        notrust = 0
90        prescribe = 0 # Kyocera's Prescribe commands
91        acrobatmarker = 0
92        pagescomment = None
93        for line in self.infile : 
94            if (not prescribe) and line.startswith(r"%%BeginResource: procset pdf") \
95               and not acrobatmarker :
96                notrust = 1 # Let this stuff be managed by GhostScript, but we still extract number of copies
97            elif line.startswith(r"%ADOPrintSettings: L") :
98                acrobatmarker = 1
99            elif line.startswith("!R!") :
100                prescribe = 1
101            elif line.startswith(r"%%Pages: ") :
102                try :
103                    pagescomment = max(pagescomment or 0, int(line.split()[1]))
104                except ValueError :
105                    pass # strange, to say the least
106            elif line.startswith(r"%%Page: ") or line.startswith(r"(%%[Page: ") :
107                proceed = 1
108                try :
109                    # treats both "%%Page: x x" and "%%Page: (x-y) z" (probably N-up mode)
110                    newpagenum = int(line.split(']')[0].split()[-1])
111                except :   
112                    notinteger = 1 # It seems that sometimes it's not an integer but an EPS file name
113                else :   
114                    notinteger = 0
115                    if newpagenum == oldpagenum :
116                        proceed = 0
117                    else :
118                        oldpagenum = newpagenum
119                if proceed and not notinteger :       
120                    pagecount += 1
121                    self.pages[pagecount] = { "copies" : self.pages[pagecount-1]["copies"] }
122            elif line.startswith(r"%%Requirements: numcopies(") :   
123                try :
124                    number = int(line.strip().split('(')[1].split(')')[0])
125                except :     
126                    pass
127                else :   
128                    if number > self.pages[pagecount]["copies"] :
129                        self.pages[pagecount]["copies"] = number
130            elif line.startswith(r"%%BeginNonPPDFeature: NumCopies ") :
131                # handle # of copies set by some Windows printer driver
132                try :
133                    number = int(line.strip().split()[2])
134                except :     
135                    pass
136                else :   
137                    if number > self.pages[pagecount]["copies"] :
138                        self.pages[pagecount]["copies"] = number
139            elif line.startswith("1 dict dup /NumCopies ") :
140                # handle # of copies set by mozilla/kprinter
141                try :
142                    number = int(line.strip().split()[4])
143                except :     
144                    pass
145                else :   
146                    if number > self.pages[pagecount]["copies"] :
147                        self.pages[pagecount]["copies"] = number
148            elif line.startswith("{ pop 1 dict dup /NumCopies ") :
149                # handle # of copies set by firefox/kprinter/cups (alternate syntax)
150                try :
151                    number = int(line.strip().split()[6])
152                except :
153                    pass
154                else :
155                    if number > self.pages[pagecount]["copies"] :
156                        self.pages[pagecount]["copies"] = number
157            elif line.startswith("/languagelevel where{pop languagelevel}{1}ifelse 2 ge{1 dict dup/NumCopies") :
158                try :
159                    number = int(previousline.strip()[2:])
160                except :
161                    pass
162                else :
163                    if number > self.pages[pagecount]["copies"] :
164                        self.pages[pagecount]["copies"] = number
165            elif line.startswith("/#copies ") :
166                try :
167                    number = int(line.strip().split()[1])
168                except :     
169                    pass
170                else :   
171                    if number > self.pages[pagecount]["copies"] :
172                        self.pages[pagecount]["copies"] = number
173            elif line.startswith(r"%RBINumCopies: ") :   
174                try :
175                    number = int(line.strip().split()[1])
176                except :     
177                    pass
178                else :   
179                    if number > self.pages[pagecount]["copies"] :
180                        self.pages[pagecount]["copies"] = number
181            previousline = line
182           
183        # extract max number of copies to please the ghostscript parser, just   
184        # in case we will use it later
185        self.copies = max([ v["copies"] for (k, v) in self.pages.items() ])
186       
187        # now apply the number of copies to each page
188        if not pagecount and pagescomment :   
189            pagecount = pagescomment
190        for pnum in range(1, pagecount + 1) :
191            page = self.pages.get(pnum, self.pages.get(1, self.pages.get(0, { "copies" : 1 })))
192            copies = page["copies"]
193            pagecount += (copies - 1)
194            self.logdebug("%s * page #%s" % (copies, pnum))
195           
196        self.logdebug("Internal parser said : %s pages" % pagecount)
197        return (pagecount, notrust)
198       
199    def getJobSize(self) :   
200        """Count pages in PostScript document."""
201        self.copies = 1
202        (nbpages, notrust) = self.natively()
203        newnbpages = nbpages
204        if notrust or not nbpages :
205            try :
206                newnbpages = self.throughGhostScript()
207            except pdlparser.PDLParserError, msg :
208                self.logdebug(msg)
209        return max(nbpages, newnbpages)   
210       
211if __name__ == "__main__" :   
212    pdlparser.test(Parser)
Note: See TracBrowser for help on using the browser.