Index: /pykota/trunk/pykota/pdlanalyzer.py
===================================================================
--- /pykota/trunk/pykota/pdlanalyzer.py (revision 1743)
+++ /pykota/trunk/pykota/pdlanalyzer.py (revision 1912)
@@ -22,4 +22,7 @@
 #
 # $Log$
+# Revision 1.43  2004/11/13 20:52:26  jalet
+# Heavy work done on PCL3/4/5 and PCLXL job analyzis
+#
 # Revision 1.42  2004/09/24 21:19:48  jalet
 # Did a pass of PyChecker
@@ -179,4 +182,5 @@
     
 class PostScriptAnalyzer :
+    """A class to parse PostScript documents."""
     def __init__(self, infile) :
         """Initialize PostScript Analyzer."""
@@ -244,4 +248,5 @@
         
 class PDFAnalyzer :
+    """A class to parse PDF documents."""
     def __init__(self, infile) :
         """Initialize PDF Analyzer."""
@@ -257,4 +262,5 @@
         
 class ESCP2Analyzer :
+    """A class to parse ESC/P2 documents."""
     def __init__(self, infile) :
         """Initialize ESC/P2 Analyzer."""
@@ -297,7 +303,62 @@
         
 class PCLAnalyzer :
+    """A class to parse PCL3, PCL4, PCL5 documents."""
+    mediasizes = {  # ESC&l####A
+                    0 : "Default",
+                    1 : "Executive",
+                    2 : "Letter",
+                    3 : "Legal",
+                    6 : "Ledger", 
+                    25 : "A5",
+                    26 : "A4",
+                    27 : "A3",
+                    45 : "JB5",
+                    46 : "JB4",
+                    71 : "HagakiPostcard",
+                    72 : "OufukuHagakiPostcard",
+                    80 : "MonarchEnvelope",
+                    81 : "COM10Envelope",
+                    90 : "DLEnvelope",
+                    91 : "C5Envelope",
+                    100 : "B5Envelope",
+                    101 : "Custom",
+                 }   
+                 
+    mediasources = { # ESC&l####H
+                     0 : "Default",
+                     1 : "Main",
+                     2 : "Manual",
+                     3 : "ManualEnvelope",
+                     4 : "Alternate",
+                     5 : "OptionalLarge",
+                     6 : "EnvelopeFeeder",
+                     7 : "Auto",
+                     8 : "Tray1",
+                   }
+                   
+    orientations = { # ESC&l####O
+                     0 : "Portrait",
+                     1 : "Landscape",
+                     2 : "ReversePortrait",
+                     3 : "ReverseLandscape",
+                   }
+                   
+    mediatypes = { # ESC&l####M
+                     0 : "Plain",
+                     1 : "Bond",
+                     2 : "Special",
+                     3 : "Glossy",
+                     4 : "Transparent",
+                   }
+                   
+                   
     def __init__(self, infile) :
         """Initialize PCL Analyzer."""
         self.infile = infile
+        
+    def setPageDict(self, pages, number, attribute, value) :
+        """Initializes a page dictionnary."""
+        dict = pages.setdefault(number, { "copies" : 1, "mediasource" : "Main", "mediasize" : "Default", "mediatype" : "Plain", "orientation" : "Portrait"})
+        dict[attribute] = value
         
     def getJobSize(self) :     
@@ -332,5 +393,5 @@
                      ")s" : "W", 
                      "&p" : "X", 
-                     "&l" : "XH",
+                     # "&l" : "XHAOM",  # treated specially
                      "&a" : "G", # TODO : 0 means next side, 1 front side, 2 back side
                      "*g" : "W",
@@ -339,7 +400,7 @@
                    }  
         pagecount = resets = ejects = backsides = startgfx = endgfx = 0
-        starb = ispcl3 = 0
+        starb = ampl = ispcl3 = 0
         tag = None
-        copies = {}
+        pages = {}
         pos = 0
         try :
@@ -349,5 +410,5 @@
                     pagecount += 1
                 elif char == "\033" :    
-                    starb = 0
+                    starb = ampl = 0
                     #
                     #     <ESC>*b###y#m###v###w... -> PCL3 raster graphics
@@ -369,5 +430,8 @@
                     #     <ESC>&a2G -> Back side when duplex mode as generated by rastertohp
                     #     <ESC>*g###W -> Needed for planes in PCL3 output
-                    #     <ESC>&l0H -> Eject if NumPlanes > 1, as generated by rastertohp
+                    #     <ESC>&l###H (or only 0 ?) -> Eject if NumPlanes > 1, as generated by rastertohp. Also defines mediasource
+                    #     <ESC>&l###A -> mediasize
+                    #     <ESC>&l###O -> orientation
+                    #     <ESC>&l###M -> mediatype
                     #
                     tagstart = minfile[pos] ; pos += 1
