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

Revision 527, 8.8 kB (checked in by jerome, 17 years ago)

Now the presence of executable dependencies is tested at runtime.

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