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

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

Fixed a problem with PostScript? parser and some printer drivers.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Auth Date Id Rev
RevLine 
[193]1#! /usr/bin/env python
2# -*- coding: ISO-8859-15 -*-
[191]3#
4# pkpgcounter : a generic Page Description Language parser
5#
[443]6# (c) 2003, 2004, 2005, 2006, 2007 Jerome Alet <alet@librelogiciel.com>
[191]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
[211]19# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
[191]20#
21# $Id$
22#
[193]23
[357]24"""This modules implements a page counter for PostScript documents."""
25
[193]26import sys
[283]27import os
28import tempfile
[193]29import popen2
30
[235]31import pdlparser
[283]32import inkcoverage
[193]33
[220]34class Parser(pdlparser.PDLParser) :
[193]35    """A parser for PostScript documents."""
[428]36    totiffcommands = [ 'gs -sDEVICE=tiff24nc -dPARANOIDSAFER -dNOPAUSE -dBATCH -dQUIET -r%(dpi)i -sOutputFile="%(fname)s" -' ]
[220]37    def isValid(self) :   
[387]38        """Returns True if data is PostScript, else False."""
[220]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) :
[252]47            self.logdebug("DEBUG: Input file is in the PostScript format.")
[387]48            return True
[220]49        else :   
[387]50            return False
[220]51       
[193]52    def throughGhostScript(self) :
53        """Get the count through GhostScript, useful for non-DSC compliant PS files."""
[252]54        self.logdebug("Internal parser sucks, using GhostScript instead...")
[193]55        self.infile.seek(0)
[283]56        command = 'gs -sDEVICE=bbox -dPARANOIDSAFER -dNOPAUSE -dBATCH -dQUIET - 2>&1 | grep -c "%%HiResBoundingBox:" 2>/dev/null'
[193]57        child = popen2.Popen4(command)
58        try :
[237]59            data = self.infile.read(pdlparser.MEGABYTE)   
[193]60            while data :
61                child.tochild.write(data)
[237]62                data = self.infile.read(pdlparser.MEGABYTE)
[193]63            child.tochild.flush()
64            child.tochild.close()   
65        except (IOError, OSError), msg :   
[200]66            raise pdlparser.PDLParserError, "Problem during analysis of Binary PostScript document : %s" % msg
[193]67           
68        pagecount = 0
69        try :
70            pagecount = int(child.fromchild.readline().strip())
71        except (IOError, OSError, AttributeError, ValueError), msg :
[200]72            raise pdlparser.PDLParserError, "Problem during analysis of Binary PostScript document : %s" % msg
[193]73        child.fromchild.close()
74       
75        try :
76            child.wait()
77        except OSError, msg :   
[200]78            raise pdlparser.PDLParserError, "Problem during analysis of Binary PostScript document : %s" % msg
[273]79        self.logdebug("GhostScript said : %s pages" % pagecount)   
[193]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
[273]86        self.pages = { 0 : { "copies" : 1 } }
[263]87        oldpagenum = None
[252]88        previousline = ""
[273]89        notrust = 0
[314]90        prescribe = 0 # Kyocera's Prescribe commands
[323]91        acrobatmarker = 0
[444]92        pagescomment = None
[193]93        for line in self.infile.xreadlines() : 
[321]94            if (not prescribe) and line.startswith(r"%%BeginResource: procset pdf") \
[323]95               and not acrobatmarker :
[273]96                notrust = 1 # Let this stuff be managed by GhostScript, but we still extract number of copies
[334]97            elif line.startswith(r"%ADOPrintSettings: L") :
[323]98                acrobatmarker = 1
[314]99            elif line.startswith("!R!") :
100                prescribe = 1
[444]101            elif line.startswith(r"%%Pages: ") :
102                try :
103                    pagescomment = int(line.split()[1])
104                except ValueError :
105                    pass # strange, to say the least
[272]106            elif line.startswith(r"%%Page: ") or line.startswith(r"(%%[Page: ") :
[263]107                proceed = 1
108                try :
[437]109                    # treats both "%%Page: x x" and "%%Page: (x-y) z" (probably N-up mode)
110                    newpagenum = int(line.split(']')[0].split()[-1])
[263]111                except :   
[323]112                    notinteger = 1 # It seems that sometimes it's not an integer but an EPS file name
[263]113                else :   
[323]114                    notinteger = 0
[263]115                    if newpagenum == oldpagenum :
116                        proceed = 0
117                    else :
118                        oldpagenum = newpagenum
[323]119                if proceed and not notinteger :       
[263]120                    pagecount += 1
[273]121                    self.pages[pagecount] = { "copies" : self.pages[pagecount-1]["copies"] }
[208]122            elif line.startswith(r"%%Requirements: numcopies(") :   
[193]123                try :
124                    number = int(line.strip().split('(')[1].split(')')[0])
125                except :     
126                    pass
127                else :   
[273]128                    if number > self.pages[pagecount]["copies"] :
129                        self.pages[pagecount]["copies"] = number
[208]130            elif line.startswith(r"%%BeginNonPPDFeature: NumCopies ") :
[193]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 :   
[273]137                    if number > self.pages[pagecount]["copies"] :
138                        self.pages[pagecount]["copies"] = number
[193]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 :   
[273]146                    if number > self.pages[pagecount]["copies"] :
147                        self.pages[pagecount]["copies"] = number
[389]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
[248]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 :
[273]163                    if number > self.pages[pagecount]["copies"] :
164                        self.pages[pagecount]["copies"] = number
[290]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
[248]173            previousline = line
174           
175        # extract max number of copies to please the ghostscript parser, just   
176        # in case we will use it later
[273]177        self.copies = max([ v["copies"] for (k, v) in self.pages.items() ])
[193]178       
[248]179        # now apply the number of copies to each page
180        for pnum in range(1, pagecount + 1) :
[273]181            page = self.pages.get(pnum, self.pages.get(1, { "copies" : 1 }))
[248]182            copies = page["copies"]
183            pagecount += (copies - 1)
[252]184            self.logdebug("%s * page #%s" % (copies, pnum))
[444]185           
186        if not pagecount and pagescomment :   
187            pagecount = pagescomment
[273]188        self.logdebug("Internal parser said : %s pages" % pagecount)
[384]189        return (pagecount, notrust)
[273]190       
[193]191    def getJobSize(self) :   
192        """Count pages in PostScript document."""
[202]193        self.copies = 1
[384]194        (nbpages, notrust) = self.natively()
195        newnbpages = nbpages
[444]196        if notrust or not nbpages :
[384]197            try :
198                newnbpages = self.throughGhostScript()
199            except pdlparser.PDLParserError, msg :
200                self.logdebug(msg)
201        return max(nbpages, newnbpages)   
[283]202       
[193]203if __name__ == "__main__" :   
[415]204    pdlparser.test(Parser)
Note: See TracBrowser for help on using the browser.