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

Revision 314, 9.4 kB (checked in by jerome, 18 years ago)

1.76 is out.

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