@@ -380,4 +444,7 @@
                         starb = 1
                         tagend = "VW"
+                    elif tag == "&l" :    
+                        ampl = 1
+                        tagend = "XHAOM"
                     else :    
                         try :
@@ -393,9 +460,17 @@
                         size = (size * 10) + int(char)    
                     if char in tagend :    
-                        if (tag == "&l") and (char == "X") : # copies for current page
-                            copies[pagecount] = size
-                        elif (tag == "&l") and (char == "H") and (size == 0) :    
-                            ejects += 1         # Eject 
-                        elif (tag == "*r") :
+                        if tag == "&l" :
+                            if char == "X" : 
+                                self.setPageDict(pages, pagecount, "copies", size)
+                            elif char == "H" :
+                                self.setPageDict(pages, pagecount, "mediasource", self.mediasources.get(size, str(size)))
+                                ejects += 1 
+                            elif char == "A" :
+                                self.setPageDict(pages, pagecount, "mediasize", self.mediasizes.get(size, str(size)))
+                            elif char == "O" :
+                                self.setPageDict(pages, pagecount, "orientation", self.orientations.get(size, str(size)))
+                            elif char == "M" :
+                                self.setPageDict(pages, pagecount, "mediatype", self.mediatypes.get(size, str(size)))
+                        elif tag == "*r" :
                             # Special tests for PCL3
                             if (char == "s") and size :
@@ -433,4 +508,24 @@
                             ispcl3 = 1  # certainely a PCL3 document
                             pos -= 1    # fix position : we were ahead
+                    elif ampl :        
+                        # special handling of PCL3 in which 
+                        # &l introduces combined ESCape sequences
+                        size = 0
+                        while 1 :
+                            char = minfile[pos] ; pos += 1
+                            if not char.isdigit() :
+                                break
+                            size = (size * 10) + int(char)    
+                        if char in ("a", "o", "h", "m") :    
+                            ispcl3 = 1  # certainely a PCL3 document
+                            pos -= 1    # fix position : we were ahead
+                            if char == "h" :
+                                self.setPageDict(pages, pagecount, "mediasource", self.mediasources.get(size, str(size)))
+                            elif char == "a" :
+                                self.setPageDict(pages, pagecount, "mediasize", self.mediasizes.get(size, str(size)))
+                            elif char == "o" :
+                                self.setPageDict(pages, pagecount, "orientation", self.orientations.get(size, str(size)))
+                            elif char == "m" :
+                                self.setPageDict(pages, pagecount, "mediatype", self.mediatypes.get(size, str(size)))
         except IndexError : # EOF ?
             minfile.close() # reached EOF
@@ -456,6 +551,6 @@
         for pnum in range(pagecount) :
             # if no number of copies defined, take the preceding one else the one set before any page else 1.
-            nb = copies.get(pnum, copies.get(pnum-1, copies.get(0, 1)))
-            pagecount += (nb - 1)
+            page = pages.get(pnum, pages.get(pnum - 1, pages.get(0, { "copies" : 1 })))
+            pagecount += (page["copies"] - 1)
             
         # in PCL3 files, there's one Start Gfx tag per page
@@ -467,8 +562,53 @@
             elif endgfx :    
                 pagecount = endgfx
+                
+        #for pnum in range(pagecount) :
+        #    # if no number of copies defined, take the preceding one else the one set before any page else 1.
+        #    page = pages.get(pnum, pages.get(pnum - 1, pages.get(0, { "copies" : 1, "mediasource" : "Main", "mediasize" : "Default", "mediatype" : "Plain", "orientation" : "Portrait"})))
+        #    print "%s*%s*%s*%s*%s" % (page["copies"], page["mediatype"], page["mediasize"], page["orientation"], page["mediasource"])
             
         return pagecount
         
 class PCLXLAnalyzer :
+    """A class to parse PCL6 (aka XL) documents."""
+    mediasizes = { 
+                    0 : "Letter",
+                    1 : "Legal",
+                    2 : "A4",
+                    3 : "Executive",
+                    4 : "Ledger",
+                    5 : "A3",
+                    6 : "COM10Envelope",
+                    7 : "MonarchEnvelope",
+                    8 : "C5Envelope",
+                    9 : "DLEnvelope",
+                    10 : "JB4",
+                    11 : "JB5",
+                    12 : "B5Envelope",
+                    14 : "JPostcard",
+                    15 : "JDoublePostcard",
+                    16 : "A5",
+                    17 : "A6",
+                    18 : "JB6",
+                 }   
+                 
+    mediasources = {             
+                     0 : "Default",
+                     1 : "Auto",
+                     2 : "Manual",
+                     3 : "MultiPurpose",
+                     4 : "UpperCassette",
+                     5 : "LowerCassette",
+                     6 : "EnvelopeTray",
+                     7 : "ThirdCassette",
+                   }
+                   
+    orientations = {               
+                     0 : "Portrait",
+                     1 : "Landscape",
+                     2 : "ReversePortrait",
+                     3 : "ReverseLandscape",
+                   }
+                   
     def __init__(self, infile) :
         """Initialize PCLXL Analyzer."""
