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

Revision 3474, 8.0 kB (checked in by jerome, 15 years ago)

Changed copyright years.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Auth Date Id Rev
RevLine 
[3410]1# -*- coding: utf-8 -*-
[191]2#
3# pkpgcounter : a generic Page Description Language parser
4#
[3474]5# (c) 2003-2009 Jerome Alet <alet@librelogiciel.com>
[463]6# This program is free software: you can redistribute it and/or modify
[191]7# it under the terms of the GNU General Public License as published by
[463]8# the Free Software Foundation, either version 3 of the License, or
[191]9# (at your option) any later version.
[3436]10#
[191]11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14# GNU General Public License for more details.
[3436]15#
[191]16# You should have received a copy of the GNU General Public License
[463]17# along with this program.  If not, see <http://www.gnu.org/licenses/>.
[191]18#
19# $Id$
20#
[193]21
[357]22"""This modules implements a page counter for PostScript documents."""
23
[193]24import sys
[283]25import os
[193]26
[235]27import pdlparser
[283]28import inkcoverage
[193]29
[220]30class Parser(pdlparser.PDLParser) :
[193]31    """A parser for PostScript documents."""
[492]32    totiffcommands = [ 'gs -sDEVICE=tiff24nc -dPARANOIDSAFER -dNOPAUSE -dBATCH -dQUIET -r"%(dpi)i" -sOutputFile="%(outfname)s" "%(infname)s"' ]
[527]33    required = [ "gs" ]
[491]34    openmode = "rU"
[555]35    format = "PostScript"
[3436]36    def isValid(self) :
[387]37        """Returns True if data is PostScript, else False."""
[522]38        if self.firstblock.startswith("%!") or \
39           self.firstblock.startswith("\004%!") or \
40           self.firstblock.startswith("\033%-12345X%!PS") or \
41           ((self.firstblock[:128].find("\033%-12345X") != -1) and \
42             ((self.firstblock.find("LANGUAGE=POSTSCRIPT") != -1) or \
43              (self.firstblock.find("LANGUAGE = POSTSCRIPT") != -1) or \
44              (self.firstblock.find("LANGUAGE = Postscript") != -1))) or \
45              (self.firstblock.find("%!PS-Adobe") != -1) :
[387]46            return True
[3436]47        else :
[387]48            return False
[3436]49
[193]50    def throughGhostScript(self) :
51        """Get the count through GhostScript, useful for non-DSC compliant PS files."""
[252]52        self.logdebug("Internal parser sucks, using GhostScript instead...")
[527]53        if self.isMissing(self.required) :
54            raise pdlparser.PDLParserError, "The gs interpreter is nowhere to be found in your PATH (%s)" % os.environ.get("PATH", "")
[522]55        infname = self.filename
[521]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
[3436]64        finally :
[521]65            if fromchild.close() is not None :
66                raise pdlparser.PDLParserError, "Problem during analysis of Binary PostScript document"
[3436]67        self.logdebug("GhostScript said : %s pages" % pagecount)
[193]68        return pagecount * self.copies
[3436]69
70    def setcopies(self, pagenum, txtvalue) :
[561]71        """Tries to extract a number of copies from a textual value and set the instance attributes accordingly."""
72        try :
73            number = int(txtvalue)
[3436]74        except (ValueError, TypeError) :
[561]75            pass
[3436]76        else :
[561]77            if number > self.pages[pagenum]["copies"] :
78                self.pages[pagenum]["copies"] = number
[3436]79
[193]80    def natively(self) :
81        """Count pages in a DSC compliant PostScript document."""
82        pagecount = 0
[273]83        self.pages = { 0 : { "copies" : 1 } }
[263]84        oldpagenum = None
[252]85        previousline = ""
[521]86        notrust = False
87        prescribe = False # Kyocera's Prescribe commands
88        acrobatmarker = False
[444]89        pagescomment = None
[491]90        for line in self.infile :
91            line = line.strip()
[561]92            parts = line.split()
93            nbparts = len(parts)
[562]94            if nbparts >= 1 :
95                part0 = parts[0]
[3436]96            else :
[562]97                part0 = ""
[561]98            if part0 == r"%ADOPrintSettings:" :
[521]99                acrobatmarker = True
[3436]100            elif part0 == "!R!" :
[521]101                prescribe = True
[561]102            elif part0 == r"%%Pages:" :
[444]103                try :
[561]104                    pagescomment = max(pagescomment or 0, int(parts[1]))
[444]105                except ValueError :
106                    pass # strange, to say the least
[561]107            elif (part0 == r"%%BeginNonPPDFeature:") \
108                  and (nbparts > 2) \
109                  and (parts[1] == "NumCopies") :
110                self.setcopies(pagecount, parts[2])
111            elif (part0 == r"%%Requirements:") \
112                  and (nbparts > 1) \
113                  and (parts[1] == "numcopies(") :
[263]114                try :
[561]115                    self.setcopies(pagecount, line.split('(')[1].split(')')[0])
116                except IndexError :
117                    pass
118            elif part0 == "/#copies" :
119                if nbparts > 1 :
120                    self.setcopies(pagecount, parts[1])
[3436]121            elif part0 == r"%RBINumCopies:" :
[561]122                if nbparts > 1 :
123                    self.setcopies(pagecount, parts[1])
124            elif (parts[:4] == ["1", "dict", "dup", "/NumCopies"]) \
125                  and (nbparts > 4) :
126                # handle # of copies set by mozilla/kprinter
127                self.setcopies(pagecount, parts[4])
128            elif (parts[:6] == ["{", "pop", "1", "dict", "dup", "/NumCopies"]) \
129                  and (nbparts > 6) :
130                # handle # of copies set by firefox/kprinter/cups (alternate syntax)
131                self.setcopies(pagecount, parts[6])
132            elif (part0 == r"%%Page:") or (part0 == r"(%%[Page:") :
133                proceed = True
134                try :
[437]135                    # treats both "%%Page: x x" and "%%Page: (x-y) z" (probably N-up mode)
136                    newpagenum = int(line.split(']')[0].split()[-1])
[3436]137                except :
[561]138                    notinteger = True # It seems that sometimes it's not an integer but an EPS file name
[3436]139                else :
[561]140                    notinteger = False
[263]141                    if newpagenum == oldpagenum :
[561]142                        proceed = False
[263]143                    else :
144                        oldpagenum = newpagenum
[3436]145                if proceed and not notinteger :
[263]146                    pagecount += 1
[273]147                    self.pages[pagecount] = { "copies" : self.pages[pagecount-1]["copies"] }
[561]148            elif (not prescribe) \
149               and (parts[:3] == [r"%%BeginResource:", "procset", "pdf"]) \
150               and not acrobatmarker :
151                notrust = True # Let this stuff be managed by GhostScript, but we still extract number of copies
[248]152            elif line.startswith("/languagelevel where{pop languagelevel}{1}ifelse 2 ge{1 dict dup/NumCopies") :
[561]153                self.setcopies(pagecount, previousline[2:])
154            elif (nbparts > 1) and (parts[1] == "@copies") :
155                self.setcopies(pagecount, part0)
[248]156            previousline = line
[3436]157
158        # extract max number of copies to please the ghostscript parser, just
[248]159        # in case we will use it later
[273]160        self.copies = max([ v["copies"] for (k, v) in self.pages.items() ])
[3436]161
[248]162        # now apply the number of copies to each page
[3436]163        if not pagecount and pagescomment :
[448]164            pagecount = pagescomment
[248]165        for pnum in range(1, pagecount + 1) :
[448]166            page = self.pages.get(pnum, self.pages.get(1, self.pages.get(0, { "copies" : 1 })))
[248]167            copies = page["copies"]
168            pagecount += (copies - 1)
[252]169            self.logdebug("%s * page #%s" % (copies, pnum))
[3436]170
[273]171        self.logdebug("Internal parser said : %s pages" % pagecount)
[384]172        return (pagecount, notrust)
[3436]173
174    def getJobSize(self) :
[193]175        """Count pages in PostScript document."""
[202]176        self.copies = 1
[384]177        (nbpages, notrust) = self.natively()
178        newnbpages = nbpages
[444]179        if notrust or not nbpages :
[384]180            try :
181                newnbpages = self.throughGhostScript()
182            except pdlparser.PDLParserError, msg :
183                self.logdebug(msg)
[3436]184        return max(nbpages, newnbpages)
Note: See TracBrowser for help on using the browser.