Changeset 3436 for pkpgcounter
- Timestamp:
- 10/06/08 00:22:07 (16 years ago)
- Location:
- pkpgcounter/trunk
- Files:
-
- 38 modified
Legend:
- Unmodified
- Added
- Removed
-
pkpgcounter/trunk/bin/pkpgcounter
r564 r3436 1 1 #! /usr/bin/env python 2 # -*- coding: UTF-8 -*-2 # -*- coding: utf-8 -*- 3 3 # 4 4 # pkpgcounter : a generic Page Description Language parser … … 9 9 # the Free Software Foundation, either version 3 of the License, or 10 10 # (at your option) any later version. 11 # 11 # 12 12 # This program is distributed in the hope that it will be useful, 13 13 # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 15 # GNU General Public License for more details. 16 # 16 # 17 17 # You should have received a copy of the GNU General Public License 18 18 # along with this program. If not, see <http://www.gnu.org/licenses/>. … … 71 71 -v | --version Prints pkpgcounter's version number then exits. 72 72 -h | --help Prints this message then exits. 73 74 -d | --debug Activate debug mode. 75 73 74 -d | --debug Activate debug mode. 75 76 76 -cCOLORSPACE, --colorspace=COLORSPACE 77 77 Activate the computation of ink usage, and defines the 78 78 colorspace to use. Supported values are 'BW' (Black), 79 79 'RGB', 'CMYK', 'CMY', and 'GC' (Grayscale vs Color). 80 'GC' is useful if you only need to differentiate 80 'GC' is useful if you only need to differentiate 81 81 grayscale pages from coloured pages but don't care 82 82 about ink usage per se. 83 83 84 84 -rRESOLUTION, --resolution=RESOLUTION 85 85 The resolution in DPI to use when checking ink usage. 86 86 Lower resolution is faster but less accurate. Default 87 87 is 72 dpi. 88 89 examples : 88 89 examples : 90 90 91 91 $ pkpgcounter file1.ps file2.escp2 file3.pclxl <file4.pcl345 92 92 93 93 Will launch pkpgcounter and will output the total number of pages 94 94 needed to print all the documents specified. 95 95 96 96 $ pkpgcounter --colorspace bw --resolution 150 file1.ps 97 97 98 98 Will output the percent of black ink needed on each page of 99 99 the file1.ps file rendered at 150 dpi. 100 101 %(__gplblurb__)s 100 101 %(__gplblurb__)s 102 102 103 103 Please e-mail bugs to: %(__authoremail__)s""" 104 104 105 if __name__ == "__main__" : 105 if __name__ == "__main__" : 106 106 if (len(sys.argv) >= 2) and (sys.argv[1] in ("-h", "--help")) : 107 107 print __doc__ % globals() 108 else : 108 else : 109 109 analyzer.main() 110 110 -
pkpgcounter/trunk/BUGS
r564 r3436 20 20 21 21 pkpgcounter BUGS : 22 22 23 23 - All parsers need more testing, especially those which allow the number 24 24 of copies to be specified as part of the data streams, as well as those -
pkpgcounter/trunk/clean.sh
r564 r3436 8 8 # the Free Software Foundation, either version 3 of the License, or 9 9 # (at your option) any later version. 10 # 10 # 11 11 # This program is distributed in the hope that it will be useful, 12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 14 # GNU General Public License for more details. 15 # 15 # 16 16 # You should have received a copy of the GNU General Public License 17 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. -
pkpgcounter/trunk/CREDITS
r3409 r3436 20 20 21 21 The following people helped the pkpgcounter project : 22 23 22 23 24 24 - Eduardo Gielamo Oliveira & Rodolfo Broco Manin : initial PCL parser. 25 25 26 26 - Valcir C. Vargas : helped on PCL3GUI support. 27 28 - Daniel Franklin : pkpgcounter's algorithm for the computation 27 28 - Daniel Franklin : pkpgcounter's algorithm for the computation 29 29 of ink coverage comes from Daniel's PrintBill project. 30 31 - Aur�en Croc : reverse engineered Samsung QPDL and published 30 31 - Aur�en Croc : reverse engineered Samsung QPDL and published 32 32 a language specification. 33 33 34 34 - Franck (?) : sent a path to improve the PostScript parser when 35 35 the PS code generated several copies. 36 37 - Yves Lavoie : reported a bug with Python 2.5 38 36 37 - Yves Lavoie : reported a bug with Python 2.5 38 39 39 - Kumar Appaiah : put pkpgcounter in Debian. 40 40 41 41 - Roger Jochem : reported problems and test files for custom 42 42 made printer drivers. … … 50 50 THINK YOU DESERVE TO BE LISTED, JUST SEND ME AN EMAIL 51 51 ASKING FOR BEING INCLUDED. 52 52 53 53 I APOLOGIZE IN ADVANCE IF I'VE FORGOTTEN TO ADD YOUR NAME. 54 55 ALSO, SOME PEOPLE MAY HAVE REFUSED TO BE LISTED HERE, THIS 54 55 ALSO, SOME PEOPLE MAY HAVE REFUSED TO BE LISTED HERE, THIS 56 56 IS YOUR RIGHT TO DO SO TOO, JUST TELL ME. 57 57 58 58 ============================================================== -
pkpgcounter/trunk/man/genman.sh
r564 r3436 8 8 # the Free Software Foundation, either version 3 of the License, or 9 9 # (at your option) any later version. 10 # 10 # 11 11 # This program is distributed in the hope that it will be useful, 12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 14 # GNU General Public License for more details. 15 # 15 # 16 16 # You should have received a copy of the GNU General Public License 17 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. … … 19 19 # $Id$ 20 20 # 21 for prog in pkpgcounter ; do 21 for prog in pkpgcounter ; do 22 22 echo "$prog" ; 23 23 help2man --no-info \ … … 27 27 --source="C@LL - Conseil Internet & Logiciels Libres" \ 28 28 --output="temp$prog.1" \ 29 $prog ; 29 $prog ; 30 30 /bin/sed -e "s/--/\\\-\\\-/g" <"temp$prog.1" >"$prog.1" ; 31 31 /bin/rm -f "temp$prog.1" -
pkpgcounter/trunk/NEWS
r3402 r3436 22 22 23 23 * 3.51 : 24 24 25 25 - Partial rewrite of the PostScript parser, with huge performance increase. 26 26 27 27 - Fixed a problem in the PostScript parser where the number of copies set with 28 e.g. "dvips -c 9 -o output.ps input.dvi" wasn't correctly detected. 28 e.g. "dvips -c 9 -o output.ps input.dvi" wasn't correctly detected. 29 29 30 30 - Increased accuracy in the PDF parser, with some speed expense but 31 31 some big room for future improvements. 32 32 33 33 * 3.50 : 34 34 35 35 - Added support for Canon BJ/BJC documents in page counting mode. 36 36 37 37 - Added support for Structured Fax documents in page counting mode. 38 38 39 39 - Added support for ASCII PNM (Netpbm) documents in page counting mode. 40 40 41 41 - Fixed a problem with the parsing of SPL1 (GDI) documents : all such 42 42 documents should now be correctly parsed. 43 43 44 44 - Improved the PCL3/4/5 parser. 45 45 46 46 * 3.40 : 47 47 48 48 - Fixed a major bug in the detection of OpenDocument (ISO/IEC DIS 26300) 49 49 files. 50 50 51 51 - Added support for page counting in Microsoft Word (c) (tm) (r) (etc...) 52 52 documents. 53 53 54 54 - Added support for ink accounting in Microsoft Word (c) (tm) (r) (etc...) 55 55 and OpenDocument (ISO/IEC DIS 26300) documents. 56 57 - Now automatically detects missing executable dependencies at runtime. 58 56 57 - Now automatically detects missing executable dependencies at runtime. 58 59 59 * 3.30 : 60 60 61 61 - Added support for all file formats supported by the Python Imaging 62 62 Library. 63 64 - Added a minimal parser for Hewlett-Packard LIDIL documents, as 63 64 - Added a minimal parser for Hewlett-Packard LIDIL documents, as 65 65 produced by the hpijs driver. 66 66 67 67 - Improved general reliability. 68 68 69 69 - Improved detection of PCL3/4/5 jobs. 70 71 - Major code cleaning. 72 70 71 - Major code cleaning. 72 73 73 * 3.20 : 74 74 75 75 - Added a minimal parser for Brother HBP documents. 76 76 77 77 - Added support for Canon ImageRunner commands to the PCLXL parser, 78 78 much like it was already done for the PCL3/4/5 parser. 79 79 80 80 * 3.10 : 81 81 82 82 - Added a minimal parser for ESC/PAGES03 style documents. 83 83 84 84 - Fixed another problem in the code handling the output of Canon's 85 85 ImageRunner printer drivers. 86 86 87 87 * 3.00 : 88 88 89 89 - Fixed a problem in the code handling the output of Canon's ImageRunner 90 90 printer drivers. 91 92 - Licensing terms changed to GNU GPL v3.0 or higher. 93 91 92 - Licensing terms changed to GNU GPL v3.0 or higher. 93 94 94 * 2.18 : 95 95 96 96 - Fixed an incompatibility with Python v2.5 97 98 * 2.17 : 99 97 98 * 2.17 : 99 100 100 - Fixed problems in the handling of PJL number of copies (COPIES= and QTY=) 101 101 which caused the number of copies for each page to be squared. 102 103 * 2.16 : 104 102 103 * 2.16 : 104 105 105 - Fixed a problem in the PCL3/4/5 parser to accomodate line based 106 106 report generators which expect the printer to skip to next page 107 107 based on lines per page instead of sending a specific command. 108 108 109 109 * 2.15 : 110 110 111 111 - Fixed a regexp in the PDF parser which caused some files to be 112 112 incorrectly accounted for. 113 113 114 114 - Improved the detection of the number of copies in PostScript documents. 115 115 116 116 * 2.14 : 117 117 118 118 - Fixed a problem with some PS drivers which don't output %%Page: 119 119 comments. 120 121 * 2.13 : 122 120 121 * 2.13 : 122 123 123 - Nowrecognizes the GC colorspace if you only want to differentiate 124 124 grayscale pages from coloured pages. In this colorspace, the 125 125 percents are always "G : 100.0 C : 0.0" or "G : 0.0 C : 100.0" 126 126 respectively for a grayscale page and a coloured page. 127 127 128 128 * 2.12 : 129 129 130 130 - Fixed a problem with the PostScript parser. 131 131 132 132 * 2.11 : 133 133 134 134 - Improved overall robustness in ink accounting mode. 135 135 136 136 * 2.10 : 137 137 138 138 - Added a plain text parser, with support for both page counting and 139 139 ink coverage. 140 140 141 141 - Added a minimal SPL1 parser. 142 142 143 143 - Fixed a problem in the PCLXL parser related to Kyocera printer drivers. 144 144 145 145 * 2.00 : 146 146 147 147 - The PCL3/4/5 parser was rewritten from scratch and is now 148 148 table driven. 149 149 150 150 - Improved the PostScript parser. 151 151 152 152 * 1.85 : 153 153 154 154 - Added support for Samsung QPDL (aka SPL2) file format in page 155 155 counting mode, thanks to the work done by Aur�en Croc on 156 156 reverse engineering the file format. 157 157 158 158 * 1.85alpha : 159 159 160 160 - Added test document and program to generate the test document 161 161 to check the functionning of the ink coverage computation algorithm. 162 162 163 163 * 1.84 : 164 164 165 165 - Computation of ink coverage now works and is documented. 166 166 The supported file formats are : PS, PDF, PCLXL, PCL3/4/5, 167 167 DVI and TIFF. 168 168 169 169 * 1.84alpha : 170 170 171 171 - Added initial support for the computation of ink coverage. 172 172 See python analyzer.py --help for details. The same command 173 173 line options work for pkpgcounter, although it's not 174 174 documented yet. 175 175 176 176 * 1.83 : 177 178 - Fixed a famously known 'brown paper bag' issue with file type autodetection. 179 177 178 - Fixed a famously known 'brown paper bag' issue with file type autodetection. 179 180 180 * 1.82 : 181 182 - Fixed PCL3/4/5 parser to detect recent Xerox drivers' output. 183 181 182 - Fixed PCL3/4/5 parser to detect recent Xerox drivers' output. 183 184 184 * 1.81 : 185 185 186 186 - Improved the gs+Acrobat Reader fix done in 1.78. 187 187 188 188 * 1.80 : 189 189 190 190 - Added support for Zenographics ZjStream input format. 191 191 192 192 * 1.79 : 193 193 194 194 - Fixed a small problem in PostScript parser with dvips output. 195 195 196 196 * 1.78 : 197 197 198 198 - Launches gs less often when printing is done from Acrobat 199 199 Reader. 200 201 * 1.77 : 202 200 201 * 1.77 : 202 203 203 - Fixed some problems with the code introduced in 1.76. 204 204 205 205 - Improved PCLXL parser's speed by almost 10%. 206 206 207 207 * 1.76 : 208 208 209 209 - Improved the PCLXL parser wrt undocumented tags. 210 210 211 211 - Preliminary support for Kyocera Prescribe commands. 212 212 213 213 * 1.75 : 214 214 215 215 - Added preliminary support for Canon ImageRunner's "LIPS" or "UFR" 216 (I don't know which) Page Description Language. 217 216 (I don't know which) Page Description Language. 217 218 218 * 1.74 : 219 219 220 220 - Fixed a problem in the retrieval of named media sizes in PCLXL. 221 221 222 222 - Changed copyright years. 223 223 224 224 * 1.73 : 225 225 226 226 - Fixed duplex detection code in PCL3/4/5 parser. 227 227 228 228 - Fixed PCLXL detection code. 229 229 230 230 - Fixed retrieval of custom media types' names in PCLXL. 231 231 232 232 - Added inactive code to compute ink coverage ala PrintBill. 233 233 234 234 * 1.72 : 235 235 236 236 - Improved heuristic to detect when ghostscript has to be used to do 237 237 the parsing. 238 238 239 239 * 1.71 : 240 240 241 241 - Now uses ghostscript as the parser when the PS stream was created by a 242 242 particular MSWindows driver. 243 243 244 244 - Only complains about Psyco missing at install time, and stays 245 245 quiet when running without Psyco. 246 246 247 247 * 1.70 : 248 248 249 249 - Fixed some PJL escaping issue in the PCLXL and PCL3/4/5 parsers. 250 250 251 251 * 1.69 : 252 253 - Improved PCL3/4/5 parser. 254 252 253 - Improved PCL3/4/5 parser. 254 255 255 - Improved PCLXL parser wrt PJL stuff. 256 256 257 257 * 1.68 : 258 258 259 259 - Improved PostScript parser. 260 260 261 261 * 1.67 : 262 262 263 263 - Improved PostScript parser. 264 265 - Improved PCL3/4/5 parser. 266 267 * 1.66 : 268 264 265 - Improved PCL3/4/5 parser. 266 267 * 1.66 : 268 269 269 - Improved PCLXL parser. 270 271 - Improved PCL3/4/5 parser. 272 270 271 - Improved PCL3/4/5 parser. 272 273 273 * 1.65 : 274 274 275 275 - Improved PostScript parser. 276 277 - Improved PCL3/4/5 parser. 278 276 277 - Improved PCL3/4/5 parser. 278 279 279 * 1.64 : 280 281 - Improved PCL3/4/5 parser. 282 280 281 - Improved PCL3/4/5 parser. 282 283 283 * 1.63 : 284 284 285 285 - Now handles PJL statements to extract number of copies, duplex mode 286 286 and paper size in the PCLXL and PCL3/4/5 parsers. 287 287 288 288 * 1.62 : 289 289 290 290 - Better handling of the number of copies in the PCLXL parser. 291 291 292 292 - Better handling of the number of copies in the PCL3/4/5 parser. 293 293 294 294 * 1.61 : 295 296 - Improved PCL3/4/5 parser. 297 295 296 - Improved PCL3/4/5 parser. 297 298 298 - Better handling of the number of copies in the PostScript parser. 299 299 Now the number can be different from page to page. 300 301 * 1.60 : 302 300 301 * 1.60 : 302 303 303 - Improved PCLXL parser. 304 304 305 305 * 1.59 : 306 306 307 307 - Major rewrite of the PDF parser to correctly handle all line endings. 308 308 309 309 * 1.58 : 310 310 311 311 - Fix for PDF files which contain several versions of the same PDF object. 312 312 313 313 - Doesn't break when python-psyco is not available. 314 315 * 1.57 : 316 317 - Fixed a recently introduced bug in binary postscript handling code. 318 314 315 * 1.57 : 316 317 - Fixed a recently introduced bug in binary postscript handling code. 318 319 319 * 1.56 : 320 320 321 321 - Renamed the library from pdlanalyzer into pkpgpdls to avoid problems 322 322 when the code will be integrated back into PyKota. 323 324 * 1.55 : 325 323 324 * 1.55 : 325 326 326 - Added support for OpenOffice.org Writer and Impress documents. 327 327 328 328 * 1.54 : 329 329 330 330 - Added online documentation and a manual page. 331 332 * 1.53 : 333 331 332 * 1.53 : 333 334 334 - Moved code around to improve maintainability. 335 336 * 1.52 : 337 335 336 * 1.52 : 337 338 338 - Added support for the TIFF format. 339 340 * 1.51 : 341 339 340 * 1.51 : 341 342 342 - Added support for the DVI format. 343 343 344 344 * 1.50 : 345 345 346 346 - Major code changes to modularize. The pkpgcounter command line tool 347 347 is now just an almost empty skeleton, which uses the pdlanalyzer 348 348 library. 349 350 - Several improvements to the parsing code. 351 349 350 - Several improvements to the parsing code. 351 352 352 * 1.00 : 353 353 -
pkpgcounter/trunk/pkpgpdls/analyzer.py
r3410 r3436 8 8 # the Free Software Foundation, either version 3 of the License, or 9 9 # (at your option) any later version. 10 # 10 # 11 11 # This program is distributed in the hope that it will be useful, 12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 14 # GNU General Public License for more details. 15 # 15 # 16 16 # You should have received a copy of the GNU General Public License 17 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. … … 43 43 self.colorspace = colorspace 44 44 self.resolution = resolution 45 46 47 class PDLAnalyzer : 45 46 47 class PDLAnalyzer : 48 48 """Class for PDL autodetection.""" 49 49 def __init__(self, filename, options=AnalyzerOptions()) : 50 50 """Initializes the PDL analyzer. 51 51 52 52 filename is the name of the file or '-' for stdin. 53 filename can also be a file-like object which 53 filename can also be a file-like object which 54 54 supports read() and seek(). 55 55 """ 56 56 self.options = options 57 57 self.filename = filename 58 self.workfile = None 58 self.workfile = None 59 59 self.mustclose = None 60 61 def getJobSize(self) : 60 61 def getJobSize(self) : 62 62 """Returns the job's size.""" 63 63 size = 0 … … 67 67 pdlhandler = self.detectPDLHandler() 68 68 size = pdlhandler.getJobSize() 69 except pdlparser.PDLParserError, msg : 69 except pdlparser.PDLParserError, msg : 70 70 raise pdlparser.PDLParserError, "Unsupported file format for %s (%s)" % (self.filename, msg) 71 finally : 71 finally : 72 72 self.closeFile() 73 73 return size 74 74 75 75 def getInkCoverage(self, colorspace=None, resolution=None) : 76 76 """Extracts the percents of ink coverage from the input file.""" … … 89 89 pdlhandler.convertToTiffMultiPage24NC(filename, self.options.resolution) 90 90 result = inkcoverage.getInkCoverage(filename, cspace) 91 finally : 91 finally : 92 92 dummyfile.close() 93 except pdlparser.PDLParserError, msg : 93 except pdlparser.PDLParserError, msg : 94 94 raise pdlparser.PDLParserError, "Unsupported file format for %s (%s)" % (self.filename, msg) 95 95 finally : 96 96 self.closeFile() 97 97 return result 98 99 def openFile(self) : 98 99 def openFile(self) : 100 100 """Opens the job's data stream for reading.""" 101 101 self.mustclose = False # by default we don't want to close the file when finished 102 102 if hasattr(self.filename, "read") and hasattr(self.filename, "seek") : 103 # filename is in fact a file-like object 103 # filename is in fact a file-like object 104 104 infile = self.filename 105 105 elif self.filename == "-" : 106 106 # we must read from stdin 107 107 infile = sys.stdin 108 else : 108 else : 109 109 # normal file 110 110 self.workfile = open(self.filename, "rb") 111 111 self.mustclose = True 112 112 return 113 113 114 114 # Use a temporary file, always seekable contrary to standard input. 115 115 self.workfile = tempfile.NamedTemporaryFile(mode="w+b") 116 116 self.filename = self.workfile.name 117 117 while True : 118 data = infile.read(pdlparser.MEGABYTE) 118 data = infile.read(pdlparser.MEGABYTE) 119 119 if not data : 120 120 break 121 121 self.workfile.write(data) 122 self.workfile.flush() 122 self.workfile.flush() 123 123 self.workfile.seek(0) 124 125 def closeFile(self) : 124 125 def closeFile(self) : 126 126 """Closes the job's data stream if we have to.""" 127 127 if self.mustclose : 128 self.workfile.close() 129 128 self.workfile.close() 129 130 130 def readFirstAndLastBlocks(self, inputfile) : 131 131 """Reads the first and last blocks of data.""" … … 136 136 inputfile.seek(-pdlparser.LASTBLOCKSIZE, 2) 137 137 lastblock = inputfile.read(pdlparser.LASTBLOCKSIZE) 138 except IOError : 138 except IOError : 139 139 lastblock = "" 140 return (firstblock, lastblock) 141 142 def detectPDLHandler(self) : 140 return (firstblock, lastblock) 141 142 def detectPDLHandler(self) : 143 143 """Tries to autodetect the document format. 144 144 145 145 Returns the correct PDL handler class or None if format is unknown 146 """ 146 """ 147 147 if not os.stat(self.filename).st_size : 148 148 raise pdlparser.PDLParserError, "input file %s is empty !" % str(self.filename) 149 149 (firstblock, lastblock) = self.readFirstAndLastBlocks(self.workfile) 150 150 151 151 # IMPORTANT : the order is important below. FIXME. 152 152 for module in (postscript, \ … … 170 170 mscrap, \ 171 171 plain) : # IMPORTANT : don't move this one up ! 172 try : 173 return module.Parser(self, self.filename, 172 try : 173 return module.Parser(self, self.filename, 174 174 (firstblock, lastblock)) 175 175 except pdlparser.PDLParserError : 176 176 pass # try next parser 177 177 raise pdlparser.PDLParserError, "Analysis of first data block failed." 178 179 def main() : 178 179 def main() : 180 180 """Entry point for PDL Analyzer.""" 181 181 import optparse 182 182 from copy import copy 183 183 184 184 def check_cichoice(option, opt, value) : 185 185 """To add a CaseIgnore Choice option type.""" … … 187 187 if valower in [v.lower() for v in option.cichoices] : 188 188 return valower 189 else : 189 else : 190 190 choices = ", ".join([repr(o) for o in option.cichoices]) 191 191 raise optparse.OptionValueError( 192 192 "option %s: invalid choice: %r (choose from %s)" 193 193 % (opt, value, choices)) 194 194 195 195 class MyOption(optparse.Option) : 196 196 """New Option class, with CaseIgnore Choice type.""" … … 199 199 TYPE_CHECKER = copy(optparse.Option.TYPE_CHECKER) 200 200 TYPE_CHECKER["cichoice"] = check_cichoice 201 202 parser = optparse.OptionParser(option_class=MyOption, 201 202 parser = optparse.OptionParser(option_class=MyOption, 203 203 usage="python analyzer.py [options] file1 [file2 ...]") 204 parser.add_option("-v", "--version", 205 action="store_true", 204 parser.add_option("-v", "--version", 205 action="store_true", 206 206 dest="version", 207 207 help="Show pkpgcounter's version number and exit.") 208 parser.add_option("-d", "--debug", 209 action="store_true", 208 parser.add_option("-d", "--debug", 209 action="store_true", 210 210 dest="debug", 211 211 help="Activate debug mode.") 212 parser.add_option("-c", "--colorspace", 212 parser.add_option("-c", "--colorspace", 213 213 dest="colorspace", 214 214 type="cichoice", 215 215 cichoices=["bw", "rgb", "cmyk", "cmy", "gc"], 216 216 help="Activate the computation of ink usage, and defines the colorspace to use. Supported values are 'BW', 'RGB', 'CMYK', 'CMY', and 'GC'.") 217 parser.add_option("-r", "--resolution", 218 type="int", 219 default=72, 217 parser.add_option("-r", "--resolution", 218 type="int", 219 default=72, 220 220 dest="resolution", 221 221 help="The resolution in DPI to use when checking ink usage. Lower resolution is faster but less accurate. Default is 72 dpi.") … … 223 223 if options.version : 224 224 print "%s" % version.__version__ 225 elif not (72 <= options.resolution <= 1200) : 225 elif not (72 <= options.resolution <= 1200) : 226 226 sys.stderr.write("ERROR: the argument to the --resolution command line option must be between 72 and 1200.\n") 227 227 sys.stderr.flush() … … 229 229 if (not arguments) or ((not sys.stdin.isatty()) and ("-" not in arguments)) : 230 230 arguments.append("-") 231 totalsize = 0 231 totalsize = 0 232 232 lines = [] 233 233 try : … … 246 246 except KeyError : 247 247 pass 248 lines.append(" ".join(lineparts)) 249 except (IOError, pdlparser.PDLParserError), msg : 248 lines.append(" ".join(lineparts)) 249 except (IOError, pdlparser.PDLParserError), msg : 250 250 sys.stderr.write("ERROR: %s\n" % msg) 251 251 sys.stderr.flush() 252 except KeyboardInterrupt : 252 except KeyboardInterrupt : 253 253 sys.stderr.write("WARN: Aborted at user's request.\n") 254 254 sys.stderr.flush() 255 if not options.colorspace : 255 if not options.colorspace : 256 256 print "%i" % totalsize 257 else : 257 else : 258 258 print "\n".join(lines) 259 260 if __name__ == "__main__" : 259 260 if __name__ == "__main__" : 261 261 main() -
pkpgcounter/trunk/pkpgpdls/bj.py
r3410 r3436 8 8 # the Free Software Foundation, either version 3 of the License, or 9 9 # (at your option) any later version. 10 # 10 # 11 11 # This program is distributed in the hope that it will be useful, 12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 14 # GNU General Public License for more details. 15 # 15 # 16 16 # You should have received a copy of the GNU General Public License 17 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. … … 30 30 """A parser for Canon BJ documents.""" 31 31 format = "Canon BJ/BJC" 32 def isValid(self) : 32 def isValid(self) : 33 33 """Returns True if data is BJ/BJC, else False.""" 34 34 if self.firstblock.startswith("\033[K\002\000") : 35 35 return True 36 else : 36 else : 37 37 return False 38 38 39 39 def getJobSize(self) : 40 40 """Counts pages in a Canon BJ document. 41 41 42 42 Algorithm by Jerome Alet. 43 43 44 44 The documentation used for this was : 45 45 46 46 ghostscript-8.60/src/gdevbj*.c 47 47 """ … … 57 57 # through the Set Initial Condition command 58 58 pageheader = minfile[pos:pos+7] 59 if pageheader in ("\033[K\002\000\000\017", 59 if pageheader in ("\033[K\002\000\000\017", 60 60 "\033[K\002\000\000\044", 61 61 "\033[K\002\000\004\044") : … … 65 65 except IndexError : # EOF ? 66 66 pass 67 finally : 67 finally : 68 68 minfile.close() # reached EOF 69 69 return pagecount -
pkpgcounter/trunk/pkpgpdls/cfax.py
r3410 r3436 8 8 # the Free Software Foundation, either version 3 of the License, or 9 9 # (at your option) any later version. 10 # 10 # 11 11 # This program is distributed in the hope that it will be useful, 12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 14 # GNU General Public License for more details. 15 # 15 # 16 16 # You should have received a copy of the GNU General Public License 17 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. … … 29 29 """A parser for Structured Fax documents.""" 30 30 format = "Structured Fax" 31 def isValid(self) : 31 def isValid(self) : 32 32 """Returns True if data is Structured Fax, else False.""" 33 33 if self.firstblock.startswith("Sfff") : 34 34 return True 35 else : 35 else : 36 36 return False 37 37 38 38 def getJobSize(self) : 39 39 """Counts pages in a Structured Fax document. 40 40 41 41 Algorithm by Jerome Alet. 42 42 43 43 The documentation used for this was : 44 44 45 45 http://delphi.pjh2.de/articles/graphic/sff_format.php 46 46 """ … … 57 57 offsetlastpage, 58 58 offsetdocumentend) = unpack("<4sBBHHHII", docheader) 59 self.infile.seek(offsetfirstpage - len(docheader), 1) 59 self.infile.seek(offsetfirstpage - len(docheader), 1) 60 60 while True : 61 61 headerid = self.infile.read(1) 62 62 if not headerid : 63 63 break 64 headerid = ord(headerid) 64 headerid = ord(headerid) 65 65 if 1 <= headerid <= 216 : # Normal record header 66 66 self.infile.seek(headerid, 1) … … 69 69 if not additionalbyte : 70 70 break 71 additionalbyte = ord(additionalbyte) 71 additionalbyte = ord(additionalbyte) 72 72 if 1 <= additionalbyte <= 255 : 73 73 # Skip additional user information (reserved) 74 74 self.infile.seek(additionalbyte, 1) 75 elif not headerid : 75 elif not headerid : 76 76 # Record with more than 216 MH-coded bytes 77 77 recordlen = self.infile.read(2) 78 78 if not recordlen : 79 79 break 80 recordlen = unpack("<H", recordlen)[0] 80 recordlen = unpack("<H", recordlen)[0] 81 81 self.infile.seek(recordlen, 1) 82 82 elif headerid == 254 : # Page header 83 83 pageheader = self.infile.read(17) 84 if not pageheader : 84 if not pageheader : 85 85 break 86 86 headerlen = ord(pageheader[0]) … … 95 95 offsetpreviouspage, 96 96 offsetnextpage) = unpack("<4BHHII", pageheader[1:]) 97 pagecount += 1 97 pagecount += 1 98 98 if (offsetnextpage == 1) or (vres == 255) : 99 99 break # End Of Document 100 self.infile.seek(offsetnextpage, 1) 101 except struct.error : 100 self.infile.seek(offsetnextpage, 1) 101 except struct.error : 102 102 raise pdlparser.PDLParserError, "Invalid Structured Fax datas" 103 return max(docpagecount, pagecount) 103 return max(docpagecount, pagecount) -
pkpgcounter/trunk/pkpgpdls/dvi.py
r3410 r3436 8 8 # the Free Software Foundation, either version 3 of the License, or 9 9 # (at your option) any later version. 10 # 10 # 11 11 # This program is distributed in the hope that it will be useful, 12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 14 # GNU General Public License for more details. 15 # 15 # 16 16 # You should have received a copy of the GNU General Public License 17 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. … … 34 34 required = [ "dvips", "gs" ] 35 35 format = "DVI" 36 def isValid(self) : 36 def isValid(self) : 37 37 """Returns True if data is DVI, else False.""" 38 38 try : … … 40 40 and (ord(self.lastblock[-1]) == 0xdf) : 41 41 return True 42 else : 42 else : 43 43 return False 44 except IndexError : 44 except IndexError : 45 45 return False 46 46 47 47 def getJobSize(self) : 48 48 """Counts pages in a DVI document. 49 49 50 50 Algorithm by Jerome Alet. 51 51 52 52 The documentation used for this was : 53 53 54 54 http://www.math.umd.edu/~asnowden/comp-cont/dvi.html 55 55 """ … … 64 64 while minfile[pos] == eofchar : 65 65 pos -= 1 66 idbyte = minfile[pos] 66 idbyte = minfile[pos] 67 67 if idbyte != minfile[1] : 68 68 raise IndexError, "Invalid DVI file." … … 73 73 except IndexError : # EOF ? 74 74 pass 75 finally : 75 finally : 76 76 minfile.close() # reached EOF 77 77 return pagecount -
pkpgcounter/trunk/pkpgpdls/escp2.py
r3410 r3436 8 8 # the Free Software Foundation, either version 3 of the License, or 9 9 # (at your option) any later version. 10 # 10 # 11 11 # This program is distributed in the hope that it will be useful, 12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 14 # GNU General Public License for more details. 15 # 15 # 16 16 # You should have received a copy of the GNU General Public License 17 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. … … 29 29 """A parser for ESC/P2 documents.""" 30 30 format = "ESC/P2" 31 def isValid(self) : 31 def isValid(self) : 32 32 """Returns True if data is ESC/P2, else False.""" 33 33 if self.firstblock.startswith("\033@") or \ … … 36 36 self.firstblock.startswith("\0\0\0\033\1@EJL") : # ESC/P Raster ??? Seen on Stylus Photo 1284 37 37 return True 38 else : 38 else : 39 39 return False 40 41 def getJobSize(self) : 40 41 def getJobSize(self) : 42 42 """Counts pages in an ESC/P2 document.""" 43 43 # with Gimpprint, at least, for each page there 44 44 # are two Reset Printer sequences (ESC + @) 45 45 marker1 = "\033@" 46 46 47 47 # with other software or printer driver, we 48 48 # may prefer to search for "\r\n\fESCAPE" … … 50 50 marker2r = "\r\f\033" 51 51 marker2rn = "\r\n\f\033" 52 52 53 53 # and ghostscript's stcolor for example seems to 54 54 # output ESC + @ + \f for each page plus one 55 55 marker3 = "\033@\f" 56 56 57 57 # while ghostscript's escp driver outputs instead 58 58 # \f + ESC + @ 59 59 marker4 = "\f\033@" 60 60 61 61 data = self.infile.read() 62 62 pagecount1 = data.count(marker1) … … 64 64 pagecount3 = data.count(marker3) 65 65 pagecount4 = data.count(marker4) 66 67 if pagecount2 : 66 67 if pagecount2 : 68 68 return pagecount2 69 elif pagecount3 > 1 : 69 elif pagecount3 > 1 : 70 70 return pagecount3 - 1 71 elif pagecount4 : 71 elif pagecount4 : 72 72 return pagecount4 73 else : 74 return int(pagecount1 / 2) 73 else : 74 return int(pagecount1 / 2) -
pkpgcounter/trunk/pkpgpdls/escpages03.py
r3410 r3436 8 8 # the Free Software Foundation, either version 3 of the License, or 9 9 # (at your option) any later version. 10 # 10 # 11 11 # This program is distributed in the hope that it will be useful, 12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 14 # GNU General Public License for more details. 15 # 15 # 16 16 # You should have received a copy of the GNU General Public License 17 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. … … 33 33 """A parser for ESC/PageS03 documents.""" 34 34 format = "ESC/PageS03" 35 def isValid(self) : 35 def isValid(self) : 36 36 """Returns True if data is TIFF, else False.""" 37 37 if self.firstblock.startswith("\033\1@EJL") and \ 38 38 (self.firstblock.find("=ESC/PAGES03\n") != -1) : 39 39 return True 40 else : 40 else : 41 41 return False 42 42 43 43 def getJobSize(self) : 44 44 """Counts pages in an ESC/PageS03 document. 45 45 46 46 Algorithm by Jerome Alet. 47 47 Reverse engineered the file format. … … 61 61 lgendsequence = len(endsequence) 62 62 try : 63 try : 63 try : 64 64 while True : 65 65 if minfile[startpos] == startsequence : … … 82 82 if pagecount.startswith('"') and pagecount.endswith('"') : 83 83 pagecount = pagecount[1:-1] 84 pagecount = int(pagecount) 84 pagecount = int(pagecount) 85 85 if pagecount <= 0 : 86 86 pagecount = 1 # TODO : 0 or 1000000 ??? ;-) 87 87 break 88 startpos += 1 89 except IndexError : 88 startpos += 1 89 except IndexError : 90 90 pass 91 finally : 91 finally : 92 92 minfile.close() 93 93 return pagecount -
pkpgcounter/trunk/pkpgpdls/hbp.py
r3410 r3436 8 8 # the Free Software Foundation, either version 3 of the License, or 9 9 # (at your option) any later version. 10 # 10 # 11 11 # This program is distributed in the hope that it will be useful, 12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 14 # GNU General Public License for more details. 15 # 15 # 16 16 # You should have received a copy of the GNU General Public License 17 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. … … 32 32 """A parser for HBP documents.""" 33 33 format = "Brother HBP" 34 def isValid(self) : 34 def isValid(self) : 35 35 """Returns True if data is HBP, else False.""" 36 36 if self.firstblock.find("@PJL ENTER LANGUAGE = HBP\n") != -1 : 37 37 return True 38 else : 38 else : 39 39 return False 40 40 41 41 def getJobSize(self) : 42 42 """Counts pages in a HBP document. 43 43 44 44 Algorithm by Jerome Alet. 45 45 46 46 The documentation used for this was : 47 47 48 48 http://sf.net/projects/hbp-for-brother/ 49 49 50 50 IMPORTANT : this may not work since @F should be sufficient, 51 51 but the documentation really is unclear and I don't know … … 55 55 minfile = mmap.mmap(infileno, os.fstat(infileno)[6], prot=mmap.PROT_READ, flags=mmap.MAP_SHARED) 56 56 pagecount = 0 57 57 58 58 formfeed = "@G" + chr(0) + chr(0) + chr(1) + chr(0xff) + "@F" 59 59 fflen = len(formfeed) … … 66 66 pagecount += 1 67 67 pos += fflen 68 else : 68 else : 69 69 pos += 1 70 70 except IndexError : # EOF ? 71 71 pass 72 finally : 72 finally : 73 73 minfile.close() # reached EOF 74 74 return pagecount -
pkpgcounter/trunk/pkpgpdls/__init__.py
r3410 r3436 8 8 # the Free Software Foundation, either version 3 of the License, or 9 9 # (at your option) any later version. 10 # 10 # 11 11 # This program is distributed in the hope that it will be useful, 12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 14 # GNU General Public License for more details. 15 # 15 # 16 16 # You should have received a copy of the GNU General Public License 17 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. -
pkpgcounter/trunk/pkpgpdls/inkcoverage.py
r3410 r3436 8 8 # the Free Software Foundation, either version 3 of the License, or 9 9 # (at your option) any later version. 10 # 10 # 11 11 # This program is distributed in the hope that it will be useful, 12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 14 # GNU General Public License for more details. 15 # 15 # 16 16 # You should have received a copy of the GNU General Public License 17 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. … … 28 28 try : 29 29 from PIL import Image 30 except ImportError : 30 except ImportError : 31 31 sys.stderr.write("ERROR: You MUST install the Python Imaging Library (python-imaging) for pkpgcounter to work.\n") 32 32 raise pdlparser.PDLParserError, "The Python Imaging Library is missing." … … 34 34 def getPercent(img, nbpix) : 35 35 """Extracts the percents per color component from a picture. 36 36 37 37 Faster without Psyco on my own machine. 38 38 """ 39 result = {} 39 result = {} 40 40 bands = img.split() 41 41 for (i, bandname) in enumerate(img.getbands()) : 42 42 result[bandname] = 100.0 * (reduce(lambda current, next: current + (next[1] * next[0]), enumerate(bands[i].histogram()), 0) / 255.0) / nbpix 43 return result 44 43 return result 44 45 45 def getPercentCMYK(img, nbpix) : 46 46 """Extracts the percents of Cyan, Magenta, Yellow, and Black from a picture. 47 47 48 48 PIL doesn't produce useable CMYK for our algorithm, so we use the algorithm from PrintBill. 49 49 Psyco speeds this function up by around 2.5 times on my computer. … … 51 51 if img.mode != "RGB" : 52 52 img = img.convert("RGB") 53 cyan = magenta = yellow = black = 0 53 cyan = magenta = yellow = black = 0 54 54 for (r, g, b) in img.getdata() : 55 55 if r == g == b : 56 56 black += 255 - r 57 else : 57 else : 58 58 cyan += 255 - r 59 59 magenta += 255 - g … … 64 64 "K" : 100.0 * (black / 255.0) / nbpix, 65 65 } 66 67 def getPercentGC(img, nbpix) : 66 67 def getPercentGC(img, nbpix) : 68 68 """Determines if a page is in grayscale or colour mode.""" 69 69 if img.mode != "RGB" : … … 75 75 return { "G" : 0.0, "C" : 100.0 } 76 76 return { "G" : 100.0, "C" : 0.0 } 77 77 78 78 def getPercentBW(img, nbpix) : 79 79 """Extracts the percents of Black from a picture, once converted to gray levels.""" … … 81 81 img = img.convert("L") 82 82 return { "B" : 100.0 - getPercent(img, nbpix)["L"] } 83 83 84 84 def getPercentRGB(img, nbpix) : 85 85 """Extracts the percents of Red, Green, Blue from a picture, once converted to RGB.""" 86 86 if img.mode != "RGB" : 87 87 img = img.convert("RGB") 88 return getPercent(img, nbpix) 89 88 return getPercent(img, nbpix) 89 90 90 def getPercentCMY(img, nbpix) : 91 91 """Extracts the percents of Cyan, Magenta, and Yellow from a picture once converted to RGB.""" … … 95 95 "Y" : 100.0 - result["B"], 96 96 } 97 97 98 98 def getInkCoverage(fname, colorspace) : 99 """Returns a list of dictionnaries containing for each page, 100 for each color component, the percent of ink coverage on 99 """Returns a list of dictionnaries containing for each page, 100 for each color component, the percent of ink coverage on 101 101 that particular page. 102 102 """ … … 107 107 try : 108 108 import psyco 109 except ImportError : 109 except ImportError : 110 110 pass 111 else : 111 else : 112 112 psyco.bind(getPercentCMYK) 113 113 114 114 index = 0 115 115 try : 116 116 image = Image.open(fname) 117 except (IOError, OverflowError), msg : 117 except (IOError, OverflowError), msg : 118 118 raise pdlparser.PDLParserError, "%s (%s)" % (msg, fname) 119 else : 119 else : 120 120 try : 121 121 while True : 122 122 nbpixels = image.size[0] * image.size[1] 123 123 result.append(computation(image, nbpixels)) 124 index += 1 124 index += 1 125 125 image.seek(index) 126 except EOFError : 126 except EOFError : 127 127 pass 128 128 return (colorspace, result) -
pkpgcounter/trunk/pkpgpdls/lidil.py
r3410 r3436 8 8 # the Free Software Foundation, either version 3 of the License, or 9 9 # (at your option) any later version. 10 # 10 # 11 11 # This program is distributed in the hope that it will be useful, 12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 14 # GNU General Public License for more details. 15 # 15 # 16 16 # You should have received a copy of the GNU General Public License 17 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. … … 22 22 """This modules implements a page counter for HP LIDIL format. 23 23 24 Documentation used : 25 24 Documentation used : 25 26 26 hplip-2.7.10/prnt/ldl.py 27 27 hplip-2.7.10/prnt/hpijs/ldlencap.h … … 52 52 """A parser for HP LIDIL documents.""" 53 53 format = "Hewlett-Packard LIDIL" 54 def isValid(self) : 54 def isValid(self) : 55 55 """Returns True if data is LIDIL, else False.""" 56 56 # Beginning Of File marker is a Sync packet, followed with … … 61 61 # with a Reset packet. We ignore the preceding Sync packet 62 62 # for simplicity's sake. 63 EOFMarker = "$\x00\x10\x00\x08\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff$$\x00\x10\x00\x06\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff$" 63 EOFMarker = "$\x00\x10\x00\x08\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff$$\x00\x10\x00\x06\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff$" 64 64 if self.firstblock.startswith(BOFMarker) \ 65 65 and self.lastblock.endswith(EOFMarker) : 66 66 return True 67 else : 67 else : 68 68 return False 69 69 70 70 def getJobSize(self) : 71 71 """Computes the number of pages in a HP LIDIL document.""" … … 80 80 # Invalid header or no Frame Sync byte. 81 81 raise pdlparser.PDLParserError, "This file doesn't seem to be valid Hewlett-Packard LIDIL datas." 82 (framesync, 82 (framesync, 83 83 cmdlength, 84 84 dummy, … … 93 93 ejectpage += 1 94 94 self.infile.seek(cmdlength + datalength - len(header), 1) # relative seek 95 except struct.error : 95 except struct.error : 96 96 raise pdlparser.PDLParserError, "This file doesn't seem to be valid Hewlett-Packard LIDIL datas." 97 97 98 98 # Number of page eject commands should be sufficient, 99 99 # but we never know : someone could try to cheat the printer -
pkpgcounter/trunk/pkpgpdls/mscrap.py
r3410 r3436 8 8 # the Free Software Foundation, either version 3 of the License, or 9 9 # (at your option) any later version. 10 # 10 # 11 11 # This program is distributed in the hope that it will be useful, 12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 14 # GNU General Public License for more details. 15 # 15 # 16 16 # You should have received a copy of the GNU General Public License 17 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. … … 33 33 required = [ "xvfb-run", "xauth", "abiword", "gs" ] 34 34 format = "Microsoft shitty" 35 def isValid(self) : 35 def isValid(self) : 36 36 """Returns True if data is MS crap, else False. 37 37 38 38 Identifying datas taken from the file command's magic database. 39 39 IMPORTANT : some magic values are not reused here because they 40 40 IMPORTANT : seem to be specific to some particular i18n release. 41 """ 41 """ 42 42 if self.firstblock.startswith("PO^Q`") \ 43 43 or self.firstblock.startswith("\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1") \ … … 49 49 if self.isMissing(self.required) : 50 50 return False 51 else : 51 else : 52 52 return True 53 else : 53 else : 54 54 return False 55 55 56 56 def getJobSize(self) : 57 57 """Counts pages in a Microsoft Word (r) (tm) (c) (etc...) document. … … 71 71 (first, last) = self.parent.readFirstAndLastBlocks(psinputfile) 72 72 import postscript 73 return postscript.Parser(self.parent, 74 outfname, 73 return postscript.Parser(self.parent, 74 outfname, 75 75 (first, last)).getJobSize() 76 76 finally : 77 77 psinputfile.close() 78 finally : 78 finally : 79 79 workfile.close() 80 80 raise pdlparser.PDLParserError, "Impossible to count pages in %(infname)s" % locals() -
pkpgcounter/trunk/pkpgpdls/ooo.py
r3410 r3436 8 8 # the Free Software Foundation, either version 3 of the License, or 9 9 # (at your option) any later version. 10 # 10 # 11 11 # This program is distributed in the hope that it will be useful, 12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 14 # GNU General Public License for more details. 15 # 15 # 16 16 # You should have received a copy of the GNU General Public License 17 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. … … 32 32 required = [ "xvfb-run", "xauth", "abiword", "gs" ] 33 33 format = "ISO/IEC DIS 26300" 34 def isValid(self) : 34 def isValid(self) : 35 35 """Returns True if data is OpenDocument, else False.""" 36 36 if self.firstblock[:2] == "PK" : … … 39 39 self.contentxml = self.archive.read("content.xml") 40 40 self.metaxml = self.archive.read("meta.xml") 41 except : 41 except : 42 42 return False 43 43 else : 44 44 return True 45 else : 45 else : 46 46 return False 47 47 48 48 def getJobSize(self) : 49 49 """Counts pages in an OpenOffice.org document. 50 50 51 51 Algorithm by Jerome Alet. 52 52 """ -
pkpgcounter/trunk/pkpgpdls/pcl345.py
r3410 r3436 8 8 # the Free Software Foundation, either version 3 of the License, or 9 9 # (at your option) any later version. 10 # 10 # 11 11 # This program is distributed in the hope that it will be useful, 12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 14 # GNU General Public License for more details. 15 # 15 # 16 16 # You should have received a copy of the GNU General Public License 17 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. … … 38 38 class Parser(pdlparser.PDLParser) : 39 39 """A parser for PCL3, PCL4, PCL5 documents.""" 40 totiffcommands = [ 'pcl6 -sDEVICE=pdfwrite -r"%(dpi)i" -dPARANOIDSAFER -dNOPAUSE -dBATCH -dQUIET -sOutputFile=- "%(infname)s" | gs -sDEVICE=tiff24nc -dPARANOIDSAFER -dNOPAUSE -dBATCH -dQUIET -r%(dpi)i -sOutputFile="%(outfname)s" -', 40 totiffcommands = [ 'pcl6 -sDEVICE=pdfwrite -r"%(dpi)i" -dPARANOIDSAFER -dNOPAUSE -dBATCH -dQUIET -sOutputFile=- "%(infname)s" | gs -sDEVICE=tiff24nc -dPARANOIDSAFER -dNOPAUSE -dBATCH -dQUIET -r%(dpi)i -sOutputFile="%(outfname)s" -', 41 41 'pcl6 -sDEVICE=pswrite -r"%(dpi)i" -dPARANOIDSAFER -dNOPAUSE -dBATCH -dQUIET -sOutputFile=- "%(infname)s" | gs -sDEVICE=tiff24nc -dPARANOIDSAFER -dNOPAUSE -dBATCH -dQUIET -r%(dpi)i -sOutputFile="%(outfname)s" -', 42 42 ] … … 48 48 2 : "Letter", 49 49 3 : "Legal", 50 6 : "Ledger", 50 6 : "Ledger", 51 51 25 : "A5", 52 52 26 : "A4", … … 62 62 100 : "B5Envelope", 63 63 101 : "Custom", 64 } 65 64 } 65 66 66 mediasources = { # ESC&l####H 67 67 0 : "Default", … … 75 75 8 : "Tray1", 76 76 } 77 77 78 78 orientations = { # ESC&l####O 79 79 0 : "Portrait", … … 82 82 3 : "ReverseLandscape", 83 83 } 84 84 85 85 mediatypes = { # ESC&l####M 86 86 0 : "Plain", … … 90 90 4 : "Transparent", 91 91 } 92 93 def isValid(self) : 92 93 def isValid(self) : 94 94 """Returns True if data is PCL3/4/5, else False.""" 95 95 try : … … 97 97 while self.firstblock[pos] == chr(0) : 98 98 pos += 1 99 except IndexError : 99 except IndexError : 100 100 return False 101 else : 101 else : 102 102 firstblock = self.firstblock[pos:] 103 103 if firstblock.startswith("\033E\033") or \ … … 111 111 (firstblock.startswith(chr(0xcd)+chr(0xca)) and (firstblock.find("\033E\033") != -1)) : 112 112 return True 113 else : 113 else : 114 114 return False 115 115 116 116 def setPageDict(self, attribute, value) : 117 117 """Initializes a page dictionnary.""" 118 118 dic = self.pages.setdefault(self.pagecount, { "linescount" : 1, 119 "copies" : 1, 120 "mediasource" : "Main", 121 "mediasize" : "Default", 122 "mediatype" : "Plain", 123 "orientation" : "Portrait", 124 "escaped" : "", 119 "copies" : 1, 120 "mediasource" : "Main", 121 "mediasize" : "Default", 122 "mediatype" : "Plain", 123 "orientation" : "Portrait", 124 "escaped" : "", 125 125 "duplex": 0 }) 126 126 dic[attribute] = value 127 128 def readByte(self) : 127 128 def readByte(self) : 129 129 """Reads a byte from the input stream.""" 130 130 tag = ord(self.minfile[self.pos]) 131 131 self.pos += 1 132 132 return tag 133 134 def endPage(self) : 133 134 def endPage(self) : 135 135 """Handle the FF marker.""" 136 136 #self.logdebug("FORMFEED %i at %08x" % (self.pagecount, self.pos-1)) … … 138 138 # Increments page count only if we are not inside an HPGL2 block 139 139 self.pagecount += 1 140 141 def escPercent(self) : 140 141 def escPercent(self) : 142 142 """Handles the ESC% sequence.""" 143 143 if self.minfile[self.pos : self.pos+7] == r"-12345X" : … … 147 147 quotes = 0 148 148 char = chr(self.readByte()) 149 while ((char < ASCIILIMIT) or (quotes % 2)) and (char not in (FORMFEED, ESCAPE, NUL)) : 149 while ((char < ASCIILIMIT) or (quotes % 2)) and (char not in (FORMFEED, ESCAPE, NUL)) : 150 150 buffer.append(char) 151 151 if char == '"' : … … 155 155 #self.logdebug("ESCAPED : %s" % "".join(buffer)) 156 156 self.pos -= 1 # Adjust position 157 else : 157 else : 158 158 while 1 : 159 159 (value, end) = self.getInteger() … … 162 162 while self.minfile[self.pos] != ESCAPE : 163 163 self.pos += 1 164 self.pos -= 1 165 return 166 elif end == 'A' : 164 self.pos -= 1 165 return 166 elif end == 'A' : 167 167 self.exitHPGL2() 168 168 return 169 elif end is None : 169 elif end is None : 170 170 return 171 172 def enterHPGL2(self) : 171 172 def enterHPGL2(self) : 173 173 """Enters HPGL2 mode.""" 174 174 #self.logdebug("ENTERHPGL2 %08x" % self.pos) 175 175 self.hpgl2 = True 176 177 def exitHPGL2(self) : 176 177 def exitHPGL2(self) : 178 178 """Exits HPGL2 mode.""" 179 179 #self.logdebug("EXITHPGL2 %08x" % self.pos) 180 180 self.hpgl2 = False 181 182 def handleTag(self, tagtable) : 181 182 def handleTag(self, tagtable) : 183 183 """Handles tags.""" 184 184 tagtable[self.readByte()]() 185 186 def escape(self) : 185 186 def escape(self) : 187 187 """Handles the ESC character.""" 188 188 #self.logdebug("ESCAPE") 189 189 self.handleTag(self.esctags) 190 191 def escAmp(self) : 190 191 def escAmp(self) : 192 192 """Handles the ESC& sequence.""" 193 193 #self.logdebug("AMP") 194 194 self.handleTag(self.escamptags) 195 196 def escStar(self) : 195 196 def escStar(self) : 197 197 """Handles the ESC* sequence.""" 198 198 #self.logdebug("STAR") 199 199 self.handleTag(self.escstartags) 200 201 def escLeftPar(self) : 200 201 def escLeftPar(self) : 202 202 """Handles the ESC( sequence.""" 203 203 #self.logdebug("LEFTPAR") 204 204 self.handleTag(self.escleftpartags) 205 206 def escRightPar(self) : 205 206 def escRightPar(self) : 207 207 """Handles the ESC( sequence.""" 208 208 #self.logdebug("RIGHTPAR") 209 209 self.handleTag(self.escrightpartags) 210 211 def escE(self) : 210 211 def escE(self) : 212 212 """Handles the ESCE sequence.""" 213 213 #self.logdebug("RESET") 214 214 self.resets += 1 215 216 def escAmpl(self) : 215 216 def escAmpl(self) : 217 217 """Handles the ESC&l sequence.""" 218 218 while 1 : … … 244 244 self.setPageDict("copies", value) 245 245 #self.logdebug("COPIES %i" % value) 246 elif end == 'F' : 246 elif end == 'F' : 247 247 self.linesperpagevalues.append(value) 248 248 self.linesperpage = value … … 250 250 #else : 251 251 # self.logdebug("Unexpected end <%s> and value <%s>" % (end, value)) 252 253 def escAmpa(self) : 252 253 def escAmpa(self) : 254 254 """Handles the ESC&a sequence.""" 255 255 while 1 : … … 257 257 if value is None : 258 258 return 259 if end == 'G' : 259 if end == 'G' : 260 260 #self.logdebug("BACKSIDES %i" % value) 261 261 self.backsides.append(value) 262 262 self.setPageDict("duplex", value) 263 264 def escAmpp(self) : 263 264 def escAmpp(self) : 265 265 """Handles the ESC&p sequence.""" 266 266 while 1 : … … 268 268 if value is None : 269 269 return 270 if end == 'X' : 270 if end == 'X' : 271 271 self.pos += value 272 272 #self.logdebug("SKIPTO %08x" % self.pos) 273 274 def escStarb(self) : 273 274 def escStarb(self) : 275 275 """Handles the ESC*b sequence.""" 276 276 while 1 : … … 278 278 if (end is None) and (value is None) : 279 279 return 280 if end in ('V', 'W', 'v', 'w') : 280 if end in ('V', 'W', 'v', 'w') : 281 281 self.pos += (value or 0) 282 282 #self.logdebug("SKIPTO %08x" % self.pos) 283 284 def escStarr(self) : 283 284 def escStarr(self) : 285 285 """Handles the ESC*r sequence.""" 286 286 while 1 : … … 289 289 if end is None : 290 290 return 291 elif end in ('B', 'C') : 291 elif end in ('B', 'C') : 292 292 #self.logdebug("EndGFX") 293 293 if self.startgfx : 294 294 self.endgfx.append(1) 295 else : 295 else : 296 296 #self.logdebug("EndGFX found before StartGFX, ignored.") 297 297 pass … … 299 299 #self.logdebug("StartGFX %i" % value) 300 300 self.startgfx.append(value) 301 302 def escStaroptAmpu(self) : 301 302 def escStaroptAmpu(self) : 303 303 """Handles the ESC*o ESC*p ESC*t and ESC&u sequences.""" 304 304 while 1 : … … 306 306 if value is None : 307 307 return 308 309 def escSkipSomethingW(self) : 308 309 def escSkipSomethingW(self) : 310 310 """Handles the ESC???###W sequences.""" 311 311 while 1 : … … 313 313 if value is None : 314 314 return 315 if end == 'W' : 315 if end == 'W' : 316 316 self.pos += value 317 317 #self.logdebug("SKIPTO %08x" % self.pos) 318 319 def newLine(self) : 318 319 def newLine(self) : 320 320 """Handles new lines markers.""" 321 321 if not self.hpgl2 : 322 322 dic = self.pages.get(self.pagecount, None) 323 323 if dic is None : 324 self.setPageDict("linescount", 1) 324 self.setPageDict("linescount", 1) 325 325 dic = self.pages.get(self.pagecount) 326 nblines = dic["linescount"] 327 self.setPageDict("linescount", nblines + 1) 326 nblines = dic["linescount"] 327 self.setPageDict("linescount", nblines + 1) 328 328 if (self.linesperpage is not None) \ 329 329 and (dic["linescount"] > self.linesperpage) : 330 330 self.pagecount += 1 331 332 def getInteger(self) : 331 332 def getInteger(self) : 333 333 """Returns an integer value and the end character.""" 334 334 sign = 1 … … 346 346 else : 347 347 return (value, char) 348 else : 349 value = ((value or 0) * 10) + int(char) 350 351 def skipByte(self) : 348 else : 349 value = ((value or 0) * 10) + int(char) 350 351 def skipByte(self) : 352 352 """Skips a byte.""" 353 353 #self.logdebug("SKIPBYTE %08x ===> %02x" % (self.pos, ord(self.minfile[self.pos]))) 354 354 self.pos += 1 355 356 def handleImageRunner(self) : 355 356 def handleImageRunner(self) : 357 357 """Handles Canon ImageRunner tags.""" 358 358 tag = self.readByte() … … 367 367 else : 368 368 self.pos -= 1 # Adjust position 369 370 def getJobSize(self) : 369 370 def getJobSize(self) : 371 371 """Count pages in a PCL5 document. 372 372 373 373 Should also work for PCL3 and PCL4 documents. 374 374 375 375 Algorithm from pclcount 376 (c) 2003, by Eduardo Gielamo Oliveira & Rodolfo Broco Manin 376 (c) 2003, by Eduardo Gielamo Oliveira & Rodolfo Broco Manin 377 377 published under the terms of the GNU General Public Licence v2. 378 378 379 379 Backported from C to Python by Jerome Alet, then enhanced 380 380 with more PCL tags detected. I think all the necessary PCL tags 381 381 are recognized to correctly handle PCL5 files wrt their number 382 382 of pages. The documentation used for this was : 383 383 384 384 HP PCL/PJL Reference Set 385 385 PCL5 Printer Language Technical Quick Reference Guide 386 http://h20000.www2.hp.com/bc/docs/support/SupportManual/bpl13205/bpl13205.pdf 386 http://h20000.www2.hp.com/bc/docs/support/SupportManual/bpl13205/bpl13205.pdf 387 387 """ 388 388 infileno = self.infile.fileno() … … 405 405 self.imagerunnermarker2 = chr(0x10) + chr(0x02) 406 406 self.isimagerunner = (minfile[:2] == self.imagerunnermarker1) 407 407 408 408 tags = [ lambda : None] * 256 409 409 tags[ord(LINEFEED)] = self.newLine … … 412 412 tags[ord(ASCIILIMIT)] = self.skipByte 413 413 tags[ord(self.imagerunnermarker1[0])] = self.handleImageRunner 414 414 415 415 self.esctags = [ lambda : None ] * 256 416 416 self.esctags[ord('%')] = self.escPercent … … 420 420 self.esctags[ord(')')] = self.escRightPar 421 421 self.esctags[ord('E')] = self.escE 422 422 423 423 self.escamptags = [lambda : None ] * 256 424 424 self.escamptags[ord('a')] = self.escAmpa … … 428 428 self.escamptags[ord('n')] = self.escSkipSomethingW 429 429 self.escamptags[ord('u')] = self.escStaroptAmpu 430 430 431 431 self.escstartags = [ lambda : None ] * 256 432 432 self.escstartags[ord('b')] = self.escStarb … … 441 441 self.escstartags[ord('m')] = self.escSkipSomethingW 442 442 self.escstartags[ord('v')] = self.escSkipSomethingW 443 443 444 444 self.escleftpartags = [ lambda : None ] * 256 445 445 self.escleftpartags[ord('s')] = self.escSkipSomethingW 446 446 self.escleftpartags[ord('f')] = self.escSkipSomethingW 447 447 448 448 self.escrightpartags = [ lambda : None ] * 256 449 449 self.escrightpartags[ord('s')] = self.escSkipSomethingW 450 450 451 451 self.pos = 0 452 452 try : … … 454 454 while 1 : 455 455 tags[self.readByte()]() 456 except IndexError : # EOF ? 456 except IndexError : # EOF ? 457 457 pass 458 458 finally : 459 459 self.minfile.close() 460 460 461 461 self.logdebug("Pagecount : \t\t\t%i" % self.pagecount) 462 462 self.logdebug("Resets : \t\t\t%i" % self.resets) … … 482 482 self.logdebug("NbBackSides : \t\t\t%i" % nbbacksides) 483 483 self.logdebug("IsImageRunner : \t\t\t%s" % self.isimagerunner) 484 484 485 485 if self.isimagerunner : 486 486 self.logdebug("Adjusting PageCount : +1") 487 487 self.pagecount += 1 # ImageRunner adjustment 488 elif self.linesperpage is not None : 488 elif self.linesperpage is not None : 489 489 self.logdebug("Adjusting PageCount : +1") 490 490 self.pagecount += 1 # Adjusts for incomplete last page … … 507 507 self.logdebug("Adjusting PageCount : -1") 508 508 self.pagecount -= 1 509 509 510 510 self.pagecount = self.pagecount or nbmediasourcesdefault or nbmediasizes or nborientations or self.resets 511 511 512 512 if not self.pagecount : 513 513 if self.resets == len(self.startgfx) : 514 self.pagecount = self.resets 515 516 defaultpjlcopies = 1 514 self.pagecount = self.resets 515 516 defaultpjlcopies = 1 517 517 defaultduplexmode = "Simplex" 518 518 defaultpapersize = "" … … 539 539 pjlcopies = nbqty 540 540 else : 541 if oldpjlcopies == -1 : 541 if oldpjlcopies == -1 : 542 542 pjlcopies = defaultpjlcopies 543 else : 544 pjlcopies = oldpjlcopies 545 if page["duplex"] : 543 else : 544 pjlcopies = oldpjlcopies 545 if page["duplex"] : 546 546 duplexmode = "Duplex" 547 else : 547 else : 548 548 defaultdm = pjlparser.default_variables.get("DUPLEX", "") 549 549 if defaultdm : 550 550 if defaultdm.upper() == "ON" : 551 551 defaultduplexmode = "Duplex" 552 else : 552 else : 553 553 defaultduplexmode = "Simplex" 554 554 envdm = pjlparser.environment_variables.get("DUPLEX", "") … … 556 556 if envdm.upper() == "ON" : 557 557 duplexmode = "Duplex" 558 else : 558 else : 559 559 duplexmode = "Simplex" 560 else : 560 else : 561 561 duplexmode = oldduplexmode or defaultduplexmode 562 562 defaultps = pjlparser.default_variables.get("PAPER", "") … … 566 566 if envps : 567 567 papersize = envps 568 else : 568 else : 569 569 if not oldpapersize : 570 570 papersize = defaultpapersize 571 else : 571 else : 572 572 papersize = oldpapersize 573 else : 573 else : 574 574 if oldpjlcopies == -1 : 575 575 pjlcopies = defaultpjlcopies 576 else : 576 else : 577 577 pjlcopies = oldpjlcopies 578 578 579 579 duplexmode = (page["duplex"] and "Duplex") or oldduplexmode or defaultduplexmode 580 if not oldpapersize : 580 if not oldpapersize : 581 581 papersize = defaultpapersize 582 else : 582 else : 583 583 papersize = oldpapersize 584 584 papersize = oldpapersize or page["mediasize"] 585 585 if page["mediasize"] != "Default" : 586 586 papersize = page["mediasize"] 587 if not duplexmode : 587 if not duplexmode : 588 588 duplexmode = oldduplexmode or defaultduplexmode 589 oldpjlcopies = pjlcopies 589 oldpjlcopies = pjlcopies 590 590 oldduplexmode = duplexmode 591 591 oldpapersize = papersize … … 598 598 page["mediasource"], \ 599 599 duplexmode)) 600 600 601 601 return self.pagecount -
pkpgcounter/trunk/pkpgpdls/pclxl.py
r3409 r3436 8 8 # the Free Software Foundation, either version 3 of the License, or 9 9 # (at your option) any later version. 10 # 10 # 11 11 # This program is distributed in the hope that it will be useful, 12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 14 # GNU General Public License for more details. 15 # 15 # 16 16 # You should have received a copy of the GNU General Public License 17 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. … … 32 32 class Parser(pdlparser.PDLParser) : 33 33 """A parser for PCLXL (aka PCL6) documents.""" 34 totiffcommands = [ 'pcl6 -sDEVICE=pdfwrite -r"%(dpi)i" -dPARANOIDSAFER -dNOPAUSE -dBATCH -dQUIET -sOutputFile=- "%(infname)s" | gs -sDEVICE=tiff24nc -dPARANOIDSAFER -dNOPAUSE -dBATCH -dQUIET -r"%(dpi)i" -sOutputFile="%(outfname)s" -', 34 totiffcommands = [ 'pcl6 -sDEVICE=pdfwrite -r"%(dpi)i" -dPARANOIDSAFER -dNOPAUSE -dBATCH -dQUIET -sOutputFile=- "%(infname)s" | gs -sDEVICE=tiff24nc -dPARANOIDSAFER -dNOPAUSE -dBATCH -dQUIET -r"%(dpi)i" -sOutputFile="%(outfname)s" -', 35 35 'pcl6 -sDEVICE=pswrite -r"%(dpi)i" -dPARANOIDSAFER -dNOPAUSE -dBATCH -dQUIET -sOutputFile=- "%(infname)s" | gs -sDEVICE=tiff24nc -dPARANOIDSAFER -dNOPAUSE -dBATCH -dQUIET -r"%(dpi)i" -sOutputFile="%(outfname)s" -', 36 36 ] 37 37 required = [ "pcl6", "gs" ] 38 38 format = "PCLXL (aka PCL6)" 39 mediasizes = { 39 mediasizes = { 40 40 0 : "Letter", 41 41 1 : "Legal", … … 61 61 21 : "JISExec", 62 62 96 : "Default", 63 } 64 65 mediasources = { 63 } 64 65 mediasources = { 66 66 0 : "Default", 67 67 1 : "Auto", … … 73 73 7 : "ThirdCassette", 74 74 } 75 76 orientations = { 75 76 orientations = { 77 77 0 : "Portrait", 78 78 1 : "Landscape", … … 81 81 4 : "Default", 82 82 } 83 84 def isValid(self) : 83 84 def isValid(self) : 85 85 """Returns True if data is HP PCLXL aka PCL6, or Brother's' XL2HB, else False.""" 86 86 if (((self.firstblock[:128].find("\033%-12345X") != -1) and \ … … 94 94 self.format = "XL2HB" 95 95 return True 96 else : 96 else : 97 97 return False 98 98 99 99 def beginPage(self, nextpos) : 100 100 """Indicates the beginning of a new page, and extracts media information.""" 101 101 # self.logdebug("BeginPage at %x" % nextpos) 102 102 self.pagecount += 1 103 103 104 104 # Default values 105 105 mediatypelabel = "Plain" … … 108 108 orientationlabel = "Portrait" 109 109 duplexmode = None 110 110 111 111 # Now go upstream to decode media type, size, source, and orientation 112 112 # this saves time because we don't need a complete parser ! … … 117 117 if val in (0x44, 0x48, 0x41) : # if previous endPage or openDataSource or beginSession (first page) 118 118 break 119 if val == 0x26 : 119 if val == 0x26 : 120 120 mediasource = ord(minfile[pos - 2]) 121 121 mediasourcelabel = self.mediasources.get(mediasource, str(mediasource)) … … 132 132 mediasizelabel = minfile[pos+2:pos+2+arraylength].title() 133 133 pos -= 1 134 else : 134 else : 135 135 # if we just found an ubyte, then the media 136 136 # size is known by its index 137 137 mediasize = ord(minfile[pos+1]) 138 138 mediasizelabel = self.mediasizes.get(mediasize, str(mediasize)) 139 pos -= 1 139 pos -= 1 140 140 # self.logdebug("Media size : %s" % mediasizelabel) 141 elif val == 0x28 : 141 elif val == 0x28 : 142 142 orientation = ord(minfile[pos - 2]) 143 143 orientationlabel = self.orientations.get(orientation, str(orientation)) 144 144 pos -= 4 145 elif val == 0x27 : 145 elif val == 0x27 : 146 146 savepos = pos 147 147 pos -= 1 148 startpos = size = None 148 startpos = size = None 149 149 while pos > 0 : # safety check : don't go back to far ! 150 150 val = ord(minfile[pos]) 151 pos -= 1 151 pos -= 1 152 152 if val == 0xc8 : 153 153 length = self.tags[ord(minfile[pos+2])] # will probably always be a byte or uint16 154 if length == 1 : 154 if length == 1 : 155 155 startpos = pos + 4 156 156 size = unpack("B", self.minfile[pos+3:startpos])[0] 157 elif length == 2 : 157 elif length == 2 : 158 158 startpos = pos + 5 159 159 size = unpack(self.unpackShort, self.minfile[pos+3:startpos])[0] 160 elif length == 4 : 160 elif length == 4 : 161 161 startpos = pos + 7 162 162 size = unpack(self.unpackLong, self.minfile[pos+3:startpos])[0] 163 else : 163 else : 164 164 raise pdlparser.PDLParserError, "Error on size at %s : %s" % (pos+2, length) 165 165 break 166 166 mediatypelabel = minfile[startpos:startpos+size] 167 167 # self.logdebug("Media type : %s" % mediatypelabel) 168 elif val == 0x34 : 168 elif val == 0x34 : 169 169 duplexmode = "Simplex" 170 170 pos -= 2 171 elif val in (0x35, 0x36) : 171 elif val in (0x35, 0x36) : 172 172 duplexmode = "Duplex" 173 173 pos -= 2 174 # else : TODO : CUSTOM MEDIA SIZE AND UNIT ! 175 else : 174 # else : TODO : CUSTOM MEDIA SIZE AND UNIT ! 175 else : 176 176 pos -= 1 # ignored 177 self.pages[self.pagecount] = { "copies" : 1, 178 "orientation" : orientationlabel, 179 "mediatype" : mediatypelabel, 177 self.pages[self.pagecount] = { "copies" : 1, 178 "orientation" : orientationlabel, 179 "mediatype" : mediatypelabel, 180 180 "mediasize" : mediasizelabel, 181 181 "mediasource" : mediasourcelabel, 182 182 "duplex" : duplexmode, 183 } 183 } 184 184 return 0 185 186 def endPage(self, nextpos) : 185 186 def endPage(self, nextpos) : 187 187 """Indicates the end of a page.""" 188 188 # self.logdebug("EndPage at %x" % nextpos) … … 198 198 # self.logdebug("Number of copies : %i" % nbcopies) 199 199 self.pages[self.pagecount]["copies"] = nbcopies 200 except KeyError : 200 except KeyError : 201 201 self.logdebug("It looks like this PCLXL file is corrupted.") 202 202 return 0 203 204 def setColorSpace(self, nextpos) : 203 204 def setColorSpace(self, nextpos) : 205 205 """Changes the color space.""" 206 206 if self.minfile[nextpos-4:nextpos-1] == self.RGBColorSpace : # TODO : doesn't seem to handle all cases ! 207 207 self.iscolor = True 208 208 return 0 209 209 210 210 def array_Generic(self, nextpos, size) : 211 211 """Handles all arrays.""" … … 220 220 except KeyError : 221 221 raise pdlparser.PDLParserError, "Error on array size at %x" % nextpos 222 223 def array_8(self, nextpos) : 222 223 def array_8(self, nextpos) : 224 224 """Handles byte arrays.""" 225 225 return self.array_Generic(nextpos, 1) 226 226 227 227 def array_16(self, nextpos) : 228 228 """Handles 16 bits arrays.""" 229 229 return self.array_Generic(nextpos, 2) 230 230 231 231 def array_32(self, nextpos) : 232 232 """Handles 32 bits arrays and Canon ImageRunner tags.""" … … 243 243 toskip += length 244 244 # self.logdebug("Canon ImageRunner skip until %x" % (nextpos+toskip)) 245 return toskip 245 return toskip 246 246 else : 247 247 # This is a normal PCLXL array 248 248 return self.array_Generic(nextpos, 4) 249 249 250 250 def embeddedDataSmall(self, nextpos) : 251 251 """Handle small amounts of data.""" 252 252 return 1 + ord(self.minfile[nextpos]) 253 253 254 254 def embeddedData(self, nextpos) : 255 255 """Handle normal amounts of data.""" 256 256 return 4 + unpack(self.unpackLong, self.minfile[nextpos:nextpos+4])[0] 257 258 def skipHPPCLXL(self, nextpos) : 257 258 def skipHPPCLXL(self, nextpos) : 259 259 """Skip the 'HP-PCL XL' statement if needed.""" 260 260 minfile = self.minfile … … 265 265 while minfile[pos] != '\n' : 266 266 pos += 1 267 length = (pos - nextpos + 1) 268 # self.logdebug("Skip HP PCLXL statement until %x" % (nextpos + length)) 267 length = (pos - nextpos + 1) 268 # self.logdebug("Skip HP PCLXL statement until %x" % (nextpos + length)) 269 269 return length 270 else : 270 else : 271 271 return 0 272 272 273 273 def littleEndian(self, nextpos) : 274 274 """Toggles to little endianness.""" … … 278 278 # self.logdebug("LittleEndian at %x" % (nextpos - 1)) 279 279 return self.skipHPPCLXL(nextpos) 280 280 281 281 def bigEndian(self, nextpos) : 282 282 """Toggles to big endianness.""" … … 286 286 # self.logdebug("BigEndian at %x" % (nextpos - 1)) 287 287 return self.skipHPPCLXL(nextpos) 288 288 289 289 def reservedForFutureUse(self, nextpos) : 290 290 """Outputs something when a reserved byte is encountered.""" 291 291 self.logdebug("Byte at %x is out of the PCLXL Protocol Class 2.0 Specification" % nextpos) 292 return 0 293 292 return 0 293 294 294 def x46_class3(self, nextpos) : 295 295 """Undocumented tag 0x46 in class 3.0 streams.""" … … 301 301 try : 302 302 offset = self.x46_functions[funcid] 303 except KeyError : 303 except KeyError : 304 304 self.logdebug("Unexpected subfunction 0x%02x for undocumented tag 0x46 at %x" % (funcid, nextpos)) 305 305 break 306 else : 306 else : 307 307 pos -= offset 308 308 length = self.tags[ord(self.minfile[pos])] … … 315 315 raise pdlparser.PDLParserError, "Error on size '%s' at %x" % (length, pos+1) 316 316 val = ord(minfile[pos]) 317 return 0 318 319 def escape(self, nextpos) : 317 return 0 318 319 def escape(self, nextpos) : 320 320 """Handles the ESC code.""" 321 321 pos = endpos = nextpos … … 331 331 quotes += 1 332 332 endpos += 1 333 334 # Store this in a per page mapping. 333 334 # Store this in a per page mapping. 335 335 # NB : First time will be at page 0 (i.e. **before** page 1) ! 336 336 stuff = self.escapedStuff.setdefault(self.pagecount, []) … … 338 338 self.logdebug("Escaped datas : [%s]" % repr(minfile[pos : endpos])) 339 339 return endpos - pos 340 340 341 341 def skipKyoceraPrescribe(self, nextpos) : 342 342 """Skips Kyocera Prescribe commands.""" … … 351 351 self.logdebug("Prescribe commands : [%s]" % repr(minfile[nextpos-1:pos])) 352 352 break 353 pos += 1 353 pos += 1 354 354 return (pos - nextpos) 355 355 else : 356 356 return 0 357 357 358 358 def getJobSize(self) : 359 359 """Counts pages in a PCLXL (PCL6) document. 360 360 361 361 Algorithm by Jerome Alet. 362 362 363 363 The documentation used for this was : 364 364 365 365 HP PCL XL Feature Reference 366 366 Protocol Class 2.0 367 http://www.hpdevelopersolutions.com/downloads/64/358/xl_ref20r22.pdf 368 367 http://www.hpdevelopersolutions.com/downloads/64/358/xl_ref20r22.pdf 368 369 369 Protocol Class 2.1 Supplement 370 370 xl_ref21.pdf 371 371 372 372 Protocol Class 3.0 Supplement 373 373 xl_refsup30r089.pdf 374 374 """ 375 375 376 376 infileno = self.infile.fileno() 377 377 self.minfile = minfile = mmap.mmap(infileno, os.fstat(infileno)[6], prot=mmap.PROT_READ, flags=mmap.MAP_SHARED) 378 378 379 379 self.iscolor = False 380 380 381 381 found = False 382 382 while not found : … … 392 392 if endian == 0x29 : 393 393 self.littleEndian(0) 394 elif endian == 0x28 : 394 elif endian == 0x28 : 395 395 self.bigEndian(0) 396 396 # elif endian == 0x27 : # TODO : This is the ASCII binding code : what does it do exactly ? 397 # 398 else : 397 # 398 else : 399 399 raise pdlparser.PDLParserError, "Unknown endianness marker 0x%02x at start !" % endian 400 400 if not found : 401 401 raise pdlparser.PDLParserError, "This file doesn't seem to be PCLXL (aka PCL6)" 402 402 403 403 # Initialize Media Sources 404 404 for i in range(8, 256) : 405 405 self.mediasources[i] = "ExternalTray%03i" % (i - 7) 406 406 407 407 # Initialize table of tags 408 self.tags = [ 0 ] * 256 409 408 self.tags = [ 0 ] * 256 409 410 410 self.tags[0x1b] = self.escape # The escape code 411 411 412 412 self.tags[0x21] = self.skipKyoceraPrescribe # 0x21 is not normally used 413 413 414 414 # GhostScript's sources tell us that HP printers 415 415 # only accept little endianness, but we can handle both. 416 416 self.tags[0x28] = self.bigEndian # BigEndian 417 417 self.tags[0x29] = self.littleEndian # LittleEndian 418 418 419 419 self.tags[0x43] = self.beginPage # BeginPage 420 420 self.tags[0x44] = self.endPage # EndPage 421 421 self.tags[0x45] = self.reservedForFutureUse # reserved 422 423 self.tags[0x46] = self.x46_class3 424 422 423 self.tags[0x46] = self.x46_class3 424 425 425 self.tags[0x4a] = self.reservedForFutureUse # reserved 426 426 self.tags[0x4b] = self.reservedForFutureUse # reserved … … 428 428 self.tags[0x4d] = self.reservedForFutureUse # reserved 429 429 self.tags[0x4e] = self.reservedForFutureUse # reserved 430 430 431 431 self.tags[0x56] = self.reservedForFutureUse # TODO : documentation not clear about reserved status 432 432 433 433 self.tags[0x57] = self.reservedForFutureUse # reserved 434 434 435 435 self.tags[0x59] = self.reservedForFutureUse # reserved 436 436 self.tags[0x5a] = self.reservedForFutureUse # reserved 437 437 438 438 self.tags[0x6a] = self.setColorSpace # to detect color/b&w mode 439 439 440 440 self.tags[0x87] = self.reservedForFutureUse # reserved 441 441 self.tags[0x88] = self.reservedForFutureUse # reserved 442 442 self.tags[0x89] = self.reservedForFutureUse # reserved 443 443 self.tags[0x8a] = self.reservedForFutureUse # reserved 444 444 445 445 self.tags[0x8b] = self.reservedForFutureUse # reserved 446 446 447 447 self.tags[0x8c] = self.reservedForFutureUse # reserved 448 448 self.tags[0x8d] = self.reservedForFutureUse # reserved … … 450 450 self.tags[0x8f] = self.reservedForFutureUse # reserved 451 451 self.tags[0x90] = self.reservedForFutureUse # reserved 452 452 453 453 self.tags[0x9a] = self.reservedForFutureUse # reserved 454 454 self.tags[0x9c] = self.reservedForFutureUse # reserved 455 455 456 456 self.tags[0xa4] = self.reservedForFutureUse # reserved 457 457 self.tags[0xa5] = self.reservedForFutureUse # reserved 458 458 self.tags[0xa6] = self.reservedForFutureUse # reserved 459 459 self.tags[0xa7] = self.reservedForFutureUse # reserved 460 460 461 461 self.tags[0xaa] = self.reservedForFutureUse # reserved 462 462 self.tags[0xab] = self.reservedForFutureUse # reserved … … 465 465 self.tags[0xae] = self.reservedForFutureUse # reserved 466 466 self.tags[0xaf] = self.reservedForFutureUse # reserved 467 467 468 468 self.tags[0xb7] = self.reservedForFutureUse # reserved 469 469 470 470 self.tags[0xba] = self.reservedForFutureUse # reserved 471 471 self.tags[0xbb] = self.reservedForFutureUse # reserved … … 473 473 self.tags[0xbd] = self.reservedForFutureUse # reserved 474 474 self.tags[0xbe] = self.reservedForFutureUse # reserved 475 475 476 476 # self.tags[0xbf] = self.passThrough # PassThrough mode should already be taken care of automatically 477 477 478 478 self.tags[0xc0] = 1 # ubyte 479 479 self.tags[0xc1] = 2 # uint16 … … 482 482 self.tags[0xc4] = 4 # sint32 483 483 self.tags[0xc5] = 4 # real32 484 484 485 485 self.tags[0xc6] = self.reservedForFutureUse # reserved 486 486 self.tags[0xc7] = self.reservedForFutureUse # reserved 487 487 488 488 self.tags[0xc8] = self.array_8 # ubyte_array 489 489 self.tags[0xc9] = self.array_16 # uint16_array … … 492 492 self.tags[0xcc] = self.array_32 # sint32_array 493 493 self.tags[0xcd] = self.array_32 # real32_array and unfortunately Canon ImageRunner 494 494 495 495 self.tags[0xce] = self.reservedForFutureUse # reserved 496 496 self.tags[0xcf] = self.reservedForFutureUse # reserved 497 497 498 498 self.tags[0xd0] = 2 # ubyte_xy 499 499 self.tags[0xd1] = 4 # uint16_xy … … 512 512 self.tags[0xde] = self.reservedForFutureUse # reserved 513 513 self.tags[0xdf] = self.reservedForFutureUse # reserved 514 514 515 515 self.tags[0xe0] = 4 # ubyte_box 516 516 self.tags[0xe1] = 8 # uint16_box … … 529 529 self.tags[0xee] = self.reservedForFutureUse # reserved 530 530 self.tags[0xef] = self.reservedForFutureUse # reserved 531 531 532 532 self.tags[0xf0] = self.reservedForFutureUse # reserved 533 533 self.tags[0xf1] = self.reservedForFutureUse # reserved … … 538 538 self.tags[0xf6] = self.reservedForFutureUse # reserved 539 539 self.tags[0xf7] = self.reservedForFutureUse # reserved 540 540 541 541 self.tags[0xf8] = 1 # attr_ubyte 542 542 self.tags[0xf9] = 2 # attr_uint16 543 543 544 544 self.tags[0xfa] = self.embeddedData # dataLength 545 545 self.tags[0xfb] = self.embeddedDataSmall # dataLengthByte 546 546 547 547 self.tags[0xfc] = self.reservedForFutureUse # reserved 548 548 self.tags[0xfd] = self.reservedForFutureUse # reserved 549 549 self.tags[0xfe] = self.reservedForFutureUse # reserved 550 550 self.tags[0xff] = self.reservedForFutureUse # reserved 551 552 # color spaces 551 552 # color spaces 553 553 self.BWColorSpace = "".join([chr(0x00), chr(0xf8), chr(0x03)]) 554 554 self.GrayColorSpace = "".join([chr(0x01), chr(0xf8), chr(0x03)]) 555 555 self.RGBColorSpace = "".join([chr(0x02), chr(0xf8), chr(0x03)]) 556 556 557 557 # set number of copies 558 self.setNumberOfCopies = "".join([chr(0xf8), chr(0x31)]) 559 558 self.setNumberOfCopies = "".join([chr(0xf8), chr(0x31)]) 559 560 560 # subcodes for undocumented tag 0x46 and the negative 561 561 # offset to grab the value from. … … 569 569 0x98 : 2, 570 570 } 571 571 572 572 # Markers for Canon ImageRunner printers 573 573 self.imagerunnermarker1 = chr(0xcd) + chr(0xca) + chr(0x10) + chr(0x00) 574 574 self.imagerunnermarker2 = chr(0xcd) + chr(0xca) + chr(0x10) + chr(0x02) 575 576 self.pages = { 0 : { "copies" : 1, 577 "orientation" : "Default", 578 "mediatype" : "Plain", 579 "mediasize" : "Default", 580 "mediasource" : "Default", 575 576 self.pages = { 0 : { "copies" : 1, 577 "orientation" : "Default", 578 "mediatype" : "Plain", 579 "mediasize" : "Default", 580 "mediasource" : "Default", 581 581 "duplex" : None, 582 } 583 } 582 } 583 } 584 584 tags = self.tags 585 585 self.pagecount = 0 … … 592 592 try : 593 593 tag = ord(minfile[pos]) 594 except OverflowError : 594 except OverflowError : 595 595 pos = oldpos + 1 596 596 pos += 1 597 597 length = tags[tag] 598 598 if length : 599 if callable(length) : 599 if callable(length) : 600 600 length = length(pos) 601 oldpos = pos 602 pos += length 603 except IndexError : # EOF ? 601 oldpos = pos 602 pos += length 603 except IndexError : # EOF ? 604 604 pass 605 605 finally : 606 606 self.minfile.close() 607 607 608 608 # now handle number of copies for each page (may differ). 609 609 if self.iscolor : 610 610 colormode = "Color" 611 else : 611 else : 612 612 colormode = "BW" 613 613 614 614 defaultduplexmode = "Simplex" 615 615 defaultpapersize = "" 616 defaultpjlcopies = 1 616 defaultpjlcopies = 1 617 617 oldpjlcopies = -1 618 618 oldduplexmode = "" … … 622 622 # in PCLXL documentation. 623 623 # NB : is number of copies is 0, the page won't be output 624 # but the formula below is still correct : we want 624 # but the formula below is still correct : we want 625 625 # to decrease the total number of pages in this case. 626 626 page = self.pages.get(pnum, self.pages.get(1, { "copies" : 1, "mediasize" : "Default", "duplex" : None })) … … 641 641 pjlcopies = nbqty 642 642 else : 643 if oldpjlcopies == -1 : 643 if oldpjlcopies == -1 : 644 644 pjlcopies = defaultpjlcopies 645 else : 646 pjlcopies = oldpjlcopies 647 if page["duplex"] : 645 else : 646 pjlcopies = oldpjlcopies 647 if page["duplex"] : 648 648 duplexmode = page["duplex"] 649 else : 649 else : 650 650 defaultdm = pjlparser.default_variables.get("DUPLEX", "") 651 651 if defaultdm : 652 652 if defaultdm.upper() == "ON" : 653 653 defaultduplexmode = "Duplex" 654 else : 654 else : 655 655 defaultduplexmode = "Simplex" 656 656 envdm = pjlparser.environment_variables.get("DUPLEX", "") … … 658 658 if envdm.upper() == "ON" : 659 659 duplexmode = "Duplex" 660 else : 660 else : 661 661 duplexmode = "Simplex" 662 else : 662 else : 663 663 if not oldduplexmode : 664 664 duplexmode = defaultduplexmode 665 else : 665 else : 666 666 duplexmode = oldduplexmode 667 667 defaultps = pjlparser.default_variables.get("PAPER", "") … … 671 671 if envps : 672 672 papersize = envps 673 else : 673 else : 674 674 if not oldpapersize : 675 675 papersize = defaultpapersize 676 else : 676 else : 677 677 papersize = oldpapersize 678 else : 678 else : 679 679 if oldpjlcopies == -1 : 680 680 pjlcopies = defaultpjlcopies 681 else : 681 else : 682 682 pjlcopies = oldpjlcopies 683 683 if not oldduplexmode : 684 684 duplexmode = defaultduplexmode 685 else : 685 else : 686 686 duplexmode = oldduplexmode 687 if not oldpapersize : 687 if not oldpapersize : 688 688 papersize = defaultpapersize 689 else : 689 else : 690 690 papersize = oldpapersize 691 691 duplexmode = oldduplexmode … … 693 693 if page["mediasize"] != "Default" : 694 694 papersize = page["mediasize"] 695 if not duplexmode : 695 if not duplexmode : 696 696 duplexmode = oldduplexmode or defaultduplexmode 697 oldpjlcopies = pjlcopies 697 oldpjlcopies = pjlcopies 698 698 oldduplexmode = duplexmode 699 699 oldpapersize = papersize 700 700 copies = max(pjlcopies, page["copies"]) # Was : pjlcopies * page["copies"] 701 701 self.pagecount += (copies - 1) 702 self.logdebug("%s*%s*%s*%s*%s*%s*%s" % (copies, 703 page["mediatype"], 704 papersize, 705 page["orientation"], 706 page["mediasource"], 707 duplexmode, 702 self.logdebug("%s*%s*%s*%s*%s*%s*%s" % (copies, 703 page["mediatype"], 704 papersize, 705 page["orientation"], 706 page["mediasource"], 707 duplexmode, 708 708 colormode)) 709 709 return self.pagecount -
pkpgcounter/trunk/pkpgpdls/pdf.py
r3410 r3436 8 8 # the Free Software Foundation, either version 3 of the License, or 9 9 # (at your option) any later version. 10 # 10 # 11 11 # This program is distributed in the hope that it will be useful, 12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 14 # GNU General Public License for more details. 15 # 15 # 16 16 # You should have received a copy of the GNU General Public License 17 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. … … 37 37 PDFDELIMITERS = r"()<>[]{}/%" 38 38 PDFMEDIASIZE = "/MediaBox [xmin ymin xmax ymax]" # an example. MUST be present in Page objects 39 39 40 40 class Parser(pdlparser.PDLParser) : 41 41 """A parser for PDF documents.""" … … 44 44 openmode = "rU" 45 45 format = "PDF" 46 def isValid(self) : 46 def isValid(self) : 47 47 """Returns True if data is PDF, else False.""" 48 48 if self.firstblock.startswith("%PDF-") or \ … … 51 51 (self.firstblock.find("%PDF-") != -1) : 52 52 return True 53 else : 53 else : 54 54 return False 55 56 def veryFastAndNotAlwaysCorrectgetJobSize(self) : 55 56 def veryFastAndNotAlwaysCorrectgetJobSize(self) : 57 57 """Counts pages in a PDF document. 58 58 59 59 This method works great in the general case, 60 60 and is around 30 times faster than the active … … 70 70 def getJobSize(self) : 71 71 """Counts pages in a PDF document. 72 72 73 73 A faster way seems to be possible by extracting the 74 74 "/Type/Pages/Count xxxx" value where there's no /Parent 75 75 (i.e. the root of the page tree) 76 76 Unfortunately I can't make a regexp work for this currently. 77 77 78 78 At least the actual method below is accurate, even if 25% 79 79 slower than the old one. But we will be able to extract … … 84 84 oregexp = re.compile(r"\s+(\d+)\s+(\d+)\s+(obj\s*.+?\s*?endobj)", \ 85 85 re.DOTALL) 86 86 87 87 # Regular expression indicating a new page 88 88 npregexp = re.compile(r"/Type\s*/Page[/>\s]") 89 90 # Regular expression indicating an empty page 89 90 # Regular expression indicating an empty page 91 91 # (usually to delete an existing one with a lower minor number) 92 epregexp = re.compile(r"obj\s*<<\s*/Type\s*/Page\s*>>\s*endobj") 93 92 epregexp = re.compile(r"obj\s*<<\s*/Type\s*/Page\s*>>\s*endobj") 93 94 94 # First we build a mapping of objects to keep because 95 95 # if two objects with the same major number are found, … … 109 109 #else : 110 110 # self.logdebug("Object %i.%i OK" % (major, minor)) 111 112 # Now that we have deleted all unneeded objects, we 111 112 # Now that we have deleted all unneeded objects, we 113 113 # can count the ones which are new pages, minus the ones 114 114 # which are empty and not displayed pages (in fact pages -
pkpgcounter/trunk/pkpgpdls/pdlparser.py
r3410 r3436 8 8 # the Free Software Foundation, either version 3 of the License, or 9 9 # (at your option) any later version. 10 # 10 # 11 11 # This program is distributed in the hope that it will be useful, 12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 14 # GNU General Public License for more details. 15 # 15 # 16 16 # You should have received a copy of the GNU General Public License 17 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. … … 25 25 import os 26 26 27 KILOBYTE = 1024 28 MEGABYTE = 1024 * KILOBYTE 27 KILOBYTE = 1024 28 MEGABYTE = 1024 * KILOBYTE 29 29 FIRSTBLOCKSIZE = 16 * KILOBYTE 30 30 LASTBLOCKSIZE = int(KILOBYTE / 4) … … 38 38 return self.message 39 39 __str__ = __repr__ 40 40 41 41 class PDLParser : 42 42 """Generic PDL parser.""" … … 56 56 if not self.isValid() : 57 57 raise PDLParserError, "Invalid file format !" 58 else : 58 else : 59 59 self.logdebug("Input file is in the '%s' file format." % self.format) 60 60 try : 61 import psyco 62 except ImportError : 61 import psyco 62 except ImportError : 63 63 pass # Psyco is not installed 64 else : 64 else : 65 65 # Psyco is installed, tell it to compile 66 66 # the CPU intensive methods : PCL and PCLXL … … 69 69 self.infile = open(self.filename, self.openmode) 70 70 # self.logdebug("Opened %s in '%s' mode." % (self.filename, self.openmode)) 71 71 72 72 def __del__(self) : 73 73 """Ensures the input file gets closed.""" 74 74 if self.infile : 75 75 self.infile.close() 76 76 77 77 def findExecutable(self, command) : 78 78 """Finds an executable in the PATH and returns True if found else False.""" … … 83 83 return True 84 84 return False 85 86 def isMissing(self, commands) : 87 """Returns True if some required commands are missing, else False.""" 85 86 def isMissing(self, commands) : 87 """Returns True if some required commands are missing, else False.""" 88 88 howmanythere = 0 89 89 for command in commands : … … 91 91 sys.stderr.write("ERROR: %(command)s is missing or not executable. You MUST install it for pkpgcounter to be able to do what you want.\n" % locals()) 92 92 sys.stderr.flush() 93 else : 93 else : 94 94 howmanythere += 1 95 95 if howmanythere == len(commands) : 96 96 return False 97 else : 97 else : 98 98 return True 99 100 def logdebug(self, message) : 99 100 def logdebug(self, message) : 101 101 """Logs a debug message if needed.""" 102 102 if self.parent.options.debug : 103 103 sys.stderr.write("%s\n" % message) 104 105 def isValid(self) : 104 105 def isValid(self) : 106 106 """Returns True if data is in the expected format, else False.""" 107 107 raise RuntimeError, "Not implemented !" 108 109 def getJobSize(self) : 108 109 def getJobSize(self) : 110 110 """Counts pages in a document.""" 111 111 raise RuntimeError, "Not implemented !" 112 112 113 113 def convertToTiffMultiPage24NC(self, outfname, dpi) : 114 114 """Converts the input file to TIFF format, X dpi, 24 bits per pixel, uncompressed. 115 115 Writes TIFF datas to the file named by outfname. 116 """ 116 """ 117 117 if self.totiffcommands : 118 118 if self.isMissing(self.required) : … … 127 127 if os.WEXITSTATUS(status) : 128 128 error = True 129 else : 129 else : 130 130 error = True 131 131 if not os.path.exists(outfname) : … … 133 133 elif not os.stat(outfname).st_size : 134 134 error = True 135 else : 135 else : 136 136 break # Conversion worked fine it seems. 137 137 sys.stderr.write("Command failed : %s\n" % repr(commandline)) 138 138 if error : 139 139 raise PDLParserError, "Problem during conversion to TIFF." 140 else : 140 else : 141 141 raise PDLParserError, "Impossible to compute ink coverage for this file format." -
pkpgcounter/trunk/pkpgpdls/pil.py
r3410 r3436 8 8 # the Free Software Foundation, either version 3 of the License, or 9 9 # (at your option) any later version. 10 # 10 # 11 11 # This program is distributed in the hope that it will be useful, 12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 14 # GNU General Public License for more details. 15 # 15 # 16 16 # You should have received a copy of the GNU General Public License 17 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. … … 26 26 try : 27 27 from PIL import Image 28 except ImportError : 28 except ImportError : 29 29 sys.stderr.write("ERROR: You MUST install the Python Imaging Library (python-imaging) for pkpgcounter to work.\n") 30 30 raise pdlparser.PDLParserError, "The Python Imaging Library is missing." … … 34 34 class Parser(pdlparser.PDLParser) : 35 35 """A parser for plain text documents.""" 36 totiffcommands = [ 'convert "%(infname)s" "%(outfname)s"' ] 36 totiffcommands = [ 'convert "%(infname)s" "%(outfname)s"' ] 37 37 required = [ "convert" ] 38 def isValid(self) : 39 """Returns True if data is an image format supported by PIL, else False.""" 38 def isValid(self) : 39 """Returns True if data is an image format supported by PIL, else False.""" 40 40 try : 41 41 image = Image.open(self.filename) 42 except (IOError, OverflowError) : 42 except (IOError, OverflowError) : 43 43 return False 44 else : 44 else : 45 45 self.format = "%s (%s)" % (image.format, image.format_description) 46 46 return True 47 47 48 48 def getJobSize(self) : 49 49 """Counts pages in an image file.""" … … 52 52 try : 53 53 while True : 54 index += 1 54 index += 1 55 55 image.seek(index) 56 except EOFError : 56 except EOFError : 57 57 pass 58 return index 58 return index -
pkpgcounter/trunk/pkpgpdls/pjl.py
r3410 r3436 8 8 # the Free Software Foundation, either version 3 of the License, or 9 9 # (at your option) any later version. 10 # 10 # 11 11 # This program is distributed in the hope that it will be useful, 12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 14 # GNU General Public License for more details. 15 # 15 # 16 16 # You should have received a copy of the GNU General Public License 17 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. … … 35 35 return self.message 36 36 __str__ = __repr__ 37 37 38 38 class PJLParser : 39 39 """A parser for PJL documents. 40 40 41 41 Information extracted for bpl11897.pdf which was 42 42 downloaded from Hewlett-Packard's website. … … 52 52 self.parsed = 0 53 53 self.parse() 54 55 def __str__(self) : 54 55 def __str__(self) : 56 56 """Outputs our variables as a string of text.""" 57 57 if not self.parsed : … … 62 62 for (k, v) in self.default_variables.items() : 63 63 mybuffer.append(" %s : %s" % (k, v)) 64 if self.environment_variables : 64 if self.environment_variables : 65 65 mybuffer.append("Environment variables :") 66 66 for (k, v) in self.environment_variables.items() : 67 67 mybuffer.append(" %s : %s" % (k, v)) 68 return "\n".join(mybuffer) 69 70 def logdebug(self, message) : 68 return "\n".join(mybuffer) 69 70 def logdebug(self, message) : 71 71 """Logs a debug message if needed.""" 72 72 if self.debug : 73 73 sys.stderr.write("%s\n" % message) 74 75 def cleanvars(self) : 74 75 def cleanvars(self) : 76 76 """Cleans the variables dictionnaries.""" 77 77 for dicname in ("default", "environment") : … … 80 80 if len(v) == 1 : 81 81 varsdic[k] = v[0] 82 82 83 83 def parse(self) : 84 84 """Parses a JL job.""" … … 95 95 or ((self.jlmarker == "@EJL") and (parts[1].upper() == "JI"))) : 96 96 # this is what we are interested in ! 97 try : 97 try : 98 98 (varname, value) = "".join(parts[2:]).split("=", 1) # TODO : parse multiple assignments on the same SET/JI statement 99 except : 99 except : 100 100 self.logdebug("Invalid JL SET statement [%s]" % repr(statement)) 101 else : 101 else : 102 102 # all still looks fine... 103 103 if parts[1].upper() == "DEFAULT" : 104 104 varsdic = self.default_variables 105 else : 106 varsdic = self.environment_variables 105 else : 106 varsdic = self.environment_variables 107 107 variable = varsdic.setdefault(varname.upper(), []) 108 108 variable.append(value) … … 121 121 self.parsed = 1 122 122 # self.logdebug("%s\n" % str(self)) 123 123 124 124 class EJLParser(PJLParser) : 125 125 """A parser for EJL (Epson Job Language) documents.""" 126 126 JL = "EJL" 127 128 def test() : 127 128 def test() : 129 129 """Test function.""" 130 130 if (len(sys.argv) < 2) or ((not sys.stdin.isatty()) and ("-" not in sys.argv[1:])) : … … 135 135 infile = sys.stdin 136 136 mustclose = 0 137 else : 137 else : 138 138 if arg.endswith(".ejl") : 139 139 klass = EJLParser … … 142 142 try : 143 143 parser = klass(infile.read(), debug=1) 144 except PJLParserError, msg : 144 except PJLParserError, msg : 145 145 sys.stderr.write("ERROR: %s\n" % msg) 146 146 sys.stderr.flush() 147 if mustclose : 147 if mustclose : 148 148 infile.close() 149 print str(parser) 150 151 if __name__ == "__main__" : 149 print str(parser) 150 151 if __name__ == "__main__" : 152 152 test() -
pkpgcounter/trunk/pkpgpdls/plain.py
r3410 r3436 8 8 # the Free Software Foundation, either version 3 of the License, or 9 9 # (at your option) any later version. 10 # 10 # 11 11 # This program is distributed in the hope that it will be useful, 12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 14 # GNU General Public License for more details. 15 # 15 # 16 16 # You should have received a copy of the GNU General Public License 17 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. … … 29 29 totiffcommands = [ 'enscript --quiet --portrait --no-header --columns 1 --output - "%(infname)s" | gs -sDEVICE=tiff24nc -dPARANOIDSAFER -dNOPAUSE -dBATCH -dQUIET -r"%(dpi)i" -sOutputFile="%(outfname)s" -', 30 30 'a2ps --borders 0 --quiet --portrait --no-header --columns 1 --output - "%(infname)s" | gs -sDEVICE=tiff24nc -dPARANOIDSAFER -dNOPAUSE -dBATCH -dQUIET -r"%(dpi)i" -sOutputFile="%(outfname)s" -', 31 ] 31 ] 32 32 required = [ "a2ps | enscript", "gs" ] 33 openmode = "rU" 33 openmode = "rU" 34 34 format = "plain text" 35 def isValid(self) : 35 def isValid(self) : 36 36 """Returns True if data is plain text, else False. 37 37 38 38 It's hard to detect a plain text file, so we just try to 39 39 extract lines from the first block (sufficiently large). 40 40 If it's impossible to find one we consider it's not plain text. 41 """ 41 """ 42 42 lines = self.firstblock.split("\r\n") 43 43 if len(lines) == 1 : … … 47 47 if len(lines) > 1 : 48 48 return True 49 else : 49 else : 50 50 return False 51 51 52 52 def getJobSize(self) : 53 53 """Counts pages in a plain text document.""" … … 58 58 for line in self.infile : 59 59 if line.endswith("\n") : 60 linecount += 1 60 linecount += 1 61 61 if (linecount > pagesize) : 62 62 pagecount += 1 63 63 linecount = 0 64 else : 64 else : 65 65 cnt = line.count("\f") 66 66 if cnt : 67 67 pagecount += cnt 68 68 linecount = 0 69 else : 69 else : 70 70 raise pdlparser.PDLParserError, "Unsupported file format. Please send the file to %s" % version.__authoremail__ 71 71 return pagecount + 1 # NB : empty files are catched in isValid() -
pkpgcounter/trunk/pkpgpdls/pnmascii.py
r3410 r3436 8 8 # the Free Software Foundation, either version 3 of the License, or 9 9 # (at your option) any later version. 10 # 10 # 11 11 # This program is distributed in the hope that it will be useful, 12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 14 # GNU General Public License for more details. 15 # 15 # 16 16 # You should have received a copy of the GNU General Public License 17 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. … … 26 26 class Parser(pdlparser.PDLParser) : 27 27 """A parser for PNM (ascii) documents.""" 28 openmode = "rU" 28 openmode = "rU" 29 29 format = "PNM (ascii)" 30 def isValid(self) : 30 def isValid(self) : 31 31 """Returns True if data is ASCII PNM, else False.""" 32 32 if self.firstblock.split()[0] in ("P1", "P2", "P3") : 33 33 self.marker = self.firstblock[:2] 34 34 return True 35 else : 35 else : 36 36 return False 37 37 38 38 def getJobSize(self) : 39 39 """Counts pages in a PNM (ascii) document.""" … … 47 47 # Special case of cmyk map 48 48 divby = 4 49 # Unfortunately any whitespace is valid, 49 # Unfortunately any whitespace is valid, 50 50 # so we do it the slow way... 51 51 pagecount += line.split().count(marker) 52 53 if not (pagecount % divby) : 52 53 if not (pagecount % divby) : 54 54 return pagecount // divby 55 else : 55 else : 56 56 return pagecount -
pkpgcounter/trunk/pkpgpdls/postscript.py
r3410 r3436 8 8 # the Free Software Foundation, either version 3 of the License, or 9 9 # (at your option) any later version. 10 # 10 # 11 11 # This program is distributed in the hope that it will be useful, 12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 14 # GNU General Public License for more details. 15 # 15 # 16 16 # You should have received a copy of the GNU General Public License 17 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. … … 34 34 openmode = "rU" 35 35 format = "PostScript" 36 def isValid(self) : 36 def isValid(self) : 37 37 """Returns True if data is PostScript, else False.""" 38 38 if self.firstblock.startswith("%!") or \ … … 45 45 (self.firstblock.find("%!PS-Adobe") != -1) : 46 46 return True 47 else : 47 else : 48 48 return False 49 49 50 50 def throughGhostScript(self) : 51 51 """Get the count through GhostScript, useful for non-DSC compliant PS files.""" … … 62 62 except (IOError, OSError, AttributeError, ValueError), msg : 63 63 raise pdlparser.PDLParserError, "Problem during analysis of Binary PostScript document : %s" % msg 64 finally : 64 finally : 65 65 if fromchild.close() is not None : 66 66 raise pdlparser.PDLParserError, "Problem during analysis of Binary PostScript document" 67 self.logdebug("GhostScript said : %s pages" % pagecount) 67 self.logdebug("GhostScript said : %s pages" % pagecount) 68 68 return pagecount * self.copies 69 70 def setcopies(self, pagenum, txtvalue) : 69 70 def setcopies(self, pagenum, txtvalue) : 71 71 """Tries to extract a number of copies from a textual value and set the instance attributes accordingly.""" 72 72 try : 73 73 number = int(txtvalue) 74 except (ValueError, TypeError) : 74 except (ValueError, TypeError) : 75 75 pass 76 else : 76 else : 77 77 if number > self.pages[pagenum]["copies"] : 78 78 self.pages[pagenum]["copies"] = number 79 79 80 80 def natively(self) : 81 81 """Count pages in a DSC compliant PostScript document.""" … … 94 94 if nbparts >= 1 : 95 95 part0 = parts[0] 96 else : 96 else : 97 97 part0 = "" 98 98 if part0 == r"%ADOPrintSettings:" : 99 99 acrobatmarker = True 100 elif part0 == "!R!" : 100 elif part0 == "!R!" : 101 101 prescribe = True 102 102 elif part0 == r"%%Pages:" : … … 119 119 if nbparts > 1 : 120 120 self.setcopies(pagecount, parts[1]) 121 elif part0 == r"%RBINumCopies:" : 121 elif part0 == r"%RBINumCopies:" : 122 122 if nbparts > 1 : 123 123 self.setcopies(pagecount, parts[1]) … … 135 135 # treats both "%%Page: x x" and "%%Page: (x-y) z" (probably N-up mode) 136 136 newpagenum = int(line.split(']')[0].split()[-1]) 137 except : 137 except : 138 138 notinteger = True # It seems that sometimes it's not an integer but an EPS file name 139 else : 139 else : 140 140 notinteger = False 141 141 if newpagenum == oldpagenum : … … 143 143 else : 144 144 oldpagenum = newpagenum 145 if proceed and not notinteger : 145 if proceed and not notinteger : 146 146 pagecount += 1 147 147 self.pages[pagecount] = { "copies" : self.pages[pagecount-1]["copies"] } … … 155 155 self.setcopies(pagecount, part0) 156 156 previousline = line 157 158 # extract max number of copies to please the ghostscript parser, just 157 158 # extract max number of copies to please the ghostscript parser, just 159 159 # in case we will use it later 160 160 self.copies = max([ v["copies"] for (k, v) in self.pages.items() ]) 161 161 162 162 # now apply the number of copies to each page 163 if not pagecount and pagescomment : 163 if not pagecount and pagescomment : 164 164 pagecount = pagescomment 165 165 for pnum in range(1, pagecount + 1) : … … 168 168 pagecount += (copies - 1) 169 169 self.logdebug("%s * page #%s" % (copies, pnum)) 170 170 171 171 self.logdebug("Internal parser said : %s pages" % pagecount) 172 172 return (pagecount, notrust) 173 174 def getJobSize(self) : 173 174 def getJobSize(self) : 175 175 """Count pages in PostScript document.""" 176 176 self.copies = 1 … … 182 182 except pdlparser.PDLParserError, msg : 183 183 self.logdebug(msg) 184 return max(nbpages, newnbpages) 184 return max(nbpages, newnbpages) -
pkpgcounter/trunk/pkpgpdls/qpdl.py
r3410 r3436 8 8 # the Free Software Foundation, either version 3 of the License, or 9 9 # (at your option) any later version. 10 # 10 # 11 11 # This program is distributed in the hope that it will be useful, 12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 14 # GNU General Public License for more details. 15 # 15 # 16 16 # You should have received a copy of the GNU General Public License 17 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. … … 33 33 """A parser for QPDL (aka SPL2) documents.""" 34 34 format = "QPDL (aka SPL2)" 35 mediasizes = { 35 mediasizes = { 36 36 # The first values are identical to that of PCLXL 37 37 0 : "Letter", … … 57 57 23 : "C6", 58 58 24 : "Folio", 59 } 60 61 mediasources = { 59 } 60 61 mediasources = { 62 62 # Again, values are identical to that of PCLXL 63 63 0 : "Default", … … 70 70 7 : "ThirdCassette", 71 71 } 72 73 def isValid(self) : 72 73 def isValid(self) : 74 74 """Returns True if data is QPDL aka SPL2, else False.""" 75 75 if ((self.firstblock[:128].find("\033%-12345X") != -1) and \ … … 77 77 (self.firstblock.find("LANGUAGE = QPDL") != -1))) : 78 78 return True 79 else : 79 else : 80 80 return False 81 81 82 82 def beginPage(self, nextpos) : 83 83 """Indicates the beginning of a new page, and extracts media information.""" 84 84 self.pagecount += 1 85 85 86 86 copies = unpack(self.unpackShort, self.minfile[nextpos+1:nextpos+3])[0] 87 87 mediasize = ord(self.minfile[nextpos+3]) 88 88 mediasource = ord(self.minfile[nextpos+8]) 89 89 duplexmode = unpack(self.unpackShort, self.minfile[nextpos+10:nextpos+12])[0] 90 91 self.pages[self.pagecount] = { "copies" : copies, 90 91 self.pages[self.pagecount] = { "copies" : copies, 92 92 "mediasize" : self.mediasizes.get(mediasize, str(mediasize)), 93 93 "mediasource" : self.mediasources.get(mediasource, str(mediasource)), 94 94 "duplex" : duplexmode, 95 } 95 } 96 96 return 16 # Length of a page header 97 98 def endPage(self, nextpos) : 97 98 def endPage(self, nextpos) : 99 99 """Indicates the end of a page.""" 100 100 epcopies = unpack(self.unpackShort, self.minfile[nextpos:nextpos+2])[0] … … 103 103 self.logdebug("ERROR: discrepancy between beginPage (%i) and endPage (%i) copies" % (bpcopies, epcopies)) 104 104 return 2 # Length of a page footer 105 105 106 106 def beginBand(self, nextpos) : 107 107 """Indicates the beginning of a new band.""" 108 108 bandlength = unpack(self.unpackLong, self.minfile[nextpos+6:nextpos+10])[0] 109 109 return bandlength + 10 # Length of a band header - length of checksum 110 110 111 111 def littleEndian(self) : 112 112 """Toggles to little endianness.""" … … 115 115 self.unpackLong = self.unpackType[4] 116 116 return 0 117 117 118 118 def bigEndian(self) : 119 119 """Toggles to big endianness.""" … … 122 122 self.unpackLong = self.unpackType[4] 123 123 return 0 124 125 def escape(self, nextpos) : 124 125 def escape(self, nextpos) : 126 126 """Handles the ESC code.""" 127 127 pos = endpos = nextpos … … 137 137 quotes += 1 138 138 endpos += 1 139 140 # Store this in a per page mapping. 139 140 # Store this in a per page mapping. 141 141 # NB : First time will be at page 0 (i.e. **before** page 1) ! 142 142 stuff = self.escapedStuff.setdefault(self.pagecount, []) … … 144 144 self.logdebug("Escaped datas : [%s]" % repr(minfile[pos : endpos])) 145 145 return endpos - pos 146 147 def maybeEOF(self, nextpos) : 146 147 def maybeEOF(self, nextpos) : 148 148 """Tries to detect the EOF marker.""" 149 149 if self.minfile[nextpos:nextpos+9] == self.eofmarker : 150 150 return 9 151 else : 151 else : 152 152 return 0 153 153 154 154 def getJobSize(self) : 155 155 """Counts pages in a QPDL (SPL2) document. 156 156 157 157 Algorithm by Jerome Alet. 158 158 159 159 The documentation used for this was : 160 160 161 161 Sp�fication Technique (documentation non officielle) 162 162 Le Language SPL2 … … 165 165 """ 166 166 # Initialize table of tags 167 self.tags = [ lambda pos : 0 ] * 256 167 self.tags = [ lambda pos : 0 ] * 256 168 168 self.tags[0x00] = self.beginPage 169 169 self.tags[0x01] = self.endPage … … 171 171 self.tags[0x0c] = self.beginBand 172 172 self.tags[0x1b] = self.escape # The escape code 173 173 174 174 self.eofmarker = "\033%-12345X" 175 175 176 176 infileno = self.infile.fileno() 177 self.pages = { 0 : { "copies" : 1, 178 "orientation" : "Default", 179 "mediatype" : "Plain", 180 "mediasize" : "Default", 181 "mediasource" : "Default", 177 self.pages = { 0 : { "copies" : 1, 178 "orientation" : "Default", 179 "mediatype" : "Plain", 180 "mediasize" : "Default", 181 "mediasource" : "Default", 182 182 "duplex" : None, 183 } 184 } 183 } 184 } 185 185 self.minfile = minfile = mmap.mmap(infileno, os.fstat(infileno)[6], prot=mmap.PROT_READ, flags=mmap.MAP_SHARED) 186 186 self.pagecount = 0 … … 195 195 pos += 1 196 196 pos += tags[tag](pos) 197 except IndexError : # EOF ? 197 except IndexError : # EOF ? 198 198 pass 199 199 finally : 200 200 self.minfile.close() 201 201 202 202 defaultduplexmode = "Simplex" 203 203 defaultpapersize = "" 204 defaultpjlcopies = 1 204 defaultpjlcopies = 1 205 205 oldpjlcopies = -1 206 206 oldduplexmode = "" … … 208 208 for pnum in range(1, self.pagecount + 1) : 209 209 # NB : is number of copies is 0, the page won't be output 210 # but the formula below is still correct : we want 210 # but the formula below is still correct : we want 211 211 # to decrease the total number of pages in this case. 212 212 page = self.pages.get(pnum, self.pages.get(1, { "copies" : 1, "mediasize" : "Default", "duplex" : None })) … … 227 227 pjlcopies = nbqty 228 228 else : 229 if oldpjlcopies == -1 : 229 if oldpjlcopies == -1 : 230 230 pjlcopies = defaultpjlcopies 231 else : 232 pjlcopies = oldpjlcopies 233 if page["duplex"] : 231 else : 232 pjlcopies = oldpjlcopies 233 if page["duplex"] : 234 234 duplexmode = page["duplex"] 235 else : 235 else : 236 236 defaultdm = pjlparser.default_variables.get("DUPLEX", "") 237 237 if defaultdm : 238 238 if defaultdm.upper() == "ON" : 239 239 defaultduplexmode = "Duplex" 240 else : 240 else : 241 241 defaultduplexmode = "Simplex" 242 242 envdm = pjlparser.environment_variables.get("DUPLEX", "") … … 244 244 if envdm.upper() == "ON" : 245 245 duplexmode = "Duplex" 246 else : 246 else : 247 247 duplexmode = "Simplex" 248 else : 248 else : 249 249 if not oldduplexmode : 250 250 duplexmode = defaultduplexmode 251 else : 251 else : 252 252 duplexmode = oldduplexmode 253 253 defaultps = pjlparser.default_variables.get("PAPER", "") … … 257 257 if envps : 258 258 papersize = envps 259 else : 259 else : 260 260 if not oldpapersize : 261 261 papersize = defaultpapersize 262 else : 262 else : 263 263 papersize = oldpapersize 264 else : 264 else : 265 265 if oldpjlcopies == -1 : 266 266 pjlcopies = defaultpjlcopies 267 else : 267 else : 268 268 pjlcopies = oldpjlcopies 269 269 if not oldduplexmode : 270 270 duplexmode = defaultduplexmode 271 else : 271 else : 272 272 duplexmode = oldduplexmode 273 if not oldpapersize : 273 if not oldpapersize : 274 274 papersize = defaultpapersize 275 else : 275 else : 276 276 papersize = oldpapersize 277 277 duplexmode = oldduplexmode … … 279 279 if page["mediasize"] != "Default" : 280 280 papersize = page["mediasize"] 281 if not duplexmode : 281 if not duplexmode : 282 282 duplexmode = oldduplexmode or defaultduplexmode 283 oldpjlcopies = pjlcopies 283 oldpjlcopies = pjlcopies 284 284 oldduplexmode = duplexmode 285 285 oldpapersize = papersize 286 286 copies = max(pjlcopies, page["copies"]) # Was : pjlcopies * page["copies"] 287 287 self.pagecount += (copies - 1) 288 self.logdebug("%s*%s*%s*%s" % (copies, 289 papersize, 290 page["mediasource"], 288 self.logdebug("%s*%s*%s*%s" % (copies, 289 papersize, 290 page["mediasource"], 291 291 duplexmode)) 292 292 return self.pagecount -
pkpgcounter/trunk/pkpgpdls/spl1.py
r3410 r3436 8 8 # the Free Software Foundation, either version 3 of the License, or 9 9 # (at your option) any later version. 10 # 10 # 11 11 # This program is distributed in the hope that it will be useful, 12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 14 # GNU General Public License for more details. 15 # 15 # 16 16 # You should have received a copy of the GNU General Public License 17 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. … … 34 34 """A parser for SPL1 documents.""" 35 35 format = "SPL1 (aka GDI)" 36 def isValid(self) : 36 def isValid(self) : 37 37 """Returns True if data is SPL1, else False.""" 38 38 if ((self.firstblock[:128].find("\033%-12345X") != -1) and \ … … 41 41 (self.firstblock.find("LANGUAGE = SMART") != -1))) : 42 42 return True 43 else : 43 else : 44 44 return False 45 45 46 46 def littleEndian(self) : 47 47 """Toggles to little endianness.""" … … 51 51 # self.logdebug("Little Endian") 52 52 return 0 53 53 54 54 def bigEndian(self) : 55 55 """Toggles to big endianness.""" … … 59 59 # self.logdebug("Big Endian") 60 60 return 0 61 62 def escape(self, nextpos) : 61 62 def escape(self, nextpos) : 63 63 """Handles the ESC code.""" 64 64 self.isbitmap = False … … 67 67 if minfile[pos : pos+8] == r"%-12345X" : 68 68 endpos = pos + 9 69 elif minfile[pos-1] in ESCAPECHARS : 69 elif minfile[pos-1] in ESCAPECHARS : 70 70 endpos = pos 71 else : 71 else : 72 72 return 0 73 73 endmark = (chr(0x1b), chr(0x00)) … … 79 79 quotes += 1 80 80 endpos += 1 81 82 # Store this in a per page mapping. 81 82 # Store this in a per page mapping. 83 83 # NB : First time will be at page 0 (i.e. **before** page 1) ! 84 84 stuff = self.escapedStuff.setdefault(self.pagecount, []) … … 90 90 self.logdebug("Escaped datas : [%s]" % repr(datas)) 91 91 return endpos - pos + 1 92 92 93 93 def getJobSize(self) : 94 94 """Counts pages in an SPL1 document. 95 95 96 96 Algorithm by Jerome Alet. 97 97 """ … … 110 110 if tag in ESCAPECHARS : 111 111 pos += self.escape(pos+1) 112 else : 112 else : 113 113 if not self.isbitmap : 114 114 raise pdlparser.PDLParserError, "Unfortunately SPL1 is incompletely recognized. Parsing aborted. Please report the problem to %s" % version.__authoremail__ … … 116 116 seqnum) = unpack(">IH", minfile[pos:pos+6]) 117 117 # self.logdebug("Offset : %i Sequence Number : %i" % (offset, seqnum)) 118 if not seqnum : 118 if not seqnum : 119 119 # Sequence number resets to 0 for each new page. 120 120 self.pagecount += 1 121 121 pos += 4 + offset 122 except struct.error, msg : 122 except struct.error, msg : 123 123 raise pdlparser.PDLParserError, "Unfortunately SPL1 is incompletely recognized (%s). Parsing aborted. Please report the problem to %s" % (msg, version.__authoremail__) 124 except IndexError : # EOF ? 124 except IndexError : # EOF ? 125 125 pass 126 126 finally : -
pkpgcounter/trunk/pkpgpdls/tiff.py
r3410 r3436 8 8 # the Free Software Foundation, either version 3 of the License, or 9 9 # (at your option) any later version. 10 # 10 # 11 11 # This program is distributed in the hope that it will be useful, 12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 14 # GNU General Public License for more details. 15 # 15 # 16 16 # You should have received a copy of the GNU General Public License 17 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. … … 34 34 required = [ "cp" ] 35 35 format = "TIFF" 36 def isValid(self) : 36 def isValid(self) : 37 37 """Returns True if data is TIFF, else False.""" 38 38 littleendian = (chr(0x49)*2) + chr(0x2a) + chr(0) … … 40 40 if self.firstblock[:4] in (littleendian, bigendian) : 41 41 return True 42 else : 42 else : 43 43 return False 44 44 45 45 def getJobSize(self) : 46 46 """Counts pages in a TIFF document. 47 47 48 48 Algorithm by Jerome Alet. 49 49 50 50 The documentation used for this was : 51 51 52 52 http://www.ee.cooper.edu/courses/course_pages/past_courses/EE458/TIFF/ 53 53 """ … … 63 63 integerbyteorder = ">I" 64 64 shortbyteorder = ">H" 65 else : 65 else : 66 66 raise pdlparser.PDLParserError, "Unknown file endianness." 67 pos = 4 67 pos = 4 68 68 try : 69 try : 69 try : 70 70 nextifdoffset = unpack(integerbyteorder, minfile[pos : pos + 4])[0] 71 71 while nextifdoffset : … … 74 74 nextifdoffset = unpack(integerbyteorder, minfile[pos : pos + 4])[0] 75 75 pagecount += 1 76 except IndexError : 76 except IndexError : 77 77 pass 78 finally : 78 finally : 79 79 minfile.close() 80 80 return pagecount -
pkpgcounter/trunk/pkpgpdls/version.py
r3410 r3436 8 8 # the Free Software Foundation, either version 3 of the License, or 9 9 # (at your option) any later version. 10 # 10 # 11 11 # This program is distributed in the hope that it will be useful, 12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 14 # GNU General Public License for more details. 15 # 15 # 16 16 # You should have received a copy of the GNU General Public License 17 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. -
pkpgcounter/trunk/pkpgpdls/zjstream.py
r3410 r3436 8 8 # the Free Software Foundation, either version 3 of the License, or 9 9 # (at your option) any later version. 10 # 10 # 11 11 # This program is distributed in the hope that it will be useful, 12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 14 # GNU General Public License for more details. 15 # 15 # 16 16 # You should have received a copy of the GNU General Public License 17 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. … … 28 28 class Parser(pdlparser.PDLParser) : 29 29 """A parser for ZjStream documents.""" 30 def isValid(self) : 30 def isValid(self) : 31 31 """Returns True if data is ZjStream, else False.""" 32 32 if self.firstblock[:4] == "ZJZJ" : 33 33 self.format = "Zenographics ZjStream (little endian)" 34 34 return self.littleEndian() 35 elif self.firstblock[:4] == "JZJZ" : 35 elif self.firstblock[:4] == "JZJZ" : 36 36 self.format = "Zenographics ZjStream (big endian)" 37 37 return self.bigEndian() 38 else : 38 else : 39 39 return False 40 40 41 41 def littleEndian(self) : 42 42 """Toggles to little endianness.""" 43 43 self.unpackHeader = "<IIIHH" 44 44 return True 45 45 46 46 def bigEndian(self) : 47 47 """Toggles to big endianness.""" 48 48 self.unpackHeader = ">IIIHH" 49 49 return True 50 50 51 51 def getJobSize(self) : 52 52 """Computes the number of pages in a ZjStream document.""" … … 66 66 signature) = unpack(unpackHeader, header) 67 67 self.infile.seek(totalChunkSize - len(header), 1) 68 if chunkType == 2 : 68 if chunkType == 2 : 69 69 #self.logdebug("startPage") 70 70 startpagecount += 1 … … 74 74 #elif chunkType == 0 : 75 75 # self.logdebug("startDoc") 76 #elif chunkType == 1 : 76 #elif chunkType == 1 : 77 77 # self.logdebug("endDoc") 78 # 78 # 79 79 #self.logdebug("Chunk size : %s" % totalChunkSize) 80 80 #self.logdebug("Chunk type : 0x%08x" % chunkType) … … 85 85 except struct.error : 86 86 raise pdlparser.PDLParserError, "This file doesn't seem to be valid ZjStream datas." 87 87 88 88 # Number of endpage commands should be sufficient, 89 89 # but we never know : someone could try to cheat the printer -
pkpgcounter/trunk/README
r3409 r3436 26 26 27 27 - PostScript (both DSC compliant and binary) 28 28 29 29 - PDF 30 30 31 31 - PCL3/4/5 32 32 33 33 - PCLXL (aka PCL6) 34 34 35 35 - DVI 36 36 37 37 - OpenDocument (ISO/IEC DIS 26300) 38 38 39 39 - Microsoft Word (c) (tm) (r) (etc...) 40 40 41 41 - Plain text 42 42 43 43 - TIFF 44 44 45 45 - Several other image formats 46 46 47 47 - ESC/P2 48 48 49 49 - Zenographics ZjStream 50 50 51 51 - Samsung QPDL (aka SPL2) 52 52 53 53 - Samsung SPL1 54 54 55 55 - ESC/PageS03 56 56 57 57 - Brother HBP 58 58 59 59 - Brother XL2HB 60 60 61 61 - Hewlett-Packard Lightweight Imaging Device Interface Language 62 62 63 63 - Structured Fax 64 64 65 65 - Canon BJ/BJC 66 66 67 67 - ASCII PNM (Netpbm) 68 69 The eleven latter ones, as well as some TIFF documents, are currently70 only supported in page counting mode.71 68 72 By default, when launched pkpgcounter prints on its standard output 73 a single integer representing the total number of pages in all the 74 files which filenames you've passed on the command line. 69 The eleven latter ones, as well as some TIFF documents, are currently 70 only supported in page counting mode. 71 72 By default, when launched pkpgcounter prints on its standard output 73 a single integer representing the total number of pages in all the 74 files which filenames you've passed on the command line. 75 75 76 76 With no argument, or with a single dash in non-option arguments, … … 87 87 88 88 0 - Download pkpgcounter from : 89 89 90 90 http://www.pykota.com/software/pkpgcounter/download 91 92 and extract it : 93 91 92 and extract it : 93 94 94 $ tar -zxf pkpgcounter-x.yy.tar.gz 95 95 96 96 where x.yy is pkpgcounter' version number. 97 97 98 98 1 - Run the installation script : 99 99 100 100 $ python setup.py install 101 102 This will usually install the pkpgcounter into /usr/bin and 101 102 This will usually install the pkpgcounter into /usr/bin and 103 103 the library into /usr/lib/python2.?/site-packages/pkpgpdls/ 104 104 105 105 2 - Use pkpgcounter : 106 106 107 107 $ pkpgcounter file1.ps file2.pclxl ... <fileN.escp2 108 108 109 109 pkpgcounter will display the total size in pages of all the files 110 110 passed on the command line. 111 111 112 112 $ pkpgcounter --colorspace bw --resolution 150 file1.ps 113 113 114 114 Will output the percent of black ink needed on each page of 115 115 the file1.ps file rendered at 150 dpi. 116 117 3 - That's all ! 118 116 117 3 - That's all ! 118 119 119 DEPENDENCIES : 120 120 121 121 Most of the time, pkpgcounter only depends on the presence of : 122 122 123 123 - The Python Imaging Library (python-imaging) 124 124 125 125 But, depending on the file formats you plan to work with, and on the 126 126 accounting mode you want to use (pages vs ink), you may need to install 127 127 some or all of the additional software listed below. Usually, if one is 128 needed then pkpgcounter will complain. So your best bet is probably 128 needed then pkpgcounter will complain. So your best bet is probably 129 129 to NOT INSTALL anything until pkpgcounter asks you to do so on its 130 130 standard error stream. Here's the list of software which may be needed … … 132 132 133 133 - GhostScript (gs) 134 134 135 135 - The X Virtual Frame Buffer (xvfb) 136 136 137 137 - The X authority file utility xauth (xbase-clients) 138 138 139 139 - The dvips converter from TeX DVI to PostScript (tetex-bin) 140 140 141 141 - The ImageMagick image manipulation toolkit (imagemagick) 142 142 143 143 - The AbiWord word processor (abiword) 144 144 145 145 - The GhostPCL/GhostPDL's pcl6 converter from PCL to PostScript 146 146 147 147 ============================================================================= 148 148 149 Troubleshooting : 149 Troubleshooting : 150 150 ----------------- 151 151 152 152 If pkpgcounter gives incorrect results to you, please make an incorrectly 153 parsed data file available to us on some website, and tell us which 154 driver was used, how many pages are in the file, and any additional 153 parsed data file available to us on some website, and tell us which 154 driver was used, how many pages are in the file, and any additional 155 155 information you think is relevant. 156 156 157 157 If pkpgcounter complain about your system lacking the Python Psyco module, 158 158 please consider installing it to speedup file parsing. However, don't forget 159 that Psyco currently only runs on the 32 bits x86 platform, so no need to 159 that Psyco currently only runs on the 32 bits x86 platform, so no need to 160 160 install it if you've got another system type. 161 161 162 162 ============================================================================= 163 163 … … 167 167 168 168 http://www.fea.unicamp.br/pclcount/ 169 170 Their software is distributed under either the terms of a BSD-like license, 169 170 Their software is distributed under either the terms of a BSD-like license, 171 171 or the terms of the GNU General Public License of the Free Software Foundation. 172 172 … … 176 176 The old parser was still available until pkpgcounter v2.18 was published, but 177 177 was definitely removed after that, just before pkpgcounter v3.00 was published. 178 179 pkpgcounter's PCLXL (aka PCL6) parser doesn't originate from PCLCount, but 178 179 pkpgcounter's PCLXL (aka PCL6) parser doesn't originate from PCLCount, but 180 180 was written from scratch, just like all the other parsers included in 181 181 pkpgcounter. … … 183 183 ============================================================================= 184 184 185 pkpgcounter's ink coverage algorithm for the CMYK colorspace is a 186 direct Python port from the PrintBill project by Daniel Franklin. 187 PrintBill is distributed under the terms of the GNU General Public 188 License of the Free Software Foundation, version 2 or higher. The 189 algorithms used for the other colorspaces are a complete rewrite of 190 PrintBill's algorithms using both Python and the Python Imaging 191 Library's facilities. 185 pkpgcounter's ink coverage algorithm for the CMYK colorspace is a 186 direct Python port from the PrintBill project by Daniel Franklin. 187 PrintBill is distributed under the terms of the GNU General Public 188 License of the Free Software Foundation, version 2 or higher. The 189 algorithms used for the other colorspaces are a complete rewrite of 190 PrintBill's algorithms using both Python and the Python Imaging 191 Library's facilities. 192 192 193 193 ============================================================================= -
pkpgcounter/trunk/setup.py
r564 r3436 1 1 #! /usr/bin/env python 2 # -*- coding: UTF-8 -*-2 # -*- coding: utf-8 -*- 3 3 # 4 4 # pkpgcounter : a generic Page Description Language parser … … 9 9 # the Free Software Foundation, either version 3 of the License, or 10 10 # (at your option) any later version. 11 # 11 # 12 12 # This program is distributed in the hope that it will be useful, 13 13 # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 15 # GNU General Public License for more details. 16 # 16 # 17 17 # You should have received a copy of the GNU General Public License 18 18 # along with this program. If not, see <http://www.gnu.org/licenses/>. … … 28 28 try : 29 29 from distutils.core import setup 30 except ImportError, msg : 30 except ImportError, msg : 31 31 sys.stderr.write("%s\n" % msg) 32 32 sys.stderr.write("You need the DistUtils Python module.\nunder Debian, you may have to install the python-dev package.\nOf course, YMMV.\n") 33 33 sys.exit(-1) 34 35 try : 34 35 try : 36 36 from PIL import Image 37 except ImportError : 37 except ImportError : 38 38 sys.stderr.write("You need the Python Imaging Library (aka PIL).\nYou can grab it from http://www.pythonware.com\n") 39 39 sys.exit(-1) 40 41 try : 40 41 try : 42 42 import psyco 43 except ImportError : 43 except ImportError : 44 44 sys.stderr.write("WARN: If you are running on a 32 Bits x86 platform, you should install the Python Psyco module if possible, this would greatly speedup parsing. NB : Psyco doesn't work on other platforms, so don't worry if you're in this case.\n") 45 45 46 46 sys.path.insert(0, "pkpgpdls") 47 47 from pkpgpdls.version import __version__, __doc__ … … 53 53 directory = os.sep.join(["share", "locale", lang, "LC_MESSAGES"]) 54 54 data_files.append((directory, [ mofile ])) 55 56 docdir = "share/doc/pkpgcounter" 55 56 docdir = "share/doc/pkpgcounter" 57 57 docfiles = ["README", "COPYING", "BUGS", "CREDITS", "NEWS"] 58 58 data_files.append((docdir, docfiles)) 59 59 60 60 directory = os.sep.join(["share", "man", "man1"]) 61 manpages = glob.glob(os.sep.join(["man", "*.1"])) 61 manpages = glob.glob(os.sep.join(["man", "*.1"])) 62 62 data_files.append((directory, manpages)) 63 63 … … 71 71 scripts = [ "bin/pkpgcounter" ], 72 72 data_files = data_files) 73 73 -
pkpgcounter/trunk/tests/gstests.py
r564 r3436 1 1 #! /usr/bin/env python 2 # -*- coding: UTF-8 -*-2 # -*- coding: utf-8 -*- 3 3 # 4 4 # pkpgcounter : a generic Page Description Language parser … … 9 9 # the Free Software Foundation, either version 3 of the License, or 10 10 # (at your option) any later version. 11 # 11 # 12 12 # This program is distributed in the hope that it will be useful, 13 13 # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 15 # GNU General Public License for more details. 16 # 16 # 17 17 # You should have received a copy of the GNU General Public License 18 18 # along with this program. If not, see <http://www.gnu.org/licenses/>. … … 43 43 self.md5sum = self.computeChecksum() 44 44 self.mastersize = None 45 46 def __del__(self) : 45 46 def __del__(self) : 47 47 """Remove temporary file, if any.""" 48 48 if self.tmp is not None : 49 49 self.tmp.close() 50 51 def computeChecksum(self) : 50 51 def computeChecksum(self) : 52 52 """Computes an MD5 checksum for the input file's content.""" 53 53 checksum = md5.new() … … 60 60 infile = sys.stdin 61 61 istemp = True 62 else : 62 else : 63 63 infile = open(self.inputfile, "rb") 64 64 65 65 while True : 66 66 data = infile.read(MEGABYTE) 67 67 if not data : 68 68 break 69 if istemp : 69 if istemp : 70 70 self.tmp.write(data) 71 71 checksum.update(data) 72 73 if istemp : 74 self.tmp.flush() 72 73 if istemp : 74 self.tmp.flush() 75 75 else : 76 76 infile.close() 77 78 return checksum.hexdigest() 79 77 78 return checksum.hexdigest() 79 80 80 def getAvailableDevices(self) : 81 81 """Returns a list of available GhostScript devices. 82 82 83 83 The list is returned without any x11, bbox, nor ijs related device. 84 84 """ … … 93 93 and (not dev == "/nullpage") \ 94 94 and (not dev == "/bbox") ] 95 devices.sort() 95 devices.sort() 96 96 return devices 97 97 return [] 98 98 99 99 def getAvailableIJSPrintClasses(self) : 100 100 """Returns a list of available IJS Print Classes. 101 101 102 102 Currently the list is a static one and doesn't contain all the available print classes. 103 103 """ 104 return [ "DJ3600", "DJ3320", "DJ9xx", "DJGenericVIP", "LJColor", 104 return [ "DJ3600", "DJ3320", "DJ9xx", "DJGenericVIP", "LJColor", 105 105 "DJ850", "DJ890", "DJ9xxVIP", "DJ8xx", "DJ540", "DJ660", 106 106 "DJ6xx", "DJ350", "DJ6xxPhoto", "DJ630", "DJ8x5", "DJ4100", … … 108 108 "Postscript", "LJJetReady", "LJMono", "LJFastRaster", 109 109 "LJZjsMono", ] 110 110 111 111 def batchGeneration(self, infilename, devices, root, command) : 112 112 """Loops over a set of devices calling a particular command.""" … … 121 121 if os.path.exists(outfilename) and os.stat(outfilename).st_size : 122 122 sys.stdout.write("Skipping %(outfilename)s : already exists.\n" % locals()) 123 else : 123 else : 124 124 sys.stdout.write("Generating %(outfilename)s " % locals()) 125 125 sys.stdout.flush() 126 126 os.system(cmd) 127 127 sys.stdout.write("\n") 128 128 129 129 if not os.path.exists(outfilename) : 130 130 sys.stderr.write("ERROR : During the generation of %(outfilename)s\n" % locals()) 131 elif not os.stat(outfilename).st_size : 131 elif not os.stat(outfilename).st_size : 132 132 sys.stderr.write("ERROR : Unsupported driver, impossible to generate %(outfilename)s\n" % locals()) 133 133 os.remove(outfilename) 134 else : 134 else : 135 135 self.results[outfilename] = { "command" : cmd, 136 136 "device" : "%s" % (devprefix + device), … … 138 138 "details" : None, 139 139 } 140 140 141 141 def genTestSuite(self) : 142 142 """Generate the testsuite.""" 143 143 root = "testsuite.%s" % self.md5sum 144 self.batchGeneration(self.inputfile, self.getAvailableDevices(), 145 root, 144 self.batchGeneration(self.inputfile, self.getAvailableDevices(), 145 root, 146 146 'gs -dQUIET -dBATCH -dNOPAUSE -dPARANOIDSAFER -sOutputFile="%(outfilename)s" -sDEVICE="%(device)s" "%(infilename)s"') 147 148 self.batchGeneration(self.inputfile, self.getAvailableIJSPrintClasses(), 149 "%(root)s.hpijs" % locals(), 147 148 self.batchGeneration(self.inputfile, self.getAvailableIJSPrintClasses(), 149 "%(root)s.hpijs" % locals(), 150 150 'gs -dBATCH -dQUIET -dPARANOIDSAFER -dNOPAUSE -sDEVICE=ijs -sIjsServer=hpijs -dIjsUseOutputFD -sDeviceManufacturer="HEWLETT-PACKARD" -sDeviceModel="%(device)s" -sOutputFile="%(outfilename)s" "%(infilename)s"') 151 152 def runPipe(self, cmd) : 151 152 def runPipe(self, cmd) : 153 153 """Runs a command in a pipe, returns the command's output as a string.""" 154 154 answerfd = os.popen(cmd, "r") 155 155 try : 156 156 return answerfd.read().strip() 157 finally : 157 finally : 158 158 answerfd.close() 159 160 def computeSize(self, filename) : 159 160 def computeSize(self, filename) : 161 161 """Computes the size in pages of a file in the testsuite.""" 162 162 answer = self.runPipe('pkpgcounter "%(filename)s" 2>/dev/null' % locals()) 163 163 try : 164 164 return int(answer) 165 except (ValueError, TypeError) : 165 except (ValueError, TypeError) : 166 166 return 0 167 167 168 168 def runTests(self) : 169 169 """Launches the page counting tests against the testsuite.""" … … 172 172 if not mastersize : 173 173 raise RuntimeError, "Unable to compute the size of the testsuite's master file %(masterfilename)s" % locals() 174 else : 174 else : 175 175 sys.stdout.write("Master file's contains %(mastersize)i pages.\n" % locals()) 176 176 testsuite = glob.glob("testsuite.*") … … 181 181 if len(parts) > 3 : 182 182 devname = ".".join(parts[2:]) 183 else : 183 else : 184 184 devname = parts[-1] 185 result = self.results.setdefault(testfname, { "command" : "See above", 186 "device" : devname, 187 "result" : None, 185 result = self.results.setdefault(testfname, { "command" : "See above", 186 "device" : devname, 187 "result" : None, 188 188 "details" : None }) 189 189 sys.stdout.write("Testing %(testfname)s ... " % locals()) … … 194 194 result["result"] = "UNSUPPORTED" 195 195 result["details"] = "Unsupported file format" 196 else : 196 else : 197 197 result["result"] = "FAILED" 198 198 result["details"] = "Found %(size)i pages instead of %(mastersize)i\n" % locals() 199 else : 199 else : 200 200 result["result"] = "SUPPORTED" 201 201 result["details"] = None 202 sys.stdout.write("%s\n" % result["result"]) 202 sys.stdout.write("%s\n" % result["result"]) 203 203 self.supportedpct = 100.0 * len([True for r in self.results.values() if r["result"] == "SUPPORTED"]) / nbtests 204 204 self.failedpct = 100.0 * len([True for r in self.results.values() if r["result"] == "FAILED"]) / nbtests 205 205 self.unsupportedpct = 100.0 * len([True for r in self.results.values() if r["result"] == "UNSUPPORTED"]) / nbtests 206 206 207 207 def genHTMLReport(self, filename) : 208 208 """Generates an HTML report.""" … … 232 232 for key in keys : 233 233 value = self.results[key] 234 linecount += 1 235 if not (linecount % 2) : 234 linecount += 1 235 if not (linecount % 2) : 236 236 linecolor = "#DEDEDE" 237 else : 237 else : 238 238 linecolor = "#FFFFFF" 239 out.write('<tr bgcolor="%s">\n' % linecolor) 239 out.write('<tr bgcolor="%s">\n' % linecolor) 240 240 if value["result"] == "SUPPORTED" : 241 241 color = "#00FF00" 242 elif value["result"] == "UNSUPPORTED" : 242 elif value["result"] == "UNSUPPORTED" : 243 243 color = "#FF0000" 244 else : 244 else : 245 245 color = "orange" 246 246 out.write('<td bgcolor="%s"><strong>%s</strong></td>\n' % (color, value["device"])) … … 250 250 out.write("</table></body></html>\n") 251 251 out.close() 252 253 def main() : 252 253 def main() : 254 254 """Main function.""" 255 255 try : … … 259 259 sys.stderr.write("usage : %s [inputfile.ps]\n" % sys.argv[0]) 260 260 sys.exit(-1) 261 else : 261 else : 262 262 testsuite = TestSuite(sys.argv[1]) 263 263 testsuite.genTestSuite() 264 264 testsuite.runTests() 265 265 testsuite.genHTMLReport("%s.html" % testsuite.md5sum) 266 except KeyboardInterrupt : 266 except KeyboardInterrupt : 267 267 sys.stderr.write("Interrupted at user's request !\n") 268 268 269 269 if __name__ == "__main__" : 270 270 sys.exit(main()) 271 271 -
pkpgcounter/trunk/tests/README
r463 r3436 19 19 ==================================================================== 20 20 21 This directory contains a gzipped PostScript file, master.ps.gz, 22 obtained by printing to a file a part of the SlashDot.org website 23 from the Galeon web browser on July 2nd 2004. 21 This directory contains a gzipped PostScript file, master.ps.gz, 22 obtained by printing to a file a part of the SlashDot.org website 23 from the Galeon web browser on July 2nd 2004. 24 24 25 When uncompressed, this 16 pages file can be used to test the 26 accuracy of the generic Page Description Language analyzer included 27 in pkpgcounter for different conversion outputs of gs. 25 When uncompressed, this 16 pages file can be used to test the 26 accuracy of the generic Page Description Language analyzer included 27 in pkpgcounter for different conversion outputs of gs. 28 28 29 29 To see if it works, simply launch : 30 30 31 31 $ ./runtests.sh 32 33 It should report 16 pages for each document. If it doesn't then 34 there's a bug in the generic Page Description Language analyzer. 32 33 It should report 16 pages for each document. If it doesn't then 34 there's a bug in the generic Page Description Language analyzer. 35 35 Please report this to : alet@librelogiciel.com 36 36 37 37 This script will also launch the test for the computation of 38 ink coverage. Look at the file colors.pdf to see what the 38 ink coverage. Look at the file colors.pdf to see what the 39 39 results should look like. -
pkpgcounter/trunk/tests/runcolors.py
r564 r3436 1 1 #! /usr/bin/env python 2 # -*- coding: UTF-8 -*-2 # -*- coding: utf-8 -*- 3 3 # 4 4 # pkpgcounter : a generic Page Description Language parser … … 9 9 # the Free Software Foundation, either version 3 of the License, or 10 10 # (at your option) any later version. 11 # 11 # 12 12 # This program is distributed in the hope that it will be useful, 13 13 # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 15 # GNU General Public License for more details. 16 # 16 # 17 17 # You should have received a copy of the GNU General Public License 18 18 # along with this program. If not, see <http://www.gnu.org/licenses/>. … … 52 52 pkpgcounter --colorspace $cspace colors.pdf ; 53 53 echo ; 54 done 54 done 55 55 56 56 Please report any problem to : alet@librelogiciel.com … … 115 115 from reportlab.lib.units import cm 116 116 from reportlab.pdfgen import canvas 117 except ImportError : 117 except ImportError : 118 118 sys.stderr.write("Please download and install ReportLab\n\tfrom http://www.reportlab.org\n") 119 119 sys.exit(-1) … … 124 124 xbase = 2*cm 125 125 ybase = height - 2*cm 126 126 127 127 # First we output the explanations on the first page. 128 128 canv.setFont("Courier", 14) … … 131 131 ybase -= 18 132 132 canv.showPage() 133 133 134 134 # Then we output each page 135 135 for color in (colors.Color(1, 0, 0), # Red 136 136 colors.Color(0, 1, 0), # Green 137 137 colors.Color(0, 0, 1)) : # Blue 138 canv.setStrokeColorRGB(*color.rgb()) 139 canv.setFillColorRGB(*color.rgb()) 138 canv.setStrokeColorRGB(*color.rgb()) 139 canv.setFillColorRGB(*color.rgb()) 140 140 canv.rect(0, 0, width, height, fill=1) 141 141 canv.showPage() 142 142 143 143 for color in (colors.CMYKColor(1, 0, 0, 0), # Cyan 144 144 colors.CMYKColor(0, 1, 0, 0), # Magenta … … 146 146 colors.CMYKColor(0, 0, 0, 1), # Black 147 147 colors.CMYKColor(0, 0, 0, 0)) : # White 148 canv.setStrokeColorCMYK(*color.cmyk()) 149 canv.setFillColorCMYK(*color.cmyk()) 148 canv.setStrokeColorCMYK(*color.cmyk()) 149 canv.setFillColorCMYK(*color.cmyk()) 150 150 canv.rect(0, 0, width, height, fill=1) 151 151 canv.showPage() 152 152 153 153 # Finally outputs the expected results. 154 154 canv.setFont("Helvetica-Bold", 16) … … 160 160 ybase -= 14 161 161 canv.showPage() 162 163 canv.save() 162 163 canv.save() -
pkpgcounter/trunk/tests/runtest.sh
r564 r3436 8 8 # the Free Software Foundation, either version 3 of the License, or 9 9 # (at your option) any later version. 10 # 10 # 11 11 # This program is distributed in the hope that it will be useful, 12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 14 # GNU General Public License for more details. 15 # 15 # 16 16 # You should have received a copy of the GNU General Public License 17 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. … … 19 19 echo "Analyzing colors..." 20 20 if ! [ -f "colors.pdf" ] ; then 21 python ./runcolors.py ; 21 python ./runcolors.py ; 22 22 fi ; 23 echo 23 echo 24 24 25 25 for cspace in BW RGB CMY CMYK GC ; do … … 27 27 pkpgcounter --colorspace $cspace colors.pdf ; 28 28 echo ; 29 done 29 done 30 30 31 31 echo "Generating testsuite..."