@@ -487,5 +627,5 @@
                 elif endian == 0x28 :    
                     self.bigEndian()
-                # elif endian == 0x27 : TODO : What can we do here ?    
+                # elif endian == 0x27 : # TODO : This is the ESC code : parse it for PJL statements !
                 # 
                 else :    
@@ -540,6 +680,51 @@
             
     def beginPage(self) :
-        """Indicates the beginning of a new page."""
+        """Indicates the beginning of a new page, and extracts media information."""
         self.pagecount += 1
+        
+        # Default values
+        mediatypelabel = "Plain"
+        mediasourcelabel = "Main"
+        mediasizelabel = "Default"
+        orientationlabel = "Portrait"
+        
+        # Now go upstream to decode media type, size, source, and orientation
+        # this saves time because we don't need a complete parser !
+        minfile = self.minfile
+        pos = self.pos - 2
+        while pos > 0 : # safety check : don't go back to far !
+            val = ord(minfile[pos])
+            if val in (0x44, 0x48, 0x41) : # if previous endPage or openDataSource or beginSession (first page)
+                break
+            if val == 0x26 :    
+                mediasource = ord(minfile[pos - 2])
+                mediasourcelabel = self.mediasources.get(mediasource, str(mediasource))
+                pos = pos - 4
+            elif val == 0x25 :
+                mediasize = ord(minfile[pos - 2])
+                mediasizelabel = self.mediasizes.get(mediasize, str(mediasize))
+                pos = pos - 4
+            elif val == 0x28 :    
+                orientation = ord(minfile[pos - 2])
+                orienationlabel = self.orientations.get(orientation, str(orientation))
+                pos = pos - 4
+            elif val == 0x27 :    
+                savepos = pos
+                pos = pos - 1
+                while pos > 0 : # safety check : don't go back to far !
+                    val = ord(minfile[pos])
+                    pos -= 1    
+                    if val == 0xc8 :
+                        break
+                mediatypelabel = minfile[pos:savepos] # TODO : INCORRECT, WE HAVE TO STRIP OUT THE UBYTE ARRAY'S LENGTH !!!
+            # else : TODO : CUSTOM MEDIA SIZE AND UNIT ! 
+            else :    
+                pos = pos - 2   # ignored
+        self.pages[self.pagecount] = { "copies" : 1, 
+                                       "orientation" : orientationlabel, 
+                                       "mediatype" : mediatypelabel, 
+                                       "mediasize" : mediasizelabel,
+                                       "mediasource" : mediasourcelabel,
+                                     } 
         return 0
         
@@ -549,9 +734,9 @@
         minfile = self.minfile
         if (ord(minfile[pos-3]) == 0xf8) and (ord(minfile[pos-2]) == 0x31) :
-            # The EndPage operator is preceded by a PageCopies attribute
+            # The EndPage operator may be preceded by a PageCopies attribute
             # So set number of copies for current page.
             # From what I read in PCLXL documentation, the number
             # of copies is an unsigned 16 bits integer
-            self.copies[self.pagecount] = unpack(self.endianness + "H", minfile[pos-5:pos-3])[0]
+            self.pages[self.pagecount]["copies"] = unpack(self.endianness + "H", minfile[pos-5:pos-3])[0]
         return 0
         
@@ -655,5 +840,5 @@
         """
         infileno = self.infile.fileno()
-        self.copies = {}
+        self.pages = {}
         self.minfile = minfile = mmap.mmap(infileno, os.fstat(infileno)[6], prot=mmap.PROT_READ, flags=mmap.MAP_SHARED)
         tags = self.tags
@@ -682,5 +867,8 @@
             # but the formula below is still correct : we want 
             # to decrease the total number of pages in this case.
-            self.pagecount += (self.copies.get(pnum, 1) - 1)
+            page = self.pages.get(pnum, 1)
+            copies = page["copies"]
+            self.pagecount += (copies - 1)
+            # print "%s*%s*%s*%s*%s" % (copies, page["mediatype"], page["mediasize"], page["orientation"], page["mediasource"])
             
         return self.pagecount
