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

Revision 357, 9.8 kB (checked in by jerome, 16 years ago)

Added missing docstrings, thanks to pylint.

  • 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."""
[220]36    def isValid(self) :   
37        """Returns 1 if data is PostScript, else 0."""
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) :
[252]46            self.logdebug("DEBUG: Input file is in the PostScript format.")
[220]47            return 1
48        else :   
49            return 0
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...")
[193]54        self.infile.seek(0)
[283]55        command = 'gs -sDEVICE=bbox -dPARANOIDSAFER -dNOPAUSE -dBATCH -dQUIET - 2>&1 | grep -c "%%HiResBoundingBox:" 2>/dev/null'
[193]56        child = popen2.Popen4(command)
57        try :
[237]58            data = self.infile.read(pdlparser.MEGABYTE)   
[193]59            while data :
60                child.tochild.write(data)
[237]61                data = self.infile.read(pdlparser.MEGABYTE)
[193]62            child.tochild.flush()
63            child.tochild.close()   
64        except (IOError, OSError), msg :   
[200]65            raise pdlparser.PDLParserError, "Problem during analysis of Binary PostScript document : %s" % msg
[193]66           
67        pagecount = 0
68        try :
69            pagecount = int(child.fromchild.readline().strip())
70        except (IOError, OSError, AttributeError, ValueError), msg :
[200]71            raise pdlparser.PDLParserError, "Problem during analysis of Binary PostScript document : %s" % msg
[193]72        child.fromchild.close()
73       
74        try :
75            child.wait()
76        except OSError, msg :   
[200]77            raise pdlparser.PDLParserError, "Problem during analysis of Binary PostScript document : %s" % msg
[273]78        self.logdebug("GhostScript said : %s pages" % pagecount)   
[193]79        return pagecount * self.copies
80       
81    def natively(self) :
82        """Count pages in a DSC compliant PostScript document."""
83        self.infile.seek(0)
84        pagecount = 0
[273]85        self.pages = { 0 : { "copies" : 1 } }
[263]86        oldpagenum = None
[252]87        previousline = ""
[273]88        notrust = 0
[314]89        prescribe = 0 # Kyocera's Prescribe commands
[323]90        acrobatmarker = 0
[193]91        for line in self.infile.xreadlines() : 
[321]92            if (not prescribe) and line.startswith(r"%%BeginResource: procset pdf") \
[323]93               and not acrobatmarker :
[273]94                notrust = 1 # Let this stuff be managed by GhostScript, but we still extract number of copies
[334]95            elif line.startswith(r"%ADOPrintSettings: L") :
[323]96                acrobatmarker = 1
[314]97            elif line.startswith("!R!") :
98                prescribe = 1
[272]99            elif line.startswith(r"%%Page: ") or line.startswith(r"(%%[Page: ") :
[263]100                proceed = 1
101                try :
102                    newpagenum = int(line.split(']')[0].split()[1])
103                except :   
[323]104                    notinteger = 1 # It seems that sometimes it's not an integer but an EPS file name
[263]105                else :   
[323]106                    notinteger = 0
[263]107                    if newpagenum == oldpagenum :
108                        proceed = 0
109                    else :
110                        oldpagenum = newpagenum
[323]111                if proceed and not notinteger :       
[263]112                    pagecount += 1
[273]113                    self.pages[pagecount] = { "copies" : self.pages[pagecount-1]["copies"] }
[208]114            elif line.startswith(r"%%Requirements: numcopies(") :   
[193]115                try :
116                    number = int(line.strip().split('(')[1].split(')')[0])
117                except :     
118                    pass
119                else :   
[273]120                    if number > self.pages[pagecount]["copies"] :
121                        self.pages[pagecount]["copies"] = number
[208]122            elif line.startswith(r"%%BeginNonPPDFeature: NumCopies ") :
[193]123                # handle # of copies set by some Windows printer driver
124                try :
125                    number = int(line.strip().split()[2])
126                except :     
127                    pass
128                else :   
[273]129                    if number > self.pages[pagecount]["copies"] :
130                        self.pages[pagecount]["copies"] = number
[193]131            elif line.startswith("1 dict dup /NumCopies ") :
132                # handle # of copies set by mozilla/kprinter
133                try :
134                    number = int(line.strip().split()[4])
135                except :     
136                    pass
137                else :   
[273]138                    if number > self.pages[pagecount]["copies"] :
139                        self.pages[pagecount]["copies"] = number
[248]140            elif line.startswith("/languagelevel where{pop languagelevel}{1}ifelse 2 ge{1 dict dup/NumCopies") :
141                try :
142                    number = int(previousline.strip()[2:])
143                except :
144                    pass
145                else :
[273]146                    if number > self.pages[pagecount]["copies"] :
147                        self.pages[pagecount]["copies"] = number
[290]148            elif line.startswith("/#copies ") :
149                try :
150                    number = int(line.strip().split()[1])
151                except :     
152                    pass
153                else :   
154                    if number > self.pages[pagecount]["copies"] :
155                        self.pages[pagecount]["copies"] = number
[248]156            previousline = line
157           
158        # extract max number of copies to please the ghostscript parser, just   
159        # in case we will use it later
[273]160        self.copies = max([ v["copies"] for (k, v) in self.pages.items() ])
[193]161       
[248]162        # now apply the number of copies to each page
163        for pnum in range(1, pagecount + 1) :
[273]164            page = self.pages.get(pnum, self.pages.get(1, { "copies" : 1 }))
[248]165            copies = page["copies"]
166            pagecount += (copies - 1)
[252]167            self.logdebug("%s * page #%s" % (copies, pnum))
[273]168        self.logdebug("Internal parser said : %s pages" % pagecount)
169       
170        if notrust :   
171            pagecount = 0 # Let gs do counting
[248]172        return pagecount
173       
[193]174    def getJobSize(self) :   
175        """Count pages in PostScript document."""
[202]176        self.copies = 1
[193]177        return self.natively() or self.throughGhostScript()
[283]178       
179    def throughTiffMultiPage24NC(self, dpi) :
180        """Converts the input file to TIFF format, X dpi, 24 bits per pixel, uncompressed.
181           Returns percents of ink coverage and number of pages.
182        """   
183        self.logdebug("Converting input datas to TIFF...")
[292]184        result = None   
[283]185        self.infile.seek(0)
186        (handle, filename) = tempfile.mkstemp(".tmp", "pkpgcounter")   
187        os.close(handle)
188        command = 'gs -sDEVICE=tiff24nc -dPARANOIDSAFER -dNOPAUSE -dBATCH -dQUIET -r%i -sOutputFile="%s" -' % (dpi, filename)
189        try :
[291]190            child = popen2.Popen4(command)
191            try :
192                data = self.infile.read(pdlparser.MEGABYTE)   
193                while data :
194                    child.tochild.write(data)
195                    data = self.infile.read(pdlparser.MEGABYTE)
196                child.tochild.flush()
197                child.tochild.close()   
198            except (IOError, OSError), msg :   
199                raise pdlparser.PDLParserError, "Problem during conversion to TIFF : %s" % msg
200               
201            child.fromchild.close()
202            try :
203                child.wait()
204            except OSError, msg :   
205                raise pdlparser.PDLParserError, "Problem during conversion to TIFF : %s" % msg
206               
207            result = inkcoverage.getPercents(filename)   
208        finally :   
209            try :
210                os.remove(filename)
211            except :   
212                pass
[283]213        return result   
[193]214       
215def test() :       
216    """Test function."""
[196]217    if (len(sys.argv) < 2) or ((not sys.stdin.isatty()) and ("-" not in sys.argv[1:])) :
218        sys.argv.append("-")
219    totalsize = 0   
220    for arg in sys.argv[1:] :
221        if arg == "-" :
222            infile = sys.stdin
223            mustclose = 0
224        else :   
225            infile = open(arg, "rb")
226            mustclose = 1
227        try :
[220]228            parser = Parser(infile, debug=1)
[196]229            totalsize += parser.getJobSize()
[200]230        except pdlparser.PDLParserError, msg :   
[196]231            sys.stderr.write("ERROR: %s\n" % msg)
232            sys.stderr.flush()
233        if mustclose :   
234            infile.close()
235    print "%s" % totalsize
[193]236   
237if __name__ == "__main__" :   
238    test()
Note: See TracBrowser for help on using the browser.