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
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 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 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
19# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20#
21# $Id$
22#
23
24"""This modules implements a page counter for PCLXL (aka PCL6) documents."""
25
26import sys
27import os
28import mmap
29from struct import unpack
30
31import pdlparser
32import pjl
33
34class Parser(pdlparser.PDLParser) :
35    """A parser for PCLXL (aka PCL6) documents."""
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                     ]
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",
53                    12 : "B5",
54                    14 : "JPostcard",
55                    15 : "JDoublePostcard",
56                    16 : "A5",
57                    17 : "A6",
58                    18 : "JB6",
59                    19 : "JIS8K",
60                    20 : "JIS16K",
61                    21 : "JISExec",
62                    96 : "Default",
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",
81                     4 : "Default",
82                   }
83           
84    def isValid(self) :   
85        """Returns True if data is PCLXL aka PCL6, else False."""
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))) :
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.pagecount += 1
98       
99        # Default values
100        mediatypelabel = "Plain"
101        mediasourcelabel = "Main"
102        mediasizelabel = "Default"
103        orientationlabel = "Portrait"
104        duplexmode = None
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
109        pos = nextpos - 2
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))
117                pos -= 4
118            elif val == 0x25 :
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 
135                    # self.logdebug("Media size : %s" % mediasizelabel)
136            elif val == 0x28 :   
137                orientation = ord(minfile[pos - 2])
138                orientationlabel = self.orientations.get(orientation, str(orientation))
139                pos -= 4
140            elif val == 0x27 :   
141                savepos = pos
142                pos -= 1
143                startpos = size = None 
144                while pos > 0 : # safety check : don't go back to far !
145                    val = ord(minfile[pos])
146                    pos -= 1   
147                    if val == 0xc8 :
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
154                            size = unpack(self.unpackShort, self.minfile[pos+3:startpos])[0]
155                        elif length == 4 :   
156                            startpos = pos + 7
157                            size = unpack(self.unpackLong, self.minfile[pos+3:startpos])[0]
158                        else :   
159                            raise pdlparser.PDLParserError, "Error on size at %s : %s" % (pos+2, length)
160                        break
161                mediatypelabel = minfile[startpos:startpos+size]
162                # self.logdebug("Media type : %s" % mediatypelabel)
163            elif val == 0x34 :   
164                duplexmode = "Simplex"
165                pos -= 2
166            elif val in (0x35, 0x36) :   
167                duplexmode = "Duplex"
168                pos -= 2
169            # else : TODO : CUSTOM MEDIA SIZE AND UNIT !
170            else :   
171                pos -= 1  # ignored
172        self.pages[self.pagecount] = { "copies" : 1, 
173                                       "orientation" : orientationlabel, 
174                                       "mediatype" : mediatypelabel, 
175                                       "mediasize" : mediasizelabel,
176                                       "mediasource" : mediasourcelabel,
177                                       "duplex" : duplexmode,
178                                     } 
179        return 0
180       
181    def endPage(self, nextpos) :   
182        """Indicates the end of a page."""
183        pos3 = nextpos - 3
184        minfile = self.minfile
185        if minfile[pos3:nextpos-1] == self.setNumberOfCopies :
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
190            try :
191                self.pages[self.pagecount]["copies"] = unpack(self.unpackShort, minfile[pos3-2:pos3])[0]
192            except KeyError :   
193                self.logdebug("It looks like this PCLXL file is corrupted.")
194        return 0
195       
196    def setColorSpace(self, nextpos) :   
197        """Changes the color space."""
198        if self.minfile[nextpos-4:nextpos-1] == self.RGBColorSpace : # TODO : doesn't seem to handle all cases !
199            self.iscolor = 1
200        return 0
201           
202    def array_Generic(self, nextpos, size) :
203        """Handles all arrays."""
204        pos = nextpos
205        datatype = ord(self.minfile[pos])
206        pos += 1
207        length = self.tags[datatype]
208        if callable(length) :
209            length = length(pos)
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
214           
215    def array_8(self, nextpos) :   
216        """Handles byte arrays."""
217        return self.array_Generic(nextpos, 1)
218       
219    def array_16(self, nextpos) :
220        """Handles 16 bits arrays."""
221        return self.array_Generic(nextpos, 2)
222       
223    def array_32(self, nextpos) :
224        """Handles 32 bits arrays."""
225        return self.array_Generic(nextpos, 4)
226       
227    def embeddedDataSmall(self, nextpos) :
228        """Handle small amounts of data."""
229        return 1 + ord(self.minfile[nextpos])
230       
231    def embeddedData(self, nextpos) :
232        """Handle normal amounts of data."""
233        return 4 + unpack(self.unpackLong, self.minfile[nextpos:nextpos+4])[0]
234       
235    def littleEndian(self, nextpos) :
236        """Toggles to little endianness."""
237        self.unpackType = { 1 : "B", 2 : "<H", 4 : "<I" }
238        self.unpackShort = self.unpackType[2]
239        self.unpackLong = self.unpackType[4]
240        return 0
241       
242    def bigEndian(self, nextpos) :
243        """Toggles to big endianness."""
244        self.unpackType = { 1 : "B", 2 : ">H", 4 : ">I" }
245        self.unpackShort = self.unpackType[2]
246        self.unpackLong = self.unpackType[4]
247        return 0
248   
249    def reservedForFutureUse(self, nextpos) :
250        """Outputs something when a reserved byte is encountered."""
251        self.logdebug("Byte at %x is out of the PCLXL Protocol Class 2.0 Specification" % nextpos)
252        return 0   
253       
254    def x46_class3(self, nextpos) :
255        """Undocumented tag 0x46 in class 3.0 streams."""
256        pos = nextpos - 3
257        minfile = self.minfile
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 :   
267                pos -= offset
268                length = self.tags[ord(self.minfile[pos])]
269                if callable(length) :
270                    length = length(pos+1)
271                if funcid == 0x92 : # we want to skip these blocks
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)
276            val = ord(minfile[pos])
277        return 0   
278       
279    def escape(self, nextpos) :   
280        """Handles the ESC code."""
281        pos = endpos = nextpos
282        minfile = self.minfile
283        if minfile[pos : pos+8] == r"%-12345X" :
284            endpos = pos + 9
285            endmark = chr(0x0c) + chr(0x00) + chr(0x1b)
286            asciilimit = chr(0x80)
287            quotes = 0
288            while (minfile[endpos] not in endmark) and \
289                   ((minfile[endpos] < asciilimit) or (quotes % 2)) :
290                if minfile[endpos] == '"' :
291                    quotes += 1
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, [])
297            stuff.append(minfile[pos : endpos])
298            self.logdebug("Escaped datas : [%s]" % repr(minfile[pos : endpos]))
299        return endpos - pos
300       
301    def skipKyoceraPrescribe(self, nextpos) :
302        """Skips Kyocera Prescribe commands."""
303        pos = nextpos - 1
304        minfile = self.minfile
305        if minfile[pos:pos+3] == "!R!" :
306            while (pos - nextpos) < 1024 :   # This is a realistic upper bound, to avoid infinite loops
307                if (minfile[pos] == ";") and (minfile[pos-4:pos] == "EXIT") :
308                    pos += 1
309                    prescribe = self.prescribeStuff.setdefault(self.pagecount, [])
310                    prescribe.append(minfile[nextpos-1:pos])
311                    self.logdebug("Prescribe commands : [%s]" % repr(minfile[nextpos-1:pos]))
312                    break
313                pos += 1   
314            return (pos - nextpos)
315        else :
316            return 0
317           
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
328           
329           Protocol Class 2.1 Supplement
330           xl_ref21.pdf
331           
332           Protocol Class 3.0 Supplement
333           xl_refsup30r089.pdf
334        """
335        self.iscolor = None
336        found = 0
337        while not found :
338            line = self.infile.readline()
339            if not line :
340                break
341            pos = line.find(" HP-PCL XL;")   
342            if pos != -1 :
343                found = 1
344                endian = ord(line[pos - 1])
345                if endian == 0x29 :
346                    self.littleEndian(0)
347                elif endian == 0x28 :   
348                    self.bigEndian(0)
349                # elif endian == 0x27 : # TODO : This is the ESC code : parse it for PJL statements !
350                #
351                else :   
352                    raise pdlparser.PDLParserError, "Unknown endianness marker 0x%02x at start !" % endian
353        if not found :
354            raise pdlparser.PDLParserError, "This file doesn't seem to be PCLXL (aka PCL6)"
355           
356        # Initialize Media Sources
357        for i in range(8, 256) :
358            self.mediasources[i] = "ExternalTray%03i" % (i - 7)
359           
360        # Initialize table of tags
361        self.tags = [ 0 ] * 256   
362       
363        self.tags[0x1b] = self.escape # The escape code
364       
365        self.tags[0x21] = self.skipKyoceraPrescribe # 0x21 is not normally used
366       
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
374        self.tags[0x45] = self.reservedForFutureUse # reserved
375       
376        self.tags[0x46] = self.x46_class3 
377       
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
387       
388        self.tags[0x59] = self.reservedForFutureUse # reserved
389        self.tags[0x5a] = self.reservedForFutureUse # reserved
390       
391        self.tags[0x6a] = self.setColorSpace    # to detect color/b&w mode
392       
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
397       
398        self.tags[0x8b] = self.reservedForFutureUse # reserved
399       
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       
429        # self.tags[0xbf] = self.passThrough # PassThrough mode should already be taken care of automatically
430       
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       
438        self.tags[0xc6] = self.reservedForFutureUse # reserved
439        self.tags[0xc7] = self.reservedForFutureUse # reserved
440       
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       
448        self.tags[0xce] = self.reservedForFutureUse # reserved
449        self.tags[0xcf] = self.reservedForFutureUse # reserved
450       
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
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
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
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
484       
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       
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
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
504           
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       
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                             
525        infileno = self.infile.fileno()
526        self.pages = { 0 : { "copies" : 1, 
527                             "orientation" : "Default", 
528                             "mediatype" : "Plain", 
529                             "mediasize" : "Default", 
530                             "mediasource" : "Default", 
531                             "duplex" : None,
532                           } 
533                     }     
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
537        self.escapedStuff = {}   # For escaped datas, mostly PJL commands
538        self.prescribeStuff = {} # For Kyocera Prescribe commands
539        pos = oldpos = 0
540        try :
541            try :
542                while 1 :
543                    try :
544                        tag = ord(minfile[pos])
545                    except OverflowError :   
546                        pos = oldpos + 1
547                    pos += 1
548                    length = tags[tag]
549                    if length :
550                        if callable(length) :   
551                            length = length(pos)
552                        oldpos = pos   
553                        pos += length   
554            except IndexError : # EOF ?           
555                pass
556        finally :
557            self.minfile.close()
558           
559        # now handle number of copies for each page (may differ).
560        if self.iscolor :
561            colormode = "Color"
562        else :   
563            colormode = "BW"
564           
565        defaultduplexmode = "Simplex"
566        defaultpapersize = ""
567        defaultpjlcopies = 1   
568        oldpjlcopies = -1
569        oldduplexmode = ""
570        oldpapersize = ""
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.
577            page = self.pages.get(pnum, self.pages.get(1, { "copies" : 1, "mediasize" : "Default", "duplex" : None }))
578            pjlstuff = self.escapedStuff.get(pnum, self.escapedStuff.get(0, []))
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 :
590                    pjlcopies = nbcopies
591                elif nbqty > -1 :
592                    pjlcopies = nbqty
593                else :
594                    if oldpjlcopies == -1 :   
595                        pjlcopies = defaultpjlcopies
596                    else :   
597                        pjlcopies = oldpjlcopies   
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
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
629            else :       
630                if oldpjlcopies == -1 :
631                    pjlcopies = defaultpjlcopies
632                else :   
633                    pjlcopies = oldpjlcopies
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
651            copies = pjlcopies * page["copies"]
652            self.pagecount += (copies - 1)
653            self.logdebug("%s*%s*%s*%s*%s*%s*%s" % (copies, 
654                                                 page["mediatype"], 
655                                                 papersize, 
656                                                 page["orientation"], 
657                                                 page["mediasource"], 
658                                                 duplexmode, 
659                                                 colormode))
660        return self.pagecount
661       
662if __name__ == "__main__" :   
663    pdlparser.test(Parser)
Note: See TracBrowser for help on using the browser.