root / pkpgcounter / trunk / pkpgpdls / pclxl.py @ 428

Revision 428, 28.5 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
RevLine 
[193]1#! /usr/bin/env python
2# -*- coding: ISO-8859-15 -*-
[191]3#
4# pkpgcounter : a generic Page Description Language parser
5#
[302]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 PCLXL (aka PCL6) documents."""
25
[193]26import sys
[202]27import os
[193]28import mmap
29from struct import unpack
30
[235]31import pdlparser
[252]32import pjl
[193]33
[220]34class Parser(pdlparser.PDLParser) :
[193]35    """A parser for PCLXL (aka PCL6) documents."""
[428]36    totiffcommands = [ 'pcl6 -sDEVICE=pdfwrite -dPARANOIDSAFER -dNOPAUSE -dBATCH -dQUIET -sOutputFile=- - | gs -sDEVICE=tiff24nc -dPARANOIDSAFER -dNOPAUSE -dBATCH -dQUIET -r%(dpi)i -sOutputFile="%(fname)s" -', 
37                       'pcl6 -sDEVICE=pswrite -dPARANOIDSAFER -dNOPAUSE -dBATCH -dQUIET -sOutputFile=- - | gs -sDEVICE=tiff24nc -dPARANOIDSAFER -dNOPAUSE -dBATCH -dQUIET -r%(dpi)i -sOutputFile="%(fname)s" -',
38                     ]
[193]39    mediasizes = { 
40                    0 : "Letter",
41                    1 : "Legal",
42                    2 : "A4",
43                    3 : "Executive",
44                    4 : "Ledger",
45                    5 : "A3",
46                    6 : "COM10Envelope",
47                    7 : "MonarchEnvelope",
48                    8 : "C5Envelope",
49                    9 : "DLEnvelope",
50                    10 : "JB4",
51                    11 : "JB5",
52                    12 : "B5Envelope",
[246]53                    12 : "B5",
[193]54                    14 : "JPostcard",
55                    15 : "JDoublePostcard",
56                    16 : "A5",
57                    17 : "A6",
58                    18 : "JB6",
[246]59                    19 : "JIS8K",
60                    20 : "JIS16K",
61                    21 : "JISExec",
62                    96 : "Default",
[193]63                 }   
64                 
65    mediasources = {             
66                     0 : "Default",
67                     1 : "Auto",
68                     2 : "Manual",
69                     3 : "MultiPurpose",
70                     4 : "UpperCassette",
71                     5 : "LowerCassette",
72                     6 : "EnvelopeTray",
73                     7 : "ThirdCassette",
74                   }
75                   
76    orientations = {               
77                     0 : "Portrait",
78                     1 : "Landscape",
79                     2 : "ReversePortrait",
80                     3 : "ReverseLandscape",
[246]81                     4 : "Default",
[193]82                   }
83           
[220]84    def isValid(self) :   
[387]85        """Returns True if data is PCLXL aka PCL6, else False."""
[220]86        if ((self.firstblock[:128].find("\033%-12345X") != -1) and \
87             (self.firstblock.find(" HP-PCL XL;") != -1) and \
88             ((self.firstblock.find("LANGUAGE=PCLXL") != -1) or \
89              (self.firstblock.find("LANGUAGE = PCLXL") != -1))) :
[252]90            self.logdebug("DEBUG: Input file is in the PCLXL (aka PCL6) format.")
[387]91            return True
[220]92        else :   
[387]93            return False
[220]94           
[318]95    def beginPage(self, nextpos) :
[193]96        """Indicates the beginning of a new page, and extracts media information."""
97        self.pagecount += 1
98       
99        # Default values
100        mediatypelabel = "Plain"
101        mediasourcelabel = "Main"
102        mediasizelabel = "Default"
103        orientationlabel = "Portrait"
[299]104        duplexmode = None
[193]105       
106        # Now go upstream to decode media type, size, source, and orientation
107        # this saves time because we don't need a complete parser !
108        minfile = self.minfile
[318]109        pos = nextpos - 2
[193]110        while pos > 0 : # safety check : don't go back to far !
111            val = ord(minfile[pos])
112            if val in (0x44, 0x48, 0x41) : # if previous endPage or openDataSource or beginSession (first page)
113                break
114            if val == 0x26 :   
115                mediasource = ord(minfile[pos - 2])
116                mediasourcelabel = self.mediasources.get(mediasource, str(mediasource))
[302]117                pos -= 4
[193]118            elif val == 0x25 :
[302]119                while (pos > 0) and (ord(minfile[pos]) != 0xc0) :
120                    # we search the preceding ubyte tag
121                    pos -= 1
122                if pos > 0 :
123                    if ord(minfile[pos-1]) == 0xc8 :
124                        # if we found an ubyte_array then the media
125                        # size is completely spelled
126                        arraylength = ord(minfile[pos+1])
127                        mediasizelabel = minfile[pos+2:pos+2+arraylength].title()
128                        pos -= 1
129                    else :   
130                        # if we just found an ubyte, then the media
131                        # size is known by its index
132                        mediasize = ord(minfile[pos+1])
133                        mediasizelabel = self.mediasizes.get(mediasize, str(mediasize))
134                    pos -= 1 
[310]135                    # self.logdebug("Media size : %s" % mediasizelabel)
[193]136            elif val == 0x28 :   
137                orientation = ord(minfile[pos - 2])
[252]138                orientationlabel = self.orientations.get(orientation, str(orientation))
[302]139                pos -= 4
[193]140            elif val == 0x27 :   
141                savepos = pos
[302]142                pos -= 1
[293]143                startpos = size = None 
[193]144                while pos > 0 : # safety check : don't go back to far !
145                    val = ord(minfile[pos])
146                    pos -= 1   
147                    if val == 0xc8 :
[293]148                        length = self.tags[ord(minfile[pos+2])] # will probably always be a byte or uint16
149                        if length == 1 :   
150                            startpos = pos + 4
151                            size = unpack("B", self.minfile[pos+3:startpos])[0]
152                        elif length == 2 :   
153                            startpos = pos + 5
[316]154                            size = unpack(self.unpackShort, self.minfile[pos+3:startpos])[0]
[293]155                        elif length == 4 :   
156                            startpos = pos + 7
[316]157                            size = unpack(self.unpackLong, self.minfile[pos+3:startpos])[0]
[293]158                        else :   
[301]159                            raise pdlparser.PDLParserError, "Error on size at %s : %s" % (pos+2, length)
[193]160                        break
[293]161                mediatypelabel = minfile[startpos:startpos+size]
[310]162                # self.logdebug("Media type : %s" % mediatypelabel)
[299]163            elif val == 0x34 :   
164                duplexmode = "Simplex"
[302]165                pos -= 2
[299]166            elif val in (0x35, 0x36) :   
167                duplexmode = "Duplex"
[302]168                pos -= 2
[193]169            # else : TODO : CUSTOM MEDIA SIZE AND UNIT !
170            else :   
[302]171                pos -= 1  # ignored
[193]172        self.pages[self.pagecount] = { "copies" : 1, 
173                                       "orientation" : orientationlabel, 
174                                       "mediatype" : mediatypelabel, 
175                                       "mediasize" : mediasizelabel,
176                                       "mediasource" : mediasourcelabel,
[299]177                                       "duplex" : duplexmode,
[193]178                                     } 
179        return 0
180       
[318]181    def endPage(self, nextpos) :   
[193]182        """Indicates the end of a page."""
[318]183        pos3 = nextpos - 3
[193]184        minfile = self.minfile
[318]185        if minfile[pos3:nextpos-1] == self.setNumberOfCopies :
[193]186            # The EndPage operator may be preceded by a PageCopies attribute
187            # So set number of copies for current page.
188            # From what I read in PCLXL documentation, the number
189            # of copies is an unsigned 16 bits integer
[258]190            try :
[317]191                self.pages[self.pagecount]["copies"] = unpack(self.unpackShort, minfile[pos3-2:pos3])[0]
[258]192            except KeyError :   
193                self.logdebug("It looks like this PCLXL file is corrupted.")
[193]194        return 0
195       
[318]196    def setColorSpace(self, nextpos) :   
[205]197        """Changes the color space."""
[318]198        if self.minfile[nextpos-4:nextpos-1] == self.RGBColorSpace : # TODO : doesn't seem to handle all cases !
[206]199            self.iscolor = 1
[205]200        return 0
201           
[318]202    def array_Generic(self, nextpos, size) :
[317]203        """Handles all arrays."""
[318]204        pos = nextpos
[317]205        datatype = ord(self.minfile[pos])
[193]206        pos += 1
[317]207        length = self.tags[datatype]
[193]208        if callable(length) :
[316]209            length = length(pos)
[318]210        try :
211            return 1 + length + size * unpack(self.unpackType[length], self.minfile[pos:pos+length])[0]
212        except KeyError :
213            raise pdlparser.PDLParserError, "Error on array size at %x" % nextpos
[317]214           
[318]215    def array_8(self, nextpos) :   
[317]216        """Handles byte arrays."""
[318]217        return self.array_Generic(nextpos, 1)
[193]218       
[318]219    def array_16(self, nextpos) :
[424]220        """Handles 16 bits arrays."""
[318]221        return self.array_Generic(nextpos, 2)
[193]222       
[318]223    def array_32(self, nextpos) :
[424]224        """Handles 32 bits arrays."""
[318]225        return self.array_Generic(nextpos, 4)
[193]226       
[318]227    def embeddedDataSmall(self, nextpos) :
[193]228        """Handle small amounts of data."""
[318]229        return 1 + ord(self.minfile[nextpos])
[193]230       
[318]231    def embeddedData(self, nextpos) :
[193]232        """Handle normal amounts of data."""
[318]233        return 4 + unpack(self.unpackLong, self.minfile[nextpos:nextpos+4])[0]
[193]234       
[318]235    def littleEndian(self, nextpos) :
[193]236        """Toggles to little endianness."""
[318]237        self.unpackType = { 1 : "B", 2 : "<H", 4 : "<I" }
238        self.unpackShort = self.unpackType[2]
239        self.unpackLong = self.unpackType[4]
[193]240        return 0
241       
[318]242    def bigEndian(self, nextpos) :
[193]243        """Toggles to big endianness."""
[318]244        self.unpackType = { 1 : "B", 2 : ">H", 4 : ">I" }
245        self.unpackShort = self.unpackType[2]
246        self.unpackLong = self.unpackType[4]
[193]247        return 0
248   
[318]249    def reservedForFutureUse(self, nextpos) :
[245]250        """Outputs something when a reserved byte is encountered."""
[318]251        self.logdebug("Byte at %x is out of the PCLXL Protocol Class 2.0 Specification" % nextpos)
[245]252        return 0   
253       
[318]254    def x46_class3(self, nextpos) :
[310]255        """Undocumented tag 0x46 in class 3.0 streams."""
[318]256        pos = nextpos - 3
[309]257        minfile = self.minfile
[318]258        val = ord(minfile[pos])
259        while val == 0xf8 :
260            funcid = ord(minfile[pos+1])
261            try :
262                offset = self.x46_functions[funcid]
263            except KeyError :   
264                self.logdebug("Unexpected subfunction 0x%02x for undocumented tag 0x46 at %x" % (funcid, nextpos))
265                break
266            else :   
[320]267                pos -= offset
268                length = self.tags[ord(self.minfile[pos])]
[318]269                if callable(length) :
[320]270                    length = length(pos+1)
271                if funcid == 0x92 : # we want to skip these blocks
[318]272                    try :
273                        return unpack(self.unpackType[length], self.minfile[pos+1:pos+length+1])[0]
274                    except KeyError :
275                        raise pdlparser.PDLParserError, "Error on size '%s' at %x" % (length, pos+1)
[309]276            val = ord(minfile[pos])
277        return 0   
278       
[318]279    def escape(self, nextpos) :   
[245]280        """Handles the ESC code."""
[318]281        pos = endpos = nextpos
[314]282        minfile = self.minfile
283        if minfile[pos : pos+8] == r"%-12345X" :
[245]284            endpos = pos + 9
[252]285            endmark = chr(0x0c) + chr(0x00) + chr(0x1b)
[246]286            asciilimit = chr(0x80)
[269]287            quotes = 0
[314]288            while (minfile[endpos] not in endmark) and \
289                   ((minfile[endpos] < asciilimit) or (quotes % 2)) :
290                if minfile[endpos] == '"' :
[269]291                    quotes += 1
[245]292                endpos += 1
293               
294            # Store this in a per page mapping.   
295            # NB : First time will be at page 0 (i.e. **before** page 1) !
296            stuff = self.escapedStuff.setdefault(self.pagecount, [])
[314]297            stuff.append(minfile[pos : endpos])
298            self.logdebug("Escaped datas : [%s]" % repr(minfile[pos : endpos]))
[245]299        return endpos - pos
300       
[318]301    def skipKyoceraPrescribe(self, nextpos) :
[312]302        """Skips Kyocera Prescribe commands."""
[318]303        pos = nextpos - 1
[312]304        minfile = self.minfile
305        if minfile[pos:pos+3] == "!R!" :
[318]306            while (pos - nextpos) < 1024 :   # This is a realistic upper bound, to avoid infinite loops
[312]307                if (minfile[pos] == ";") and (minfile[pos-4:pos] == "EXIT") :
[314]308                    pos += 1
309                    prescribe = self.prescribeStuff.setdefault(self.pagecount, [])
[318]310                    prescribe.append(minfile[nextpos-1:pos])
311                    self.logdebug("Prescribe commands : [%s]" % repr(minfile[nextpos-1:pos]))
[417]312                    break
[312]313                pos += 1   
[417]314            return (pos - nextpos)
[312]315        else :
316            return 0
317           
[193]318    def getJobSize(self) :
319        """Counts pages in a PCLXL (PCL6) document.
320       
321           Algorithm by Jerome Alet.
322           
323           The documentation used for this was :
324         
325           HP PCL XL Feature Reference
326           Protocol Class 2.0
327           http://www.hpdevelopersolutions.com/downloads/64/358/xl_ref20r22.pdf
[246]328           
329           Protocol Class 2.1 Supplement
330           xl_ref21.pdf
331           
332           Protocol Class 3.0 Supplement
333           xl_refsup30r089.pdf
[193]334        """
[206]335        self.iscolor = None
[193]336        found = 0
337        while not found :
338            line = self.infile.readline()
339            if not line :
340                break
[293]341            pos = line.find(" HP-PCL XL;")   
342            if pos != -1 :
[193]343                found = 1
[293]344                endian = ord(line[pos - 1])
[193]345                if endian == 0x29 :
[316]346                    self.littleEndian(0)
[193]347                elif endian == 0x28 :   
[316]348                    self.bigEndian(0)
[193]349                # elif endian == 0x27 : # TODO : This is the ESC code : parse it for PJL statements !
350                #
351                else :   
[200]352                    raise pdlparser.PDLParserError, "Unknown endianness marker 0x%02x at start !" % endian
[193]353        if not found :
[200]354            raise pdlparser.PDLParserError, "This file doesn't seem to be PCLXL (aka PCL6)"
[193]355           
[246]356        # Initialize Media Sources
357        for i in range(8, 256) :
358            self.mediasources[i] = "ExternalTray%03i" % (i - 7)
359           
[193]360        # Initialize table of tags
361        self.tags = [ 0 ] * 256   
362       
[245]363        self.tags[0x1b] = self.escape # The escape code
364       
[352]365        self.tags[0x21] = self.skipKyoceraPrescribe # 0x21 is not normally used
[312]366       
[193]367        # GhostScript's sources tell us that HP printers
368        # only accept little endianness, but we can handle both.
369        self.tags[0x28] = self.bigEndian    # BigEndian
370        self.tags[0x29] = self.littleEndian # LittleEndian
371       
372        self.tags[0x43] = self.beginPage    # BeginPage
373        self.tags[0x44] = self.endPage      # EndPage
[245]374        self.tags[0x45] = self.reservedForFutureUse # reserved
[313]375       
[309]376        self.tags[0x46] = self.x46_class3 
[193]377       
[245]378        self.tags[0x4a] = self.reservedForFutureUse # reserved
379        self.tags[0x4b] = self.reservedForFutureUse # reserved
380        self.tags[0x4c] = self.reservedForFutureUse # reserved
381        self.tags[0x4d] = self.reservedForFutureUse # reserved
382        self.tags[0x4e] = self.reservedForFutureUse # reserved
383       
384        self.tags[0x56] = self.reservedForFutureUse # TODO : documentation not clear about reserved status
385       
386        self.tags[0x57] = self.reservedForFutureUse # reserved
[313]387       
[245]388        self.tags[0x59] = self.reservedForFutureUse # reserved
389        self.tags[0x5a] = self.reservedForFutureUse # reserved
390       
[206]391        self.tags[0x6a] = self.setColorSpace    # to detect color/b&w mode
[205]392       
[245]393        self.tags[0x87] = self.reservedForFutureUse # reserved
394        self.tags[0x88] = self.reservedForFutureUse # reserved
395        self.tags[0x89] = self.reservedForFutureUse # reserved
396        self.tags[0x8a] = self.reservedForFutureUse # reserved
[313]397       
[245]398        self.tags[0x8b] = self.reservedForFutureUse # reserved
[313]399       
[245]400        self.tags[0x8c] = self.reservedForFutureUse # reserved
401        self.tags[0x8d] = self.reservedForFutureUse # reserved
402        self.tags[0x8e] = self.reservedForFutureUse # reserved
403        self.tags[0x8f] = self.reservedForFutureUse # reserved
404        self.tags[0x90] = self.reservedForFutureUse # reserved
405       
406        self.tags[0x9a] = self.reservedForFutureUse # reserved
407        self.tags[0x9c] = self.reservedForFutureUse # reserved
408       
409        self.tags[0xa4] = self.reservedForFutureUse # reserved
410        self.tags[0xa5] = self.reservedForFutureUse # reserved
411        self.tags[0xa6] = self.reservedForFutureUse # reserved
412        self.tags[0xa7] = self.reservedForFutureUse # reserved
413       
414        self.tags[0xaa] = self.reservedForFutureUse # reserved
415        self.tags[0xab] = self.reservedForFutureUse # reserved
416        self.tags[0xac] = self.reservedForFutureUse # reserved
417        self.tags[0xad] = self.reservedForFutureUse # reserved
418        self.tags[0xae] = self.reservedForFutureUse # reserved
419        self.tags[0xaf] = self.reservedForFutureUse # reserved
420       
421        self.tags[0xb7] = self.reservedForFutureUse # reserved
422       
423        self.tags[0xba] = self.reservedForFutureUse # reserved
424        self.tags[0xbb] = self.reservedForFutureUse # reserved
425        self.tags[0xbc] = self.reservedForFutureUse # reserved
426        self.tags[0xbd] = self.reservedForFutureUse # reserved
427        self.tags[0xbe] = self.reservedForFutureUse # reserved
428       
[316]429        # self.tags[0xbf] = self.passThrough # PassThrough mode should already be taken care of automatically
[298]430       
[193]431        self.tags[0xc0] = 1 # ubyte
432        self.tags[0xc1] = 2 # uint16
433        self.tags[0xc2] = 4 # uint32
434        self.tags[0xc3] = 2 # sint16
435        self.tags[0xc4] = 4 # sint32
436        self.tags[0xc5] = 4 # real32
437       
[245]438        self.tags[0xc6] = self.reservedForFutureUse # reserved
439        self.tags[0xc7] = self.reservedForFutureUse # reserved
440       
[193]441        self.tags[0xc8] = self.array_8  # ubyte_array
442        self.tags[0xc9] = self.array_16 # uint16_array
443        self.tags[0xca] = self.array_32 # uint32_array
444        self.tags[0xcb] = self.array_16 # sint16_array
445        self.tags[0xcc] = self.array_32 # sint32_array
446        self.tags[0xcd] = self.array_32 # real32_array
447       
[245]448        self.tags[0xce] = self.reservedForFutureUse # reserved
449        self.tags[0xcf] = self.reservedForFutureUse # reserved
450       
[193]451        self.tags[0xd0] = 2 # ubyte_xy
452        self.tags[0xd1] = 4 # uint16_xy
453        self.tags[0xd2] = 8 # uint32_xy
454        self.tags[0xd3] = 4 # sint16_xy
455        self.tags[0xd4] = 8 # sint32_xy
456        self.tags[0xd5] = 8 # real32_xy
[245]457        self.tags[0xd6] = self.reservedForFutureUse # reserved
458        self.tags[0xd7] = self.reservedForFutureUse # reserved
459        self.tags[0xd8] = self.reservedForFutureUse # reserved
460        self.tags[0xd9] = self.reservedForFutureUse # reserved
461        self.tags[0xda] = self.reservedForFutureUse # reserved
462        self.tags[0xdb] = self.reservedForFutureUse # reserved
463        self.tags[0xdc] = self.reservedForFutureUse # reserved
464        self.tags[0xdd] = self.reservedForFutureUse # reserved
465        self.tags[0xde] = self.reservedForFutureUse # reserved
466        self.tags[0xdf] = self.reservedForFutureUse # reserved
[193]467       
468        self.tags[0xe0] = 4  # ubyte_box
469        self.tags[0xe1] = 8  # uint16_box
470        self.tags[0xe2] = 16 # uint32_box
471        self.tags[0xe3] = 8  # sint16_box
472        self.tags[0xe4] = 16 # sint32_box
473        self.tags[0xe5] = 16 # real32_box
[245]474        self.tags[0xe6] = self.reservedForFutureUse # reserved
475        self.tags[0xe7] = self.reservedForFutureUse # reserved
476        self.tags[0xe8] = self.reservedForFutureUse # reserved
477        self.tags[0xe9] = self.reservedForFutureUse # reserved
478        self.tags[0xea] = self.reservedForFutureUse # reserved
479        self.tags[0xeb] = self.reservedForFutureUse # reserved
480        self.tags[0xec] = self.reservedForFutureUse # reserved
481        self.tags[0xed] = self.reservedForFutureUse # reserved
482        self.tags[0xee] = self.reservedForFutureUse # reserved
483        self.tags[0xef] = self.reservedForFutureUse # reserved
[193]484       
[245]485        self.tags[0xf0] = self.reservedForFutureUse # reserved
486        self.tags[0xf1] = self.reservedForFutureUse # reserved
487        self.tags[0xf2] = self.reservedForFutureUse # reserved
488        self.tags[0xf3] = self.reservedForFutureUse # reserved
489        self.tags[0xf4] = self.reservedForFutureUse # reserved
490        self.tags[0xf5] = self.reservedForFutureUse # reserved
491        self.tags[0xf6] = self.reservedForFutureUse # reserved
492        self.tags[0xf7] = self.reservedForFutureUse # reserved
493       
[193]494        self.tags[0xf8] = 1 # attr_ubyte
495        self.tags[0xf9] = 2 # attr_uint16
496       
497        self.tags[0xfa] = self.embeddedData      # dataLength
498        self.tags[0xfb] = self.embeddedDataSmall # dataLengthByte
[245]499       
500        self.tags[0xfc] = self.reservedForFutureUse # reserved
501        self.tags[0xfd] = self.reservedForFutureUse # reserved
502        self.tags[0xfe] = self.reservedForFutureUse # reserved
503        self.tags[0xff] = self.reservedForFutureUse # reserved
[193]504           
[206]505        # color spaces   
506        self.BWColorSpace = "".join([chr(0x00), chr(0xf8), chr(0x03)])
507        self.GrayColorSpace = "".join([chr(0x01), chr(0xf8), chr(0x03)])
508        self.RGBColorSpace = "".join([chr(0x02), chr(0xf8), chr(0x03)])
509       
510        # set number of copies
511        self.setNumberOfCopies = "".join([chr(0xf8), chr(0x31)]) 
512       
[318]513        # subcodes for undocumented tag 0x46 and the negative
514        # offset to grab the value from.
515        self.x46_functions = { 0x91 : 5,
516                               0x92 : 5,
517                               0x93 : 3,
518                               0x94 : 3,
519                               0x95 : 5,
520                               0x96 : 2,
521                               0x97 : 2,
522                               0x98 : 2,
523                             }
524                             
[193]525        infileno = self.infile.fileno()
[258]526        self.pages = { 0 : { "copies" : 1, 
527                             "orientation" : "Default", 
528                             "mediatype" : "Plain", 
529                             "mediasize" : "Default", 
530                             "mediasource" : "Default", 
[299]531                             "duplex" : None,
[258]532                           } 
533                     }     
[193]534        self.minfile = minfile = mmap.mmap(infileno, os.fstat(infileno)[6], prot=mmap.PROT_READ, flags=mmap.MAP_SHARED)
535        tags = self.tags
536        self.pagecount = 0
[314]537        self.escapedStuff = {}   # For escaped datas, mostly PJL commands
538        self.prescribeStuff = {} # For Kyocera Prescribe commands
[316]539        pos = oldpos = 0
[193]540        try :
[307]541            try :
542                while 1 :
543                    try :
[316]544                        tag = ord(minfile[pos])
[307]545                    except OverflowError :   
546                        pos = oldpos + 1
547                    pos += 1
[316]548                    length = tags[tag]
[307]549                    if length :
550                        if callable(length) :   
[316]551                            length = length(pos)
[307]552                        oldpos = pos   
553                        pos += length   
554            except IndexError : # EOF ?           
555                pass
556        finally :
557            self.minfile.close()
[193]558           
559        # now handle number of copies for each page (may differ).
[206]560        if self.iscolor :
561            colormode = "Color"
562        else :   
[253]563            colormode = "BW"
[252]564           
[253]565        defaultduplexmode = "Simplex"
566        defaultpapersize = ""
[252]567        defaultpjlcopies = 1   
568        oldpjlcopies = -1
[253]569        oldduplexmode = ""
570        oldpapersize = ""
[193]571        for pnum in range(1, self.pagecount + 1) :
572            # if no number of copies defined, take 1, as explained
573            # in PCLXL documentation.
574            # NB : is number of copies is 0, the page won't be output
575            # but the formula below is still correct : we want
576            # to decrease the total number of pages in this case.
[299]577            page = self.pages.get(pnum, self.pages.get(1, { "copies" : 1, "mediasize" : "Default", "duplex" : None }))
[266]578            pjlstuff = self.escapedStuff.get(pnum, self.escapedStuff.get(0, []))
[252]579            if pjlstuff :
580                pjlparser = pjl.PJLParser("".join(pjlstuff))
581                nbdefaultcopies = int(pjlparser.default_variables.get("COPIES", -1))
582                nbcopies = int(pjlparser.environment_variables.get("COPIES", -1))
583                nbdefaultqty = int(pjlparser.default_variables.get("QTY", -1))
584                nbqty = int(pjlparser.environment_variables.get("QTY", -1))
585                if nbdefaultcopies > -1 :
586                    defaultpjlcopies = nbdefaultcopies
587                if nbdefaultqty > -1 :
588                    defaultpjlcopies = nbdefaultqty
589                if nbcopies > -1 :
[253]590                    pjlcopies = nbcopies
[252]591                elif nbqty > -1 :
[253]592                    pjlcopies = nbqty
[252]593                else :
594                    if oldpjlcopies == -1 :   
[253]595                        pjlcopies = defaultpjlcopies
596                    else :   
597                        pjlcopies = oldpjlcopies   
[299]598                if page["duplex"] :       
599                    duplexmode = page["duplex"]
600                else :   
601                    defaultdm = pjlparser.default_variables.get("DUPLEX", "")
602                    if defaultdm :
603                        if defaultdm.upper() == "ON" :
604                            defaultduplexmode = "Duplex"
605                        else :   
606                            defaultduplexmode = "Simplex"
607                    envdm = pjlparser.environment_variables.get("DUPLEX", "")
608                    if envdm :
609                        if envdm.upper() == "ON" :
610                            duplexmode = "Duplex"
611                        else :   
612                            duplexmode = "Simplex"
613                    else :       
614                        if not oldduplexmode :
615                            duplexmode = defaultduplexmode
616                        else :   
617                            duplexmode = oldduplexmode
[253]618                defaultps = pjlparser.default_variables.get("PAPER", "")
619                if defaultps :
620                    defaultpapersize = defaultps
621                envps = pjlparser.environment_variables.get("PAPER", "")
622                if envps :
623                    papersize = envps
624                else :   
625                    if not oldpapersize :
626                        papersize = defaultpapersize
627                    else :   
628                        papersize = oldpapersize
[252]629            else :       
630                if oldpjlcopies == -1 :
631                    pjlcopies = defaultpjlcopies
632                else :   
633                    pjlcopies = oldpjlcopies
[253]634                if not oldduplexmode :
635                    duplexmode = defaultduplexmode
636                else :   
637                    duplexmode = oldduplexmode
638                if not oldpapersize :   
639                    papersize = defaultpapersize
640                else :   
641                    papersize = oldpapersize
642                duplexmode = oldduplexmode
643                papersize = oldpapersize or page["mediasize"]
644            if page["mediasize"] != "Default" :
645                papersize = page["mediasize"]
646            if not duplexmode :   
647                duplexmode = oldduplexmode or defaultduplexmode
648            oldpjlcopies = pjlcopies   
649            oldduplexmode = duplexmode
650            oldpapersize = papersize
[252]651            copies = pjlcopies * page["copies"]
[193]652            self.pagecount += (copies - 1)
[253]653            self.logdebug("%s*%s*%s*%s*%s*%s*%s" % (copies, 
[252]654                                                 page["mediatype"], 
[253]655                                                 papersize, 
[252]656                                                 page["orientation"], 
657                                                 page["mediasource"], 
[253]658                                                 duplexmode, 
[252]659                                                 colormode))
[193]660        return self.pagecount
661       
662if __name__ == "__main__" :   
[415]663    pdlparser.test(Parser)
Note: See TracBrowser for help on using the browser.