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
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, 2007 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 3 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, see <http://www.gnu.org/licenses/>.
19#
20# $Id$
21#
22
23"""This modules implements a page counter for PCLXL (aka PCL6) documents."""
24
25import sys
26import os
27import mmap
28from struct import unpack
29
30import pdlparser
31import pjl
32
33class Parser(pdlparser.PDLParser) :
34    """A parser for PCLXL (aka PCL6) documents."""
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" -',
37                     ]
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",
52                    12 : "B5",
53                    14 : "JPostcard",
54                    15 : "JDoublePostcard",
55                    16 : "A5",
56                    17 : "A6",
57                    18 : "JB6",
58                    19 : "JIS8K",
59                    20 : "JIS16K",
60                    21 : "JISExec",
61                    96 : "Default",
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",
80                     4 : "Default",
81                   }
82           
83    def isValid(self) :   
84        """Returns True if data is PCLXL aka PCL6, else False."""
85        if (((self.firstblock[:128].find("\033%-12345X") != -1) and \
86             (self.firstblock.find(" HP-PCL XL;") != -1) and \
87             ((self.firstblock.find("LANGUAGE=PCLXL") != -1) or \
88              (self.firstblock.find("LANGUAGE = PCLXL") != -1)))) \
89             or ((self.firstblock.startswith(chr(0xcd)+chr(0xca)) and (self.firstblock.find(" HP-PCL XL;") != -1))) :
90            self.logdebug("DEBUG: Input file is in the PCLXL (aka PCL6) format.")
91            return True
92        else :   
93            return False
94           
95    def beginPage(self, nextpos) :
96        """Indicates the beginning of a new page, and extracts media information."""
97        # self.logdebug("BeginPage at %x" % nextpos)
98        self.pagecount += 1
99       
100        # Default values
101        mediatypelabel = "Plain"
102        mediasourcelabel = "Main"
103        mediasizelabel = "Default"
104        orientationlabel = "Portrait"
105        duplexmode = None
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
110        pos = nextpos - 2
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))
118                pos -= 4
119            elif val == 0x25 :
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 
136                    # self.logdebug("Media size : %s" % mediasizelabel)
137            elif val == 0x28 :   
138                orientation = ord(minfile[pos - 2])
139                orientationlabel = self.orientations.get(orientation, str(orientation))
140                pos -= 4
141            elif val == 0x27 :   
142                savepos = pos
143                pos -= 1
144                startpos = size = None 
145                while pos > 0 : # safety check : don't go back to far !
146                    val = ord(minfile[pos])
147                    pos -= 1   
148                    if val == 0xc8 :
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
155                            size = unpack(self.unpackShort, self.minfile[pos+3:startpos])[0]
156                        elif length == 4 :   
157                            startpos = pos + 7
158                            size = unpack(self.unpackLong, self.minfile[pos+3:startpos])[0]
159                        else :   
160                            raise pdlparser.PDLParserError, "Error on size at %s : %s" % (pos+2, length)
161                        break
162                mediatypelabel = minfile[startpos:startpos+size]
163                # self.logdebug("Media type : %s" % mediatypelabel)
164            elif val == 0x34 :   
165                duplexmode = "Simplex"
166                pos -= 2
167            elif val in (0x35, 0x36) :   
168                duplexmode = "Duplex"
169                pos -= 2
170            # else : TODO : CUSTOM MEDIA SIZE AND UNIT !
171            else :   
172                pos -= 1  # ignored
173        self.pages[self.pagecount] = { "copies" : 1, 
174                                       "orientation" : orientationlabel, 
175                                       "mediatype" : mediatypelabel, 
176                                       "mediasize" : mediasizelabel,
177                                       "mediasource" : mediasourcelabel,
178                                       "duplex" : duplexmode,
179                                     } 
180        return 0
181       
182    def endPage(self, nextpos) :   
183        """Indicates the end of a page."""
184        # self.logdebug("EndPage at %x" % nextpos)
185        pos3 = nextpos - 3
186        minfile = self.minfile
187        if minfile[pos3:nextpos-1] == self.setNumberOfCopies :
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
192            try :
193                nbcopies = unpack(self.unpackShort, minfile[pos3-2:pos3])[0]
194                # self.logdebug("Number of copies : %i" % nbcopies)
195                self.pages[self.pagecount]["copies"] = nbcopies
196            except KeyError :   
197                self.logdebug("It looks like this PCLXL file is corrupted.")
198        return 0
199       
200    def setColorSpace(self, nextpos) :   
201        """Changes the color space."""
202        if self.minfile[nextpos-4:nextpos-1] == self.RGBColorSpace : # TODO : doesn't seem to handle all cases !
203            self.iscolor = True
204        return 0
205           
206    def array_Generic(self, nextpos, size) :
207        """Handles all arrays."""
208        pos = nextpos
209        datatype = ord(self.minfile[pos])
210        pos += 1
211        length = self.tags[datatype]
212        if callable(length) :
213            length = length(pos)
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
218           
219    def array_8(self, nextpos) :   
220        """Handles byte arrays."""
221        return self.array_Generic(nextpos, 1)
222       
223    def array_16(self, nextpos) :
224        """Handles 16 bits arrays."""
225        return self.array_Generic(nextpos, 2)
226       
227    def array_32(self, nextpos) :
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)
245       
246    def embeddedDataSmall(self, nextpos) :
247        """Handle small amounts of data."""
248        return 1 + ord(self.minfile[nextpos])
249       
250    def embeddedData(self, nextpos) :
251        """Handle normal amounts of data."""
252        return 4 + unpack(self.unpackLong, self.minfile[nextpos:nextpos+4])[0]
253       
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       
267    def littleEndian(self, nextpos) :
268        """Toggles to little endianness."""
269        self.unpackType = { 1 : "B", 2 : "<H", 4 : "<I" }
270        self.unpackShort = self.unpackType[2]
271        self.unpackLong = self.unpackType[4]
272        # self.logdebug("LittleEndian at %x" % (nextpos - 1))
273        return self.skipHPPCLXL(nextpos)
274       
275    def bigEndian(self, nextpos) :
276        """Toggles to big endianness."""
277        self.unpackType = { 1 : "B", 2 : ">H", 4 : ">I" }
278        self.unpackShort = self.unpackType[2]
279        self.unpackLong = self.unpackType[4]
280        # self.logdebug("BigEndian at %x" % (nextpos - 1))
281        return self.skipHPPCLXL(nextpos)
282   
283    def reservedForFutureUse(self, nextpos) :
284        """Outputs something when a reserved byte is encountered."""
285        self.logdebug("Byte at %x is out of the PCLXL Protocol Class 2.0 Specification" % nextpos)
286        return 0   
287       
288    def x46_class3(self, nextpos) :
289        """Undocumented tag 0x46 in class 3.0 streams."""
290        pos = nextpos - 3
291        minfile = self.minfile
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 :   
301                pos -= offset
302                length = self.tags[ord(self.minfile[pos])]
303                if callable(length) :
304                    length = length(pos+1)
305                if funcid == 0x92 : # we want to skip these blocks
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)
310            val = ord(minfile[pos])
311        return 0   
312       
313    def escape(self, nextpos) :   
314        """Handles the ESC code."""
315        pos = endpos = nextpos
316        minfile = self.minfile
317        if minfile[pos : pos+8] == r"%-12345X" :
318            endpos = pos + 9
319            endmark = chr(0x0c) + chr(0x00) + chr(0x1b)
320            asciilimit = chr(0x80)
321            quotes = 0
322            while (minfile[endpos] not in endmark) and \
323                   ((minfile[endpos] < asciilimit) or (quotes % 2)) :
324                if minfile[endpos] == '"' :
325                    quotes += 1
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, [])
331            stuff.append(minfile[pos : endpos])
332            self.logdebug("Escaped datas : [%s]" % repr(minfile[pos : endpos]))
333        return endpos - pos
334       
335    def skipKyoceraPrescribe(self, nextpos) :
336        """Skips Kyocera Prescribe commands."""
337        pos = nextpos - 1
338        minfile = self.minfile
339        if minfile[pos:pos+3] == "!R!" :
340            while (pos - nextpos) < 1024 :   # This is a realistic upper bound, to avoid infinite loops
341                if (minfile[pos] == ";") and (minfile[pos-4:pos] == "EXIT") :
342                    pos += 1
343                    prescribe = self.prescribeStuff.setdefault(self.pagecount, [])
344                    prescribe.append(minfile[nextpos-1:pos])
345                    self.logdebug("Prescribe commands : [%s]" % repr(minfile[nextpos-1:pos]))
346                    break
347                pos += 1   
348            return (pos - nextpos)
349        else :
350            return 0
351           
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
362           
363           Protocol Class 2.1 Supplement
364           xl_ref21.pdf
365           
366           Protocol Class 3.0 Supplement
367           xl_refsup30r089.pdf
368        """
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       
375        found = False
376        while not found :
377            line = self.infile.readline()
378            if not line :
379                break
380            pos = line.find(" HP-PCL XL;")   
381            if pos != -1 :
382                found = True
383                endian = ord(line[pos - 1])
384                if endian == 0x29 :
385                    self.littleEndian(0)
386                elif endian == 0x28 :   
387                    self.bigEndian(0)
388                # elif endian == 0x27 : # TODO : This is the ASCII binding code : what does it do exactly ?
389                #
390                else :   
391                    raise pdlparser.PDLParserError, "Unknown endianness marker 0x%02x at start !" % endian
392        if not found :
393            raise pdlparser.PDLParserError, "This file doesn't seem to be PCLXL (aka PCL6)"
394           
395        # Initialize Media Sources
396        for i in range(8, 256) :
397            self.mediasources[i] = "ExternalTray%03i" % (i - 7)
398           
399        # Initialize table of tags
400        self.tags = [ 0 ] * 256   
401       
402        self.tags[0x1b] = self.escape # The escape code
403       
404        self.tags[0x21] = self.skipKyoceraPrescribe # 0x21 is not normally used
405       
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
413        self.tags[0x45] = self.reservedForFutureUse # reserved
414       
415        self.tags[0x46] = self.x46_class3 
416       
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
426       
427        self.tags[0x59] = self.reservedForFutureUse # reserved
428        self.tags[0x5a] = self.reservedForFutureUse # reserved
429       
430        self.tags[0x6a] = self.setColorSpace    # to detect color/b&w mode
431       
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
436       
437        self.tags[0x8b] = self.reservedForFutureUse # reserved
438       
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       
468        # self.tags[0xbf] = self.passThrough # PassThrough mode should already be taken care of automatically
469       
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       
477        self.tags[0xc6] = self.reservedForFutureUse # reserved
478        self.tags[0xc7] = self.reservedForFutureUse # reserved
479       
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
485        self.tags[0xcd] = self.array_32 # real32_array and unfortunately Canon ImageRunner
486       
487        self.tags[0xce] = self.reservedForFutureUse # reserved
488        self.tags[0xcf] = self.reservedForFutureUse # reserved
489       
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
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
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
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
523       
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       
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
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
543           
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       
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                             
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                             
568        self.pages = { 0 : { "copies" : 1, 
569                             "orientation" : "Default", 
570                             "mediatype" : "Plain", 
571                             "mediasize" : "Default", 
572                             "mediasource" : "Default", 
573                             "duplex" : None,
574                           } 
575                     }     
576        tags = self.tags
577        self.pagecount = 0
578        self.escapedStuff = {}   # For escaped datas, mostly PJL commands
579        self.prescribeStuff = {} # For Kyocera Prescribe commands
580        pos = oldpos = 0
581        try :
582            try :
583                while 1 :
584                    try :
585                        tag = ord(minfile[pos])
586                    except OverflowError :   
587                        pos = oldpos + 1
588                    pos += 1
589                    length = tags[tag]
590                    if length :
591                        if callable(length) :   
592                            length = length(pos)
593                        oldpos = pos   
594                        pos += length   
595            except IndexError : # EOF ?           
596                pass
597        finally :
598            self.minfile.close()
599           
600        # now handle number of copies for each page (may differ).
601        if self.iscolor :
602            colormode = "Color"
603        else :   
604            colormode = "BW"
605           
606        defaultduplexmode = "Simplex"
607        defaultpapersize = ""
608        defaultpjlcopies = 1   
609        oldpjlcopies = -1
610        oldduplexmode = ""
611        oldpapersize = ""
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.
618            page = self.pages.get(pnum, self.pages.get(1, { "copies" : 1, "mediasize" : "Default", "duplex" : None }))
619            pjlstuff = self.escapedStuff.get(pnum, self.escapedStuff.get(0, []))
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 :
631                    pjlcopies = nbcopies
632                elif nbqty > -1 :
633                    pjlcopies = nbqty
634                else :
635                    if oldpjlcopies == -1 :   
636                        pjlcopies = defaultpjlcopies
637                    else :   
638                        pjlcopies = oldpjlcopies   
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
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
670            else :       
671                if oldpjlcopies == -1 :
672                    pjlcopies = defaultpjlcopies
673                else :   
674                    pjlcopies = oldpjlcopies
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
692            copies = max(pjlcopies, page["copies"]) # Was : pjlcopies * page["copies"]
693            self.pagecount += (copies - 1)
694            self.logdebug("%s*%s*%s*%s*%s*%s*%s" % (copies, 
695                                                 page["mediatype"], 
696                                                 papersize, 
697                                                 page["orientation"], 
698                                                 page["mediasource"], 
699                                                 duplexmode, 
700                                                 colormode))
701        return self.pagecount
Note: See TracBrowser for help on using the browser.