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

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
Line 
1#! /usr/bin/env python
2# -*- coding: ISO-8859-15 -*-
3#
4# pkpgcounter : a generic Page Description Language parser
5#
6# (c) 2003, 2004, 2005, 2006 Jerome Alet <alet@librelogiciel.com>
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
19# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20#
21# $Id$
22#
23
24"""This modules implements a page counter for PostScript documents."""
25
26import sys
27import os
28import tempfile
29import popen2
30
31import pdlparser
32import inkcoverage
33
34class Parser(pdlparser.PDLParser) :
35    """A parser for PostScript documents."""
36    totiffcommands = [ 'gs -sDEVICE=tiff24nc -dPARANOIDSAFER -dNOPAUSE -dBATCH -dQUIET -r%(dpi)i -sOutputFile="%(fname)s" -' ]
37    def isValid(self) :   
38        """Returns True if data is PostScript, else False."""
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) :
47            self.logdebug("DEBUG: Input file is in the PostScript format.")
48            return True
49        else :   
50            return False
51       
52    def throughGhostScript(self) :
53        """Get the count through GhostScript, useful for non-DSC compliant PS files."""
54        self.logdebug("Internal parser sucks, using GhostScript instead...")
55        self.infile.seek(0)
56        command = 'gs -sDEVICE=bbox -dPARANOIDSAFER -dNOPAUSE -dBATCH -dQUIET - 2>&1 | grep -c "%%HiResBoundingBox:" 2>/dev/null'
57        child = popen2.Popen4(command)
58        try :
59            data = self.infile.read(pdlparser.MEGABYTE)   
60            while data :
61                child.tochild.write(data)
62                data = self.infile.read(pdlparser.MEGABYTE)
63            child.tochild.flush()
64            child.tochild.close()   
65        except (IOError, OSError), msg :   
66            raise pdlparser.PDLParserError, "Problem during analysis of Binary PostScript document : %s" % msg
67           
68        pagecount = 0
69        try :
70            pagecount = int(child.fromchild.readline().strip())
71        except (IOError, OSError, AttributeError, ValueError), msg :
72            raise pdlparser.PDLParserError, "Problem during analysis of Binary PostScript document : %s" % msg
73        child.fromchild.close()
74       
75        try :
76            child.wait()
77        except OSError, msg :   
78            raise pdlparser.PDLParserError, "Problem during analysis of Binary PostScript document : %s" % msg
79        self.logdebug("GhostScript said : %s pages" % pagecount)   
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
86        self.pages = { 0 : { "copies" : 1 } }
87        oldpagenum = None
88        previousline = ""
89        notrust = 0
90        prescribe = 0 # Kyocera's Prescribe commands
91        acrobatmarker = 0
92        for line in self.infile.xreadlines() : 
93            if (not prescribe) and line.startswith(r"%%BeginResource: procset pdf") \
94               and not acrobatmarker :
95                notrust = 1 # Let this stuff be managed by GhostScript, but we still extract number of copies
96            elif line.startswith(r"%ADOPrintSettings: L") :
97                acrobatmarker = 1
98            elif line.startswith("!R!") :
99                prescribe = 1
100            elif line.startswith(r"%%Page: ") or line.startswith(r"(%%[Page: ") :
101                proceed = 1
102                try :
103                    newpagenum = int(line.split(']')[0].split()[1])
104                except :   
105                    notinteger = 1 # It seems that sometimes it's not an integer but an EPS file name
106                else :   
107                    notinteger = 0
108                    if newpagenum == oldpagenum :
109                        proceed = 0
110                    else :
111                        oldpagenum = newpagenum
112                if proceed and not notinteger :       
113                    pagecount += 1
114                    self.pages[pagecount] = { "copies" : self.pages[pagecount-1]["copies"] }
115            elif line.startswith(r"%%Requirements: numcopies(") :   
116                try :
117                    number = int(line.strip().split('(')[1].split(')')[0])
118                except :     
119                    pass
120                else :   
121                    if number > self.pages[pagecount]["copies"] :
122                        self.pages[pagecount]["copies"] = number
123            elif line.startswith(r"%%BeginNonPPDFeature: NumCopies ") :
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 :   
130                    if number > self.pages[pagecount]["copies"] :
131                        self.pages[pagecount]["copies"] = number
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 :   
139                    if number > self.pages[pagecount]["copies"] :
140                        self.pages[pagecount]["copies"] = number
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
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 :
156                    if number > self.pages[pagecount]["copies"] :
157                        self.pages[pagecount]["copies"] = number
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
166            previousline = line
167           
168        # extract max number of copies to please the ghostscript parser, just   
169        # in case we will use it later
170        self.copies = max([ v["copies"] for (k, v) in self.pages.items() ])
171       
172        # now apply the number of copies to each page
173        for pnum in range(1, pagecount + 1) :
174            page = self.pages.get(pnum, self.pages.get(1, { "copies" : 1 }))
175            copies = page["copies"]
176            pagecount += (copies - 1)
177            self.logdebug("%s * page #%s" % (copies, pnum))
178        self.logdebug("Internal parser said : %s pages" % pagecount)
179        return (pagecount, notrust)
180       
181    def getJobSize(self) :   
182        """Count pages in PostScript document."""
183        self.copies = 1
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)   
192       
193if __name__ == "__main__" :   
194    pdlparser.test(Parser)
Note: See TracBrowser for help on using the browser.