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
Line 
1# -*- coding: utf-8 -*-
2#
3# pkpgcounter : a generic Page Description Language parser
4#
5# (c) 2003-2009 Jerome Alet <alet@librelogiciel.com>
6# This program is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation, either version 3 of the License, or
9# (at your option) any later version.
10#
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.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program.  If not, see <http://www.gnu.org/licenses/>.
18#
19# $Id$
20#
21
22"""This modules implements a page counter for PostScript documents."""
23
24import sys
25import os
26
27import pdlparser
28import inkcoverage
29
30class Parser(pdlparser.PDLParser) :
31    """A parser for PostScript documents."""
32    totiffcommands = [ 'gs -sDEVICE=tiff24nc -dPARANOIDSAFER -dNOPAUSE -dBATCH -dQUIET -r"%(dpi)i" -sOutputFile="%(outfname)s" "%(infname)s"' ]
33    required = [ "gs" ]
34    openmode = "rU"
35    format = "PostScript"
36    def isValid(self) :
37        """Returns True if data is PostScript, else False."""
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) :
46            return True
47        else :
48            return False
49
50    def throughGhostScript(self) :
51        """Get the count through GhostScript, useful for non-DSC compliant PS files."""
52        self.logdebug("Internal parser sucks, using GhostScript instead...")
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", "")
55        infname = self.filename
56        command = 'gs -sDEVICE=bbox -dPARANOIDSAFER -dNOPAUSE -dBATCH -dQUIET "%(infname)s" 2>&1 | grep -c "%%HiResBoundingBox:" 2>/dev/null'
57        pagecount = 0
58        fromchild = os.popen(command % locals(), "r")
59        try :
60            try :
61                pagecount = int(fromchild.readline().strip())
62            except (IOError, OSError, AttributeError, ValueError), msg :
63                raise pdlparser.PDLParserError, "Problem during analysis of Binary PostScript document : %s" % msg
64        finally :
65            if fromchild.close() is not None :
66                raise pdlparser.PDLParserError, "Problem during analysis of Binary PostScript document"
67        self.logdebug("GhostScript said : %s pages" % pagecount)
68        return pagecount * self.copies
69
70    def setcopies(self, pagenum, txtvalue) :
71        """Tries to extract a number of copies from a textual value and set the instance attributes accordingly."""
72        try :
73            number = int(txtvalue)
74        except (ValueError, TypeError) :
75            pass
76        else :
77            if number > self.pages[pagenum]["copies"] :
78                self.pages[pagenum]["copies"] = number
79
80    def natively(self) :
81        """Count pages in a DSC compliant PostScript document."""
82        pagecount = 0
83        self.pages = { 0 : { "copies" : 1 } }
84        oldpagenum = None
85        previousline = ""
86        notrust = False
87        prescribe = False # Kyocera's Prescribe commands
88        acrobatmarker = False
89        pagescomment = None
90        for line in self.infile :
91            line = line.strip()
92            parts = line.split()
93            nbparts = len(parts)
94            if nbparts >= 1 :
95                part0 = parts[0]
96            else :
97                part0 = ""
98            if part0 == r"%ADOPrintSettings:" :
99                acrobatmarker = True
100            elif part0 == "!R!" :
101                prescribe = True
102            elif part0 == r"%%Pages:" :
103                try :
104                    pagescomment = max(pagescomment or 0, int(parts[1]))
105                except ValueError :
106                    pass # strange, to say the least
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(") :
114                try :
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])
121            elif part0 == r"%RBINumCopies:" :
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 :
135                    # treats both "%%Page: x x" and "%%Page: (x-y) z" (probably N-up mode)
136                    newpagenum = int(line.split(']')[0].split()[-1])
137                except :
138                    notinteger = True # It seems that sometimes it's not an integer but an EPS file name
139                else :
140                    notinteger = False
141                    if newpagenum == oldpagenum :
142                        proceed = False
143                    else :
144                        oldpagenum = newpagenum
145                if proceed and not notinteger :
146                    pagecount += 1
147                    self.pages[pagecount] = { "copies" : self.pages[pagecount-1]["copies"] }
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
152            elif line.startswith("/languagelevel where{pop languagelevel}{1}ifelse 2 ge{1 dict dup/NumCopies") :
153                self.setcopies(pagecount, previousline[2:])
154            elif (nbparts > 1) and (parts[1] == "@copies") :
155                self.setcopies(pagecount, part0)
156            previousline = line
157
158        # extract max number of copies to please the ghostscript parser, just
159        # in case we will use it later
160        self.copies = max([ v["copies"] for (k, v) in self.pages.items() ])
161
162        # now apply the number of copies to each page
163        if not pagecount and pagescomment :
164            pagecount = pagescomment
165        for pnum in range(1, pagecount + 1) :
166            page = self.pages.get(pnum, self.pages.get(1, self.pages.get(0, { "copies" : 1 })))
167            copies = page["copies"]
168            pagecount += (copies - 1)
169            self.logdebug("%s * page #%s" % (copies, pnum))
170
171        self.logdebug("Internal parser said : %s pages" % pagecount)
172        return (pagecount, notrust)
173
174    def getJobSize(self) :
175        """Count pages in PostScript document."""
176        self.copies = 1
177        (nbpages, notrust) = self.natively()
178        newnbpages = nbpages
179        if notrust or not nbpages :
180            try :
181                newnbpages = self.throughGhostScript()
182            except pdlparser.PDLParserError, msg :
183                self.logdebug(msg)
184        return max(nbpages, newnbpages)
Note: See TracBrowser for help on using the browser.