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

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

Changed copyright years.

  • Property svn:keywords set to Author Date Id Revision
RevLine 
[3410]1# -*- coding: utf-8 -*-
[386]2#
3# pkpgcounter : a generic Page Description Language parser
4#
[3474]5# (c) 2003-2009 Jerome Alet <alet@librelogiciel.com>
[463]6# This program is free software: you can redistribute it and/or modify
[386]7# it under the terms of the GNU General Public License as published by
[463]8# the Free Software Foundation, either version 3 of the License, or
[386]9# (at your option) any later version.
[3436]10#
[386]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.
[3436]15#
[386]16# You should have received a copy of the GNU General Public License
[463]17# along with this program.  If not, see <http://www.gnu.org/licenses/>.
[386]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."""
[555]34    format = "QPDL (aka SPL2)"
[3436]35    mediasizes = {
[386]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",
[3436]59                 }
60
61    mediasources = {
[386]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                   }
[3436]72
73    def isValid(self) :
[387]74        """Returns True if data is QPDL aka SPL2, else False."""
[522]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))) :
[387]78            return True
[3436]79        else :
[387]80            return False
[3436]81
[386]82    def beginPage(self, nextpos) :
83        """Indicates the beginning of a new page, and extracts media information."""
84        self.pagecount += 1
[3436]85
[386]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]
[3436]90
91        self.pages[self.pagecount] = { "copies" : copies,
[386]92                                       "mediasize" : self.mediasizes.get(mediasize, str(mediasize)),
93                                       "mediasource" : self.mediasources.get(mediasource, str(mediasource)),
94                                       "duplex" : duplexmode,
[3436]95                                     }
[386]96        return 16       # Length of a page header
[3436]97
98    def endPage(self, nextpos) :
[386]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
[3436]105
[386]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
[3436]110
[386]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
[3436]117
[386]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
[3436]124
125    def escape(self, nextpos) :
[386]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
[3436]139
140            # Store this in a per page mapping.
[386]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
[3436]146
147    def maybeEOF(self, nextpos) :
[386]148        """Tries to detect the EOF marker."""
149        if self.minfile[nextpos:nextpos+9] == self.eofmarker :
150            return 9
[3436]151        else :
[386]152            return 0
[3436]153
[386]154    def getJobSize(self) :
155        """Counts pages in a QPDL (SPL2) document.
[3436]156
[386]157           Algorithm by Jerome Alet.
[3436]158
[386]159           The documentation used for this was :
[3436]160
[386]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
[3436]167        self.tags = [ lambda pos : 0 ] * 256
[386]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
[3436]173
[386]174        self.eofmarker = "\033%-12345X"
[3436]175
[386]176        infileno = self.infile.fileno()
[3436]177        self.pages = { 0 : { "copies" : 1,
178                             "orientation" : "Default",
179                             "mediatype" : "Plain",
180                             "mediasize" : "Default",
181                             "mediasource" : "Default",
[386]182                             "duplex" : None,
[3436]183                           }
184                     }
[386]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)
[3436]197            except IndexError : # EOF ?
[386]198                pass
199        finally :
200            self.minfile.close()
[3436]201
[386]202        defaultduplexmode = "Simplex"
203        defaultpapersize = ""
[3436]204        defaultpjlcopies = 1
[386]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
[3436]210            # but the formula below is still correct : we want
[386]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 :
[3436]229                    if oldpjlcopies == -1 :
[386]230                        pjlcopies = defaultpjlcopies
[3436]231                    else :
232                        pjlcopies = oldpjlcopies
233                if page["duplex"] :
[386]234                    duplexmode = page["duplex"]
[3436]235                else :
[386]236                    defaultdm = pjlparser.default_variables.get("DUPLEX", "")
237                    if defaultdm :
238                        if defaultdm.upper() == "ON" :
239                            defaultduplexmode = "Duplex"
[3436]240                        else :
[386]241                            defaultduplexmode = "Simplex"
242                    envdm = pjlparser.environment_variables.get("DUPLEX", "")
243                    if envdm :
244                        if envdm.upper() == "ON" :
245                            duplexmode = "Duplex"
[3436]246                        else :
[386]247                            duplexmode = "Simplex"
[3436]248                    else :
[386]249                        if not oldduplexmode :
250                            duplexmode = defaultduplexmode
[3436]251                        else :
[386]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
[3436]259                else :
[386]260                    if not oldpapersize :
261                        papersize = defaultpapersize
[3436]262                    else :
[386]263                        papersize = oldpapersize
[3436]264            else :
[386]265                if oldpjlcopies == -1 :
266                    pjlcopies = defaultpjlcopies
[3436]267                else :
[386]268                    pjlcopies = oldpjlcopies
269                if not oldduplexmode :
270                    duplexmode = defaultduplexmode
[3436]271                else :
[386]272                    duplexmode = oldduplexmode
[3436]273                if not oldpapersize :
[386]274                    papersize = defaultpapersize
[3436]275                else :
[386]276                    papersize = oldpapersize
277                duplexmode = oldduplexmode
278                papersize = oldpapersize or page["mediasize"]
279            if page["mediasize"] != "Default" :
280                papersize = page["mediasize"]
[3436]281            if not duplexmode :
[386]282                duplexmode = oldduplexmode or defaultduplexmode
[3436]283            oldpjlcopies = pjlcopies
[386]284            oldduplexmode = duplexmode
285            oldpapersize = papersize
[456]286            copies = max(pjlcopies, page["copies"]) # Was : pjlcopies * page["copies"]
[386]287            self.pagecount += (copies - 1)
[3436]288            self.logdebug("%s*%s*%s*%s" % (copies,
289                                           papersize,
290                                           page["mediasource"],
[386]291                                           duplexmode))
292        return self.pagecount
Note: See TracBrowser for help on using the browser.