root / pkpgcounter / trunk / pkpgpdls / qpdl.py @ 3474

Revision 3474, 11.4 kB (checked in by jerome, 13 years ago)

Changed copyright years.

  • Property svn:keywords set to Author Date Id Revision
Line 
1# -*- coding: utf-8 -*-
2#
3# pkpgcounter : a generic Page Description Language parser
4#
5# (c) 2003-2009 Jerome Alet <alet@librelogiciel.com>
6# This program is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation, either version 3 of the License, or
9# (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program.  If not, see <http://www.gnu.org/licenses/>.
18#
19# $Id$
20#
21
22"""This modules implements a page counter for QPDL (aka SPL2) documents."""
23
24import sys
25import os
26import mmap
27from struct import unpack
28
29import pdlparser
30import pjl
31
32class Parser(pdlparser.PDLParser) :
33    """A parser for QPDL (aka SPL2) documents."""
34    format = "QPDL (aka SPL2)"
35    mediasizes = {
36                    # The first values are identical to that of PCLXL
37                    0 : "Letter",
38                    1 : "Legal",
39                    2 : "A4",
40                    3 : "Executive",
41                    4 : "Ledger",
42                    5 : "A3",
43                    6 : "COM10Envelope",
44                    7 : "MonarchEnvelope",
45                    8 : "C5Envelope",
46                    9 : "DLEnvelope",
47                    10 : "JB4",
48                    11 : "JB5",
49                    12 : "B5Envelope",
50                    12 : "B5",
51                    14 : "JPostcard",
52                    15 : "JDoublePostcard",
53                    16 : "A5",
54                    17 : "A6",
55                    18 : "JB6",
56                    21 : "Custom",
57                    23 : "C6",
58                    24 : "Folio",
59                 }
60
61    mediasources = {
62                     # Again, values are identical to that of PCLXL
63                     0 : "Default",
64                     1 : "Auto",
65                     2 : "Manual",
66                     3 : "MultiPurpose",
67                     4 : "UpperCassette",
68                     5 : "LowerCassette",
69                     6 : "EnvelopeTray",
70                     7 : "ThirdCassette",
71                   }
72
73    def isValid(self) :
74        """Returns True if data is QPDL aka SPL2, else False."""
75        if ((self.firstblock[:128].find("\033%-12345X") != -1) and \
76             ((self.firstblock.find("LANGUAGE=QPDL") != -1) or \
77              (self.firstblock.find("LANGUAGE = QPDL") != -1))) :
78            return True
79        else :
80            return False
81
82    def beginPage(self, nextpos) :
83        """Indicates the beginning of a new page, and extracts media information."""
84        self.pagecount += 1
85
86        copies = unpack(self.unpackShort, self.minfile[nextpos+1:nextpos+3])[0]
87        mediasize = ord(self.minfile[nextpos+3])
88        mediasource = ord(self.minfile[nextpos+8])
89        duplexmode = unpack(self.unpackShort, self.minfile[nextpos+10:nextpos+12])[0]
90
91        self.pages[self.pagecount] = { "copies" : copies,
92                                       "mediasize" : self.mediasizes.get(mediasize, str(mediasize)),
93                                       "mediasource" : self.mediasources.get(mediasource, str(mediasource)),
94                                       "duplex" : duplexmode,
95                                     }
96        return 16       # Length of a page header
97
98    def endPage(self, nextpos) :
99        """Indicates the end of a page."""
100        epcopies = unpack(self.unpackShort, self.minfile[nextpos:nextpos+2])[0]
101        bpcopies = self.pages[self.pagecount]["copies"]
102        if epcopies != bpcopies :
103            self.logdebug("ERROR: discrepancy between beginPage (%i) and endPage (%i) copies" % (bpcopies, epcopies))
104        return 2        # Length of a page footer
105
106    def beginBand(self, nextpos) :
107        """Indicates the beginning of a new band."""
108        bandlength = unpack(self.unpackLong, self.minfile[nextpos+6:nextpos+10])[0]
109        return bandlength + 10 # Length of a band header - length of checksum
110
111    def littleEndian(self) :
112        """Toggles to little endianness."""
113        self.unpackType = { 1 : "B", 2 : "<H", 4 : "<I" }
114        self.unpackShort = self.unpackType[2]
115        self.unpackLong = self.unpackType[4]
116        return 0
117
118    def bigEndian(self) :
119        """Toggles to big endianness."""
120        self.unpackType = { 1 : "B", 2 : ">H", 4 : ">I" }
121        self.unpackShort = self.unpackType[2]
122        self.unpackLong = self.unpackType[4]
123        return 0
124
125    def escape(self, nextpos) :
126        """Handles the ESC code."""
127        pos = endpos = nextpos
128        minfile = self.minfile
129        if minfile[pos : pos+8] == r"%-12345X" :
130            endpos = pos + 9
131            endmark = chr(0x0c) + chr(0x00) + chr(0x1b)
132            asciilimit = chr(0x80)
133            quotes = 0
134            while (minfile[endpos] not in endmark) and \
135                   ((minfile[endpos] < asciilimit) or (quotes % 2)) :
136                if minfile[endpos] == '"' :
137                    quotes += 1
138                endpos += 1
139
140            # Store this in a per page mapping.
141            # NB : First time will be at page 0 (i.e. **before** page 1) !
142            stuff = self.escapedStuff.setdefault(self.pagecount, [])
143            stuff.append(minfile[pos : endpos])
144            self.logdebug("Escaped datas : [%s]" % repr(minfile[pos : endpos]))
145        return endpos - pos
146
147    def maybeEOF(self, nextpos) :
148        """Tries to detect the EOF marker."""
149        if self.minfile[nextpos:nextpos+9] == self.eofmarker :
150            return 9
151        else :
152            return 0
153
154    def getJobSize(self) :
155        """Counts pages in a QPDL (SPL2) document.
156
157           Algorithm by Jerome Alet.
158
159           The documentation used for this was :
160
161           Sp�fication Technique (documentation non officielle)
162           Le Language SPL2
163           par Aur�en Croc
164           http://splix.ap2c.org
165        """
166        # Initialize table of tags
167        self.tags = [ lambda pos : 0 ] * 256
168        self.tags[0x00] = self.beginPage
169        self.tags[0x01] = self.endPage
170        self.tags[0x09] = self.maybeEOF
171        self.tags[0x0c] = self.beginBand
172        self.tags[0x1b] = self.escape # The escape code
173
174        self.eofmarker = "\033%-12345X"
175
176        infileno = self.infile.fileno()
177        self.pages = { 0 : { "copies" : 1,
178                             "orientation" : "Default",
179                             "mediatype" : "Plain",
180                             "mediasize" : "Default",
181                             "mediasource" : "Default",
182                             "duplex" : None,
183                           }
184                     }
185        self.minfile = minfile = mmap.mmap(infileno, os.fstat(infileno)[6], prot=mmap.PROT_READ, flags=mmap.MAP_SHARED)
186        self.pagecount = 0
187        self.escapedStuff = {}   # For escaped datas, mostly PJL commands
188        self.bigEndian()
189        pos = 0
190        tags = self.tags
191        try :
192            try :
193                while 1 :
194                    tag = ord(minfile[pos])
195                    pos += 1
196                    pos += tags[tag](pos)
197            except IndexError : # EOF ?
198                pass
199        finally :
200            self.minfile.close()
201
202        defaultduplexmode = "Simplex"
203        defaultpapersize = ""
204        defaultpjlcopies = 1
205        oldpjlcopies = -1
206        oldduplexmode = ""
207        oldpapersize = ""
208        for pnum in range(1, self.pagecount + 1) :
209            # NB : is number of copies is 0, the page won't be output
210            # but the formula below is still correct : we want
211            # to decrease the total number of pages in this case.
212            page = self.pages.get(pnum, self.pages.get(1, { "copies" : 1, "mediasize" : "Default", "duplex" : None }))
213            pjlstuff = self.escapedStuff.get(pnum, self.escapedStuff.get(0, []))
214            if pjlstuff :
215                pjlparser = pjl.PJLParser("".join(pjlstuff))
216                nbdefaultcopies = int(pjlparser.default_variables.get("COPIES", -1))
217                nbcopies = int(pjlparser.environment_variables.get("COPIES", -1))
218                nbdefaultqty = int(pjlparser.default_variables.get("QTY", -1))
219                nbqty = int(pjlparser.environment_variables.get("QTY", -1))
220                if nbdefaultcopies > -1 :
221                    defaultpjlcopies = nbdefaultcopies
222                if nbdefaultqty > -1 :
223                    defaultpjlcopies = nbdefaultqty
224                if nbcopies > -1 :
225                    pjlcopies = nbcopies
226                elif nbqty > -1 :
227                    pjlcopies = nbqty
228                else :
229                    if oldpjlcopies == -1 :
230                        pjlcopies = defaultpjlcopies
231                    else :
232                        pjlcopies = oldpjlcopies
233                if page["duplex"] :
234                    duplexmode = page["duplex"]
235                else :
236                    defaultdm = pjlparser.default_variables.get("DUPLEX", "")
237                    if defaultdm :
238                        if defaultdm.upper() == "ON" :
239                            defaultduplexmode = "Duplex"
240                        else :
241                            defaultduplexmode = "Simplex"
242                    envdm = pjlparser.environment_variables.get("DUPLEX", "")
243                    if envdm :
244                        if envdm.upper() == "ON" :
245                            duplexmode = "Duplex"
246                        else :
247                            duplexmode = "Simplex"
248                    else :
249                        if not oldduplexmode :
250                            duplexmode = defaultduplexmode
251                        else :
252                            duplexmode = oldduplexmode
253                defaultps = pjlparser.default_variables.get("PAPER", "")
254                if defaultps :
255                    defaultpapersize = defaultps
256                envps = pjlparser.environment_variables.get("PAPER", "")
257                if envps :
258                    papersize = envps
259                else :
260                    if not oldpapersize :
261                        papersize = defaultpapersize
262                    else :
263                        papersize = oldpapersize
264            else :
265                if oldpjlcopies == -1 :
266                    pjlcopies = defaultpjlcopies
267                else :
268                    pjlcopies = oldpjlcopies
269                if not oldduplexmode :
270                    duplexmode = defaultduplexmode
271                else :
272                    duplexmode = oldduplexmode
273                if not oldpapersize :
274                    papersize = defaultpapersize
275                else :
276                    papersize = oldpapersize
277                duplexmode = oldduplexmode
278                papersize = oldpapersize or page["mediasize"]
279            if page["mediasize"] != "Default" :
280                papersize = page["mediasize"]
281            if not duplexmode :
282                duplexmode = oldduplexmode or defaultduplexmode
283            oldpjlcopies = pjlcopies
284            oldduplexmode = duplexmode
285            oldpapersize = papersize
286            copies = max(pjlcopies, page["copies"]) # Was : pjlcopies * page["copies"]
287            self.pagecount += (copies - 1)
288            self.logdebug("%s*%s*%s*%s" % (copies,
289                                           papersize,
290                                           page["mediasource"],
291                                           duplexmode))
292        return self.pagecount
Note: See TracBrowser for help on using the browser.