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

Revision 521, 8.7 kB (checked in by jerome, 16 years ago)

More code cleaning : let ghostscript do the job of reading the input
file directly instead of from a pipe.

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