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

Revision 428, 8.4 kB (checked in by jerome, 18 years ago)

Improved ink accounting by allowing several commands to be launch to convert to TIFF in case one of them fails.

  • 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#
[303]6# (c) 2003, 2004, 2005, 2006 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
[193]92        for line in self.infile.xreadlines() : 
[321]93            if (not prescribe) and line.startswith(r"%%BeginResource: procset pdf") \
[323]94               and not acrobatmarker :
[273]95                notrust = 1 # Let this stuff be managed by GhostScript, but we still extract number of copies
[334]96            elif line.startswith(r"%ADOPrintSettings: L") :
[323]97                acrobatmarker = 1
[314]98            elif line.startswith("!R!") :
99                prescribe = 1
[272]100            elif line.startswith(r"%%Page: ") or line.startswith(r"(%%[Page: ") :
[263]101                proceed = 1
102                try :
103                    newpagenum = int(line.split(']')[0].split()[1])
104                except :   
[323]105                    notinteger = 1 # It seems that sometimes it's not an integer but an EPS file name
[263]106                else :   
[323]107                    notinteger = 0
[263]108                    if newpagenum == oldpagenum :
109                        proceed = 0
110                    else :
111                        oldpagenum = newpagenum
[323]112                if proceed and not notinteger :       
[263]113                    pagecount += 1
[273]114                    self.pages[pagecount] = { "copies" : self.pages[pagecount-1]["copies"] }
[208]115            elif line.startswith(r"%%Requirements: numcopies(") :   
[193]116                try :
117                    number = int(line.strip().split('(')[1].split(')')[0])
118                except :     
119                    pass
120                else :   
[273]121                    if number > self.pages[pagecount]["copies"] :
122                        self.pages[pagecount]["copies"] = number
[208]123            elif line.startswith(r"%%BeginNonPPDFeature: NumCopies ") :
[193]124                # handle # of copies set by some Windows printer driver
125                try :
126                    number = int(line.strip().split()[2])
127                except :     
128                    pass
129                else :   
[273]130                    if number > self.pages[pagecount]["copies"] :
131                        self.pages[pagecount]["copies"] = number
[193]132            elif line.startswith("1 dict dup /NumCopies ") :
133                # handle # of copies set by mozilla/kprinter
134                try :
135                    number = int(line.strip().split()[4])
136                except :     
137                    pass
138                else :   
[273]139                    if number > self.pages[pagecount]["copies"] :
140                        self.pages[pagecount]["copies"] = number
[389]141            elif line.startswith("{ pop 1 dict dup /NumCopies ") :
142                # handle # of copies set by firefox/kprinter/cups (alternate syntax)
143                try :
144                    number = int(line.strip().split()[6])
145                except :
146                    pass
147                else :
148                    if number > self.pages[pagecount]["copies"] :
149                        self.pages[pagecount]["copies"] = number
[248]150            elif line.startswith("/languagelevel where{pop languagelevel}{1}ifelse 2 ge{1 dict dup/NumCopies") :
151                try :
152                    number = int(previousline.strip()[2:])
153                except :
154                    pass
155                else :
[273]156                    if number > self.pages[pagecount]["copies"] :
157                        self.pages[pagecount]["copies"] = number
[290]158            elif line.startswith("/#copies ") :
159                try :
160                    number = int(line.strip().split()[1])
161                except :     
162                    pass
163                else :   
164                    if number > self.pages[pagecount]["copies"] :
165                        self.pages[pagecount]["copies"] = number
[248]166            previousline = line
167           
168        # extract max number of copies to please the ghostscript parser, just   
169        # in case we will use it later
[273]170        self.copies = max([ v["copies"] for (k, v) in self.pages.items() ])
[193]171       
[248]172        # now apply the number of copies to each page
173        for pnum in range(1, pagecount + 1) :
[273]174            page = self.pages.get(pnum, self.pages.get(1, { "copies" : 1 }))
[248]175            copies = page["copies"]
176            pagecount += (copies - 1)
[252]177            self.logdebug("%s * page #%s" % (copies, pnum))
[273]178        self.logdebug("Internal parser said : %s pages" % pagecount)
[384]179        return (pagecount, notrust)
[273]180       
[193]181    def getJobSize(self) :   
182        """Count pages in PostScript document."""
[202]183        self.copies = 1
[384]184        (nbpages, notrust) = self.natively()
185        newnbpages = nbpages
186        if notrust :
187            try :
188                newnbpages = self.throughGhostScript()
189            except pdlparser.PDLParserError, msg :
190                self.logdebug(msg)
191        return max(nbpages, newnbpages)   
[283]192       
[193]193if __name__ == "__main__" :   
[415]194    pdlparser.test(Parser)
Note: See TracBrowser for help on using the browser.