Changeset 1575

Show
Ignore:
Timestamp:
06/28/04 23:20:32 (20 years ago)
Author:
jalet
Message:

PCLXL support now works !

Location:
pykota/trunk
Files:
6 modified

Legend:

Unmodified
Added
Removed
  • pykota/trunk/conf/pykota.conf.sample

    r1517 r1575  
    284284#                 pkpgcounter is a command line tool which is 
    285285#                 part of PyKota and which can handle both 
    286 #                 DSC compliant PostScript documents and PCL5 
    287 #                 documents. More file formats will be added 
     286#                 DSC compliant PostScript, PCL5, PCL6 (aka PCLXL) 
     287#                 and PDF documents. More file formats will be added 
    288288#                 in the future, as time permits. 
    289289# 
  • pykota/trunk/man/pkpgcounter.1

    r1548 r1575  
    11.TH PKPGCOUNTER "1" "April 2004" "C@LL - Conseil Internet & Logiciels Libres" "User Commands" 
    22.SH NAME 
    3 pkpgcounter \- PyKota script to count pages in PostScript, PDF or PCL documents 
     3pkpgcounter \- PyKota script to count pages in PostScript, PDF, PCL5 and PCL6 documents 
    44.SH SYNOPSIS 
    55.B pkpgcounter 
     
    1313.PP 
    1414\fBpkpgcounter\fP is a convenience script provided by pykota that attempts 
    15 to count the number of pages in a PostScript, PDF or PCL document. This may be of use 
     15to count the number of pages in a DSC compliant PostScript, PDF, PCL5 or  
     16PCL6 (aka PCLXL) document. This may be of use 
    1617as an external accounter as defined in /etc/pykota/pykota.conf, since currently 
    1718\fBpkpgcounter\fP is the smarter software accounter included with PyKota. 
  • pykota/trunk/NEWS

    r1573 r1575  
    2222PyKota NEWS : 
    2323 
     24    - 1.19alpha28 : 
     25     
     26        - First working PCL6 (aka PCLXL) parser ! 
     27          Doesn't handle copies yet. 
     28         
    2429    - 1.19alpha27 : 
    2530     
  • pykota/trunk/pykota/pdlanalyzer.py

    r1574 r1575  
    2222# 
    2323# $Log$ 
     24# Revision 1.19  2004/06/28 21:20:30  jalet 
     25# PCLXL support now works ! 
     26# 
    2427# Revision 1.18  2004/06/27 22:59:37  jalet 
    2528# More work on PCLXL parser 
     
    256259    def __init__(self, infile) : 
    257260        """Initialize PCLXL Analyzer.""" 
    258         raise PDLAnalyzerError, "PCLXL (aka PCL6) is not supported yet." 
     261        # raise PDLAnalyzerError, "PCLXL (aka PCL6) is not supported yet." 
    259262        self.infile = infile 
    260263        self.islittleendian = None 
     
    268271                endian = ord(line[0]) 
    269272                if endian == 0x29 : 
    270                     self.littleendian() 
     273                    self.littleEndian() 
    271274                elif endian == 0x28 :     
    272                     self.bigendian() 
     275                    self.bigEndian() 
     276                # elif endian == 0x27 : TODO : What can we do here ?     
     277                #  
    273278                else :     
    274279                    raise PDLAnalyzerError, "No endianness marker 0x%02x at start !" % endian 
     
    276281            raise PDLAnalyzerError, "This file doesn't seem to be PCLXL (aka PCL6)" 
    277282        else :     
    278             self.tags = [ self.skipped ] * 256     
    279             self.tags[0x27] = lambda: self.debug("%08x : ASCII Binding" % self.infile.tell()) 
    280             self.tags[0x28] = self.bigendian  
    281             self.tags[0x29] = self.littleendian 
    282              
    283             self.tags[0x41] = lambda: self.debug("%08x : BeginSession" % self.infile.tell()) 
    284             self.tags[0x42] = lambda: self.debug("%08x : EndSession" % self.infile.tell()) 
     283            # Initialize table of tags 
     284            self.tags = [ 0 ] * 256     
     285             
     286            # GhostScript's sources tell us that HP printers 
     287            # only accept little endianness, but we can handle both. 
     288            self.tags[0x28] = self.bigEndian    # BigEndian 
     289            self.tags[0x29] = self.littleEndian # LittleEndian 
    285290             
    286291            self.tags[0x43] = self.beginPage    # BeginPage 
    287             self.tags[0x44] = self.endPage      # EndPage 
    288              
    289             self.tags[0x47] = lambda: self.debug("%08x : Comment" % self.infile.tell()) 
    290             self.tags[0x48] = lambda: self.debug("%08x : OpenDataSource" % self.infile.tell()) 
    291             self.tags[0x49] = lambda: self.debug("%08x : CloseDataSource" % self.infile.tell()) 
    292              
    293             self.tags[0xc0] = lambda: self.debug("%08x : ubyte" % self.infile.tell()) or 1 # ubyte 
    294             self.tags[0xc1] = lambda: self.debug("%08x : uint16" % self.infile.tell()) or 2 # uint16 
    295             self.tags[0xc2] = lambda: self.debug("%08x : uint32" % self.infile.tell()) or 4 # uint32 
    296             self.tags[0xc3] = lambda: self.debug("%08x : sint16" % self.infile.tell()) or 2 # sint16 
    297             self.tags[0xc4] = lambda: self.debug("%08x : sint32" % self.infile.tell()) or 4 # sint32 
    298             self.tags[0xc5] = lambda: self.debug("%08x : real32" % self.infile.tell()) or 4 # real32 
     292             
     293            self.tags[0xc0] = 1 # ubyte 
     294            self.tags[0xc1] = 2 # uint16 
     295            self.tags[0xc2] = 4 # uint32 
     296            self.tags[0xc3] = 2 # sint16 
     297            self.tags[0xc4] = 4 # sint32 
     298            self.tags[0xc5] = 4 # real32 
    299299             
    300300            self.tags[0xc8] = self.array_8  # ubyte_array 
     
    305305            self.tags[0xcd] = self.array_32 # real32_array 
    306306             
    307             self.tags[0xd0] = lambda: self.debug("%08x : ubyte_xy" % self.infile.tell()) or 2 # ubyte_xy 
    308             self.tags[0xd1] = lambda: self.debug("%08x : uint16_xy" % self.infile.tell()) or 4 # uint16_xy 
    309             self.tags[0xd2] = lambda: self.debug("%08x : uint32_xy" % self.infile.tell()) or 8 # uint32_xy 
    310             self.tags[0xd3] = lambda: self.debug("%08x : sint16_xy" % self.infile.tell()) or 4 # sint16_xy 
    311             self.tags[0xd4] = lambda: self.debug("%08x : sint32_xy" % self.infile.tell()) or 8 # sint32_xy 
    312             self.tags[0xd5] = lambda: self.debug("%08x : real32_xy" % self.infile.tell()) or 8 # real32_xy 
    313              
    314             self.tags[0xd0] = lambda: self.debug("%08x : ubyte_box" % self.infile.tell()) or 4  # ubyte_box 
    315             self.tags[0xd1] = lambda: self.debug("%08x : uint16_box" % self.infile.tell()) or 8  # uint16_box 
    316             self.tags[0xd2] = lambda: self.debug("%08x : uint32_box" % self.infile.tell()) or 16 # uint32_box 
    317             self.tags[0xd3] = lambda: self.debug("%08x : sint16_box" % self.infile.tell()) or 8  # sint16_box 
    318             self.tags[0xd4] = lambda: self.debug("%08x : sint32_box" % self.infile.tell()) or 16 # sint32_box 
    319             self.tags[0xd5] = lambda: self.debug("%08x : real32_box" % self.infile.tell()) or 16 # real32_box 
    320              
    321             self.tags[0xf8] = lambda: self.debug("%08x : attr_ubyte" % self.infile.tell()) or 1 # attr_ubyte 
    322             self.tags[0xf9] = lambda: self.debug("%08x : attr_uint16" % self.infile.tell()) or 2 # attr_uint16 
     307            self.tags[0xd0] = 2 # ubyte_xy 
     308            self.tags[0xd1] = 4 # uint16_xy 
     309            self.tags[0xd2] = 8 # uint32_xy 
     310            self.tags[0xd3] = 4 # sint16_xy 
     311            self.tags[0xd4] = 8 # sint32_xy 
     312            self.tags[0xd5] = 8 # real32_xy 
     313             
     314            self.tags[0xe0] = 4  # ubyte_box 
     315            self.tags[0xe1] = 8  # uint16_box 
     316            self.tags[0xe2] = 16 # uint32_box 
     317            self.tags[0xe3] = 8  # sint16_box 
     318            self.tags[0xe4] = 16 # sint32_box 
     319            self.tags[0xe5] = 16 # real32_box 
     320             
     321            self.tags[0xf8] = 1 # attr_ubyte 
     322            self.tags[0xf9] = 2 # attr_uint16 
    323323             
    324324            self.tags[0xfa] = self.embeddedData      # dataLength 
    325325            self.tags[0xfb] = self.embeddedDataSmall # dataLengthByte 
    326326             
    327     def debug(self, msg) : 
    328         """Outputs a debug message on stderr.""" 
    329         sys.stderr.write("%s\n" % msg) 
    330         sys.stderr.flush() 
    331          
    332     def skipped(self) :     
    333         """Skips a byte.""" 
    334         self.debug("%08x : skip" % self.infile.tell()) 
    335          
    336327    def beginPage(self) : 
    337328        """Indicates the beginning of a new page.""" 
    338329        self.pagecount += 1 
    339         self.debug("%08x : beginPage (%i)" % (self.infile.tell(), self.pagecount)) 
    340          
    341     def endPage(self) : 
    342         """Indicates the end of a page.""" 
    343         self.debug("%08x : endPage (%i)" % (self.infile.tell(), self.pagecount)) 
     330        return 0 
    344331         
    345332    def handleArray(self, itemsize) :         
    346333        """Handles arrays.""" 
    347         pos = self.infile.tell() 
    348         datatype = self.infile.read(1) 
    349         self.debug("%08x : Array of datatype 0x%02x" % (pos, ord(datatype))) 
    350         length = self.tags[ord(datatype)]() 
     334        datatype = self.minfile[self.pos] 
     335        self.pos += 1 
     336        length = self.tags[ord(datatype)] 
    351337        if length is None : 
    352             self.debug("Bogus array length at %s" % pos) 
    353         else :     
    354             sarraysize = self.infile.read(length) 
    355             if self.islittleendian : 
    356                 fmt = "<" 
    357             else :     
    358                 fmt = ">" 
    359             if length == 1 :     
    360                 fmt += "B" 
    361             elif length == 2 :     
    362                 fmt += "H" 
    363             elif length == 4 :     
    364                 fmt += "I" 
    365             else :     
    366                 raise PDLAnalyzerError, "Error on array size at %s" % self.infile.tell() 
    367             arraysize = struct.unpack(fmt, sarraysize)[0] 
    368             self.debug("itemsize %s * size %s = %s" % (itemsize, arraysize, itemsize*arraysize)) 
    369             return arraysize * itemsize 
     338            raise PDLAnalyzerError, "Error on array length at %s" % self.pos 
     339        elif callable(length) : 
     340            length = length() 
     341        pos = self.pos     
     342        posl = pos + length 
     343        sarraysize = self.minfile[pos:posl] 
     344        self.pos = posl 
     345        if self.islittleendian : 
     346            fmt = "<" 
     347        else :     
     348            fmt = ">" 
     349        if length == 1 :     
     350            fmt += "B" 
     351        elif length == 2 :     
     352            fmt += "H" 
     353        elif length == 4 :     
     354            fmt += "I" 
     355        else :     
     356            raise PDLAnalyzerError, "Error on array size at %s" % self.pos 
     357        arraysize = struct.unpack(fmt, sarraysize)[0] 
     358        return arraysize * itemsize 
    370359         
    371360    def array_8(self) :     
    372361        """Handles byte arrays.""" 
    373         self.debug("%08x : array_8" % self.infile.tell()) 
    374362        return self.handleArray(1) 
    375363         
    376364    def array_16(self) :     
    377365        """Handles byte arrays.""" 
    378         self.debug("%08x : array_16" % self.infile.tell()) 
    379366        return self.handleArray(2) 
    380367         
    381368    def array_32(self) :     
    382369        """Handles byte arrays.""" 
    383         self.debug("%08x : array_32" % self.infile.tell()) 
    384370        return self.handleArray(4) 
    385371         
    386372    def embeddedDataSmall(self) : 
    387373        """Handle small amounts of data.""" 
    388         self.debug("%08x : small_datablock" % self.infile.tell()) 
    389         pos = self.infile.tell() 
    390         val = ord(self.infile.read(1)) 
    391         self.debug("%08x : Small datablock length : 0x%02x" % (self.infile.tell()-1, val)) 
    392         return val 
     374        length = ord(self.minfile[self.pos]) 
     375        self.pos += 1 
     376        return length 
    393377         
    394378    def embeddedData(self) : 
    395379        """Handle normal amounts of data.""" 
    396         self.debug("%08x : large_datablock" % self.infile.tell()) 
    397380        if self.islittleendian : 
    398381            fmt = "<I" 
    399382        else :     
    400383            fmt = ">I" 
    401         pos = self.infile.tell() 
    402         data = self.infile.read(4) 
    403         val = struct.unpack(fmt, data)[0] 
    404         if val & 0xff000000 : # tries to detect possible errors when we missed an indianness tag maybe 
    405             if fmt == "<I" : 
    406                 fmt = ">I" 
    407             else :     
    408                 fmt = "<I" 
    409             val = struct.unpack(fmt, data)[0] 
    410         self.debug("%08x : Large datablock length : 0x%08x" % (self.infile.tell()-4, val)) 
    411         self.debug("Endian : %i" % self.islittleendian)  
    412         self.debug("Data read : %s" % str(["0x%02x" % ord(x) for x in data])) 
    413         return val 
    414          
    415     def littleendian(self) :         
     384        pos = self.pos 
     385        pos4 = pos + 4 
     386        data = self.minfile[pos:pos4] 
     387        self.pos = pos4 
     388        return struct.unpack(fmt, data)[0] 
     389         
     390    def littleEndian(self) :         
    416391        """Toggles to little endianness.""" 
    417         self.debug("%08x : littleendian" % self.infile.tell()) 
    418392        self.islittleendian = 1 # little endian 
    419          
    420     def bigendian(self) :     
     393        return 0 
     394         
     395    def bigEndian(self) :     
    421396        """Toggles to big endianness.""" 
    422         self.debug("%08x : bigendian" % self.infile.tell()) 
    423397        self.islittleendian = 0 # big endian 
     398        return 0 
    424399     
    425400    def getJobSize(self) : 
    426401        """Counts pages in a PCLXL (PCL6) document.""" 
     402        infileno = self.infile.fileno() 
     403        self.minfile = mmap.mmap(infileno, os.fstat(infileno).st_size, access=mmap.ACCESS_READ) 
    427404        self.pagecount = 0 
    428         while 1 : 
    429             char = self.infile.read(1) 
    430             if not char : 
    431                 break 
    432             index = ord(char)     
    433             length = self.tags[index]() 
    434             if length :     
    435                 self.infile.read(length)     
     405        self.pos = self.infile.tell() 
     406        try : 
     407            while 1 : 
     408                char = self.minfile[self.pos] 
     409                self.pos += 1 
     410                length = self.tags[ord(char)] 
     411                if not length :     
     412                    continue 
     413                if callable(length) :     
     414                    length = length() 
     415                self.pos += length     
     416        except IndexError : # EOF ? 
     417            self.minfile.close() # reached EOF 
    436418        return self.pagecount 
    437419         
  • pykota/trunk/pykota/version.py

    r1570 r1575  
    2222# 
    2323 
    24 __version__ = "1.19alpha27_unofficial" 
     24__version__ = "1.19alpha28_unofficial" 
    2525 
    2626__doc__ = """PyKota : a complete Printing Quota Solution for CUPS and LPRng.""" 
  • pykota/trunk/README

    r1571 r1575  
    7171          This is completely configurable. 
    7272           
    73         - Supports PostScript, PDF and PCL5 printers natively 
    74           for software accounting methods. More formats to come. 
     73        - Supports DSC compliant PostScript, PDF, PCL5 and  
     74          PCLXL (aka PCL6) printers natively for software  
     75          accounting methods. More formats to come. 
    7576             
    7677    Quota systems :         
     
    226227     
    227228  On Intel i386 architecture, and for performance reasons if you  
    228   print in PCL format, it is strongly suggested that you install  
    229   the Python accelerator Psyco, available at : 
     229  print in PCL5 or PCL6 format, it is strongly suggested that you  
     230  install the Python accelerator Psyco, available at : 
    230231   
    231232      http://psyco.sourceforge.net 
     
    238239  on Psyco's website. 
    239240   
    240   Installing Psyco is not mandatory, but it woud speedup PCL parsing 
     241  Installing Psyco is not mandatory, but it woud speedup PCL5 parsing 
    241242  by almost 3 times. PostScript and PDF parsing can also benefit, 
    242243  but in an almost unnoticeable manner since this part of the code 
    243   is already optimal. For PCL this is a completely different matter, 
     244  is already optimal. For PCL5 this is a completely different matter, 
    244245  and if you install Psyco you will never regret it ! 
     246  Same remark applies for PCL6 (aka PCLXL) but performance improvement 
     247  is yet to be tested (it is high anyway). 
    245248   
    246249  You may also benefit from having the following tools installed to