diff options
| author | pinky <> | 2007-01-07 21:41:39 +0000 |
|---|---|---|
| committer | pinky <> | 2007-01-07 21:41:39 +0000 |
| commit | 05bb5d58295cd959c34f1fbcd5b605cd6dafaacc (patch) | |
| tree | 3b3651037037c1617a54ef3d216e8c11ea8cdddf /bin/reportlab/tools/py2pdf | |
| parent | 62524af2f41454a2302efb21f15a95318d257a76 (diff) | |
Repotrlab 2.0
bzr revid: pinky-b8333ccaab5dad8ff965b6c380fadadf30d5212a
Diffstat (limited to 'bin/reportlab/tools/py2pdf')
| -rw-r--r-- | bin/reportlab/tools/py2pdf/README | 8 | ||||
| -rw-r--r-- | bin/reportlab/tools/py2pdf/__init__.py | 3 | ||||
| -rw-r--r-- | bin/reportlab/tools/py2pdf/demo-config.txt | 11 | ||||
| -rw-r--r-- | bin/reportlab/tools/py2pdf/demo.py | 198 | ||||
| -rw-r--r-- | bin/reportlab/tools/py2pdf/idle_print.py | 56 | ||||
| -rw-r--r-- | bin/reportlab/tools/py2pdf/py2pdf.py | 1567 | ||||
| -rw-r--r-- | bin/reportlab/tools/py2pdf/vertpython.jpg | bin | 0 -> 22872 bytes |
7 files changed, 1843 insertions, 0 deletions
diff --git a/bin/reportlab/tools/py2pdf/README b/bin/reportlab/tools/py2pdf/README new file mode 100644 index 00000000000..9a3e9c8fb7e --- /dev/null +++ b/bin/reportlab/tools/py2pdf/README @@ -0,0 +1,8 @@ +py2pdf - converts Python source code to PDF +with a LOT of options. + +See the header of py2pdf.py for full details. +execute demo.py to see some output. + +Contributed by Dinu Gherman and copyrighted by him. +Uses Just van Rossum's PyFontify. diff --git a/bin/reportlab/tools/py2pdf/__init__.py b/bin/reportlab/tools/py2pdf/__init__.py new file mode 100644 index 00000000000..e0226c4a6fd --- /dev/null +++ b/bin/reportlab/tools/py2pdf/__init__.py @@ -0,0 +1,3 @@ +#Copyright ReportLab Europe Ltd. 2000-2004 +#see license.txt for license details +#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/tools/py2pdf/__init__.py diff --git a/bin/reportlab/tools/py2pdf/demo-config.txt b/bin/reportlab/tools/py2pdf/demo-config.txt new file mode 100644 index 00000000000..4c5ed1a314b --- /dev/null +++ b/bin/reportlab/tools/py2pdf/demo-config.txt @@ -0,0 +1,11 @@ +--bgCol=(1,.9,.9) +--lineNum +#--fontSize=12 +#--paperFormat=B5 +#--fontName=Helvetica +#--landscape +#--restCol=(0,1,0) +#--mode=mono +--kwCol=(1,0,1) +#--kwCol=#dd00ff +#--kwCol=(.5,.5,.5) diff --git a/bin/reportlab/tools/py2pdf/demo.py b/bin/reportlab/tools/py2pdf/demo.py new file mode 100644 index 00000000000..aa69f8cf042 --- /dev/null +++ b/bin/reportlab/tools/py2pdf/demo.py @@ -0,0 +1,198 @@ +#!/usr/bin/env python + +"""demo.py - Demo script for py2pdf 0.5. + +The main idea is: take one Python file and make a whole +bunch of PDFs out of it for test purposes. + +Dinu Gherman +""" + + +import string, re, os, os.path, sys, shutil +from py2pdf import * + + +### Custom layouter class used with test3(). + +class ImgPDFLayouter (PythonPDFLayouter): + "A custom layouter displaying an image on each page." + + def setMainFrame(self, frame=None): + "Make a frame in the right half of the page." + + width, height = self.options.realPaperFormat.size + self.frame = height - 2*cm, 2*cm, 250, width-1*cm + + self.makeForm() + + + def makeForm(self): + "Use the experimental ReportLab form support." + + width, height = self.options.realPaperFormat.size + tm, bm, lm, rm = self.frame + c = self.canvas + + # Define a PDF form containing an image frame + # that will be included on every page, but + # stored only once in the resulting file. + c.beginForm("imageFrame") + c.saveState() + x, y = 219.0, 655.0 # Known size of the picture. + c.scale((lm - 1*cm)/x, height/y) + path = 'vertpython.jpg' + c.drawImage(path, 0, 0) + c.restoreState() + c.endForm() + + + def putPageDecoration(self): + "Draw the left border image and page number." + + width, height = self.options.realPaperFormat.size + tm, bm, lm, rm = self.frame + c = self.canvas + + # Footer. + x, y = lm + 0.5 * (rm - lm), 0.5 * bm + c.setFillColor(Color(0, 0, 0)) + c.setFont('Times-Italic', 12) + label = "Page %d" % self.pageNum + c.drawCentredString(x, y, label) + + # Call the previously stored form. + c.doForm("imageFrame") + + +### Helpers. + +def modifyPath(path, new, ext='.py'): + "Modifying the base name of a file." + + rest, ext = os.path.splitext(path) + path, base = os.path.split(rest) + format = "%s-%s%s" % (base, new, ext) + return os.path.join(path, format) + + +def getAllTestFunctions(): + "Return a list of all test functions available." + + globs = globals().keys() + tests = filter(lambda g: re.match('test[\d]+', g), globs) + tests.sort() + return map(lambda t: globals()[t], tests) + + +### Test functions. +### +### In order to be automatically found and applied to +### a Python file all test functions must follow the +### following naming pattern: 'test[0-9]+' and contain +### a doc string. + +def test0(path): + "Creating a PDF assuming an ASCII file." + + p = PDFPrinter() + p.process(path) + + +def test1(path): + "Creating a PDF using only default options." + + p = PythonPDFPrinter() + p.process(path) + + +def test2(path): + "Creating a PDF with some modified options." + + p = PythonPDFPrinter() + p.options.updateOption('landscape', 1) + p.options.updateOption('fontName', 'Helvetica') + p.options.updateOption('fontSize', '14') + p.options.display() + p.process(path) + + +def test3(path): + "Creating several PDFs as 'magazine listings'." + + p = PythonPDFPrinter() + p.Layouter = EmptyPythonPDFLayouter + p.options.updateOption('paperSize', '(250,400)') + p.options.updateOption('multiPage', 1) + p.options.updateOption('lineNum', 1) + p.process(path) + + +def test4(path): + "Creating a PDF in monochrome mode." + + p = PythonPDFPrinter() + p.options.updateOption('mode', 'mono') + p.process(path) + + +def test5(path): + "Creating a PDF with options from a config file." + + p = PythonPDFPrinter() + i = string.find(path, 'test5') + newPath = modifyPath(path[:i-1], 'config') + '.txt' + + try: + p.options.updateWithContentsOfFile(newPath) + p.options.display() + p.process(path) + except IOError: + print "Skipping test5() due to IOError." + + +def test6(path): + "Creating a PDF with modified layout." + + p = PythonPDFPrinter() + p.Layouter = ImgPDFLayouter + p.options.updateOption('fontName', 'Helvetica') + p.options.updateOption('fontSize', '12') + p.options.display() + p.process(path) + + +### Main. + +def main(inPath, *tests): + "Apply various tests to one Python source file." + + for t in tests: + newPath = modifyPath(inPath, t.__name__) + shutil.copyfile(inPath, newPath) + try: + print t.__doc__ + t(newPath) + finally: + os.remove(newPath) + print + + +if __name__=='__main__': + # Usage: "python demo.py <file> <test1> [<test2> ...]" + try: + try: + tests = map(lambda a: globals()[a], sys.argv[2:]) + except IndexError: + tests = getAllTestFunctions() + + fileName = sys.argv[1] + apply(main, [fileName]+tests) + + # Usage: "python demo.py" (implicitly does this: + # "python demo.py demo.py" <allTestsAvailable>) + except IndexError: + print "Performing self-test..." + fileName = sys.argv[0] + tests = getAllTestFunctions() + apply(main, [fileName]+tests) diff --git a/bin/reportlab/tools/py2pdf/idle_print.py b/bin/reportlab/tools/py2pdf/idle_print.py new file mode 100644 index 00000000000..18b3080a259 --- /dev/null +++ b/bin/reportlab/tools/py2pdf/idle_print.py @@ -0,0 +1,56 @@ +#Copyright ReportLab Europe Ltd. 2000-2004 +#see license.txt for license details +#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/tools/py2pdf/idle_print.py + +# idle_print [py2pdf_options] filename +__version__=''' $Id: idle_print.py 2385 2004-06-17 15:26:05Z rgbecker $ ''' +# you should adjust the globals below to configure for your system + +import sys, os, py2pdf, string, time +#whether we remove input/output files; if you get trouble on windows try setting _out to 0 +auto_rm_in = 1 +auto_rm_out = 1 +viewOnly = 0 + +#how to call up your acrobat reader +if sys.platform=='win32': + acrord = 'C:\\Program Files\\Adobe\\Acrobat 4.0\\Reader\\AcroRd32.exe' + def printpdf(pdfname): + args = [acrord, pdfname] + os.spawnv(os.P_WAIT, args[0], args) +else: + acrord = 'acroread' + def printpdf(pdfname): + if viewOnly: + cmd = "%s %s" % (acrord,pdfname) + else: + cmd = "%s -toPostScript < %s | lpr" % (acrord,pdfname) + os.system(cmd) + +args = ['--input=python'] +files = [] +for f in sys.argv[1:]: + if f[:2]=='--': + opt = f[2:] + if opt =='no_auto_rm_in': + auto_rm_in = 0 + elif opt =='auto_rm_in': + auto_rm_in = 1 + elif opt =='no_auto_rm_out': + auto_rm_out = 0 + elif opt =='auto_rm_out': + auto_rm_out = 1 + elif opt =='viewonly': + viewOnly = 1 + elif opt[:9] =='acroread=': + acrord = opt[9:] + else: + args.append(f) + else: files.append(f) + +for f in files: + py2pdf.main(args+[f]) + if auto_rm_in: os.remove(f) + pdfname = os.path.splitext(f)[0]+'.pdf' + printpdf(pdfname) + if auto_rm_out: os.remove(pdfname)
\ No newline at end of file diff --git a/bin/reportlab/tools/py2pdf/py2pdf.py b/bin/reportlab/tools/py2pdf/py2pdf.py new file mode 100644 index 00000000000..faaeacbc21f --- /dev/null +++ b/bin/reportlab/tools/py2pdf/py2pdf.py @@ -0,0 +1,1567 @@ +#!/usr/bin/env python + +""" Python Highlighter for PDF Version: 0.6 + + py2pdf.py [options] <file1> [<file2> ...] + + options: + -h or --help print help (this message) + - read from stdin (writes to stdout) + --stdout read from file, write to stdout + (restricted to first file only) + --title=<title> specify title + --config=<file> read configuration options from <file> + --input=<type> set input file type + 'python': Python code (default) + 'ascii': arbitrary ASCII files (b/w) + --mode=<mode> set output mode + 'color': output in color (default) + 'mono': output in b/w + --paperFormat= set paper format (ISO A, B, C series, + <format> US legal & letter; default: 'A4') + e.g. 'letter', 'A3', 'A4', 'B5', 'C6', ... + --paperSize= set paper size in points (size being a valid + <size> numeric 2-tuple (x,y) w/o any whitespace) + --landscape set landscape format (default: portrait) + --bgCol=<col> set page background-color in hex code like + '#FFA024' or '0xFFA024' for RGB components + (overwrites mono mode) + --<cat>Col=<col> set color of certain code categories, i.e. + <cat> can be the following: + 'comm': comments + 'ident': identifiers + 'kw': keywords + 'strng': strings + 'param': parameters + 'rest': all the rest + --fontName=<name> set base font name (default: 'Courier') + like 'Helvetica', 'Times-Roman' + --fontSize=<size> set font size (default: 8) + --tabSize=<size> set tab size (default: 4) + --lineNum print line numbers + --multiPage generate one file per page (with filenames + tagged by 1, 2...), disables PDF outline + --noOutline don't generate PDF outline (default: unset) + -v or --verbose set verbose mode + + Takes the input, assuming it is Python source code and formats + it into PDF. + + * Uses Just van Rossum's PyFontify version 0.4 to tag Python + scripts, now included in reportlab.lib. + + * Uses the ReportLab library (version 0.92 or higher) to + generate PDF. You can get it without charge from ReportLab: + http://www.reportlab.com + + * Parts of this code still borrow heavily from Marc-Andre + Lemburg's py2html who has kindly given permission to + include them in py2pdf. Thanks, M.-A.! +""" + +__copyright__ = """ +---------------------------------------------------------------------- +(c) Copyright by Dinu C. Gherman, 2000 (gherman@europemail.com) + + Permission to use, copy, modify, and distribute this + software and its documentation for any purpose and + without fee or royalty is hereby granted, provided + that the above copyright notice appear in all copies + and that both that copyright notice and this permission + notice appear in supporting documentation or portions + thereof, including modifications, that you make. + + THE AUTHOR DINU C. GHERMAN DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT + SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT + OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE + OR PERFORMANCE OF THIS SOFTWARE! +""" + +__version__ = '0.6' +__author__ = 'Dinu C. Gherman' +__date__ = '2001-08-17' +__url__ = 'http://starship.python.net/crew/gherman/programs/py2pdf' + + +import sys, string, re, os, getopt + +from reportlab.pdfgen import canvas +from reportlab.lib.colors import Color, HexColor +from reportlab.lib import fonts +from reportlab.lib.units import cm, inch + + +### Helpers functions. + +def makeTuple(aString): + """Evaluate a string securely into a tuple. + + Match the string and return an evaluated object thereof if and + only if we think it is a tuple of two or more numeric decimal(!) + values, integers or floats. E-notation, hex or octal is not + supported, though! Shorthand notation (omitting leading or trail- + zeros, before or after the decimal point) like .25 or 25. is + supported. + """ + + c = string.count(aString, ',') + num = '(\d*?\.?\d+?)|(\d+?\.?\d*?)' + tup = string.join([num]*c, ',') + + if re.match('\(' + tup + '\)', aString): + return eval(aString) + else: + details = '%s cannot be parsed into a numeric tuple!' % aString + raise 'ValueError', details + + +def loadFontifier(options=None): + "Load a tagging module and return a corresponding function." + + # We can't use 'if options' because of the modified + # attribute lookup it seems. + + if type(options) != type(None) and options.marcs: + # Use mxTextTool's tagging engine. + + from mxTextTools import tag + from mxTextTools.Examples.Python import python_script + tagFunc = lambda text, tag=tag, pytable=python_script: \ + tag(text,pytable)[1] + + else: + # Load Just's. + + try: + from reportlab.lib import PyFontify + + if PyFontify.__version__ < '0.3': + raise ValueError + + tagFunc = PyFontify.fontify + + except: + print """ + Sorry, but this script needs the PyFontify.py module version 0.3 + or higher; You can download it from Just's homepage at + + URL: http://starship.python.net/crew/just +""" + sys.exit() + + return tagFunc + + +def makeColorFromString(aString): + """Convert a string to a ReportLab RGB color object. + + Supported formats are: '0xFFFFFF', '#FFFFFF' and '(R,G,B)' + where the latter is a tuple of three floats in the range + [0.0, 1.0]. + """ + + s = aString + + if s[0] == '#' or s[:2] in ('0x', '0X'): + if s[:2] in ('0x', '0X'): + return HexColor('#' + s[2:]) + + elif s[0] == '(': + r, g, b = makeTuple(aString) + return Color(r, g, b) + + +### Utility classes. + +# For py2pdf this is some kind of overkill, but it's fun, too. +class PaperFormat: + """Class to capture a paper format/size and its orientation. + + This class represents an abstract paper format, including + the size and orientation of a sheet of paper in some formats + plus a few operations to change its size and orientation. + """ + + _A4W, _A4H = 21*cm, 29.7*cm + A0 = (4*_A4W, 4*_A4H) + + _B4W, _B4H = 25*cm, 35.3*cm + B0 = (4*_B4W, 4*_B4H) + + _C4W, _C4H = 22.9*cm, 32.4*cm + C0 = (4*_C4W, 4*_C4H) + + letter = (8.5*inch, 11*inch) + legal = (8.5*inch, 14*inch) + + + def __init__(self, nameOrSize='A4', landscape=0): + "Initialisation." + + t = type(nameOrSize) + + if t == type(''): + self.setFormatName(nameOrSize, landscape) + elif t == type((1,)): + self.setSize(nameOrSize) + self.setLandscape(landscape) + + + def __repr__(self): + """Return a string representation of ourself. + + The returned string can also be used to recreate + the same PaperFormat object again. + """ + + if self.name != 'custom': + nos = `self.name` + else: + nos = `self.size` + + format = "PaperFormat(nameOrSize=%s, landscape=%d)" + tuple = (nos, self.landscape) + + return format % tuple + + + def setSize(self, size=None): + "Set explicit paper size." + + self.name = 'custom' + x, y = self.size = size + self.landscape = x < y + + + def setLandscape(self, flag): + "Set paper orientation as desired." + + # Swap paper orientation if needed. + + self.landscape = flag + x, y = self.size + + ls = self.landscape + if (ls and x < y) or (not ls and x > y): + self.size = y, x + + + def setFormatName(self, name='A4', landscape=0): + "Set paper size derived from a format name." + + if name[0] in 'ABC': + # Assume ISO-A, -B, -C series. + # (Hmm, are B and C really ISO standards + # or just DIN? Well...) + c, f = name[0], int(name[1:]) + self.size = getattr(self, c + '0') + self.name = c + '0' + self.makeHalfSize(f) + + elif name == 'letter': + self.size = self.letter + self.name = 'letter' + + elif name == 'legal': + self.size = self.legal + self.name = 'legal' + + self.setLandscape(landscape) + + + def makeHalfSize(self, times=1): + "Reduce paper size (surface) by 50% multiple times." + + # Orientation remains unchanged. + + # Iterates only for times >= 1. + for i in xrange(times): + s = self.size + self.size = s[1] / 2.0, s[0] + + if self.name[0] in 'ABC': + self.name = self.name[0] + `int(self.name[1:]) + 1` + else: + self.name = 'custom' + + + def makeDoubleSize(self, times=1): + "Increase paper size (surface) by 50% multiple times." + + # Orientation remains unchanged. + + # Iterates only for times >= 1. + for i in xrange(times): + s = self.size + self.size = s[1], s[0] * 2.0 + + if self.name[0] in 'ABC': + self.name = self.name[0] + `int(self.name[1:]) - 1` + else: + self.name = 'custom' + + +class Options: + """Container class for options from command line and config files. + + This class is a container for options as they are specified + when a program is called from a command line, but also from + the same kind of options that are saved in configuration + files. Both the short UNIX style (e.g. '-v') as well as the + extended GNU style (e.g. '--help') are supported. + + An 'option' is a <name>/<value> pair with both parts being + strings at the beginning, but where <value> might be conver- + ted later into any other Python object. + + Option values can be accessed by using their name as an attri- + bute of an Options object, returning None if no such option + name exists (that makes None values impossible, but so what?). + """ + + # Hmm, could also use UserDict... maybe later. + + def __init__(self): + "Initialize with some default options name/values." + + # Hmm, could also pass an initial optional dict... + + # Create a dictionary containing the options + # and populate it with defaults. + self.pool = {} + self.setDefaults() + + + def __getattr__(self, name): + "Turn attribute access into dictionary lookup." + + return self.pool.get(name) + + + def setDefaults(self): + "Set default options." + + ### Maybe get these from a site config file... + self.pool.update({'fontName' : 'Courier', + 'fontSize' : 8, + 'bgCol' : Color(1, 1, 1), + 'mode' : 'color', + 'lineNum' : 0, + 'tabSize' : 4, + 'paperFormat': 'A4', + 'landscape' : 0, + 'title' : None, + 'multiPage' : 0}) + + # Default colors (for color mode), mostly taken from py2html. + self.pool.update({'commCol' : HexColor('#1111CC'), + 'kwCol' : HexColor('#3333CC'), + 'identCol' : HexColor('#CC0000'), + 'paramCol' : HexColor('#000066'), + 'strngCol' : HexColor('#119911'), + 'restCol' : HexColor('#000000')}) + + # Add a default 'real' paper format object. + pf = PaperFormat(self.paperFormat, self.landscape) + self.pool.update({'realPaperFormat' : pf}) + self.pool.update({'files' : []}) + + + def display(self): + "Display all current option names and values." + + self.saveToFile(sys.stdout) + + + def saveToFile(self, path): + "Save options as a log file." + + if type(path) == type(''): + f = open(path, 'w') + else: + # Assume a file-like object. + f = path + + items = self.pool.items() + items.sort() + + for n, v in items: + f.write("%-15s : %s\n" % (n, `v`)) + + + def updateWithContentsOfFile(self, path): + """Update options as specified in a config file. + + The file is expected to contain one option name/value pair + (seperated by an equal sign) per line, but no other whitespace. + Option values may contain equal signs, but no whitespace. + Option names must be valid Python strings, preceeded by one + or two dashes. + """ + + config = open(path).read() + config = string.split(config, '\n') + + for cfg in config: + if cfg == None: + break + + if cfg == '' or cfg[0] == '#': + continue + + # GNU long options + if '=' in cfg and cfg[:2] == '--': + p = string.find(cfg, '=') + opt, arg = cfg[2:p], cfg[p+1:] + self.updateOption(opt, arg) + + # Maybe treat single-letter options as well? + # elif ':' in cfg and cfg[0] == '-' and cfg[1] != '-': + # pass + + else: + self.updateOption(cfg[2:], None) + + + def updateWithContentsOfArgv(self, argv): + "Update options as specified in a (command line) argument vector." + + # Specify accepted short option names (UNIX style). + shortOpts = 'hv' + + # Specify accepted long option names (GNU style). + lo = 'tabSize= paperFormat= paperSize= landscape stdout title= fontName= fontSize=' + lo = lo + ' bgCol= verbose lineNum marcs help multiPage noOutline config= input= mode=' + lo = lo + ' commCol= identCol= kwCol= strngCol= paramCol= restCol=' + longOpts = string.split(lo, ' ') + + try: + optList, args = getopt.getopt(argv, shortOpts, longOpts) + except getopt.error, msg: + sys.stderr.write("%s\nuse -h or --help for help\n" % str(msg)) + sys.exit(2) + + self.updateOption('files', args) # Hmm, really needed? + + for o, v in optList: + # Remove leading dashes (max. two). + if o[0] == '-': + o = o[1:] + if o[0] == '-': + o = o[1:] + + self.updateOption(o, v) + + + def updateOption(self, name, value): + "Update an option from a string value." + + # Special treatment for coloring options... + if name[-3:] == 'Col': + if name[:-3] in string.split('bg comm ident kw strng param rest', ' '): + self.pool[name] = makeColorFromString(value) + + elif name == 'paperSize': + tup = makeTuple(value) + self.pool['paperSize'] = tup + if not self.realPaperFormat: + pf = PaperFormat(self.paperFormat, self.landscape) + self.pool['realPaperFormat'] = pf + self.pool['realPaperFormat'].setSize(tup) + + elif name == 'paperFormat': + self.pool['paperFormat'] = value + if not self.realPaperFormat: + pf = PaperFormat(self.paperFormat, self.landscape) + self.pool['realPaperFormat'] = pf + self.pool['realPaperFormat'].setFormatName(self.paperFormat, self.landscape) + + elif name == 'landscape': + self.pool['landscape'] = 1 + if self.realPaperFormat: + self.pool['realPaperFormat'].setLandscape(1) + + elif name == 'fontSize': + self.pool['fontSize'] = int(value) + + elif name == 'tabSize': + self.pool['tabSize'] = int(value) + + elif name == 'mode': + self.pool['mode'] = value + if value == 'mono': + cats = 'comm ident kw strng param rest' + for cat in string.split(cats, ' '): + self.pool[cat + 'Col'] = Color(0, 0, 0) + + # Parse configuration file... + elif name == 'config': + self.updateWithContentsOfFile(value) + + elif name == 'stdout': + self.pool['stdout'] = 1 + + elif name == 'files': + self.pool['files'] = value + + else: + # Set the value found or 1 for options without values. + self.pool[name] = value or 1 + + + def update(self, **options): + "Update options." + + # Not much tested and/or used, yet!! + + for n, v in options.items(): + self.pool[n] = v + + +### Layouting classes. + +class PDFLayouter: + """A class to layout a simple PDF document. + + This is intended to help generate PDF documents where all pages + follow the same kind of 'template' which is supposed to be the + same adornments (header, footer, etc.) on each page plus a main + 'frame' on each page. These frames are 'connected' such that one + can add individual text lines one by one with automatic line + wrapping, page breaks and text flow between frames. + """ + + def __init__(self, options): + "Initialisation." + + self.options = options + self.canvas = None + self.multiLineStringStarted = 0 + self.lineNum = 0 + + # Set a default color and font. + o = self.options + self.currColor = o.restCol + self.currFont = (o.fontName, o.fontSize) + + + ### Helper methods. + + def setMainFrame(self, frame=None): + "Define the main drawing frame of interest for each page." + + if frame: + self.frame = frame + else: + # Maybe a long-term candidate for additional options... + width, height = self.options.realPaperFormat.size + self.frame = height - 3*cm, 3*cm, 2*cm, width - 2*cm + + # self.frame is a 4-tuple: + # (topMargin, bottomMargin, leftMargin, rightMargin) + + + def setPDFMetaInfo(self): + "Set PDF meta information." + + o = self.options + c = self.canvas + c.setAuthor('py2pdf %s' % __version__) + c.setSubject('') + + # Set filename. + filename = '' + + # For stdin use title option or empty... + if self.srcPath == sys.stdin: + if o.title: + filename = o.title + # otherwise take the input file's name. + else: + path = os.path.basename(self.srcPath) + filename = o.title or path + + c.setTitle(filename) + + + def setFillColorAndFont(self, color, font): + "Set new color/font (maintaining the current 'state')." + + self.currFont = font + self.currColor = color + + fontName, fontSize = font + self.text.setFont(fontName, fontSize) + self.text.setFillColor(color) + + + ### API + + def begin(self, srcPath, numLines): + "Things to do before doing anything else." + + self.lineNum = 0 + self.pageNum = 0 + self.numLines = numLines + self.srcPath = srcPath + + # Set output filename (stdout if desired). + o = self.options + if o.stdout: + self.pdfPath = sys.stdout + else: + if srcPath != sys.stdin: + self.pdfPath = os.path.splitext(srcPath)[0] + '.pdf' + else: + self.pdfPath = sys.stdout + + + def beginDocument(self): + """Things to do when a new document should be started. + + The initial page counter is 0, meaning that beginPage() + will be called by beginLine()... + """ + + # Set initial page number and store file name. + self.pageNum = 0 + o = self.options + + if not o.multiPage: + # Create canvas. + size = o.realPaperFormat.size + self.canvas = canvas.Canvas(self.pdfPath, size, verbosity=0) + c = self.canvas + c.setPageCompression(1) + c.setFont(o.fontName, o.fontSize) + + # Create document meta information. + self.setPDFMetaInfo() + + # Set drawing frame. + self.setMainFrame() + + # Determine the left text margin by adding the with + # of the line number to the left margin of the main frame. + format = "%%%dd " % len(`self.numLines`) + fn, fs = self.currFont + text = format % self.lineNum + tm, bm, lm, rm = self.frame + self.txm = lm + if o.lineNum: + self.txm = self.txm + c.stringWidth(text, fn, fs) + + + def beginPage(self): + "Things to do when a new page has to be added." + + o = self.options + self.pageNum = self.pageNum + 1 + + if not o.multiPage: + tm, bm, lm, rm = self.frame + self.text = self.canvas.beginText(lm, tm - o.fontSize) + self.setFillColorAndFont(self.currColor, self.currFont) + else: + # Fail if stdout desired (with multiPage). + if o.stdout: + raise "IOError", "Can't create multiple pages on stdout!" + + # Create canvas with a modified path name. + base, ext = os.path.splitext(self.pdfPath) + newPath = "%s-%d%s" % (base, self.pageNum, ext) + size = o.realPaperFormat.size + self.canvas = canvas.Canvas(newPath, size, verbosity=0) + c = self.canvas + c.setPageCompression(1) + c.setFont(o.fontName, o.fontSize) + + # Create document meta information. + self.setPDFMetaInfo() + + # Set drawing frame. + self.setMainFrame() + + tm, bm, lm, rm = self.frame + self.text = self.canvas.beginText(lm, tm - o.fontSize) + self.setFillColorAndFont(self.currColor, self.currFont) + + self.putPageDecoration() + + + def beginLine(self, wrapped=0): + "Things to do when a new line has to be added." + + # If there is no page yet, create the first one. + if self.pageNum == 0: + self.beginPage() + + # If bottom of current page reached, do a page break. + # (This works only with one text object being used + # for the entire page. Otherwise we need to maintain + # the vertical position of the current line ourself.) + y = self.text.getY() + tm, bm, lm, rm = self.frame + if y < bm: + self.endPage() + self.beginPage() + + # Print line number label, if needed. + o = self.options + if o.lineNum: + #self.putLineNumLabel() + font = ('Courier', o.fontSize) + + if not wrapped: + # Print a label containing the line number. + self.setFillColorAndFont(o.restCol, font) + format = "%%%dd " % len(`self.numLines`) + self.text.textOut(format % self.lineNum) + else: + # Print an empty label (using bgCol). Hackish! + currCol = self.currColor + currFont = self.currFont + self.setFillColorAndFont(o.bgCol, font) + self.text.textOut(' '*(len(`self.numLines`) + 1)) + self.setFillColorAndFont(currCol, currFont) + + + def endLine(self, wrapped=0): + "Things to do after a line is basically done." + + # End the current line by adding an 'end of line'. + # (Actually done by the text object...) + self.text.textLine('') + + if not wrapped: + self.lineNum = self.lineNum + 1 + + + def endPage(self): + "Things to do after a page is basically done." + + c = self.canvas + + # Draw the current text object (later we might want + # to do that after each line...). + c.drawText(self.text) + c.showPage() + + if self.options.multiPage: + c.save() + + + def endDocument(self): + "Things to do after the document is basically done." + + c = self.canvas + + # Display rest of last page and save it. + c.drawText(self.text) + c.showPage() + c.save() + + + def end(self): + "Things to do after everything has been done." + + pass + + + ### The real meat: methods writing something to a canvas. + + def putLineNumLabel(self, text, wrapped=0): + "Add a long text that can't be split into chunks." + + o = self.options + font = ('Courier', o.fontSize) + + if not wrapped: + # Print a label containing the line number. + self.setFillColorAndFont(o.restCol, font) + format = "%%%dd " % len(`self.numLines`) + self.text.textOut(format % self.lineNum) + else: + # Print an empty label (using bgCol). Hackish! + currCol = self.currColor + currFont = self.currFont + self.setFillColorAndFont(o.bgCol, font) + self.text.textOut(' '*(len(`self.numLines`) + 1)) + self.setFillColorAndFont(currCol, currFont) + + + # Tried this recursively before, in order to determine + # an appropriate string limit rapidly, but this wasted + # much space and showed very poor results... + # This linear method is very slow, but such lines should + # be very rare, too! + + def putSplitLongText(self, text): + "Add a long text that can't be split into chunks." + + # Now, the splitting will be with 'no mercy', + # at the right margin of the main drawing area. + + M = len(text) + t = self.text + x = t.getX() + o = self.options + tm, bm, lm, rm = self.frame + + width = self.canvas.stringWidth + fn, fs = self.currFont + tw = width(text, fn, fs) + + tx = self.text + if tw > rm - lm - x: + i = 1 + T = '' + while text: + T = text[:i] + tx = self.text # Can change after a page break. + tw = width(T, fn, fs) + + if x + tw > rm: + tx.textOut(T[:-1]) + self.endLine(wrapped=1) + self.beginLine(wrapped=1) + x = tx.getX() + text = text[i-1:] + M = len(text) + i = 0 + + i = i + 1 + + if i > M: + break + + tx.textOut(T) + + else: + t.textOut(text) + + + def putLongText(self, text): + "Add a long text by gracefully splitting it into chunks." + + # Splitting is currently done only at blanks, but other + # characters such as '.' or braces are also good + # possibilities... later... + + o = self.options + tm, bm, lm, rm = self.frame + + width = self.canvas.stringWidth + fn, fs = self.currFont + tw = width(text, fn, fs) + + arr = string.split(text, ' ') + + for i in range(len(arr)): + a = arr[i] + t = self.text # Can change during the loop... + tw = width(a, fn, fs) + x = t.getX() + + # If current item does not fit on current line, have it + # split and then put before/after a line (maybe also + # page) break. + if x + tw > rm: + self.putSplitLongText(a) + t = self.text # Can change after a page break... + + # If it fits, just add it to the current text object. + else: + t.textOut(a) + + # Add the character we used to split th original text. + if i < len(arr) - 1: + t.textOut(' ') + + + def putText(self, text): + "Add some text to the current line." + + t = self.text + x = t.getX() + o = self.options + fn, fs = o.fontName, o.fontSize + tw = self.canvas.stringWidth(text, fn, fs) + rm = self.frame[3] + + if x + tw < rm: + t.textOut(text) + else: + self.putLongText(text) + + + # Not yet tested. + def putLine(self, text): + "Add a line to the current text." + + self.putText(text) + self.endLine() + + + def putPageDecoration(self): + "Draw some decoration on each page." + + # Use some abbreviations. + o = self.options + c = self.canvas + tm, bm, lm, rm = self.frame + + # Restore default font. + c.setFont(o.fontName, o.fontSize) + + c.setLineWidth(0.5) # in pt. + + # Background color. + c.setFillColor(o.bgCol) + pf = o.realPaperFormat.size + c.rect(0, 0, pf[0], pf[1], stroke=0, fill=1) + + # Header. + c.setFillColorRGB(0, 0, 0) + c.line(lm, tm + .5*cm, rm, tm + .5*cm) + c.setFont('Times-Italic', 12) + + if self.pdfPath == sys.stdout: + filename = o.title or ' ' + else: + path = os.path.basename(self.srcPath) + filename = o.title or path + + c.drawString(lm, tm + 0.75*cm + 2, filename) + + # Footer. + c.line(lm, bm - .5*cm, rm, bm - .5*cm) + c.drawCentredString(0.5 * pf[0], 0.5*bm, "Page %d" % self.pageNum) + + # Box around main frame. + # c.rect(lm, bm, rm - lm, tm - bm) + + +class PythonPDFLayouter (PDFLayouter): + """A class to layout a simple multi-page PDF document. + """ + + ### API for adding specific Python entities. + + def addKw(self, t): + "Add a keyword." + + o = self.options + + # Make base font bold. + fam, b, i = fonts.ps2tt(o.fontName) + ps = fonts.tt2ps(fam, 1, i) + font = (ps, o.fontSize) + + self.setFillColorAndFont(o.kwCol, font) + + # Do bookmarking... + if not o.noOutline and not o.multiPage: + if t in ('class', 'def'): + tm, bm, lm, rm = self.frame + pos = self.text.getX() + + if pos == self.txm: + self.startPositions = [] + + self.startPos = pos + + if not hasattr(self, 'startPositions'): + self.startPositions = [] + + if pos not in self.startPositions: + self.startPositions.append(pos) + + # Memorize certain keywords. + self.itemFound = t + + else: + self.itemFound = None + self.startPos = None + + self.putText(t) + + + def addIdent(self, t): + "Add an identifier." + + o = self.options + + # Make base font bold. + fam, b, i = fonts.ps2tt(o.fontName) + ps = fonts.tt2ps(fam, 1, i) + font = (ps, o.fontSize) + + self.setFillColorAndFont(o.identCol, font) + self.putText(t) + + # Bookmark certain identifiers (class and function names). + if not o.noOutline and not o.multiPage: + item = self.itemFound + if item: + # Add line height to current vert. position. + pos = self.text.getY() + o.fontSize + + nameTag = "p%sy%s" % (self.pageNum, pos) + c = self.canvas + i = self.startPositions.index(self.startPos) + c.bookmarkHorizontalAbsolute(nameTag, pos) + c.addOutlineEntry('%s %s' % (item, t), nameTag, i) + + + def addParam(self, t): + "Add a parameter." + + o = self.options + font = (o.fontName, o.fontSize) + self.setFillColorAndFont(o.paramCol, font) + self.text.putText(t) + + + def addSimpleString(self, t): + "Add a simple string." + + o = self.options + font = (o.fontName, o.fontSize) + self.setFillColorAndFont(o.strngCol, font) + self.putText(t) + + + def addTripleStringBegin(self): + "Memorize begin of a multi-line string." + + # Memorise that we started a multi-line string. + self.multiLineStringStarted = 1 + + + def addTripleStringEnd(self, t): + "Add a multi-line string." + + self.putText(t) + + # Forget about the multi-line string again. + self.multiLineStringStarted = 0 + + + def addComm(self, t): + "Add a comment." + + o = self.options + + # Make base font slanted. + fam, b, i = fonts.ps2tt(o.fontName) + ps = fonts.tt2ps(fam, b, 1) + font = (ps, o.fontSize) + + self.setFillColorAndFont(o.commCol, font) + self.putText(t) + + + def addRest(self, line, eol): + "Add a regular thing." + + o = self.options + + # Nothing else to be done, print line as-is... + if line: + font = (o.fontName, o.fontSize) + self.setFillColorAndFont(o.restCol, font) + + # ... except if the multi-line-string flag is set, then we + # decide to change the current color to that of strings and + # just go on. + if self.multiLineStringStarted: + self.setFillColorAndFont(o.strngCol, font) + + self.putText(line) + + # Print an empty line. + else: + if eol != -1: + self.putText('') + + ### End of API. + + +class EmptyPythonPDFLayouter (PythonPDFLayouter): + """A PDF layout with no decoration and no margins. + + The main frame extends fully to all paper edges. This is + useful for creating PDFs when writing one page per file, + in order to provide pre-rendered, embellished Python + source code to magazine publishers, who can include the + individual files and only need to add their own captures. + """ + + def setMainFrame(self, frame=None): + "Make a frame extending to all paper edges." + + width, height = self.options.realPaperFormat.size + self.frame = height, 0, 0, width + + + def putPageDecoration(self): + "Draw no decoration at all." + + pass + + +### Pretty-printing classes. + +class PDFPrinter: + """Generic PDF Printer class. + + Does not do much, but write a PDF file created from + any ASCII input file. + """ + + outFileExt = '.pdf' + + + def __init__(self, options=None): + "Initialisation." + + self.data = None # Contains the input file. + self.inPath = None # Path of input file. + self.Layouter = PDFLayouter + + if type(options) != type(None): + self.options = options + else: + self.options = Options() + + + ### I/O. + + def readFile(self, path): + "Read the content of a file." + + if path == sys.stdin: + f = path + else: + f = open(path) + + self.inPath = path + + data = f.read() + o = self.options + self.data = re.sub('\t', ' '*o.tabSize, data) + f.close() + + + def formatLine(self, line, eol=0): + "Format one line of Python source code." + + font = ('Courier', 8) + self.layouter.setFillColorAndFont(Color(0, 0, 0), font) + self.layouter.putText(line) + + + def writeData(self, srcCodeLines, inPath, outPath=None): + "Convert Python source code lines into a PDF document." + + # Create a layouter object. + self.layouter = self.Layouter(self.options) + l = self.layouter + + # Loop over all tagged source lines, dissect them into + # Python entities ourself and let the layouter do the + # rendering. + splitCodeLines = string.split(srcCodeLines, '\n') + + ### Must also handle the case of outPath being sys.stdout!! + l.begin(inPath, len(splitCodeLines)) + l.beginDocument() + + for line in splitCodeLines: + l.beginLine() + self.formatLine(line) + l.endLine() + + l.endDocument() + l.end() + + + def writeFile(self, data, inPath=None, outPath=None): + "Write some data into a file." + + if inPath == sys.stdin: + self.outPath = sys.stdout + else: + if not outPath: + path = os.path.splitext(self.inPath)[0] + self.outPath = path + self.outFileExt + + self.writeData(data, inPath, outPath or self.outPath) + + + def process(self, inPath, outPath=None): + "The real 'action point' for working with Pretty-Printers." + + self.readFile(inPath) + self.writeFile(self.data, inPath, outPath) + + +class PythonPDFPrinter (PDFPrinter): + """A class to nicely format tagged Python source code. + + """ + + comm = 'COMMENT' + kw = 'KEYWORD' + strng = 'STRING' + ident = 'IDENT' + param = 'PARAMETER' + + outFileExt = '.pdf' + + + def __init__(self, options=None): + "Initialisation, calling self._didInit() at the end." + + if type(options) != type(None): + self.options = options + else: + self.options = Options() + + self._didInit() + + + def _didInit(self): + "Post-Initialising" + + # Define regular expression patterns. + s = self + comp = re.compile + + s.commPat = comp('(.*?)<' + s.comm + '>(.*?)</' + s.comm + '>(.*)') + s.kwPat = comp('(.*?)<' + s.kw + '>(.*?)</' + s.kw + '>(.*)') + s.identPat = comp('(.*?)<' + s.ident + '>(.*?)</' + s.ident + '>(.*)') + s.paramPat = comp('(.*?)<' + s.param + '>(.*?)</' + s.param + '>(.*)') + s.stPat = comp('(.*?)<' + s.strng + '>(.*?)</' + s.strng + '>(.*)') + s.strng1Pat = comp('(.*?)<' + s.strng + '>(.*)') + s.strng2Pat = comp('(.*?)</' + s.strng + '>(.*)') + s.allPat = comp('(.*)') + + cMatch = s.commPat.match + kMatch = s.kwPat.match + iMatch = s.identPat.match + pMatch = s.paramPat.match + sMatch = s.stPat.match + s1Match = s.strng1Pat.match + s2Match = s.strng2Pat.match + aMatch = s.allPat.match + + self.matchList = ((cMatch, 'Comm'), + (kMatch, 'Kw'), + (iMatch, 'Ident'), + (pMatch, 'Param'), + (sMatch, 'SimpleString'), + (s1Match, 'TripleStringBegin'), + (s2Match, 'TripleStringEnd'), + (aMatch, 'Rest')) + + self.Layouter = PythonPDFLayouter + + # Load fontifier. + self.tagFunc = loadFontifier(self.options) + + + ### + + def formatLine(self, line, eol=0): + "Format one line of Python source code." + + # Values for eol: -1:no-eol, 0:dunno-yet, 1:do-eol. + + for match, meth in self.matchList: + res = match(line) + + if res: + groups = res.groups() + method = getattr(self, '_format%s' % meth) + method(groups, eol) + break + + + def _formatIdent(self, groups, eol): + "Format a Python identifier." + + before, id, after = groups + self.formatLine(before, -1) + self.layouter.addIdent(id) + self.formatLine(after, eol) + + + def _formatParam(self, groups, eol): + "Format a Python parameter." + + before, param, after = groups + self.formatLine(before, -1) + self.layouter.addParam(before) + self.formatLine(after, eol) + + + def _formatSimpleString(self, groups, eol): + "Format a Python one-line string." + + before, s, after = groups + self.formatLine(before, -1) + self.layouter.addSimpleString(s) + self.formatLine(after, eol) + + + def _formatTripleStringBegin(self, groups, eol): + "Format a Python multi-line line string (1)." + + before, after = groups + self.formatLine(before, -1) + self.layouter.addTripleStringBegin() + self.formatLine(after, 1) + + + def _formatTripleStringEnd(self, groups, eol): + "Format a Python multi-line line string (2)." + + before, after = groups + self.layouter.addTripleStringEnd(before) + self.formatLine(after, 1) + + + def _formatKw(self, groups, eol): + "Format a Python keyword." + + before, kw, after = groups + self.formatLine(before, -1) + self.layouter.addKw(kw) + self.formatLine(after, eol) + + + def _formatComm(self, groups, eol): + "Format a Python comment." + + before, comment, after = groups + self.formatLine(before, -1) + self.layouter.addComm(comment) + self.formatLine(after, 1) + + + def _formatRest(self, groups, eol): + "Format a piece of a Python line w/o anything special." + + line = groups[0] + self.layouter.addRest(line, eol) + + + ### + + def writeData(self, srcCodeLines, inPath, outPath=None): + "Convert Python source code lines into a PDF document." + + # Create a layouter object. + self.layouter = self.Layouter(self.options) + l = self.layouter + + # Loop over all tagged source lines, dissect them into + # Python entities ourself and let the layouter do the + # rendering. + splitCodeLines = string.split(srcCodeLines, '\n') + l.begin(inPath, len(splitCodeLines)) + l.beginDocument() + + for line in splitCodeLines: + l.beginLine() + self.formatLine(line) + l.endLine() + + l.endDocument() + l.end() + + + def process(self, inPath, outPath=None): + "The real 'action point' for working with Pretty-Printers." + + self.readFile(inPath) + self.taggedData = self._fontify(self.data) + self.writeFile(self.taggedData, inPath, outPath) + + + ### Fontifying. + + def _fontify(self, pytext): + "" + + formats = { + 'rest' : ('', ''), + 'comment' : ('<%s>' % self.comm, '</%s>' % self.comm), + 'keyword' : ('<%s>' % self.kw, '</%s>' % self.kw), + 'parameter' : ('<%s>' % self.param, '</%s>' % self.param), + 'identifier' : ('<%s>' % self.ident, '</%s>' % self.ident), + 'string' : ('<%s>' % self.strng, '</%s>' % self.strng) } + + # Parse. + taglist = self.tagFunc(pytext) + + # Prepend special 'rest' tag. + taglist[:0] = [('rest', 0, len(pytext), None)] + + # Prepare splitting. + splits = [] + self._addSplits(splits, pytext, formats, taglist) + + # Do splitting & inserting. + splits.sort() + l = [] + li = 0 + + for ri, dummy, insert in splits: + if ri > li: + l.append(pytext[li:ri]) + + l.append(insert) + li = ri + + if li < len(pytext): + l.append(pytext[li:]) + + return string.join(l, '') + + + def _addSplits(self, splits, text, formats, taglist): + "" + + # Helper for fontify(). + for id, left, right, sublist in taglist: + + try: + pre, post = formats[id] + except KeyError: + # msg = 'Warning: no format ' + # msg = msg + 'for %s specified\n'%repr(id) + # sys.stderr.write(msg) + pre, post = '', '' + + if type(pre) != type(''): + pre = pre(text[left:right]) + + if type(post) != type(''): + post = post(text[left:right]) + + # len(splits) is a dummy used to make sorting stable. + splits.append((left, len(splits), pre)) + + if sublist: + self._addSplits(splits, text, formats, sublist) + + splits.append((right, len(splits), post)) + + +### Main + +def main(cmdline): + "Process command line as if it were sys.argv" + + # Create default options and initialize with argv + # from the command line. + options = Options() + options.updateWithContentsOfArgv(cmdline[1:]) + + # Print help message if desired, then exit. + if options.h or options.help: + print __doc__ + sys.exit() + + # Apply modest consistency checks and exit if needed. + cmdStr = string.join(cmdline, ' ') + find = string.find + if find(cmdStr, 'paperSize') >= 0 and find(cmdStr, 'paperFormat') >= 0: + details = "You can specify either paperSize or paperFormat, " + details = detail + "but not both!" + raise 'ValueError', details + + # Create PDF converter and pass options to it. + if options.input: + input = string.lower(options.input) + + if input == 'python': + P = PythonPDFPrinter + elif input == 'ascii': + P = PDFPrinter + else: + details = "Input file type must be 'python' or 'ascii'." + raise 'ValueError', details + + else: + P = PythonPDFPrinter + + p = P(options) + + # Display options if needed. + if options.v or options.verbose: + pass # p.options.display() + + # Start working. + verbose = options.v or options.verbose + + if options.stdout: + if len(options.files) > 1 and verbose: + print "Warning: will only convert first file on command line." + f = options.files[0] + p.process(f, sys.stdout) + else: + if verbose: + print 'py2pdf: working on:' + + for f in options.files: + try: + if verbose: + print ' %s' % f + if f != '-': + p.process(f) + else: + p.process(sys.stdin, sys.stdout) + except IOError: + if verbose: + print '(IOError!)', + + if verbose: + print + print 'Done.' + + +def process(*args, **kwargs): + "Provides a way of using py2pdf from within other Python scripts." + + noValOpts = 'h v verbose landscape stdout lineNum marcs help multiPage noOutline' + noValOpts = string.split(noValOpts, ' ') + + s = 'py2pdf.py' + for k, v in kwargs.items(): + if len(k) == 1: + s = s + ' -%s' % k + if k not in noValOpts: + s = s + ' %s' % v + elif len(k) > 1: + s = s + ' --%s' % k + if k not in noValOpts: + s = s + '=%s' % v + s = s + ' ' + string.join(args, ' ') + s = string.split(s, ' ') + + main(s) + + +### + +if __name__=='__main__': #NORUNTESTS + main(sys.argv)
\ No newline at end of file diff --git a/bin/reportlab/tools/py2pdf/vertpython.jpg b/bin/reportlab/tools/py2pdf/vertpython.jpg Binary files differnew file mode 100644 index 00000000000..8655230bf43 --- /dev/null +++ b/bin/reportlab/tools/py2pdf/vertpython.jpg |
