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

Revision 562, 8.2 kB (checked in by jerome, 16 years ago)

Fixed a small problem with the parser's rewrite.

  • 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
[193]27
[235]28import pdlparser
[283]29import inkcoverage
[193]30
[220]31class Parser(pdlparser.PDLParser) :
[193]32    """A parser for PostScript documents."""
[492]33    totiffcommands = [ 'gs -sDEVICE=tiff24nc -dPARANOIDSAFER -dNOPAUSE -dBATCH -dQUIET -r"%(dpi)i" -sOutputFile="%(outfname)s" "%(infname)s"' ]
[527]34    required = [ "gs" ]
[491]35    openmode = "rU"
[555]36    format = "PostScript"
[220]37    def isValid(self) :   
[387]38        """Returns True if data is PostScript, else False."""
[522]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) :
[387]47            return True
[220]48        else :   
[387]49            return False
[220]50       
[193]51    def throughGhostScript(self) :
52        """Get the count through GhostScript, useful for non-DSC compliant PS files."""
[252]53        self.logdebug("Internal parser sucks, using GhostScript instead...")
[527]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", "")
[522]56        infname = self.filename
[521]57        command = 'gs -sDEVICE=bbox -dPARANOIDSAFER -dNOPAUSE -dBATCH -dQUIET "%(infname)s" 2>&1 | grep -c "%%HiResBoundingBox:" 2>/dev/null'
[491]58        pagecount = 0
[521]59        fromchild = os.popen(command % locals(), "r")
[193]60        try :
[491]61            try :
[521]62                pagecount = int(fromchild.readline().strip())
[491]63            except (IOError, OSError, AttributeError, ValueError), msg :
64                raise pdlparser.PDLParserError, "Problem during analysis of Binary PostScript document : %s" % msg
65        finally :       
[521]66            if fromchild.close() is not None :
67                raise pdlparser.PDLParserError, "Problem during analysis of Binary PostScript document"
[273]68        self.logdebug("GhostScript said : %s pages" % pagecount)   
[193]69        return pagecount * self.copies
70       
[561]71    def setcopies(self, pagenum, txtvalue) :   
72        """Tries to extract a number of copies from a textual value and set the instance attributes accordingly."""
73        try :
74            number = int(txtvalue)
75        except (ValueError, TypeError) :     
76            pass
77        else :   
78            if number > self.pages[pagenum]["copies"] :
79                self.pages[pagenum]["copies"] = number
80               
[193]81    def natively(self) :
82        """Count pages in a DSC compliant PostScript document."""
83        pagecount = 0
[273]84        self.pages = { 0 : { "copies" : 1 } }
[263]85        oldpagenum = None
[252]86        previousline = ""
[521]87        notrust = False
88        prescribe = False # Kyocera's Prescribe commands
89        acrobatmarker = False
[444]90        pagescomment = None
[491]91        for line in self.infile :
92            line = line.strip()
[561]93            parts = line.split()
94            nbparts = len(parts)
[562]95            if nbparts >= 1 :
96                part0 = parts[0]
97            else :   
98                part0 = ""
[561]99            if part0 == r"%ADOPrintSettings:" :
[521]100                acrobatmarker = True
[561]101            elif part0 == "!R!" :   
[521]102                prescribe = True
[561]103            elif part0 == r"%%Pages:" :
[444]104                try :
[561]105                    pagescomment = max(pagescomment or 0, int(parts[1]))
[444]106                except ValueError :
107                    pass # strange, to say the least
[561]108            elif (part0 == r"%%BeginNonPPDFeature:") \
109                  and (nbparts > 2) \
110                  and (parts[1] == "NumCopies") :
111                self.setcopies(pagecount, parts[2])
112            elif (part0 == r"%%Requirements:") \
113                  and (nbparts > 1) \
114                  and (parts[1] == "numcopies(") :
[263]115                try :
[561]116                    self.setcopies(pagecount, line.split('(')[1].split(')')[0])
117                except IndexError :
118                    pass
119            elif part0 == "/#copies" :
120                if nbparts > 1 :
121                    self.setcopies(pagecount, parts[1])
122            elif part0 == r"%RBINumCopies:" :   
123                if nbparts > 1 :
124                    self.setcopies(pagecount, parts[1])
125            elif (parts[:4] == ["1", "dict", "dup", "/NumCopies"]) \
126                  and (nbparts > 4) :
127                # handle # of copies set by mozilla/kprinter
128                self.setcopies(pagecount, parts[4])
129            elif (parts[:6] == ["{", "pop", "1", "dict", "dup", "/NumCopies"]) \
130                  and (nbparts > 6) :
131                # handle # of copies set by firefox/kprinter/cups (alternate syntax)
132                self.setcopies(pagecount, parts[6])
133            elif (part0 == r"%%Page:") or (part0 == r"(%%[Page:") :
134                proceed = True
135                try :
[437]136                    # treats both "%%Page: x x" and "%%Page: (x-y) z" (probably N-up mode)
137                    newpagenum = int(line.split(']')[0].split()[-1])
[263]138                except :   
[561]139                    notinteger = True # It seems that sometimes it's not an integer but an EPS file name
[263]140                else :   
[561]141                    notinteger = False
[263]142                    if newpagenum == oldpagenum :
[561]143                        proceed = False
[263]144                    else :
145                        oldpagenum = newpagenum
[323]146                if proceed and not notinteger :       
[263]147                    pagecount += 1
[273]148                    self.pages[pagecount] = { "copies" : self.pages[pagecount-1]["copies"] }
[561]149            elif (not prescribe) \
150               and (parts[:3] == [r"%%BeginResource:", "procset", "pdf"]) \
151               and not acrobatmarker :
152                notrust = True # Let this stuff be managed by GhostScript, but we still extract number of copies
[248]153            elif line.startswith("/languagelevel where{pop languagelevel}{1}ifelse 2 ge{1 dict dup/NumCopies") :
[561]154                self.setcopies(pagecount, previousline[2:])
155            elif (nbparts > 1) and (parts[1] == "@copies") :
156                self.setcopies(pagecount, part0)
[248]157            previousline = line
158           
159        # extract max number of copies to please the ghostscript parser, just   
160        # in case we will use it later
[273]161        self.copies = max([ v["copies"] for (k, v) in self.pages.items() ])
[193]162       
[248]163        # now apply the number of copies to each page
[448]164        if not pagecount and pagescomment :   
165            pagecount = pagescomment
[248]166        for pnum in range(1, pagecount + 1) :
[448]167            page = self.pages.get(pnum, self.pages.get(1, self.pages.get(0, { "copies" : 1 })))
[248]168            copies = page["copies"]
169            pagecount += (copies - 1)
[252]170            self.logdebug("%s * page #%s" % (copies, pnum))
[444]171           
[273]172        self.logdebug("Internal parser said : %s pages" % pagecount)
[384]173        return (pagecount, notrust)
[273]174       
[193]175    def getJobSize(self) :   
176        """Count pages in PostScript document."""
[202]177        self.copies = 1
[384]178        (nbpages, notrust) = self.natively()
179        newnbpages = nbpages
[444]180        if notrust or not nbpages :
[384]181            try :
182                newnbpages = self.throughGhostScript()
183            except pdlparser.PDLParserError, msg :
184                self.logdebug(msg)
185        return max(nbpages, newnbpages)   
Note: See TracBrowser for help on using the browser.