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

Revision 492, 9.3 kB (checked in by jerome, 17 years ago)

Even more code cleaning : we use named temporary files now, so no need for pipes.

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