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

Revision 491, 30.5 kB (checked in by jerome, 16 years ago)

Major code cleaning. Now clearer, although probably a bit slower since
a file can be opened several times.
Now universal line opening mode is only used when needed (PS, PDF and plain
text), and binary opening mode is used for the other formats.
This mean we will be able to remove mmap calls wherever possible, finally.

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