diff --git a/bin/reportlab/tools/README b/bin/reportlab/tools/README new file mode 100644 index 00000000000..f5f75d08de3 --- /dev/null +++ b/bin/reportlab/tools/README @@ -0,0 +1,10 @@ +This directory is the home of various ReportLab tools. +They are packaged such that they can be used more easily. + +Tool candidates are: + + - PythonPoint + - docpy.py + - graphdocpy.py + - ... + diff --git a/bin/reportlab/tools/__init__.py b/bin/reportlab/tools/__init__.py new file mode 100644 index 00000000000..da52b52d322 --- /dev/null +++ b/bin/reportlab/tools/__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/__init__.py diff --git a/bin/reportlab/tools/docco/README b/bin/reportlab/tools/docco/README new file mode 100644 index 00000000000..b23b5661348 --- /dev/null +++ b/bin/reportlab/tools/docco/README @@ -0,0 +1,8 @@ +This directory contains a number of tools to do with documentation and +documentation building. + +Some of these are our own internal tools which we use for building the +ReportLab documentation. Some tools are obsolete and will be removed +in due course. + +In the mean time, use at your own risk. diff --git a/bin/reportlab/tools/docco/__init__.py b/bin/reportlab/tools/docco/__init__.py new file mode 100644 index 00000000000..3a0b73da24d --- /dev/null +++ b/bin/reportlab/tools/docco/__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/docco/__init__.py diff --git a/bin/reportlab/tools/docco/codegrab.py b/bin/reportlab/tools/docco/codegrab.py new file mode 100644 index 00000000000..87a580a2a3e --- /dev/null +++ b/bin/reportlab/tools/docco/codegrab.py @@ -0,0 +1,228 @@ +#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/docco/codegrab.py +#codegrab.py +""" +This grabs various Python class, method and function +headers and their doc strings to include in documents +""" + +import imp +import types +import string +import os +import sys + +class Struct: + pass + +def getObjectsDefinedIn(modulename, directory=None): + """Returns two tuple of (functions, classes) defined + in the given module. 'directory' must be the directory + containing the script; modulename should not include + the .py suffix""" + + if directory: + searchpath = [directory] + else: + searchpath = sys.path # searches usual Python path + + #might be a package. If so, check the top level + #package is there, then recalculate the path needed + words = string.split(modulename, '.') + if len(words) > 1: + packagename = words[0] + packagefound = imp.find_module(packagename, searchpath) + assert packagefound, "Package %s not found" % packagename + (file, packagepath, description) = packagefound + #now the full path should be known, if it is in the + #package + + directory = apply(os.path.join, tuple([packagepath] + words[1:-1])) + modulename = words[-1] + searchpath = [directory] + + + + #find and import the module. + found = imp.find_module(modulename, searchpath) + assert found, "Module %s not found" % modulename + (file, pathname, description) = found + mod = imp.load_module(modulename, file, pathname, description) + + #grab the code too, minus trailing newlines + lines = open(pathname, 'r').readlines() + lines = map(string.rstrip, lines) + + result = Struct() + result.functions = [] + result.classes = [] + result.doc = mod.__doc__ + for name in dir(mod): + value = getattr(mod, name) + if type(value) is types.FunctionType: + path, file = os.path.split(value.func_code.co_filename) + root, ext = os.path.splitext(file) + #we're possibly interested in it + if root == modulename: + #it was defined here + funcObj = value + fn = Struct() + fn.name = name + fn.proto = getFunctionPrototype(funcObj, lines) + if funcObj.__doc__: + fn.doc = dedent(funcObj.__doc__) + else: + fn.doc = '(no documentation string)' + #is it official? + if name[0:1] == '_': + fn.status = 'private' + elif name[-1] in '0123456789': + fn.status = 'experimental' + else: + fn.status = 'official' + + result.functions.append(fn) + elif type(value) == types.ClassType: + if value.__module__ == modulename: + cl = Struct() + cl.name = name + if value.__doc__: + cl.doc = dedent(value.__doc__) + else: + cl.doc = "(no documentation string)" + + cl.bases = [] + for base in value.__bases__: + cl.bases.append(base.__name__) + if name[0:1] == '_': + cl.status = 'private' + elif name[-1] in '0123456789': + cl.status = 'experimental' + else: + cl.status = 'official' + + cl.methods = [] + #loop over dict finding methods defined here + # Q - should we show all methods? + # loop over dict finding methods defined here + items = value.__dict__.items() + items.sort() + for (key2, value2) in items: + if type(value2) <> types.FunctionType: + continue # not a method + elif os.path.splitext(value2.func_code.co_filename)[0] == modulename: + continue # defined in base class + else: + #we want it + meth = Struct() + meth.name = key2 + name2 = value2.func_code.co_name + meth.proto = getFunctionPrototype(value2, lines) + if name2!=key2: + meth.doc = 'pointer to '+name2 + meth.proto = string.replace(meth.proto,name2,key2) + else: + if value2.__doc__: + meth.doc = dedent(value2.__doc__) + else: + meth.doc = "(no documentation string)" + #is it official? + if key2[0:1] == '_': + meth.status = 'private' + elif key2[-1] in '0123456789': + meth.status = 'experimental' + else: + meth.status = 'official' + cl.methods.append(meth) + result.classes.append(cl) + return result + +def getFunctionPrototype(f, lines): + """Pass in the function object and list of lines; + it extracts the header as a multiline text block.""" + firstLineNo = f.func_code.co_firstlineno - 1 + lineNo = firstLineNo + brackets = 0 + while 1: + line = lines[lineNo] + for char in line: + if char == '(': + brackets = brackets + 1 + elif char == ')': + brackets = brackets - 1 + if brackets == 0: + break + else: + lineNo = lineNo + 1 + + usefulLines = lines[firstLineNo:lineNo+1] + return string.join(usefulLines, '\n') + + +def dedent(comment): + """Attempts to dedent the lines to the edge. Looks at no. + of leading spaces in line 2, and removes up to that number + of blanks from other lines.""" + commentLines = string.split(comment, '\n') + if len(commentLines) < 2: + cleaned = map(string.lstrip, commentLines) + else: + spc = 0 + for char in commentLines[1]: + if char in string.whitespace: + spc = spc + 1 + else: + break + #now check other lines + cleaned = [] + for line in commentLines: + for i in range(min(len(line),spc)): + if line[0] in string.whitespace: + line = line[1:] + cleaned.append(line) + return string.join(cleaned, '\n') + + + +def dumpDoc(modulename, directory=None): + """Test support. Just prints docco on the module + to standard output.""" + docco = getObjectsDefinedIn(modulename, directory) + print 'codegrab.py - ReportLab Documentation Utility' + print 'documenting', modulename + '.py' + print '-------------------------------------------------------' + print + if docco.functions == []: + print 'No functions found' + else: + print 'Functions:' + for f in docco.functions: + print f.proto + print ' ' + f.doc + + if docco.classes == []: + print 'No classes found' + else: + print 'Classes:' + for c in docco.classes: + print c.name + print ' ' + c.doc + for m in c.methods: + print m.proto # it is already indented in the file! + print ' ' + m.doc + print + +def test(m='reportlab.platypus.paragraph'): + dumpDoc(m) + +if __name__=='__main__': + import sys + print 'Path to search:' + for line in sys.path: + print ' ',line + M = sys.argv[1:] + if M==[]: + M.append('reportlab.platypus.paragraph') + for m in M: + test(m) \ No newline at end of file diff --git a/bin/reportlab/tools/docco/docpy.py b/bin/reportlab/tools/docco/docpy.py new file mode 100644 index 00000000000..75a77efe773 --- /dev/null +++ b/bin/reportlab/tools/docco/docpy.py @@ -0,0 +1,1247 @@ +#!/usr/bin/env python +#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/docco/docpy.py + +"""Generate documentation from live Python objects. + +This is an evolving module that allows to generate documentation +for python modules in an automated fashion. The idea is to take +live Python objects and inspect them in order to use as much mean- +ingful information as possible to write in some formatted way into +different types of documents. + +In principle a skeleton captures the gathered information and +makes it available via a certain API to formatters that use it +in whatever way they like to produce something of interest. The +API allows for adding behaviour in subclasses of these formatters, +such that, e.g. for certain classes it is possible to trigger +special actions like displaying a sample image of a class that +represents some graphical widget, say. + +Type the following for usage info: + + python docpy.py -h +""" + +# Much inspired by Ka-Ping Yee's htmldoc.py. +# Needs the inspect module. + +# Dinu Gherman + + +__version__ = '0.8' + + +import sys, os, re, types, string, getopt, copy, time +from string import find, join, split, replace, expandtabs, rstrip + +from reportlab.pdfgen import canvas +from reportlab.lib import colors +from reportlab.lib.units import inch, cm +from reportlab.lib.pagesizes import A4 +from reportlab.lib import enums +from reportlab.lib.enums import TA_CENTER, TA_LEFT +from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle +from reportlab.platypus.flowables import Flowable, Spacer +from reportlab.platypus.paragraph import Paragraph +from reportlab.platypus.flowables \ + import Flowable, Preformatted,Spacer, Image, KeepTogether, PageBreak +from reportlab.platypus.tableofcontents import TableOfContents +from reportlab.platypus.xpreformatted import XPreformatted +from reportlab.platypus.frames import Frame +from reportlab.platypus.doctemplate \ + import PageTemplate, BaseDocTemplate +from reportlab.platypus.tables import TableStyle, Table +import inspect + +#################################################################### +# +# Stuff needed for building PDF docs. +# +#################################################################### + + +def mainPageFrame(canvas, doc): + "The page frame used for all PDF documents." + + canvas.saveState() + + pageNumber = canvas.getPageNumber() + canvas.line(2*cm, A4[1]-2*cm, A4[0]-2*cm, A4[1]-2*cm) + canvas.line(2*cm, 2*cm, A4[0]-2*cm, 2*cm) + if pageNumber > 1: + canvas.setFont('Times-Roman', 12) + canvas.drawString(4 * inch, cm, "%d" % pageNumber) + if hasattr(canvas, 'headerLine'): # hackish + headerline = string.join(canvas.headerLine, ' \215 ') # bullet + canvas.drawString(2*cm, A4[1]-1.75*cm, headerline) + + canvas.setFont('Times-Roman', 8) + msg = "Generated with reportlab.lib.docpy. See http://www.reportlab.com!" + canvas.drawString(2*cm, 1.65*cm, msg) + + canvas.restoreState() + + +class MyTemplate(BaseDocTemplate): + "The document template used for all PDF documents." + + _invalidInitArgs = ('pageTemplates',) + + def __init__(self, filename, **kw): + frame1 = Frame(2.5*cm, 2.5*cm, 15*cm, 25*cm, id='F1') + self.allowSplitting = 0 + apply(BaseDocTemplate.__init__, (self, filename), kw) + self.addPageTemplates(PageTemplate('normal', [frame1], mainPageFrame)) + + + def afterFlowable(self, flowable): + "Takes care of header line, TOC and outline entries." + + if flowable.__class__.__name__ == 'Paragraph': + f = flowable + name7 = f.style.name[:7] + name8 = f.style.name[:8] + + # Build a list of heading parts. + # So far, this is the *last* item on the *previous* page... + if name7 == 'Heading' and not hasattr(self.canv, 'headerLine'): + self.canv.headerLine = [] + + if name8 == 'Heading0': + self.canv.headerLine = [f.text] # hackish + elif name8 == 'Heading1': + if len(self.canv.headerLine) == 2: + del self.canv.headerLine[-1] + elif len(self.canv.headerLine) == 3: + del self.canv.headerLine[-1] + del self.canv.headerLine[-1] + self.canv.headerLine.append(f.text) + elif name8 == 'Heading2': + if len(self.canv.headerLine) == 3: + del self.canv.headerLine[-1] + self.canv.headerLine.append(f.text) + + if name7 == 'Heading': + # Register TOC entries. + headLevel = int(f.style.name[7:]) + self.notify('TOCEntry', (headLevel, flowable.getPlainText(), self.page)) + + # Add PDF outline entries. + c = self.canv + title = f.text + key = str(hash(f)) + + try: + if headLevel == 0: + isClosed = 0 + else: + isClosed = 1 + + c.bookmarkPage(key) + c.addOutlineEntry(title, key, level=headLevel, + closed=isClosed) + except ValueError: + pass + + +#################################################################### +# +# Utility functions (Ka-Ping Yee). +# +#################################################################### + +def htmlescape(text): + "Escape special HTML characters, namely &, <, >." + return replace(replace(replace(text, '&', '&'), + '<', '<'), + '>', '>') + +def htmlrepr(object): + return htmlescape(repr(object)) + + +def defaultformat(object): + return '=' + htmlrepr(object) + + +def getdoc(object): + result = inspect.getdoc(object) + if not result: + try: + result = inspect.getcomments(object) + except: + pass + return result and rstrip(result) + '\n' or '' + + +def reduceDocStringLength(docStr): + "Return first line of a multiline string." + + return split(docStr, '\n')[0] + + +#################################################################### +# +# More utility functions +# +#################################################################### + +def makeHtmlSection(text, bgcolor='#FFA0FF'): + """Create HTML code for a section. + + This is usually a header for all classes or functions. +u """ + text = htmlescape(expandtabs(text)) + result = [] + result.append("""""") + result.append("""
""" % bgcolor) + result.append("""

%s

""" % text) + result.append("""
""") + result.append('') + + return join(result, '\n') + + +def makeHtmlSubSection(text, bgcolor='#AAA0FF'): + """Create HTML code for a subsection. + + This is usually a class or function name. + """ + text = htmlescape(expandtabs(text)) + result = [] + result.append("""""") + result.append("""
""" % bgcolor) + result.append("""

%s

""" % text) + result.append("""
""") + result.append('') + + return join(result, '\n') + + +def makeHtmlInlineImage(text): + """Create HTML code for an inline image. + """ + + return """%s""" % (text, text) + + +#################################################################### +# +# Core "standard" docpy classes +# +#################################################################### + +class PackageSkeleton0: + """A class collecting 'interesting' information about a package.""" + pass # Not yet! + + +class ModuleSkeleton0: + """A class collecting 'interesting' information about a module.""" + + def __init__(self): + # This is an ad-hoc, somewhat questionable 'data structure', + # but for the time being it serves its purpose and is fairly + # self-contained. + self.module = {} + self.functions = {} + self.classes = {} + + + # Might need more like this, later. + def getModuleName(self): + """Return the name of the module being treated.""" + + return self.module['name'] + + + # These inspect methods all rely on the inspect module. + def inspect(self, object): + """Collect information about a given object.""" + + self.moduleSpace = object + + # Very non-OO, left for later... + if inspect.ismodule(object): + self._inspectModule(object) + elif inspect.isclass(object): + self._inspectClass(object) + elif inspect.ismethod(object): + self._inspectMethod(object) + elif inspect.isfunction(object): + self._inspectFunction(object) + elif inspect.isbuiltin(object): + self._inspectBuiltin(object) + else: + msg = "Don't know how to document this kind of object." + raise TypeError, msg + + + def _inspectModule(self, object): + """Collect information about a given module object.""" + name = object.__name__ + + self.module['name'] = name + if hasattr(object, '__version__'): + self.module['version'] = object.__version__ + + cadr = lambda list: list[1] + modules = map(cadr, inspect.getmembers(object, inspect.ismodule)) + + classes, cdict = [], {} + for key, value in inspect.getmembers(object, inspect.isclass): + if (inspect.getmodule(value) or object) is object: + classes.append(value) + cdict[key] = cdict[value] = '#' + key + + functions, fdict = [], {} + for key, value in inspect.getmembers(object, inspect.isroutine): + #if inspect.isbuiltin(value) or inspect.getmodule(value) is object: + functions.append(value) + fdict[key] = '#-' + key + if inspect.isfunction(value): fdict[value] = fdict[key] + + for c in classes: + for base in c.__bases__: + key, modname = base.__name__, base.__module__ + if modname != name and sys.modules.has_key(modname): + module = sys.modules[modname] + if hasattr(module, key) and getattr(module, key) is base: + if not cdict.has_key(key): + cdict[key] = cdict[base] = modname + '.txt#' + key + +## doc = getdoc(object) or 'No doc string.' + doc = getdoc(object) + self.module['doc'] = doc + + if modules: + self.module['importedModules'] = map(lambda m:m.__name__, modules) + + if classes: + for item in classes: + self._inspectClass(item, fdict, cdict) + + if functions: + for item in functions: + self._inspectFunction(item, fdict, cdict) + + + def _inspectClass(self, object, functions={}, classes={}): + """Collect information about a given class object.""" + + name = object.__name__ + bases = object.__bases__ + results = [] + + if bases: + parents = [] + for base in bases: + parents.append(base) + + self.classes[name] = {} + if bases: + self.classes[name]['bases'] = parents + + methods, mdict = [], {} + for key, value in inspect.getmembers(object, inspect.ismethod): + methods.append(value) + mdict[key] = mdict[value] = '#' + name + '-' + key + + if methods: + if not self.classes[name].has_key('methods'): + self.classes[name]['methods'] = {} + for item in methods: + self._inspectMethod(item, functions, classes, mdict, name) + +## doc = getdoc(object) or 'No doc string.' + doc = getdoc(object) + self.classes[name]['doc'] = doc + + + def _inspectMethod(self, object, functions={}, classes={}, methods={}, clname=''): + """Collect information about a given method object.""" + + self._inspectFunction(object.im_func, functions, classes, methods, clname) + + + def _inspectFunction(self, object, functions={}, classes={}, methods={}, clname=''): + """Collect information about a given function object.""" + try: + args, varargs, varkw, defaults = inspect.getargspec(object) + argspec = inspect.formatargspec( + args, varargs, varkw, defaults, + defaultformat=defaultformat) + except TypeError: + argspec = '( ... )' + +## doc = getdoc(object) or 'No doc string.' + doc = getdoc(object) + + if object.__name__ == '': + decl = [' lambda ', argspec[1:-1]] + # print ' %s' % decl + # Do something with lambda functions as well... + # ... + else: + decl = object.__name__ + if not clname: + self.functions[object.__name__] = {'signature':argspec, 'doc':doc} + else: + theMethods = self.classes[clname]['methods'] + if not theMethods.has_key(object.__name__): + theMethods[object.__name__] = {} + + theMethod = theMethods[object.__name__] + theMethod['signature'] = argspec + theMethod['doc'] = doc + + + def _inspectBuiltin(self, object): + """Collect information about a given built-in.""" + + print object.__name__ + '( ... )' + + + def walk(self, formatter): + """Call event methods in a visiting formatter.""" + + s = self + f = formatter + + # The order is fixed, but could be made flexible + # with one more template method... + + # Module + modName = s.module['name'] + modDoc = s.module['doc'] + imported = s.module.get('importedModules', []) + imported.sort() + # f.indentLevel = f.indentLevel + 1 + f.beginModule(modName, modDoc, imported) + + # Classes + f.indentLevel = f.indentLevel + 1 + f.beginClasses(s.classes.keys()) + items = s.classes.items() + items.sort() + for k, v in items: + cDoc = s.classes[k]['doc'] + bases = s.classes[k].get('bases', []) + f.indentLevel = f.indentLevel + 1 + f.beginClass(k, cDoc, bases) + + # This if should move out of this method. + if not s.classes[k].has_key('methods'): + s.classes[k]['methods'] = {} + + # Methods + #f.indentLevel = f.indentLevel + 1 + f.beginMethods(s.classes[k]['methods'].keys()) + items = s.classes[k]['methods'].items() + items.sort() + for m, v in items: + mDoc = v['doc'] + sig = v['signature'] + f.indentLevel = f.indentLevel + 1 + f.beginMethod(m, mDoc, sig) + f.indentLevel = f.indentLevel - 1 + f.endMethod(m, mDoc, sig) + + #f.indentLevel = f.indentLevel - 1 + f.endMethods(s.classes[k]['methods'].keys()) + + f.indentLevel = f.indentLevel - 1 + f.endClass(k, cDoc, bases) + + # And what about attributes?! + + f.indentLevel = f.indentLevel - 1 + f.endClasses(s.classes.keys()) + + # Functions + f.indentLevel = f.indentLevel + 1 + f.beginFunctions(s.functions.keys()) + items = s.functions.items() + items.sort() + for k, v in items: + doc = v['doc'] + sig = v['signature'] + f.indentLevel = f.indentLevel + 1 + f.beginFunction(k, doc, sig) + f.indentLevel = f.indentLevel - 1 + f.endFunction(k, doc, sig) + f.indentLevel = f.indentLevel - 1 + f.endFunctions(s.functions.keys()) + + #f.indentLevel = f.indentLevel - 1 + f.endModule(modName, modDoc, imported) + + # Constants?! + + +#################################################################### +# +# Core "standard" docpy document builders +# +#################################################################### + +class DocBuilder0: + """An abstract class to document the skeleton of a Python module. + + Instances take a skeleton instance s and call their s.walk() + method. The skeleton, in turn, will walk over its tree structure + while generating events and calling equivalent methods from a + specific interface (begin/end methods). + """ + + fileSuffix = None + + def __init__(self, skeleton=None): + self.skeleton = skeleton + self.packageName = None + self.indentLevel = 0 + + + def write(self, skeleton=None): + if skeleton: + self.skeleton = skeleton + self.skeleton.walk(self) + + + # Event-method API, called by associated skeleton instances. + # In fact, these should raise a NotImplementedError, but for now we + # just don't do anything here. + + # The following four methods are *not* called by skeletons! + def begin(self, name='', typ=''): pass + def end(self): pass + + # Methods for packaging should move into a future PackageSkeleton... + def beginPackage(self, name): + self.packageName = name + + def endPackage(self, name): + pass + + # Only this subset is really called by associated skeleton instances. + + def beginModule(self, name, doc, imported): pass + def endModule(self, name, doc, imported): pass + + def beginClasses(self, names): pass + def endClasses(self, names): pass + + def beginClass(self, name, doc, bases): pass + def endClass(self, name, doc, bases): pass + + def beginMethods(self, names): pass + def endMethods(self, names): pass + + def beginMethod(self, name, doc, sig): pass + def endMethod(self, name, doc, sig): pass + + def beginFunctions(self, names): pass + def endFunctions(self, names): pass + + def beginFunction(self, name, doc, sig): pass + def endFunction(self, name, doc, sig): pass + + +class AsciiDocBuilder0(DocBuilder0): + """Document the skeleton of a Python module in ASCII format. + + The output will be an ASCII file with nested lines representing + the hiearchical module structure. + + Currently, no doc strings are listed. + """ + + fileSuffix = '.txt' + outLines = [] + indentLabel = ' ' + + def end(self): + # This if should move into DocBuilder0... + if self.packageName: + self.outPath = self.packageName + self.fileSuffix + elif self.skeleton: + self.outPath = self.skeleton.getModuleName() + self.fileSuffix + else: + self.outPath = '' + + if self.outPath: + file = open(self.outPath, 'w') + for line in self.outLines: + file.write(line + '\n') + file.close() + + + def beginPackage(self, name): + DocBuilder0.beginPackage(self, name) + lev, label = self.indentLevel, self.indentLabel + self.outLines.append('%sPackage: %s' % (lev*label, name)) + self.outLines.append('') + + + def beginModule(self, name, doc, imported): + append = self.outLines.append + lev, label = self.indentLevel, self.indentLabel + self.outLines.append('%sModule: %s' % (lev*label, name)) +## self.outLines.append('%s%s' % ((lev+1)*label, reduceDocStringLength(doc))) + append('') + + if imported: + self.outLines.append('%sImported' % ((lev+1)*label)) + append('') + for m in imported: + self.outLines.append('%s%s' % ((lev+2)*label, m)) + append('') + + + def beginClasses(self, names): + if names: + lev, label = self.indentLevel, self.indentLabel + self.outLines.append('%sClasses' % (lev*label)) + self.outLines.append('') + + + def beginClass(self, name, doc, bases): + append = self.outLines.append + lev, label = self.indentLevel, self.indentLabel + + if bases: + bases = map(lambda b:b.__name__, bases) # hack + append('%s%s(%s)' % (lev*label, name, join(bases, ', '))) + else: + append('%s%s' % (lev*label, name)) + return + +## append('%s%s' % ((lev+1)*label, reduceDocStringLength(doc))) + self.outLines.append('') + + + def endClass(self, name, doc, bases): + self.outLines.append('') + + + def beginMethod(self, name, doc, sig): + append = self.outLines.append + lev, label = self.indentLevel, self.indentLabel + append('%s%s%s' % (lev*label, name, sig)) +## append('%s%s' % ((lev+1)*label, reduceDocStringLength(doc))) +## append('') + + + def beginFunctions(self, names): + if names: + lev, label = self.indentLevel, self.indentLabel + self.outLines.append('%sFunctions' % (lev*label)) + self.outLines.append('') + + + def endFunctions(self, names): + self.outLines.append('') + + + def beginFunction(self, name, doc, sig): + append = self.outLines.append + lev, label = self.indentLevel, self.indentLabel + self.outLines.append('%s%s%s' % (lev*label, name, sig)) +## append('%s%s' % ((lev+1)*label, reduceDocStringLength(doc))) +## append('') + + +class HtmlDocBuilder0(DocBuilder0): + "A class to write the skeleton of a Python source in HTML format." + + fileSuffix = '.html' + outLines = [] + + def begin(self, name='', typ=''): + self.outLines.append("""""") + self.outLines.append("""""") + + + def end(self): + if self.packageName: + self.outPath = self.packageName + self.fileSuffix + elif self.skeleton: + self.outPath = self.skeleton.getModuleName() + self.fileSuffix + else: + self.outPath = '' + + if self.outPath: + file = open(self.outPath, 'w') + self.outLines.append('') + for line in self.outLines: + file.write(line + '\n') + file.close() + + + def beginPackage(self, name): + DocBuilder0.beginPackage(self, name) + + self.outLines.append("""%s""" % name) + self.outLines.append("""""") + self.outLines.append("""

%s

""" % name) + self.outLines.append('') + + + def beginModule(self, name, doc, imported): + if not self.packageName: + self.outLines.append("""%s""" % name) + self.outLines.append("""""") + + self.outLines.append("""

%s

""" % name) + self.outLines.append('') + for line in split(doc, '\n'): + self.outLines.append("""%s""" % htmlescape(line)) + self.outLines.append('
') + self.outLines.append('') + + if imported: + self.outLines.append(makeHtmlSection('Imported Modules')) + self.outLines.append("""""") + + + def beginClasses(self, names): + self.outLines.append(makeHtmlSection('Classes')) + + + def beginClass(self, name, doc, bases): + DocBuilder0.beginClass(self, name, doc, bases) + +## # Keep an eye on the base classes. +## self.currentBaseClasses = bases + + if bases: + bases = map(lambda b:b.__name__, bases) # hack + self.outLines.append(makeHtmlSubSection('%s(%s)' % (name, join(bases, ', ')))) + else: + self.outLines.append(makeHtmlSubSection('%s' % name)) + for line in split(doc, '\n'): + self.outLines.append("""%s""" % htmlescape(line)) + self.outLines.append('
') + + self.outLines.append('') + + + def beginMethods(self, names): + pass +## if names: +## self.outLines.append('

Method Interface

') +## self.outLines.append('') + + + def beginMethod(self, name, doc, sig): + self.beginFunction(name, doc, sig) + + + def beginFunctions(self, names): + self.outLines.append(makeHtmlSection('Functions')) + + + def beginFunction(self, name, doc, sig): + append = self.outLines.append + append("""
%s%s
""" % (name, sig)) + append('') + for line in split(doc, '\n'): + append("""
%s
""" % htmlescape(line)) + append('
') + append('
') + append('') + + +class PdfDocBuilder0(DocBuilder0): + "Document the skeleton of a Python module in PDF format." + + fileSuffix = '.pdf' + + def makeHeadingStyle(self, level, typ=None, doc=''): + "Make a heading style for different types of module content." + + if typ in ('package', 'module', 'class'): + style = ParagraphStyle(name='Heading'+str(level), + fontName = 'Courier-Bold', + fontSize=14, + leading=18, + spaceBefore=12, + spaceAfter=6) + elif typ in ('method', 'function'): + if doc: + style = ParagraphStyle(name='Heading'+str(level), + fontName = 'Courier-Bold', + fontSize=12, + leading=18, + firstLineIndent=-18, + leftIndent=36, + spaceBefore=0, + spaceAfter=-3) + else: + style = ParagraphStyle(name='Heading'+str(level), + fontName = 'Courier-Bold', + fontSize=12, + leading=18, + firstLineIndent=-18, + leftIndent=36, + spaceBefore=0, + spaceAfter=0) + + else: + style = ParagraphStyle(name='Heading'+str(level), + fontName = 'Times-Bold', + fontSize=14, + leading=18, + spaceBefore=12, + spaceAfter=6) + + return style + + + def begin(self, name='', typ=''): + styleSheet = getSampleStyleSheet() + self.code = styleSheet['Code'] + self.bt = styleSheet['BodyText'] + self.story = [] + + # Cover page + t = time.gmtime(time.time()) + timeString = time.strftime("%Y-%m-%d %H:%M", t) + self.story.append(Paragraph('Documentation for %s "%s"' % (typ, name), self.bt)) + self.story.append(Paragraph('Generated by: docpy.py version %s' % __version__, self.bt)) + self.story.append(Paragraph('Date generated: %s' % timeString, self.bt)) + self.story.append(Paragraph('Format: PDF', self.bt)) + self.story.append(PageBreak()) + + # Table of contents + toc = TableOfContents() + self.story.append(toc) + self.story.append(PageBreak()) + + + def end(self): + if self.outPath is not None: + pass + elif self.packageName: + self.outPath = self.packageName + self.fileSuffix + elif self.skeleton: + self.outPath = self.skeleton.getModuleName() + self.fileSuffix + else: + self.outPath = '' + print 'output path is %s' % self.outPath + if self.outPath: + doc = MyTemplate(self.outPath) + doc.multiBuild(self.story) + + + def beginPackage(self, name): + DocBuilder0.beginPackage(self, name) + story = self.story + story.append(Paragraph(name, self.makeHeadingStyle(self.indentLevel, 'package'))) + + + def beginModule(self, name, doc, imported): + story = self.story + bt = self.bt + story.append(Paragraph(name, self.makeHeadingStyle(self.indentLevel, 'module'))) + if doc: + story.append(XPreformatted(htmlescape(doc), bt)) + story.append(XPreformatted('', bt)) + + if imported: + story.append(Paragraph('Imported modules', self.makeHeadingStyle(self.indentLevel + 1))) + for m in imported: + p = Paragraph('\201 %s' % m, bt) + p.style.bulletIndent = 10 + p.style.leftIndent = 18 + story.append(p) + + + def endModule(self, name, doc, imported): + DocBuilder0.endModule(self, name, doc, imported) + self.story.append(PageBreak()) + + + def beginClasses(self, names): + self.story.append(Paragraph('Classes', self.makeHeadingStyle(self.indentLevel))) + + + def beginClass(self, name, doc, bases): + bt = self.bt + story = self.story + if bases: + bases = map(lambda b:b.__name__, bases) # hack + story.append(Paragraph('%s(%s)' % (name, join(bases, ', ')), self.makeHeadingStyle(self.indentLevel, 'class'))) + else: + story.append(Paragraph(name, self.makeHeadingStyle(self.indentLevel, 'class'))) + + if doc: + story.append(XPreformatted(htmlescape(doc), bt)) + story.append(XPreformatted('', bt)) + + + def beginMethod(self, name, doc, sig): + bt = self.bt + story = self.story + story.append(Paragraph(name+sig, self.makeHeadingStyle(self.indentLevel, 'method', doc))) + if doc: + story.append(XPreformatted(htmlescape(doc), bt)) + story.append(XPreformatted('', bt)) + + + def beginFunctions(self, names): + if names: + self.story.append(Paragraph('Functions', self.makeHeadingStyle(self.indentLevel))) + + + def beginFunction(self, name, doc, sig): + bt = self.bt + story = self.story + story.append(Paragraph(name+sig, self.makeHeadingStyle(self.indentLevel, 'function'))) + if doc: + story.append(XPreformatted(htmlescape(doc), bt)) + story.append(XPreformatted('', bt)) + + +class UmlPdfDocBuilder0(PdfDocBuilder0): + "Document the skeleton of a Python module with UML class diagrams." + + fileSuffix = '.pdf' + + def begin(self, name='', typ=''): + styleSheet = getSampleStyleSheet() + self.h1 = styleSheet['Heading1'] + self.h2 = styleSheet['Heading2'] + self.h3 = styleSheet['Heading3'] + self.code = styleSheet['Code'] + self.bt = styleSheet['BodyText'] + self.story = [] + self.classCompartment = '' + self.methodCompartment = [] + + + def beginModule(self, name, doc, imported): + story = self.story + h1, h2, h3, bt = self.h1, self.h2, self.h3, self.bt + styleSheet = getSampleStyleSheet() + bt1 = styleSheet['BodyText'] + + story.append(Paragraph(name, h1)) + story.append(XPreformatted(doc, bt1)) + + if imported: + story.append(Paragraph('Imported modules', self.makeHeadingStyle(self.indentLevel + 1))) + for m in imported: + p = Paragraph('\201 %s' % m, bt1) + p.style.bulletIndent = 10 + p.style.leftIndent = 18 + story.append(p) + + + def endModule(self, name, doc, imported): + self.story.append(PageBreak()) + PdfDocBuilder0.endModule(self, name, doc, imported) + + + def beginClasses(self, names): + h1, h2, h3, bt = self.h1, self.h2, self.h3, self.bt + if names: + self.story.append(Paragraph('Classes', h2)) + + + def beginClass(self, name, doc, bases): + self.classCompartment = '' + self.methodCompartment = [] + + if bases: + bases = map(lambda b:b.__name__, bases) # hack + self.classCompartment = '%s(%s)' % (name, join(bases, ', ')) + else: + self.classCompartment = name + + + def endClass(self, name, doc, bases): + h1, h2, h3, bt, code = self.h1, self.h2, self.h3, self.bt, self.code + styleSheet = getSampleStyleSheet() + bt1 = styleSheet['BodyText'] + story = self.story + + # Use only the first line of the class' doc string -- + # no matter how long! (Do the same later for methods) + classDoc = reduceDocStringLength(doc) + + tsa = tableStyleAttributes = [] + + # Make table with class and method rows + # and add it to the story. + p = Paragraph('%s' % self.classCompartment, bt) + p.style.alignment = TA_CENTER + rows = [(p,)] + # No doc strings, now... + # rows = rows + [(Paragraph('%s' % classDoc, bt1),)] + lenRows = len(rows) + tsa.append(('BOX', (0,0), (-1,lenRows-1), 0.25, colors.black)) + for name, doc, sig in self.methodCompartment: + nameAndSig = Paragraph('%s%s' % (name, sig), bt1) + rows.append((nameAndSig,)) + # No doc strings, now... + # docStr = Paragraph('%s' % reduceDocStringLength(doc), bt1) + # rows.append((docStr,)) + tsa.append(('BOX', (0,lenRows), (-1,-1), 0.25, colors.black)) + t = Table(rows, (12*cm,)) + tableStyle = TableStyle(tableStyleAttributes) + t.setStyle(tableStyle) + self.story.append(t) + self.story.append(Spacer(1*cm, 1*cm)) + + + def beginMethod(self, name, doc, sig): + self.methodCompartment.append((name, doc, sig)) + + + def beginFunctions(self, names): + h1, h2, h3, bt = self.h1, self.h2, self.h3, self.bt + if names: + self.story.append(Paragraph('Functions', h2)) + self.classCompartment = chr(171) + ' Module-Level Functions ' + chr(187) + self.methodCompartment = [] + + + def beginFunction(self, name, doc, sig): + self.methodCompartment.append((name, doc, sig)) + + + def endFunctions(self, names): + h1, h2, h3, bt, code = self.h1, self.h2, self.h3, self.bt, self.code + styleSheet = getSampleStyleSheet() + bt1 = styleSheet['BodyText'] + story = self.story + if not names: + return + + tsa = tableStyleAttributes = [] + + # Make table with class and method rows + # and add it to the story. + p = Paragraph('%s' % self.classCompartment, bt) + p.style.alignment = TA_CENTER + rows = [(p,)] + lenRows = len(rows) + tsa.append(('BOX', (0,0), (-1,lenRows-1), 0.25, colors.black)) + for name, doc, sig in self.methodCompartment: + nameAndSig = Paragraph('%s%s' % (name, sig), bt1) + rows.append((nameAndSig,)) + # No doc strings, now... + # docStr = Paragraph('%s' % reduceDocStringLength(doc), bt1) + # rows.append((docStr,)) + tsa.append(('BOX', (0,lenRows), (-1,-1), 0.25, colors.black)) + t = Table(rows, (12*cm,)) + tableStyle = TableStyle(tableStyleAttributes) + t.setStyle(tableStyle) + self.story.append(t) + self.story.append(Spacer(1*cm, 1*cm)) + + +#################################################################### +# +# Main +# +#################################################################### + +def printUsage(): + """docpy.py - Automated documentation for Python source code. + +Usage: python docpy.py [options] + + [options] + -h Print this help message. + + -f name Use the document builder indicated by 'name', + e.g. Ascii, Html, Pdf (default), UmlPdf. + + -m module Generate document for module named 'module' + (default is 'docpy'). + 'module' may follow any of these forms: + - docpy.py + - docpy + - c:\\test\\docpy + and can be any of these: + - standard Python modules + - modules in the Python search path + - modules in the current directory + + -p package Generate document for package named 'package'. + 'package' may follow any of these forms: + - reportlab + - reportlab.platypus + - c:\\test\\reportlab + and can be any of these: + - standard Python packages (?) + - packages in the Python search path + - packages in the current directory + + -s Silent mode (default is unset). + +Examples: + + python docpy.py -h + python docpy.py -m docpy.py -f Ascii + python docpy.py -m string -f Html + python docpy.py -m signsandsymbols.py -f Pdf + python docpy.py -p reportlab.platypus -f UmlPdf + python docpy.py -p reportlab.lib -s -f UmlPdf +""" + + +def documentModule0(pathOrName, builder, opts={}): + """Generate documentation for one Python file in some format. + + This handles Python standard modules like string, custom modules + on the Python search path like e.g. docpy as well as modules + specified with their full path like C:/tmp/junk.py. + + The doc file will always be saved in the current directory with + a basename equal to that of the module, e.g. docpy. + """ + + cwd = os.getcwd() + + # Append directory to Python search path if we get one. + dirName = os.path.dirname(pathOrName) + if dirName: + sys.path.append(dirName) + + # Remove .py extension from module name. + if pathOrName[-3:] == '.py': + modname = pathOrName[:-3] + else: + modname = pathOrName + + # Remove directory paths from module name. + if dirName: + modname = os.path.basename(modname) + + # Load the module. + try: + module = __import__(modname) + except: + print 'Failed to import %s.' % modname + os.chdir(cwd) + return + + # Do the real documentation work. + s = ModuleSkeleton0() + s.inspect(module) + builder.write(s) + + # Remove appended directory from Python search path if we got one. + if dirName: + del sys.path[-1] + + os.chdir(cwd) + + +def _packageWalkCallback((builder, opts), dirPath, files): + "A callback function used when waking over a package tree." + + # Skip __init__ files. + files = filter(lambda f:f != '__init__.py', files) + + files = filter(lambda f:f[-3:] == '.py', files) + for f in files: + path = os.path.join(dirPath, f) + if not opts.get('isSilent', 0): + print path + builder.indentLevel = builder.indentLevel + 1 + documentModule0(path, builder) + builder.indentLevel = builder.indentLevel - 1 + + +def documentPackage0(pathOrName, builder, opts={}): + """Generate documentation for one Python package in some format. + + 'pathOrName' can be either a filesystem path leading to a Python + package or package name whose path will be resolved by importing + the top-level module. + + The doc file will always be saved in the current directory with + a basename equal to that of the package, e.g. reportlab.lib. + """ + + # Did we get a package path with OS-dependant seperators...? + if os.sep in pathOrName: + path = pathOrName + name = os.path.splitext(os.path.basename(path))[0] + # ... or rather a package name? + else: + name = pathOrName + package = __import__(name) + # Some special care needed for dotted names. + if '.' in name: + subname = 'package' + name[find(name, '.'):] + package = eval(subname) + path = os.path.dirname(package.__file__) + + cwd = os.getcwd() + builder.beginPackage(name) + os.path.walk(path, _packageWalkCallback, (builder, opts)) + builder.endPackage(name) + os.chdir(cwd) + + +def main(): + "Handle command-line options and trigger corresponding action." + + opts, args = getopt.getopt(sys.argv[1:], 'hsf:m:p:') + + # Make an options dictionary that is easier to use. + optsDict = {} + for k, v in opts: + optsDict[k] = v + hasOpt = optsDict.has_key + + # On -h print usage and exit immediately. + if hasOpt('-h'): + print printUsage.__doc__ + sys.exit(0) + + # On -s set silent mode. + isSilent = hasOpt('-s') + + # On -f set the appropriate DocBuilder to use or a default one. + builderClassName = optsDict.get('-f', 'Pdf') + 'DocBuilder0' + builder = eval(builderClassName + '()') + + # Set default module or package to document. + if not hasOpt('-p') and not hasOpt('-m'): + optsDict['-m'] = 'docpy' + + # Save a few options for further use. + options = {'isSilent':isSilent} + + # Now call the real documentation functions. + if hasOpt('-m'): + nameOrPath = optsDict['-m'] + if not isSilent: + print "Generating documentation for module %s..." % nameOrPath + builder.begin(name=nameOrPath, typ='module') + documentModule0(nameOrPath, builder, options) + elif hasOpt('-p'): + nameOrPath = optsDict['-p'] + if not isSilent: + print "Generating documentation for package %s..." % nameOrPath + builder.begin(name=nameOrPath, typ='package') + documentPackage0(nameOrPath, builder, options) + builder.end() + + if not isSilent: + print "Saved %s." % builder.outPath + + +if __name__ == '__main__': + main() diff --git a/bin/reportlab/tools/docco/examples.py b/bin/reportlab/tools/docco/examples.py new file mode 100644 index 00000000000..a4eeec54d07 --- /dev/null +++ b/bin/reportlab/tools/docco/examples.py @@ -0,0 +1,851 @@ +#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/docco/examples.py +import string + +testannotations=""" +def annotations(canvas): + from reportlab.lib.units import inch + canvas.drawString(inch, 2.5*inch, + "setAuthor, setTitle, setSubject have no visible effect") + canvas.drawString(inch, inch, "But if you are viewing this document dynamically") + canvas.drawString(inch, 0.5*inch, "please look at File/Document Info") + canvas.setAuthor("the ReportLab Team") + canvas.setTitle("ReportLab PDF Generation User Guide") + canvas.setSubject("How to Generate PDF files using the ReportLab modules") +""" + +# magic function making module + +test1 = """ +def f(a,b): + print "it worked", a, b + return a+b +""" + +test2 = """ +def g(n): + if n==0: return 1 + else: return n*g(n-1) + """ + +testhello = """ +def hello(c): + from reportlab.lib.units import inch + # move the origin up and to the left + c.translate(inch,inch) + # define a large font + c.setFont("Helvetica", 14) + # choose some colors + c.setStrokeColorRGB(0.2,0.5,0.3) + c.setFillColorRGB(1,0,1) + # draw some lines + c.line(0,0,0,1.7*inch) + c.line(0,0,1*inch,0) + # draw a rectangle + c.rect(0.2*inch,0.2*inch,1*inch,1.5*inch, fill=1) + # make text go straight up + c.rotate(90) + # change color + c.setFillColorRGB(0,0,0.77) + # say hello (note after rotate the y coord needs to be negative!) + c.drawString(0.3*inch, -inch, "Hello World") +""" + +testcoords = """ +def coords(canvas): + from reportlab.lib.units import inch + from reportlab.lib.colors import pink, black, red, blue, green + c = canvas + c.setStrokeColor(pink) + c.grid([inch, 2*inch, 3*inch, 4*inch], [0.5*inch, inch, 1.5*inch, 2*inch, 2.5*inch]) + c.setStrokeColor(black) + c.setFont("Times-Roman", 20) + c.drawString(0,0, "(0,0) the Origin") + c.drawString(2.5*inch, inch, "(2.5,1) in inches") + c.drawString(4*inch, 2.5*inch, "(4, 2.5)") + c.setFillColor(red) + c.rect(0,2*inch,0.2*inch,0.3*inch, fill=1) + c.setFillColor(green) + c.circle(4.5*inch, 0.4*inch, 0.2*inch, fill=1) +""" + +testtranslate = """ +def translate(canvas): + from reportlab.lib.units import cm + canvas.translate(2.3*cm, 0.3*cm) + coords(canvas) + """ + +testscale = """ +def scale(canvas): + canvas.scale(0.75, 0.5) + coords(canvas) +""" + +testscaletranslate = """ +def scaletranslate(canvas): + from reportlab.lib.units import inch + canvas.setFont("Courier-BoldOblique", 12) + # save the state + canvas.saveState() + # scale then translate + canvas.scale(0.3, 0.5) + canvas.translate(2.4*inch, 1.5*inch) + canvas.drawString(0, 2.7*inch, "Scale then translate") + coords(canvas) + # forget the scale and translate... + canvas.restoreState() + # translate then scale + canvas.translate(2.4*inch, 1.5*inch) + canvas.scale(0.3, 0.5) + canvas.drawString(0, 2.7*inch, "Translate then scale") + coords(canvas) +""" + +testmirror = """ +def mirror(canvas): + from reportlab.lib.units import inch + canvas.translate(5.5*inch, 0) + canvas.scale(-1.0, 1.0) + coords(canvas) +""" + +testcolors = """ +def colors(canvas): + from reportlab.lib import colors + from reportlab.lib.units import inch + black = colors.black + y = x = 0; dy=inch*3/4.0; dx=inch*5.5/5; w=h=dy/2; rdx=(dx-w)/2 + rdy=h/5.0; texty=h+2*rdy + canvas.setFont("Helvetica",10) + for [namedcolor, name] in ( + [colors.lavenderblush, "lavenderblush"], + [colors.lawngreen, "lawngreen"], + [colors.lemonchiffon, "lemonchiffon"], + [colors.lightblue, "lightblue"], + [colors.lightcoral, "lightcoral"]): + canvas.setFillColor(namedcolor) + canvas.rect(x+rdx, y+rdy, w, h, fill=1) + canvas.setFillColor(black) + canvas.drawCentredString(x+dx/2, y+texty, name) + x = x+dx + y = y + dy; x = 0 + for rgb in [(1,0,0), (0,1,0), (0,0,1), (0.5,0.3,0.1), (0.4,0.5,0.3)]: + r,g,b = rgb + canvas.setFillColorRGB(r,g,b) + canvas.rect(x+rdx, y+rdy, w, h, fill=1) + canvas.setFillColor(black) + canvas.drawCentredString(x+dx/2, y+texty, "r%s g%s b%s"%rgb) + x = x+dx + y = y + dy; x = 0 + for cmyk in [(1,0,0,0), (0,1,0,0), (0,0,1,0), (0,0,0,1), (0,0,0,0)]: + c,m,y1,k = cmyk + canvas.setFillColorCMYK(c,m,y1,k) + canvas.rect(x+rdx, y+rdy, w, h, fill=1) + canvas.setFillColor(black) + canvas.drawCentredString(x+dx/2, y+texty, "c%s m%s y%s k%s"%cmyk) + x = x+dx + y = y + dy; x = 0 + for gray in (0.0, 0.25, 0.50, 0.75, 1.0): + canvas.setFillGray(gray) + canvas.rect(x+rdx, y+rdy, w, h, fill=1) + canvas.setFillColor(black) + canvas.drawCentredString(x+dx/2, y+texty, "gray: %s"%gray) + x = x+dx +""" + +testspumoni = """ +def spumoni(canvas): + from reportlab.lib.units import inch + from reportlab.lib.colors import pink, green, brown, white + x = 0; dx = 0.4*inch + for i in range(4): + for color in (pink, green, brown): + canvas.setFillColor(color) + canvas.rect(x,0,dx,3*inch,stroke=0,fill=1) + x = x+dx + canvas.setFillColor(white) + canvas.setStrokeColor(white) + canvas.setFont("Helvetica-Bold", 85) + canvas.drawCentredString(2.75*inch, 1.3*inch, "SPUMONI") +""" + +testspumoni2 = """ +def spumoni2(canvas): + from reportlab.lib.units import inch + from reportlab.lib.colors import pink, green, brown, white, black + # draw the previous drawing + spumoni(canvas) + # now put an ice cream cone on top of it: + # first draw a triangle (ice cream cone) + p = canvas.beginPath() + xcenter = 2.75*inch + radius = 0.45*inch + p.moveTo(xcenter-radius, 1.5*inch) + p.lineTo(xcenter+radius, 1.5*inch) + p.lineTo(xcenter, 0) + canvas.setFillColor(brown) + canvas.setStrokeColor(black) + canvas.drawPath(p, fill=1) + # draw some circles (scoops) + y = 1.5*inch + for color in (pink, green, brown): + canvas.setFillColor(color) + canvas.circle(xcenter, y, radius, fill=1) + y = y+radius +""" + +testbezier = """ +def bezier(canvas): + from reportlab.lib.colors import yellow, green, red, black + from reportlab.lib.units import inch + i = inch + d = i/4 + # define the bezier curve control points + x1,y1, x2,y2, x3,y3, x4,y4 = d,1.5*i, 1.5*i,d, 3*i,d, 5.5*i-d,3*i-d + # draw a figure enclosing the control points + canvas.setFillColor(yellow) + p = canvas.beginPath() + p.moveTo(x1,y1) + for (x,y) in [(x2,y2), (x3,y3), (x4,y4)]: + p.lineTo(x,y) + canvas.drawPath(p, fill=1, stroke=0) + # draw the tangent lines + canvas.setLineWidth(inch*0.1) + canvas.setStrokeColor(green) + canvas.line(x1,y1,x2,y2) + canvas.setStrokeColor(red) + canvas.line(x3,y3,x4,y4) + # finally draw the curve + canvas.setStrokeColor(black) + canvas.bezier(x1,y1, x2,y2, x3,y3, x4,y4) +""" + +testbezier2 = """ +def bezier2(canvas): + from reportlab.lib.colors import yellow, green, red, black + from reportlab.lib.units import inch + # make a sequence of control points + xd,yd = 5.5*inch/2, 3*inch/2 + xc,yc = xd,yd + dxdy = [(0,0.33), (0.33,0.33), (0.75,1), (0.875,0.875), + (0.875,0.875), (1,0.75), (0.33,0.33), (0.33,0)] + pointlist = [] + for xoffset in (1,-1): + yoffset = xoffset + for (dx,dy) in dxdy: + px = xc + xd*xoffset*dx + py = yc + yd*yoffset*dy + pointlist.append((px,py)) + yoffset = -xoffset + for (dy,dx) in dxdy: + px = xc + xd*xoffset*dx + py = yc + yd*yoffset*dy + pointlist.append((px,py)) + # draw tangent lines and curves + canvas.setLineWidth(inch*0.1) + while pointlist: + [(x1,y1),(x2,y2),(x3,y3),(x4,y4)] = pointlist[:4] + del pointlist[:4] + canvas.setLineWidth(inch*0.1) + canvas.setStrokeColor(green) + canvas.line(x1,y1,x2,y2) + canvas.setStrokeColor(red) + canvas.line(x3,y3,x4,y4) + # finally draw the curve + canvas.setStrokeColor(black) + canvas.bezier(x1,y1, x2,y2, x3,y3, x4,y4) +""" + +testpencil = """ +def pencil(canvas, text="No.2"): + from reportlab.lib.colors import yellow, red, black,white + from reportlab.lib.units import inch + u = inch/10.0 + canvas.setStrokeColor(black) + canvas.setLineWidth(4) + # draw erasor + canvas.setFillColor(red) + canvas.circle(30*u, 5*u, 5*u, stroke=1, fill=1) + # draw all else but the tip (mainly rectangles with different fills) + canvas.setFillColor(yellow) + canvas.rect(10*u,0,20*u,10*u, stroke=1, fill=1) + canvas.setFillColor(black) + canvas.rect(23*u,0,8*u,10*u,fill=1) + canvas.roundRect(14*u, 3.5*u, 8*u, 3*u, 1.5*u, stroke=1, fill=1) + canvas.setFillColor(white) + canvas.rect(25*u,u,1.2*u,8*u, fill=1,stroke=0) + canvas.rect(27.5*u,u,1.2*u,8*u, fill=1, stroke=0) + canvas.setFont("Times-Roman", 3*u) + canvas.drawCentredString(18*u, 4*u, text) + # now draw the tip + penciltip(canvas,debug=0) + # draw broken lines across the body. + canvas.setDash([10,5,16,10],0) + canvas.line(11*u,2.5*u,22*u,2.5*u) + canvas.line(22*u,7.5*u,12*u,7.5*u) + """ + +testpenciltip = """ +def penciltip(canvas, debug=1): + from reportlab.lib.colors import tan, black, green + from reportlab.lib.units import inch + u = inch/10.0 + canvas.setLineWidth(4) + if debug: + canvas.scale(2.8,2.8) # make it big + canvas.setLineWidth(1) # small lines + canvas.setStrokeColor(black) + canvas.setFillColor(tan) + p = canvas.beginPath() + p.moveTo(10*u,0) + p.lineTo(0,5*u) + p.lineTo(10*u,10*u) + p.curveTo(11.5*u,10*u, 11.5*u,7.5*u, 10*u,7.5*u) + p.curveTo(12*u,7.5*u, 11*u,2.5*u, 9.7*u,2.5*u) + p.curveTo(10.5*u,2.5*u, 11*u,0, 10*u,0) + canvas.drawPath(p, stroke=1, fill=1) + canvas.setFillColor(black) + p = canvas.beginPath() + p.moveTo(0,5*u) + p.lineTo(4*u,3*u) + p.lineTo(5*u,4.5*u) + p.lineTo(3*u,6.5*u) + canvas.drawPath(p, stroke=1, fill=1) + if debug: + canvas.setStrokeColor(green) # put in a frame of reference + canvas.grid([0,5*u,10*u,15*u], [0,5*u,10*u]) +""" + +testnoteannotation = """ +from reportlab.platypus.flowables import Flowable +class NoteAnnotation(Flowable): + '''put a pencil in the margin.''' + def wrap(self, *args): + return (1,10) # I take up very little space! (?) + def draw(self): + canvas = self.canv + canvas.translate(-10,-10) + canvas.rotate(180) + canvas.scale(0.2,0.2) + pencil(canvas, text="NOTE") +""" + +testhandannotation = """ +from reportlab.platypus.flowables import Flowable +from reportlab.lib.colors import tan, green +class HandAnnotation(Flowable): + '''A hand flowable.''' + def __init__(self, xoffset=0, size=None, fillcolor=tan, strokecolor=green): + from reportlab.lib.units import inch + if size is None: size=4*inch + self.fillcolor, self.strokecolor = fillcolor, strokecolor + self.xoffset = xoffset + self.size = size + # normal size is 4 inches + self.scale = size/(4.0*inch) + def wrap(self, *args): + return (self.xoffset, self.size) + def draw(self): + canvas = self.canv + canvas.setLineWidth(6) + canvas.setFillColor(self.fillcolor) + canvas.setStrokeColor(self.strokecolor) + canvas.translate(self.xoffset+self.size,0) + canvas.rotate(90) + canvas.scale(self.scale, self.scale) + hand(canvas, debug=0, fill=1) +""" + +lyrics = '''\ +well she hit Net Solutions +and she registered her own .com site now +and filled it up with yahoo profile pics +she snarfed in one night now +and she made 50 million when Hugh Hefner +bought up the rights now +and she'll have fun fun fun +til her Daddy takes the keyboard away''' + +lyrics = string.split(lyrics, "\n") +testtextsize = """ +def textsize(canvas): + from reportlab.lib.units import inch + from reportlab.lib.colors import magenta, red + canvas.setFont("Times-Roman", 20) + canvas.setFillColor(red) + canvas.drawCentredString(2.75*inch, 2.5*inch, "Font size examples") + canvas.setFillColor(magenta) + size = 7 + y = 2.3*inch + x = 1.3*inch + for line in lyrics: + canvas.setFont("Helvetica", size) + canvas.drawRightString(x,y,"%s points: " % size) + canvas.drawString(x,y, line) + y = y-size*1.2 + size = size+1.5 +""" + +teststar = """ +def star(canvas, title="Title Here", aka="Comment here.", + xcenter=None, ycenter=None, nvertices=5): + from math import pi + from reportlab.lib.units import inch + radius=inch/3.0 + if xcenter is None: xcenter=2.75*inch + if ycenter is None: ycenter=1.5*inch + canvas.drawCentredString(xcenter, ycenter+1.3*radius, title) + canvas.drawCentredString(xcenter, ycenter-1.4*radius, aka) + p = canvas.beginPath() + p.moveTo(xcenter,ycenter+radius) + from math import pi, cos, sin + angle = (2*pi)*2/5.0 + startangle = pi/2.0 + for vertex in range(nvertices-1): + nextangle = angle*(vertex+1)+startangle + x = xcenter + radius*cos(nextangle) + y = ycenter + radius*sin(nextangle) + p.lineTo(x,y) + if nvertices==5: + p.close() + canvas.drawPath(p) +""" + +testjoins = """ +def joins(canvas): + from reportlab.lib.units import inch + # make lines big + canvas.setLineWidth(5) + star(canvas, "Default: mitered join", "0: pointed", xcenter = 1*inch) + canvas.setLineJoin(1) + star(canvas, "Round join", "1: rounded") + canvas.setLineJoin(2) + star(canvas, "Bevelled join", "2: square", xcenter=4.5*inch) +""" + +testcaps = """ +def caps(canvas): + from reportlab.lib.units import inch + # make lines big + canvas.setLineWidth(5) + star(canvas, "Default", "no projection",xcenter = 1*inch, + nvertices=4) + canvas.setLineCap(1) + star(canvas, "Round cap", "1: ends in half circle", nvertices=4) + canvas.setLineCap(2) + star(canvas, "Square cap", "2: projects out half a width", xcenter=4.5*inch, + nvertices=4) +""" + +testdashes = """ +def dashes(canvas): + from reportlab.lib.units import inch + # make lines big + canvas.setDash(6,3) + star(canvas, "Simple dashes", "6 points on, 3 off", xcenter = 1*inch) + canvas.setDash(1,2) + star(canvas, "Dots", "One on, two off") + canvas.setDash([1,1,3,3,1,4,4,1], 0) + star(canvas, "Complex Pattern", "[1,1,3,3,1,4,4,1]", xcenter=4.5*inch) +""" + +testcustomfont1 = """ +def customfont1(canvas): + # we know some glyphs are missing, suppress warnings + import reportlab.rl_config + reportlab.rl_config.warnOnMissingFontGlyphs = 0 + + import rl_doc_utils + from reportlab.pdfbase import pdfmetrics + afmFile, pfbFile = rl_doc_utils.getJustFontPaths() + justFace = pdfmetrics.EmbeddedType1Face(afmFile, pfbFile) + faceName = 'LettErrorRobot-Chrome' # pulled from AFM file + pdfmetrics.registerTypeFace(justFace) + justFont = pdfmetrics.Font('LettErrorRobot-Chrome', + faceName, + 'WinAnsiEncoding') + pdfmetrics.registerFont(justFont) + + canvas.setFont('LettErrorRobot-Chrome', 32) + canvas.drawString(10, 150, 'This should be in') + canvas.drawString(10, 100, 'LettErrorRobot-Chrome') +""" + +testttffont1 = """ +def ttffont1(canvas): + # we know some glyphs are missing, suppress warnings + import reportlab.rl_config + reportlab.rl_config.warnOnMissingFontGlyphs = 0 + from reportlab.pdfbase import pdfmetrics + from reportlab.pdfbase.ttfonts import TTFont + pdfmetrics.registerFont(TTFont('Rina', 'rina.ttf')) + from reportlab.pdfgen.canvas import Canvas + + canvas.setFont('Rina', 32) + canvas.drawString(10, 150, "Some UTF-8 text encoded") + canvas.drawString(10, 100, "in the Rina TT Font!") +""" + +testcursormoves1 = """ +def cursormoves1(canvas): + from reportlab.lib.units import inch + textobject = canvas.beginText() + textobject.setTextOrigin(inch, 2.5*inch) + textobject.setFont("Helvetica-Oblique", 14) + for line in lyrics: + textobject.textLine(line) + textobject.setFillGray(0.4) + textobject.textLines(''' + With many apologies to the Beach Boys + and anyone else who finds this objectionable + ''') + canvas.drawText(textobject) +""" + +testcursormoves2 = """ +def cursormoves2(canvas): + from reportlab.lib.units import inch + textobject = canvas.beginText() + textobject.setTextOrigin(2, 2.5*inch) + textobject.setFont("Helvetica-Oblique", 14) + for line in lyrics: + textobject.textOut(line) + textobject.moveCursor(14,14) # POSITIVE Y moves down!!! + textobject.setFillColorRGB(0.4,0,1) + textobject.textLines(''' + With many apologies to the Beach Boys + and anyone else who finds this objectionable + ''') + canvas.drawText(textobject) +""" + +testcharspace = """ +def charspace(canvas): + from reportlab.lib.units import inch + textobject = canvas.beginText() + textobject.setTextOrigin(3, 2.5*inch) + textobject.setFont("Helvetica-Oblique", 10) + charspace = 0 + for line in lyrics: + textobject.setCharSpace(charspace) + textobject.textLine("%s: %s" %(charspace,line)) + charspace = charspace+0.5 + textobject.setFillGray(0.4) + textobject.textLines(''' + With many apologies to the Beach Boys + and anyone else who finds this objectionable + ''') + canvas.drawText(textobject) +""" + +testwordspace = """ +def wordspace(canvas): + from reportlab.lib.units import inch + textobject = canvas.beginText() + textobject.setTextOrigin(3, 2.5*inch) + textobject.setFont("Helvetica-Oblique", 12) + wordspace = 0 + for line in lyrics: + textobject.setWordSpace(wordspace) + textobject.textLine("%s: %s" %(wordspace,line)) + wordspace = wordspace+2.5 + textobject.setFillColorCMYK(0.4,0,0.4,0.2) + textobject.textLines(''' + With many apologies to the Beach Boys + and anyone else who finds this objectionable + ''') + canvas.drawText(textobject) +""" +testhorizontalscale = """ +def horizontalscale(canvas): + from reportlab.lib.units import inch + textobject = canvas.beginText() + textobject.setTextOrigin(3, 2.5*inch) + textobject.setFont("Helvetica-Oblique", 12) + horizontalscale = 80 # 100 is default + for line in lyrics: + textobject.setHorizScale(horizontalscale) + textobject.textLine("%s: %s" %(horizontalscale,line)) + horizontalscale = horizontalscale+10 + textobject.setFillColorCMYK(0.0,0.4,0.4,0.2) + textobject.textLines(''' + With many apologies to the Beach Boys + and anyone else who finds this objectionable + ''') + canvas.drawText(textobject) +""" +testleading = """ +def leading(canvas): + from reportlab.lib.units import inch + textobject = canvas.beginText() + textobject.setTextOrigin(3, 2.5*inch) + textobject.setFont("Helvetica-Oblique", 14) + leading = 8 + for line in lyrics: + textobject.setLeading(leading) + textobject.textLine("%s: %s" %(leading,line)) + leading = leading+2.5 + textobject.setFillColorCMYK(0.8,0,0,0.3) + textobject.textLines(''' + With many apologies to the Beach Boys + and anyone else who finds this objectionable + ''') + canvas.drawText(textobject) +""" + +testhand = """ +def hand(canvas, debug=1, fill=0): + (startx, starty) = (0,0) + curves = [ + ( 0, 2), ( 0, 4), ( 0, 8), # back of hand + ( 5, 8), ( 7,10), ( 7,14), + (10,14), (10,13), ( 7.5, 8), # thumb + (13, 8), (14, 8), (17, 8), + (19, 8), (19, 6), (17, 6), + (15, 6), (13, 6), (11, 6), # index, pointing + (12, 6), (13, 6), (14, 6), + (16, 6), (16, 4), (14, 4), + (13, 4), (12, 4), (11, 4), # middle + (11.5, 4), (12, 4), (13, 4), + (15, 4), (15, 2), (13, 2), + (12.5, 2), (11.5, 2), (11, 2), # ring + (11.5, 2), (12, 2), (12.5, 2), + (14, 2), (14, 0), (12.5, 0), + (10, 0), (8, 0), (6, 0), # pinky, then close + ] + from reportlab.lib.units import inch + if debug: canvas.setLineWidth(6) + u = inch*0.2 + p = canvas.beginPath() + p.moveTo(startx, starty) + ccopy = list(curves) + while ccopy: + [(x1,y1), (x2,y2), (x3,y3)] = ccopy[:3] + del ccopy[:3] + p.curveTo(x1*u,y1*u,x2*u,y2*u,x3*u,y3*u) + p.close() + canvas.drawPath(p, fill=fill) + if debug: + from reportlab.lib.colors import red, green + (lastx, lasty) = (startx, starty) + ccopy = list(curves) + while ccopy: + [(x1,y1), (x2,y2), (x3,y3)] = ccopy[:3] + del ccopy[:3] + canvas.setStrokeColor(red) + canvas.line(lastx*u,lasty*u, x1*u,y1*u) + canvas.setStrokeColor(green) + canvas.line(x2*u,y2*u, x3*u,y3*u) + (lastx,lasty) = (x3,y3) +""" + +testhand2 = """ +def hand2(canvas): + canvas.translate(20,10) + canvas.setLineWidth(3) + canvas.setFillColorRGB(0.1, 0.3, 0.9) + canvas.setStrokeGray(0.5) + hand(canvas, debug=0, fill=1) +""" + +testfonts = """ +def fonts(canvas): + from reportlab.lib.units import inch + text = "Now is the time for all good men to..." + x = 1.8*inch + y = 2.7*inch + for font in canvas.getAvailableFonts(): + canvas.setFont(font, 10) + canvas.drawString(x,y,text) + canvas.setFont("Helvetica", 10) + canvas.drawRightString(x-10,y, font+":") + y = y-13 +""" + +testarcs = """ +def arcs(canvas): + from reportlab.lib.units import inch + canvas.setLineWidth(4) + canvas.setStrokeColorRGB(0.8, 1, 0.6) + # draw rectangles enclosing the arcs + canvas.rect(inch, inch, 1.5*inch, inch) + canvas.rect(3*inch, inch, inch, 1.5*inch) + canvas.setStrokeColorRGB(0, 0.2, 0.4) + canvas.setFillColorRGB(1, 0.6, 0.8) + p = canvas.beginPath() + p.moveTo(0.2*inch, 0.2*inch) + p.arcTo(inch, inch, 2.5*inch,2*inch, startAng=-30, extent=135) + p.arc(3*inch, inch, 4*inch, 2.5*inch, startAng=-45, extent=270) + canvas.drawPath(p, fill=1, stroke=1) +""" +testvariousshapes = """ +def variousshapes(canvas): + from reportlab.lib.units import inch + inch = int(inch) + canvas.setStrokeGray(0.5) + canvas.grid(range(0,11*inch/2,inch/2), range(0,7*inch/2,inch/2)) + canvas.setLineWidth(4) + canvas.setStrokeColorRGB(0, 0.2, 0.7) + canvas.setFillColorRGB(1, 0.6, 0.8) + p = canvas.beginPath() + p.rect(0.5*inch, 0.5*inch, 0.5*inch, 2*inch) + p.circle(2.75*inch, 1.5*inch, 0.3*inch) + p.ellipse(3.5*inch, 0.5*inch, 1.2*inch, 2*inch) + canvas.drawPath(p, fill=1, stroke=1) +""" + +testclosingfigures = """ +def closingfigures(canvas): + from reportlab.lib.units import inch + h = inch/3.0; k = inch/2.0 + canvas.setStrokeColorRGB(0.2,0.3,0.5) + canvas.setFillColorRGB(0.8,0.6,0.2) + canvas.setLineWidth(4) + p = canvas.beginPath() + for i in (1,2,3,4): + for j in (1,2): + xc,yc = inch*i, inch*j + p.moveTo(xc,yc) + p.arcTo(xc-h, yc-k, xc+h, yc+k, startAng=0, extent=60*i) + # close only the first one, not the second one + if j==1: + p.close() + canvas.drawPath(p, fill=1, stroke=1) +""" + +testforms = """ +def forms(canvas): + #first create a form... + canvas.beginForm("SpumoniForm") + #re-use some drawing functions from earlier + spumoni(canvas) + canvas.endForm() + + #then draw it + canvas.doForm("SpumoniForm") +""" + +def doctemplateillustration(canvas): + from reportlab.lib.units import inch + canvas.setFont("Helvetica", 10) + canvas.drawString(inch/4.0, 2.75*inch, "DocTemplate") + W = 4/3.0*inch + H = 2*inch + Wd = x = inch/4.0 + Hd =y = inch/2.0 + for name in ("two column", "chapter page", "title page"): + canvas.setFillColorRGB(0.5,1.0,1.0) + canvas.rect(x,y,W,H, fill=1) + canvas.setFillColorRGB(0,0,0) + canvas.drawString(x+inch/8, y+H-Wd, "PageTemplate") + canvas.drawCentredString(x+W/2.0, y-Wd, name) + x = x+W+Wd + canvas.saveState() + d = inch/16 + dW = (W-3*d)/2.0 + hD = H -2*d-Wd + canvas.translate(Wd+d, Hd+d) + for name in ("left Frame", "right Frame"): + canvas.setFillColorRGB(1.0,0.5,1.0) + canvas.rect(0,0, dW,hD, fill=1) + canvas.setFillGray(0.7) + dd= d/2.0 + ddH = (hD-6*dd)/5.0 + ddW = dW-2*dd + yy = dd + xx = dd + for i in range(5): + canvas.rect(xx,yy,ddW,ddH, fill=1, stroke=0) + yy = yy+ddH+dd + canvas.setFillColorRGB(0,0,0) + canvas.saveState() + canvas.rotate(90) + canvas.drawString(d,-dW/2, name) + canvas.restoreState() + canvas.translate(dW+d,0) + canvas.restoreState() + canvas.setFillColorRGB(1.0, 0.5, 1.0) + mx = Wd+W+Wd+d + my = Hd+d + mW = W-2*d + mH = H-d-Hd + canvas.rect(mx, my, mW, mH, fill=1) + canvas.rect(Wd+2*(W+Wd)+d, Hd+3*d, W-2*d, H/2.0, fill=1) + canvas.setFillGray(0.7) + canvas.rect(Wd+2*(W+Wd)+d+dd, Hd+5*d, W-2*d-2*dd, H/2.0-2*d-dd, fill=1) + xx = mx+dd + yy = my+mH/5.0 + ddH = (mH-6*dd-mH/5.0)/3.0 + ddW = mW - 2*dd + for i in range(3): + canvas.setFillGray(0.7) + canvas.rect(xx,yy,ddW,ddH, fill=1, stroke=1) + canvas.setFillGray(0) + canvas.drawString(xx+dd/2.0,yy+dd/2.0, "flowable %s" %(157-i)) + yy = yy+ddH+dd + canvas.drawCentredString(3*Wd+2*W+W/2, Hd+H/2.0, "First Flowable") + canvas.setFont("Times-BoldItalic", 8) + canvas.setFillGray(0) + canvas.drawCentredString(mx+mW/2.0, my+mH+3*dd, "Chapter 6: Lubricants") + canvas.setFont("Times-BoldItalic", 10) + canvas.drawCentredString(3*Wd+2*W+W/2, Hd+H-H/4, "College Life") + +# D = dir() +g = globals() +Dprime = {} +from types import StringType +from string import strip +for (a,b) in g.items(): + if a[:4]=="test" and type(b) is StringType: + #print 'for', a + #print b + b = strip(b) + exec(b+'\n') + +platypussetup = """ +from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer +from reportlab.lib.styles import getSampleStyleSheet +from reportlab.rl_config import defaultPageSize +from reportlab.lib.units import inch +PAGE_HEIGHT=defaultPageSize[1]; PAGE_WIDTH=defaultPageSize[0] +styles = getSampleStyleSheet() +""" +platypusfirstpage = """ +Title = "Hello world" +pageinfo = "platypus example" +def myFirstPage(canvas, doc): + canvas.saveState() + canvas.setFont('Times-Bold',16) + canvas.drawCentredString(PAGE_WIDTH/2.0, PAGE_HEIGHT-108, Title) + canvas.setFont('Times-Roman',9) + canvas.drawString(inch, 0.75 * inch, "First Page / %s" % pageinfo) + canvas.restoreState() +""" +platypusnextpage = """ +def myLaterPages(canvas, doc): + canvas.saveState() + canvas.setFont('Times-Roman',9) + canvas.drawString(inch, 0.75 * inch, "Page %d %s" % (doc.page, pageinfo)) + canvas.restoreState() +""" +platypusgo = """ +def go(): + doc = SimpleDocTemplate("phello.pdf") + Story = [Spacer(1,2*inch)] + style = styles["Normal"] + for i in range(100): + bogustext = ("This is Paragraph number %s. " % i) *20 + p = Paragraph(bogustext, style) + Story.append(p) + Story.append(Spacer(1,0.2*inch)) + doc.build(Story, onFirstPage=myFirstPage, onLaterPages=myLaterPages) +""" + +if __name__=="__main__": + # then do the platypus hello world + for b in platypussetup, platypusfirstpage, platypusnextpage, platypusgo: + b = strip(b) + exec(b+'\n') + go() diff --git a/bin/reportlab/tools/docco/graphdocpy.py b/bin/reportlab/tools/docco/graphdocpy.py new file mode 100644 index 00000000000..29aecf16c6c --- /dev/null +++ b/bin/reportlab/tools/docco/graphdocpy.py @@ -0,0 +1,980 @@ +#!/usr/bin/env python +#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/docco/graphdocpy.py + +"""Generate documentation for reportlab.graphics classes. + +Type the following for usage info: + + python graphdocpy.py -h +""" + + +__version__ = '0.8' + + +import sys +sys.path.insert(0, '.') +import os, re, types, string, getopt, pickle, copy, time, pprint, traceback +from string import find, join, split, replace, expandtabs, rstrip +import reportlab +from reportlab import rl_config + +from docpy import PackageSkeleton0, ModuleSkeleton0 +from docpy import DocBuilder0, PdfDocBuilder0, HtmlDocBuilder0 +from docpy import htmlescape, htmlrepr, defaultformat, \ + getdoc, reduceDocStringLength +from docpy import makeHtmlSection, makeHtmlSubSection, \ + makeHtmlInlineImage + +from reportlab.lib.units import inch, cm +from reportlab.lib.pagesizes import A4 +from reportlab.lib import colors +from reportlab.lib.enums import TA_CENTER, TA_LEFT +from reportlab.lib.utils import getStringIO +#from StringIO import StringIO +#getStringIO=StringIO +from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle +from reportlab.pdfgen import canvas +from reportlab.platypus.flowables import Flowable, Spacer +from reportlab.platypus.paragraph import Paragraph +from reportlab.platypus.tableofcontents import TableOfContents +from reportlab.platypus.flowables \ + import Flowable, Preformatted,Spacer, Image, KeepTogether, PageBreak +from reportlab.platypus.xpreformatted import XPreformatted +from reportlab.platypus.frames import Frame +from reportlab.platypus.doctemplate \ + import PageTemplate, BaseDocTemplate +from reportlab.platypus.tables import TableStyle, Table +from reportlab.graphics.shapes import NotImplementedError +import inspect + +# Needed to draw Widget/Drawing demos. + +from reportlab.graphics.widgetbase import Widget +from reportlab.graphics.shapes import Drawing +from reportlab.graphics import shapes +from reportlab.graphics import renderPDF + +VERBOSE = rl_config.verbose +VERIFY = 1 + +_abstractclasserr_re = re.compile(r'^\s*abstract\s*class\s*(\w+)\s*instantiated',re.I) + +#################################################################### +# +# Stuff needed for building PDF docs. +# +#################################################################### + +def mainPageFrame(canvas, doc): + "The page frame used for all PDF documents." + + canvas.saveState() + + pageNumber = canvas.getPageNumber() + canvas.line(2*cm, A4[1]-2*cm, A4[0]-2*cm, A4[1]-2*cm) + canvas.line(2*cm, 2*cm, A4[0]-2*cm, 2*cm) + if pageNumber > 1: + canvas.setFont('Times-Roman', 12) + canvas.drawString(4 * inch, cm, "%d" % pageNumber) + if hasattr(canvas, 'headerLine'): # hackish + headerline = string.join(canvas.headerLine, ' \xc2\x8d ') + canvas.drawString(2*cm, A4[1]-1.75*cm, headerline) + + canvas.setFont('Times-Roman', 8) + msg = "Generated with docpy. See http://www.reportlab.com!" + canvas.drawString(2*cm, 1.65*cm, msg) + + canvas.restoreState() + + +class MyTemplate(BaseDocTemplate): + "The document template used for all PDF documents." + + _invalidInitArgs = ('pageTemplates',) + + def __init__(self, filename, **kw): + frame1 = Frame(2.5*cm, 2.5*cm, 15*cm, 25*cm, id='F1') + self.allowSplitting = 0 + apply(BaseDocTemplate.__init__, (self, filename), kw) + self.addPageTemplates(PageTemplate('normal', [frame1], mainPageFrame)) + + def afterFlowable(self, flowable): + "Takes care of header line, TOC and outline entries." + + if flowable.__class__.__name__ == 'Paragraph': + f = flowable + + # Build a list of heading parts. + # So far, this is the *last* item on the *previous* page... + if f.style.name[:8] == 'Heading0': + self.canv.headerLine = [f.text] # hackish + elif f.style.name[:8] == 'Heading1': + if len(self.canv.headerLine) == 2: + del self.canv.headerLine[-1] + elif len(self.canv.headerLine) == 3: + del self.canv.headerLine[-1] + del self.canv.headerLine[-1] + self.canv.headerLine.append(f.text) + elif f.style.name[:8] == 'Heading2': + if len(self.canv.headerLine) == 3: + del self.canv.headerLine[-1] + self.canv.headerLine.append(f.text) + + if f.style.name[:7] == 'Heading': + # Register TOC entries. + headLevel = int(f.style.name[7:]) + self.notify('TOCEntry', (headLevel, flowable.getPlainText(), self.page)) + + # Add PDF outline entries. + c = self.canv + title = f.text + key = str(hash(f)) + lev = int(f.style.name[7:]) + try: + if lev == 0: + isClosed = 0 + else: + isClosed = 1 + c.bookmarkPage(key) + c.addOutlineEntry(title, key, level=lev, closed=isClosed) + c.showOutline() + except: + if VERBOSE: + # AR hacking in exception handlers + print 'caught exception in MyTemplate.afterFlowable with heading text %s' % f.text + traceback.print_exc() + else: + pass + + +#################################################################### +# +# Utility functions +# +#################################################################### +def indentLevel(line, spacesPerTab=4): + """Counts the indent levels on the front. + + It is assumed that one tab equals 4 spaces. + """ + + x = 0 + nextTab = 4 + for ch in line: + if ch == ' ': + x = x + 1 + elif ch == '\t': + x = nextTab + nextTab = x + spacesPerTab + else: + return x + + +assert indentLevel('hello') == 0, 'error in indentLevel' +assert indentLevel(' hello') == 1, 'error in indentLevel' +assert indentLevel(' hello') == 2, 'error in indentLevel' +assert indentLevel(' hello') == 3, 'error in indentLevel' +assert indentLevel('\thello') == 4, 'error in indentLevel' +assert indentLevel(' \thello') == 4, 'error in indentLevel' +assert indentLevel('\t hello') == 5, 'error in indentLevel' + +#################################################################### +# +# Special-purpose document builders +# +#################################################################### + +class GraphPdfDocBuilder0(PdfDocBuilder0): + """A PDF document builder displaying widgets and drawings. + + This generates a PDF file where only methods named 'demo' are + listed for any class C. If C happens to be a subclass of Widget + and has a 'demo' method, this method is assumed to generate and + return a sample widget instance, that is then appended graphi- + cally to the Platypus story. + + Something similar happens for functions. If their names start + with 'sample' they are supposed to generate and return a sample + drawing. This is then taken and appended graphically to the + Platypus story, as well. + """ + + fileSuffix = '.pdf' + + def begin(self, name='', typ=''): + styleSheet = getSampleStyleSheet() + self.code = styleSheet['Code'] + self.bt = styleSheet['BodyText'] + self.story = [] + + # Cover page + t = time.gmtime(time.time()) + timeString = time.strftime("%Y-%m-%d %H:%M", t) + self.story.append(Paragraph('Documentation for %s "%s"' % (typ, name), self.bt)) + self.story.append(Paragraph('Generated by: graphdocpy.py version %s' % __version__, self.bt)) + self.story.append(Paragraph('Date generated: %s' % timeString, self.bt)) + self.story.append(Paragraph('Format: PDF', self.bt)) + self.story.append(PageBreak()) + + # Table of contents + toc = TableOfContents() + self.story.append(toc) + self.story.append(PageBreak()) + + + def end(self, fileName=None): + if fileName: # overrides output path + self.outPath = fileName + elif self.packageName: + self.outPath = self.packageName + self.fileSuffix + elif self.skeleton: + self.outPath = self.skeleton.getModuleName() + self.fileSuffix + else: + self.outPath = '' + + if self.outPath: + doc = MyTemplate(self.outPath) + doc.multiBuild(self.story) + + + def beginModule(self, name, doc, imported): + story = self.story + bt = self.bt + + # Defer displaying the module header info to later... + self.shouldDisplayModule = (name, doc, imported) + self.hasDisplayedModule = 0 + + + def endModule(self, name, doc, imported): + if self.hasDisplayedModule: + DocBuilder0.endModule(self, name, doc, imported) + + + def beginClasses(self, names): + # Defer displaying the module header info to later... + if self.shouldDisplayModule: + self.shouldDisplayClasses = names + + + # Skip all methods. + def beginMethod(self, name, doc, sig): + pass + + + def endMethod(self, name, doc, sig): + pass + + + def beginClass(self, name, doc, bases): + "Append a graphic demo of a Widget or Drawing at the end of a class." + + if VERBOSE: + print 'GraphPdfDocBuilder.beginClass(%s...)' % name + + aClass = eval('self.skeleton.moduleSpace.' + name) + if issubclass(aClass, Widget): + if self.shouldDisplayModule: + modName, modDoc, imported = self.shouldDisplayModule + self.story.append(Paragraph(modName, self.makeHeadingStyle(self.indentLevel-2, 'module'))) + self.story.append(XPreformatted(modDoc, self.bt)) + self.shouldDisplayModule = 0 + self.hasDisplayedModule = 1 + if self.shouldDisplayClasses: + self.story.append(Paragraph('Classes', self.makeHeadingStyle(self.indentLevel-1))) + self.shouldDisplayClasses = 0 + PdfDocBuilder0.beginClass(self, name, doc, bases) + self.beginAttributes(aClass) + + elif issubclass(aClass, Drawing): + if self.shouldDisplayModule: + modName, modDoc, imported = self.shouldDisplayModule + self.story.append(Paragraph(modName, self.makeHeadingStyle(self.indentLevel-2, 'module'))) + self.story.append(XPreformatted(modDoc, self.bt)) + self.shouldDisplayModule = 0 + self.hasDisplayedModule = 1 + if self.shouldDisplayClasses: + self.story.append(Paragraph('Classes', self.makeHeadingStyle(self.indentLevel-1))) + self.shouldDisplayClasses = 0 + PdfDocBuilder0.beginClass(self, name, doc, bases) + + + def beginAttributes(self, aClass): + "Append a list of annotated attributes of a class." + + self.story.append(Paragraph( + 'Public Attributes', + self.makeHeadingStyle(self.indentLevel+1))) + + map = aClass._attrMap + if map: + map = map.items() + map.sort() + else: + map = [] + for name, typ in map: + if typ != None: + if hasattr(typ, 'desc'): + desc = typ.desc + else: + desc = '%s' % typ.__class__.__name__ + else: + desc = 'None' + self.story.append(Paragraph( + "%s %s" % (name, desc), self.bt)) + self.story.append(Paragraph("", self.bt)) + + + def endClass(self, name, doc, bases): + "Append a graphic demo of a Widget or Drawing at the end of a class." + + PdfDocBuilder0.endClass(self, name, doc, bases) + + aClass = eval('self.skeleton.moduleSpace.' + name) + if hasattr(aClass, '_nodoc'): + pass + elif issubclass(aClass, Widget): + try: + widget = aClass() + except AssertionError, err: + if _abstractclasserr_re.match(str(err)): return + raise + self.story.append(Spacer(0*cm, 0.5*cm)) + self._showWidgetDemoCode(widget) + self.story.append(Spacer(0*cm, 0.5*cm)) + self._showWidgetDemo(widget) + self.story.append(Spacer(0*cm, 0.5*cm)) + self._showWidgetProperties(widget) + self.story.append(PageBreak()) + elif issubclass(aClass, Drawing): + drawing = aClass() + self.story.append(Spacer(0*cm, 0.5*cm)) + self._showDrawingCode(drawing) + self.story.append(Spacer(0*cm, 0.5*cm)) + self._showDrawingDemo(drawing) + self.story.append(Spacer(0*cm, 0.5*cm)) + + + def beginFunctions(self, names): + srch = string.join(names, ' ') + if string.find(string.join(names, ' '), ' sample') > -1: + PdfDocBuilder0.beginFunctions(self, names) + + + # Skip non-sample functions. + def beginFunction(self, name, doc, sig): + "Skip function for 'uninteresting' names." + + if name[:6] == 'sample': + PdfDocBuilder0.beginFunction(self, name, doc, sig) + + + def endFunction(self, name, doc, sig): + "Append a drawing to the story for special function names." + + if name[:6] != 'sample': + return + + if VERBOSE: + print 'GraphPdfDocBuilder.endFunction(%s...)' % name + PdfDocBuilder0.endFunction(self, name, doc, sig) + aFunc = eval('self.skeleton.moduleSpace.' + name) + drawing = aFunc() + + self.story.append(Spacer(0*cm, 0.5*cm)) + self._showFunctionDemoCode(aFunc) + self.story.append(Spacer(0*cm, 0.5*cm)) + self._showDrawingDemo(drawing) + + self.story.append(PageBreak()) + + + def _showFunctionDemoCode(self, function): + """Show a demo code of the function generating the drawing.""" + # Heading + self.story.append(Paragraph("Example", self.bt)) + self.story.append(Paragraph("", self.bt)) + + # Sample code + codeSample = inspect.getsource(function) + self.story.append(Preformatted(codeSample, self.code)) + + + def _showDrawingCode(self, drawing): + """Show code of the drawing class.""" + # Heading + #className = drawing.__class__.__name__ + self.story.append(Paragraph("Example", self.bt)) + + # Sample code + codeSample = inspect.getsource(drawing.__class__.__init__) + self.story.append(Preformatted(codeSample, self.code)) + + + def _showDrawingDemo(self, drawing): + """Show a graphical demo of the drawing.""" + + # Add the given drawing to the story. + # Ignored if no GD rendering available + # or the demo method does not return a drawing. + try: + flo = renderPDF.GraphicsFlowable(drawing) + self.story.append(Spacer(6,6)) + self.story.append(flo) + self.story.append(Spacer(6,6)) + except: + if VERBOSE: + print 'caught exception in _showDrawingDemo' + traceback.print_exc() + else: + pass + + + def _showWidgetDemo(self, widget): + """Show a graphical demo of the widget.""" + + # Get a demo drawing from the widget and add it to the story. + # Ignored if no GD rendering available + # or the demo method does not return a drawing. + try: + if VERIFY: + widget.verify() + drawing = widget.demo() + flo = renderPDF.GraphicsFlowable(drawing) + self.story.append(Spacer(6,6)) + self.story.append(flo) + self.story.append(Spacer(6,6)) + except: + if VERBOSE: + print 'caught exception in _showWidgetDemo' + traceback.print_exc() + else: + pass + + + def _showWidgetDemoCode(self, widget): + """Show a demo code of the widget.""" + # Heading + #className = widget.__class__.__name__ + self.story.append(Paragraph("Example", self.bt)) + + # Sample code + codeSample = inspect.getsource(widget.__class__.demo) + self.story.append(Preformatted(codeSample, self.code)) + + + def _showWidgetProperties(self, widget): + """Dump all properties of a widget.""" + + props = widget.getProperties() + keys = props.keys() + keys.sort() + lines = [] + for key in keys: + value = props[key] + + f = getStringIO() + pprint.pprint(value, f) + value = f.getvalue()[:-1] + valueLines = string.split(value, '\n') + for i in range(1, len(valueLines)): + valueLines[i] = ' '*(len(key)+3) + valueLines[i] + value = string.join(valueLines, '\n') + + lines.append('%s = %s' % (key, value)) + + text = join(lines, '\n') + self.story.append(Paragraph("Properties of Example Widget", self.bt)) + self.story.append(Paragraph("", self.bt)) + self.story.append(Preformatted(text, self.code)) + + +class GraphHtmlDocBuilder0(HtmlDocBuilder0): + "A class to write the skeleton of a Python source." + + fileSuffix = '.html' + + def beginModule(self, name, doc, imported): + # Defer displaying the module header info to later... + self.shouldDisplayModule = (name, doc, imported) + self.hasDisplayedModule = 0 + + + def endModule(self, name, doc, imported): + if self.hasDisplayedModule: + HtmlDocBuilder0.endModule(self, name, doc, imported) + + + def beginClasses(self, names): + # Defer displaying the module header info to later... + if self.shouldDisplayModule: + self.shouldDisplayClasses = names + + + # Skip all methods. + def beginMethod(self, name, doc, sig): + pass + + + def endMethod(self, name, doc, sig): + pass + + + def beginClass(self, name, doc, bases): + "Append a graphic demo of a widget at the end of a class." + + aClass = eval('self.skeleton.moduleSpace.' + name) + if issubclass(aClass, Widget): + if self.shouldDisplayModule: + modName, modDoc, imported = self.shouldDisplayModule + self.outLines.append('

%s

' % modName) + self.outLines.append('
%s
' % modDoc) + self.shouldDisplayModule = 0 + self.hasDisplayedModule = 1 + if self.shouldDisplayClasses: + self.outLines.append('

Classes

') + self.shouldDisplayClasses = 0 + + HtmlDocBuilder0.beginClass(self, name, doc, bases) + + + def endClass(self, name, doc, bases): + "Append a graphic demo of a widget at the end of a class." + + HtmlDocBuilder0.endClass(self, name, doc, bases) + + aClass = eval('self.skeleton.moduleSpace.' + name) + if issubclass(aClass, Widget): + widget = aClass() + self._showWidgetDemoCode(widget) + self._showWidgetDemo(widget) + self._showWidgetProperties(widget) + + + def beginFunctions(self, names): + if string.find(string.join(names, ' '), ' sample') > -1: + HtmlDocBuilder0.beginFunctions(self, names) + + + # Skip non-sample functions. + def beginFunction(self, name, doc, sig): + "Skip function for 'uninteresting' names." + + if name[:6] == 'sample': + HtmlDocBuilder0.beginFunction(self, name, doc, sig) + + + def endFunction(self, name, doc, sig): + "Append a drawing to the story for special function names." + + if name[:6] != 'sample': + return + + HtmlDocBuilder0.endFunction(self, name, doc, sig) + aFunc = eval('self.skeleton.moduleSpace.' + name) + drawing = aFunc() + + self._showFunctionDemoCode(aFunc) + self._showDrawingDemo(drawing, aFunc.__name__) + + + def _showFunctionDemoCode(self, function): + """Show a demo code of the function generating the drawing.""" + # Heading + self.outLines.append('

Example

') + + # Sample code + codeSample = inspect.getsource(function) + self.outLines.append('
%s
' % codeSample) + + + def _showDrawingDemo(self, drawing, name): + """Show a graphical demo of the drawing.""" + + # Add the given drawing to the story. + # Ignored if no GD rendering available + # or the demo method does not return a drawing. + try: + from reportlab.graphics import renderPM + modName = self.skeleton.getModuleName() + path = '%s-%s.jpg' % (modName, name) + renderPM.drawToFile(drawing, path, fmt='JPG') + self.outLines.append('

Demo

') + self.outLines.append(makeHtmlInlineImage(path)) + except: + if VERBOSE: + print 'caught exception in GraphHTMLDocBuilder._showDrawingDemo' + traceback.print_exc() + else: + pass + + + def _showWidgetDemo(self, widget): + """Show a graphical demo of the widget.""" + + # Get a demo drawing from the widget and add it to the story. + # Ignored if no GD rendering available + # or the demo method does not return a drawing. + try: + from reportlab.graphics import renderPM + drawing = widget.demo() + if VERIFY: + widget.verify() + modName = self.skeleton.getModuleName() + path = '%s-%s.jpg' % (modName, widget.__class__.__name__) + renderPM.drawToFile(drawing, path, fmt='JPG') + self.outLines.append('

Demo

') + self.outLines.append(makeHtmlInlineImage(path)) + except: + if VERBOSE: + + print 'caught exception in GraphHTMLDocBuilder._showWidgetDemo' + traceback.print_exc() + else: + pass + + + def _showWidgetDemoCode(self, widget): + """Show a demo code of the widget.""" + # Heading + #className = widget.__class__.__name__ + self.outLines.append('

Example Code

') + + # Sample code + codeSample = inspect.getsource(widget.__class__.demo) + self.outLines.append('
%s
' % codeSample) + self.outLines.append('') + + + def _showWidgetProperties(self, widget): + """Dump all properties of a widget.""" + + props = widget.getProperties() + keys = props.keys() + keys.sort() + lines = [] + for key in keys: + value = props[key] + + # Method 3 + f = getStringIO() + pprint.pprint(value, f) + value = f.getvalue()[:-1] + valueLines = string.split(value, '\n') + for i in range(1, len(valueLines)): + valueLines[i] = ' '*(len(key)+3) + valueLines[i] + value = string.join(valueLines, '\n') + + lines.append('%s = %s' % (key, value)) + text = join(lines, '\n') + self.outLines.append('

Properties of Example Widget

') + self.outLines.append('
%s
' % text) + self.outLines.append('') + + +# Highly experimental! +class PlatypusDocBuilder0(DocBuilder0): + "Document the skeleton of a Python module as a Platypus story." + + fileSuffix = '.pps' # A pickled Platypus story. + + def begin(self, name='', typ=''): + styleSheet = getSampleStyleSheet() + self.code = styleSheet['Code'] + self.bt = styleSheet['BodyText'] + self.story = [] + + + def end(self): + if self.packageName: + self.outPath = self.packageName + self.fileSuffix + elif self.skeleton: + self.outPath = self.skeleton.getModuleName() + self.fileSuffix + else: + self.outPath = '' + + if self.outPath: + f = open(self.outPath, 'w') + pickle.dump(self.story, f) + + + def beginPackage(self, name): + DocBuilder0.beginPackage(self, name) + self.story.append(Paragraph(name, self.bt)) + + + def beginModule(self, name, doc, imported): + story = self.story + bt = self.bt + + story.append(Paragraph(name, bt)) + story.append(XPreformatted(doc, bt)) + + + def beginClasses(self, names): + self.story.append(Paragraph('Classes', self.bt)) + + + def beginClass(self, name, doc, bases): + bt = self.bt + story = self.story + if bases: + bases = map(lambda b:b.__name__, bases) # hack + story.append(Paragraph('%s(%s)' % (name, join(bases, ', ')), bt)) + else: + story.append(Paragraph(name, bt)) + + story.append(XPreformatted(doc, bt)) + + + def beginMethod(self, name, doc, sig): + bt = self.bt + story = self.story + story.append(Paragraph(name+sig, bt)) + story.append(XPreformatted(doc, bt)) + + + def beginFunctions(self, names): + if names: + self.story.append(Paragraph('Functions', self.bt)) + + + def beginFunction(self, name, doc, sig): + bt = self.bt + story = self.story + story.append(Paragraph(name+sig, bt)) + story.append(XPreformatted(doc, bt)) + + +#################################################################### +# +# Main +# +#################################################################### + +def printUsage(): + """graphdocpy.py - Automated documentation for the RL Graphics library. + +Usage: python graphdocpy.py [options] + + [options] + -h Print this help message. + + -f name Use the document builder indicated by 'name', + e.g. Html, Pdf. + + -m module Generate document for module named 'module'. + 'module' may follow any of these forms: + - docpy.py + - docpy + - c:\\test\\docpy + and can be any of these: + - standard Python modules + - modules in the Python search path + - modules in the current directory + + -p package Generate document for package named 'package' + (default is 'reportlab.graphics'). + 'package' may follow any of these forms: + - reportlab + - reportlab.graphics.charts + - c:\\test\\reportlab + and can be any of these: + - standard Python packages (?) + - packages in the Python search path + - packages in the current directory + + -s Silent mode (default is unset). + +Examples: + + python graphdocpy.py reportlab.graphics + python graphdocpy.py -m signsandsymbols.py -f Pdf + python graphdocpy.py -m flags.py -f Html + python graphdocpy.py -m barchart1.py +""" + + +# The following functions, including main(), are actually +# the same as in docpy.py (except for some defaults). + +def documentModule0(pathOrName, builder, opts={}): + """Generate documentation for one Python file in some format. + + This handles Python standard modules like string, custom modules + on the Python search path like e.g. docpy as well as modules + specified with their full path like C:/tmp/junk.py. + + The doc file will always be saved in the current directory with + a basename equal to that of the module, e.g. docpy. + """ + cwd = os.getcwd() + + # Append directory to Python search path if we get one. + dirName = os.path.dirname(pathOrName) + if dirName: + sys.path.append(dirName) + + # Remove .py extension from module name. + if pathOrName[-3:] == '.py': + modname = pathOrName[:-3] + else: + modname = pathOrName + + # Remove directory paths from module name. + if dirName: + modname = os.path.basename(modname) + + # Load the module. + try: + module = __import__(modname) + except: + print 'Failed to import %s.' % modname + os.chdir(cwd) + return + + # Do the real documentation work. + s = ModuleSkeleton0() + s.inspect(module) + builder.write(s) + + # Remove appended directory from Python search path if we got one. + if dirName: + del sys.path[-1] + + os.chdir(cwd) + + +def _packageWalkCallback((builder, opts), dirPath, files): + "A callback function used when waking over a package tree." + #must CD into a directory to document the module correctly + cwd = os.getcwd() + os.chdir(dirPath) + + + # Skip __init__ files. + files = filter(lambda f:f != '__init__.py', files) + + files = filter(lambda f:f[-3:] == '.py', files) + for f in files: + path = os.path.join(dirPath, f) +## if not opts.get('isSilent', 0): +## print path + builder.indentLevel = builder.indentLevel + 1 + #documentModule0(path, builder) + documentModule0(f, builder) + builder.indentLevel = builder.indentLevel - 1 + #CD back out + os.chdir(cwd) + +def documentPackage0(pathOrName, builder, opts={}): + """Generate documentation for one Python package in some format. + + 'pathOrName' can be either a filesystem path leading to a Python + package or package name whose path will be resolved by importing + the top-level module. + + The doc file will always be saved in the current directory with + a basename equal to that of the package, e.g. reportlab.lib. + """ + + # Did we get a package path with OS-dependant seperators...? + if os.sep in pathOrName: + path = pathOrName + name = os.path.splitext(os.path.basename(path))[0] + # ... or rather a package name? + else: + name = pathOrName + package = __import__(name) + # Some special care needed for dotted names. + if '.' in name: + subname = 'package' + name[find(name, '.'):] + package = eval(subname) + path = os.path.dirname(package.__file__) + + cwd = os.getcwd() + os.chdir(path) + builder.beginPackage(name) + os.path.walk(path, _packageWalkCallback, (builder, opts)) + builder.endPackage(name) + os.chdir(cwd) + + +def makeGraphicsReference(outfilename): + "Make graphics_reference.pdf" + builder = GraphPdfDocBuilder0() + + builder.begin(name='reportlab.graphics', typ='package') + documentPackage0('reportlab.graphics', builder, {'isSilent': 0}) + builder.end(outfilename) + print 'made graphics reference in %s' % outfilename + +def main(): + "Handle command-line options and trigger corresponding action." + + opts, args = getopt.getopt(sys.argv[1:], 'hsf:m:p:') + + # Make an options dictionary that is easier to use. + optsDict = {} + for k, v in opts: + optsDict[k] = v + hasOpt = optsDict.has_key + + # On -h print usage and exit immediately. + if hasOpt('-h'): + print printUsage.__doc__ + sys.exit(0) + + # On -s set silent mode. + isSilent = hasOpt('-s') + + # On -f set the appropriate DocBuilder to use or a default one. + builder = { 'Pdf': GraphPdfDocBuilder0, + 'Html': GraphHtmlDocBuilder0, + }[optsDict.get('-f', 'Pdf')]() + + # Set default module or package to document. + if not hasOpt('-p') and not hasOpt('-m'): + optsDict['-p'] = 'reportlab.graphics' + + # Save a few options for further use. + options = {'isSilent':isSilent} + + # Now call the real documentation functions. + if hasOpt('-m'): + nameOrPath = optsDict['-m'] + if not isSilent: + print "Generating documentation for module %s..." % nameOrPath + builder.begin(name=nameOrPath, typ='module') + documentModule0(nameOrPath, builder, options) + elif hasOpt('-p'): + nameOrPath = optsDict['-p'] + if not isSilent: + print "Generating documentation for package %s..." % nameOrPath + builder.begin(name=nameOrPath, typ='package') + documentPackage0(nameOrPath, builder, options) + builder.end() + + if not isSilent: + print "Saved %s." % builder.outPath + + #if doing the usual, put a copy in docs + if builder.outPath == 'reportlab.graphics.pdf': + import shutil, reportlab + dst = os.path.join(os.path.dirname(reportlab.__file__),'docs','graphics_reference.pdf') + shutil.copyfile('reportlab.graphics.pdf', dst) + if not isSilent: + print 'copied to '+dst + +def makeSuite(): + "standard test harness support - run self as separate process" + from reportlab.test.utils import ScriptThatMakesFileTest + return ScriptThatMakesFileTest('tools/docco', + 'graphdocpy.py', + 'reportlab.graphics.pdf') + +if __name__ == '__main__': + main() diff --git a/bin/reportlab/tools/docco/rl_doc_utils.py b/bin/reportlab/tools/docco/rl_doc_utils.py new file mode 100644 index 00000000000..a105b9d7e88 --- /dev/null +++ b/bin/reportlab/tools/docco/rl_doc_utils.py @@ -0,0 +1,411 @@ +#!/bin/env python +#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/docco/rl_doc_utils.py +__version__=''' $Id: rl_doc_utils.py 2830 2006-04-05 15:18:32Z rgbecker $ ''' + + +__doc__ = """ +This module contains utilities for generating guides +""" + +import os, sys, glob +import string + +from rltemplate import RLDocTemplate +from stylesheet import getStyleSheet +styleSheet = getStyleSheet() + +#from reportlab.platypus.doctemplate import SimpleDocTemplate +from reportlab.lib.units import inch +from reportlab.lib.pagesizes import letter, A4, A5, A3 # latter two for testing +from reportlab.rl_config import defaultPageSize +from reportlab.platypus import figures +from reportlab.platypus import Paragraph, Spacer, Preformatted,\ + PageBreak, CondPageBreak, Flowable, Table, TableStyle, \ + NextPageTemplate, KeepTogether, Image, XPreformatted +from reportlab.lib.styles import ParagraphStyle +from reportlab.lib import colors +from reportlab.lib.sequencer import getSequencer + +import examples + +appmode=0 + + +from t_parse import Template +QFcodetemplate = Template("X$X$", "X") +QFreptemplate = Template("X^X^", "X") +codesubst = "%s%s" +QFsubst = "%s%s" + + +def quickfix(text): + """inside text find any subsequence of form $subsequence$. + Format the subsequence as code. If similarly if text contains ^arg^ + format the arg as replaceable. The escape sequence for literal + $ is $\\$ (^ is ^\\^. + """ + from string import join + for (template,subst) in [(QFcodetemplate, codesubst), (QFreptemplate, QFsubst)]: + fragment = text + parts = [] + try: + while fragment: + try: + (matches, index) = template.PARSE(fragment) + except: raise ValueError + else: + [prefix, code] = matches + if code == "\\": + part = fragment[:index] + else: + part = subst % (prefix, code) + parts.append(part) + fragment = fragment[index:] + except ValueError: + parts.append(fragment) + text = join(parts, "") + return text +#print quickfix("$testing$ testing $one$ ^two^ $three(^four^)$") + + + +H1 = styleSheet['Heading1'] +H2 = styleSheet['Heading2'] +H3 = styleSheet['Heading3'] +H4 = styleSheet['Heading4'] +B = styleSheet['BodyText'] +BU = styleSheet['Bullet'] +Comment = styleSheet['Comment'] +Centred = styleSheet['Centred'] +Caption = styleSheet['Caption'] + +#set up numbering +seq = getSequencer() +seq.setFormat('Chapter','1') +seq.setFormat('Section','1') +seq.setFormat('Appendix','A') +seq.setFormat('Figure', '1') +seq.chain('Chapter','Section') +seq.chain('Chapter','Figure') + +lessonnamestyle = H2 +discussiontextstyle = B +exampletextstyle = styleSheet['Code'] +# size for every example +examplefunctionxinches = 5.5 +examplefunctionyinches = 3 +examplefunctiondisplaysizes = (examplefunctionxinches*inch, examplefunctionyinches*inch) + +def getJustFontPaths(): + '''return afm and pfb for Just's files''' + import reportlab + folder = os.path.dirname(reportlab.__file__) + os.sep + 'fonts' + return os.path.join(folder, 'LeERC___.AFM'), os.path.join(folder, 'LeERC___.PFB') + +# for testing +def NOP(*x,**y): + return None + +def CPage(inches): + getStory().append(CondPageBreak(inches*inch)) + +def newPage(): + getStory().append(PageBreak()) + +def nextTemplate(templName): + f = NextPageTemplate(templName) + getStory().append(f) + +def disc(text, klass=Paragraph, style=discussiontextstyle): + text = quickfix(text) + P = klass(text, style) + getStory().append(P) + +def restartList(): + getSequencer().reset('list1') + +def list(text, doBullet=1): + text=quickfix(text) + if doBullet: + text='.'+text + P = Paragraph(text, BU) + getStory().append(P) + +def bullet(text): + text='\xe2\x80\xa2' + quickfix(text) + P = Paragraph(text, BU) + getStory().append(P) + +def eg(text,before=0.1,after=0): + space(before) + disc(text, klass=Preformatted, style=exampletextstyle) + space(after) + +def space(inches=1./6): + if inches: getStory().append(Spacer(0,inches*inch)) + +def EmbeddedCode(code,name='t'): + eg(code) + disc("produces") + exec code+("\ngetStory().append(%s)\n"%name) + +def startKeep(): + return len(getStory()) + +def endKeep(s): + S = getStory() + k = KeepTogether(S[s:]) + S[s:] = [k] + +def title(text): + """Use this for the document title only""" + disc(text,style=styleSheet['Title']) + +#AR 3/7/2000 - defining three new levels of headings; code +#should be swapped over to using them. + +def heading1(text): + """Use this for chapters. Lessons within a big chapter + should now use heading2 instead. Chapters get numbered.""" + getStory().append(PageBreak()) + p = Paragraph('Chapter ' + quickfix(text), H1) + getStory().append(p) + +def Appendix1(text,): + global appmode + getStory().append(PageBreak()) + if not appmode: + seq.setFormat('Chapter','A') + seq.reset('Chapter') + appmode = 1 + p = Paragraph('Appendix ' + quickfix(text), H1) + getStory().append(p) + +def heading2(text): + """Used to be 'lesson'""" + getStory().append(CondPageBreak(inch)) + p = Paragraph('' + quickfix(text), H2) + getStory().append(p) + +def heading3(text): + """Used to be most of the plain old 'head' sections""" + getStory().append(CondPageBreak(inch)) + p = Paragraph(quickfix(text), H3) + getStory().append(p) + +def image(path, width=None, height=None ): + s = startKeep() + space(.2) + import reportlab + rlDocImageDir = os.path.join(os.path.dirname(reportlab.__file__), 'docs','images') + getStory().append(Image(os.path.join(rlDocImageDir,path),width,height)) + space(.2) + endKeep(s) + +def heading4(text): + """Used to be most of the plain old 'head' sections""" + getStory().append(CondPageBreak(inch)) + p = Paragraph(quickfix(text), H4) + getStory().append(p) + +def todo(text): + """Used for notes to ourselves""" + getStory().append(Paragraph(quickfix(text), Comment)) + +def centred(text): + getStory().append(Paragraph(quickfix(text), Centred)) + +def caption(text): + getStory().append(Paragraph(quickfix(text), Caption)) + +class Illustration(figures.Figure): + """The examples are all presented as functions which do + something to a canvas, with a constant height and width + used. This puts them inside a figure box with a caption.""" + + def __init__(self, operation, caption, width=None, height=None): + stdwidth, stdheight = examplefunctiondisplaysizes + if not width: + width = stdwidth + if not height: + height = stdheight + #figures.Figure.__init__(self, stdwidth * 0.75, stdheight * 0.75) + figures.Figure.__init__(self, width, height, + 'Figure : ' + quickfix(caption)) + self.operation = operation + + def drawFigure(self): + #shrink it a little... + #self.canv.scale(0.75, 0.75) + self.operation(self.canv) + + +def illust(operation, caption, width=None, height=None): + i = Illustration(operation, caption, width=width, height=height) + getStory().append(i) + + +class GraphicsDrawing(Illustration): + """Lets you include reportlab/graphics drawings seamlessly, + with the right numbering.""" + def __init__(self, drawing, caption): + figures.Figure.__init__(self, + drawing.width, + drawing.height, + 'Figure : ' + quickfix(caption) + ) + self.drawing = drawing + + def drawFigure(self): + d = self.drawing + d.wrap(d.width, d.height) + d.drawOn(self.canv, 0, 0) + +def draw(drawing, caption): + d = GraphicsDrawing(drawing, caption) + getStory().append(d) + +class ParaBox(figures.Figure): + """Illustrates paragraph examples, with style attributes on the left""" + descrStyle = ParagraphStyle('description', + fontName='Courier', + fontSize=8, + leading=9.6) + + def __init__(self, text, style, caption): + figures.Figure.__init__(self, 0, 0, caption) + self.text = text + self.style = style + self.para = Paragraph(text, style) + + styleText = self.getStyleText(style) + self.pre = Preformatted(styleText, self.descrStyle) + + def wrap(self, availWidth, availHeight): + """Left 30% is for attributes, right 50% for sample, + 10% gutter each side.""" + self.x0 = availWidth * 0.05 #left of box + self.x1 = availWidth * 0.1 #left of descriptive text + self.x2 = availWidth * 0.5 #left of para itself + self.x3 = availWidth * 0.9 #right of para itself + self.x4 = availWidth * 0.95 #right of box + self.width = self.x4 - self.x0 + self.dx = 0.5 * (availWidth - self.width) + + paw, self.pah = self.para.wrap(self.x3 - self.x2, availHeight) + self.pah = self.pah + self.style.spaceBefore + self.style.spaceAfter + prw, self.prh = self.pre.wrap(self.x2 - self.x1, availHeight) + self.figureHeight = max(self.prh, self.pah) * 10.0/9.0 + return figures.Figure.wrap(self, availWidth, availHeight) + + def getStyleText(self, style): + """Converts style to preformatted block of text""" + lines = [] + for (key, value) in style.__dict__.items(): + lines.append('%s = %s' % (key, value)) + lines.sort() + return string.join(lines, '\n') + + def drawFigure(self): + + #now we fill in the bounding box and before/after boxes + self.canv.saveState() + self.canv.setFillGray(0.95) + self.canv.setDash(1,3) + self.canv.rect(self.x2 - self.x0, + self.figureHeight * 0.95 - self.pah, + self.x3-self.x2, self.para.height, + fill=1,stroke=1) + + self.canv.setFillGray(0.90) + self.canv.rect(self.x2 - self.x0, #spaceBefore + self.figureHeight * 0.95 - self.pah + self.para.height, + self.x3-self.x2, self.style.spaceBefore, + fill=1,stroke=1) + + self.canv.rect(self.x2 - self.x0, #spaceBefore + self.figureHeight * 0.95 - self.pah - self.style.spaceAfter, + self.x3-self.x2, self.style.spaceAfter, + fill=1,stroke=1) + + self.canv.restoreState() + #self.canv.setFillColor(colors.yellow) + self.para.drawOn(self.canv, self.x2 - self.x0, + self.figureHeight * 0.95 - self.pah) + self.pre.drawOn(self.canv, self.x1 - self.x0, + self.figureHeight * 0.95 - self.prh) + + + def getStyleText(self, style): + """Converts style to preformatted block of text""" + lines = [] + for (key, value) in style.__dict__.items(): + if key not in ('name','parent'): + lines.append('%s = %s' % (key, value)) + return string.join(lines, '\n') + + +class ParaBox2(figures.Figure): + """Illustrates a paragraph side-by-side with the raw + text, to show how the XML works.""" + def __init__(self, text, caption): + figures.Figure.__init__(self, 0, 0, caption) + descrStyle = ParagraphStyle('description', + fontName='Courier', + fontSize=8, + leading=9.6) + textStyle = B + self.text = text + self.left = Paragraph('', descrStyle) + self.right = Paragraph(text, B) + + + def wrap(self, availWidth, availHeight): + self.width = availWidth * 0.9 + colWidth = 0.4 * self.width + lw, self.lh = self.left.wrap(colWidth, availHeight) + rw, self.rh = self.right.wrap(colWidth, availHeight) + self.figureHeight = max(self.lh, self.rh) * 10.0/9.0 + return figures.Figure.wrap(self, availWidth, availHeight) + + def drawFigure(self): + self.left.drawOn(self.canv, + self.width * 0.05, + self.figureHeight * 0.95 - self.lh + ) + self.right.drawOn(self.canv, + self.width * 0.55, + self.figureHeight * 0.95 - self.rh + ) + +def parabox(text, style, caption): + p = ParaBox(text, style, + 'Figure : ' + quickfix(caption) + ) + getStory().append(p) + +def parabox2(text, caption): + p = ParaBox2(text, + 'Figure : ' + quickfix(caption) + ) + getStory().append(p) + +def pencilnote(): + getStory().append(examples.NoteAnnotation()) + + +from reportlab.lib.colors import tan, green +def handnote(xoffset=0, size=None, fillcolor=tan, strokecolor=green): + getStory().append(examples.HandAnnotation(xoffset,size,fillcolor,strokecolor)) + + +#make a singleton, created when requested rather +#than each time a chapter imports it. +_story = [] +def setStory(story=[]): + global _story + _story = story +def getStory(): + return _story diff --git a/bin/reportlab/tools/docco/rltemplate.py b/bin/reportlab/tools/docco/rltemplate.py new file mode 100644 index 00000000000..466660e639f --- /dev/null +++ b/bin/reportlab/tools/docco/rltemplate.py @@ -0,0 +1,135 @@ +#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/docco/rltemplate.py +# doc template for RL manuals. Currently YAML is hard-coded +#to use this, which is wrong. + + +from reportlab.platypus import PageTemplate, \ + BaseDocTemplate, Frame, Paragraph +from reportlab.lib.units import inch, cm +from reportlab.rl_config import defaultPageSize + + +class FrontCoverTemplate(PageTemplate): + def __init__(self, id, pageSize=defaultPageSize): + self.pageWidth = pageSize[0] + self.pageHeight = pageSize[1] + frame1 = Frame(inch, + 3*inch, + self.pageWidth - 2*inch, + self.pageHeight - 518, id='cover') + PageTemplate.__init__(self, id, [frame1]) # note lack of onPage + + def afterDrawPage(self, canvas, doc): + canvas.saveState() + canvas.drawImage('../images/replogo.gif',2*inch, 8*inch) + + + canvas.setFont('Times-Roman', 10) + canvas.line(inch, 120, self.pageWidth - inch, 120) + + canvas.drawString(inch, 100, '165 The Broadway') + canvas.drawString(inch, 88, 'Wimbledon') + canvas.drawString(inch, 76, 'London SW19 1NE') + canvas.drawString(inch, 64, 'United Kingdom') + + canvas.restoreState() + + +class OneColumnTemplate(PageTemplate): + def __init__(self, id, pageSize=defaultPageSize): + self.pageWidth = pageSize[0] + self.pageHeight = pageSize[1] + frame1 = Frame(inch, + inch, + self.pageWidth - 2*inch, + self.pageHeight - 2*inch, + id='normal') + PageTemplate.__init__(self, id, [frame1]) # note lack of onPage + + def afterDrawPage(self, canvas, doc): + y = self.pageHeight - 50 + canvas.saveState() + canvas.setFont('Times-Roman', 10) + canvas.drawString(inch, y+8, doc.title) + canvas.drawRightString(self.pageWidth - inch, y+8, doc.chapter) + canvas.line(inch, y, self.pageWidth - inch, y) + canvas.drawCentredString(doc.pagesize[0] / 2, 0.75*inch, 'Page %d' % canvas.getPageNumber()) + canvas.restoreState() + +class TwoColumnTemplate(PageTemplate): + def __init__(self, id, pageSize=defaultPageSize): + self.pageWidth = pageSize[0] + self.pageHeight = pageSize[1] + colWidth = 0.5 * (self.pageWidth - 2.25*inch) + frame1 = Frame(inch, + inch, + colWidth, + self.pageHeight - 2*inch, + id='leftCol') + frame2 = Frame(0.5 * self.pageWidth + 0.125, + inch, + colWidth, + self.pageHeight - 2*inch, + id='rightCol') + PageTemplate.__init__(self, id, [frame1, frame2]) # note lack of onPage + + def afterDrawPage(self, canvas, doc): + y = self.pageHeight - 50 + canvas.saveState() + canvas.setFont('Times-Roman', 10) + canvas.drawString(inch, y+8, doc.title) + canvas.drawRightString(self.pageWidth - inch, y+8, doc.chapter) + canvas.line(inch, y, self.pageWidth - inch, y*inch) + canvas.drawCentredString(doc.pagesize[0] / 2, 0.75*inch, 'Page %d' % canvas.getPageNumber()) + canvas.restoreState() + + +class RLDocTemplate(BaseDocTemplate): + def afterInit(self): + self.addPageTemplates(FrontCoverTemplate('Cover', self.pagesize)) + self.addPageTemplates(OneColumnTemplate('Normal', self.pagesize)) + self.addPageTemplates(TwoColumnTemplate('TwoColumn', self.pagesize)) + + #just playing + self.title = "(Document Title Goes Here)" + self.chapter = "(No chapter yet)" + self.chapterNo = 1 #unique keys + self.sectionNo = 1 # unique keys + +## # AR hack +## self.counter = 1 + def beforeDocument(self): + self.canv.showOutline() + + def afterFlowable(self, flowable): + """Detect Level 1 and 2 headings, build outline, + and track chapter title.""" + if isinstance(flowable, Paragraph): + style = flowable.style.name + +## #AR debug text +## try: +## print '%d: %s...' % (self.counter, flowable.getPlainText()[0:40]) +## except AttributeError: +## print '%d: (something with ABag)' % self.counter +## self.counter = self.counter + 1 + + if style == 'Title': + self.title = flowable.getPlainText() + elif style == 'Heading1': + self.chapter = flowable.getPlainText() + key = 'ch%d' % self.chapterNo + self.canv.bookmarkPage(key) + self.canv.addOutlineEntry(flowable.getPlainText(), + key, 0, 0) + self.chapterNo = self.chapterNo + 1 + self.sectionNo = 1 + elif style == 'Heading2': + self.section = flowable.text + key = 'ch%ds%d' % (self.chapterNo, self.sectionNo) + self.canv.bookmarkPage(key) + self.canv.addOutlineEntry(flowable.getPlainText(), + key, 1, 0) + self.sectionNo = self.sectionNo + 1 \ No newline at end of file diff --git a/bin/reportlab/tools/docco/stylesheet.py b/bin/reportlab/tools/docco/stylesheet.py new file mode 100644 index 00000000000..9e8d2c65840 --- /dev/null +++ b/bin/reportlab/tools/docco/stylesheet.py @@ -0,0 +1,165 @@ +#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/docco/stylesheet.py +#standard stylesheet for our manuals +from reportlab.lib.styles import StyleSheet1, ParagraphStyle +from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_RIGHT, TA_JUSTIFY +from reportlab.lib import colors + + +def getStyleSheet(): + """Returns a stylesheet object""" + stylesheet = StyleSheet1() + + stylesheet.add(ParagraphStyle(name='Normal', + fontName='Times-Roman', + fontSize=10, + leading=12, + spaceBefore=6) + ) + + stylesheet.add(ParagraphStyle(name='Comment', + fontName='Times-Italic') + ) + + stylesheet.add(ParagraphStyle(name='Indent0', + leftIndent=18,) + ) + + stylesheet.add(ParagraphStyle(name='Indent1', + leftIndent=36, + firstLineIndent=0, + spaceBefore=1, + spaceAfter=7) + ) + + stylesheet.add(ParagraphStyle(name='Indent2', + leftIndent=50, + firstLineIndent=0, + spaceAfter=100) + ) + + stylesheet.add(ParagraphStyle(name='BodyText', + parent=stylesheet['Normal'], + spaceBefore=6) + ) + stylesheet.add(ParagraphStyle(name='Italic', + parent=stylesheet['BodyText'], + fontName = 'Times-Italic') + ) + + stylesheet.add(ParagraphStyle(name='Heading1', + parent=stylesheet['Normal'], + fontName = 'Times-Bold', + alignment=TA_CENTER, + fontSize=18, + leading=22, + spaceAfter=6), + alias='h1') + + stylesheet.add(ParagraphStyle(name='Heading2', + parent=stylesheet['Normal'], + fontName = 'Times-Bold', + fontSize=14, + leading=17, + spaceBefore=12, + spaceAfter=6), + alias='h2') + + stylesheet.add(ParagraphStyle(name='Heading3', + parent=stylesheet['Normal'], + fontName = 'Times-BoldItalic', + fontSize=12, + leading=14, + spaceBefore=12, + spaceAfter=6), + alias='h3') + + stylesheet.add(ParagraphStyle(name='Heading4', + parent=stylesheet['Normal'], + fontName = 'Times-BoldItalic', + spaceBefore=10, + spaceAfter=4), + alias='h4') + + stylesheet.add(ParagraphStyle(name='Title', + parent=stylesheet['Normal'], + fontName = 'Times-Bold', + fontSize=32, + leading=40, + spaceAfter=36, + alignment=TA_CENTER + ), + alias='t') + + stylesheet.add(ParagraphStyle(name='Bullet', + parent=stylesheet['Normal'], + firstLineIndent=0, + leftIndent=54, + bulletIndent=18, + spaceBefore=0, + bulletFontName='Symbol'), + alias='bu') + + stylesheet.add(ParagraphStyle(name='Definition', + parent=stylesheet['Normal'], + firstLineIndent=0, + leftIndent=36, + bulletIndent=0, + spaceBefore=6, + bulletFontName='Times-BoldItalic'), + alias='df') + + stylesheet.add(ParagraphStyle(name='Code', + parent=stylesheet['Normal'], + fontName='Courier', + textColor=colors.navy, + fontSize=8, + leading=8.8, + leftIndent=36, + firstLineIndent=0)) + + stylesheet.add(ParagraphStyle(name='Link', + parent=stylesheet['Code'], + spaceAfter=7, + spaceBefore=0, + leftIndent=55)) + + stylesheet.add(ParagraphStyle(name='FunctionHeader', + parent=stylesheet['Normal'], + fontName='Courier-Bold', + fontSize=8, + leading=8.8)) + + stylesheet.add(ParagraphStyle(name='DocString', + parent=stylesheet['Normal'], + fontName='Courier', + fontSize=8, + leftIndent=18, + leading=8.8)) + + stylesheet.add(ParagraphStyle(name='DocStringIndent', + parent=stylesheet['Normal'], + fontName='Courier', + fontSize=8, + leftIndent=36, + leading=8.8)) + + stylesheet.add(ParagraphStyle(name='URL', + parent=stylesheet['Normal'], + fontName='Courier', + textColor=colors.navy, + alignment=TA_CENTER), + alias='u') + + stylesheet.add(ParagraphStyle(name='Centred', + parent=stylesheet['Normal'], + alignment=TA_CENTER + )) + + stylesheet.add(ParagraphStyle(name='Caption', + parent=stylesheet['Centred'], + fontName='Times-Italic' + )) + + return stylesheet \ No newline at end of file diff --git a/bin/reportlab/tools/docco/t_parse.py b/bin/reportlab/tools/docco/t_parse.py new file mode 100644 index 00000000000..0dc66c3c879 --- /dev/null +++ b/bin/reportlab/tools/docco/t_parse.py @@ -0,0 +1,247 @@ +#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/docco/t_parse.py +""" +Template parsing module inspired by REXX (with thanks to Donn Cave for discussion). + +Template initialization has the form: + T = Template(template_string, wild_card_marker, single_char_marker, + x = regex_x, y = regex_y, ...) +Parsing has the form + ([match1, match2, ..., matchn], lastindex) = T.PARSE(string) + +Only the first argument is mandatory. + +The resultant object efficiently parses strings that match the template_string, +giving a list of substrings that correspond to each "directive" of the template. + +Template directives: + + Wildcard: + The template may be initialized with a wildcard that matches any string + up to the string matching the next directive (which may not be a wild + card or single character marker) or the next literal sequence of characters + of the template. The character that represents a wildcard is specified + by the wild_card_marker parameter, which has no default. + + For example, using X as the wildcard: + + + >>> T = Template("prefixXinteriorX", "X") + >>> T.PARSE("prefix this is before interior and this is after") + ([' this is before ', ' and this is after'], 47) + >>> T = Template("X", "X") + >>> T.PARSE('go to index') + (['A HREF="index.html"', 'go to index', '/A'], 36) + + Obviously the character used to represent the wildcard must be distinct + from the characters used to represent literals or other directives. + + Fixed length character sequences: + The template may have a marker character which indicates a fixed + length field. All adjacent instances of this marker will be matched + by a substring of the same length in the parsed string. For example: + + >>> T = Template("NNN-NN-NNNN", single_char_marker="N") + >>> T.PARSE("1-2-34-5-12") + (['1-2', '34', '5-12'], 11) + >>> T.PARSE("111-22-3333") + (['111', '22', '3333'], 11) + >>> T.PARSE("1111-22-3333") + ValueError: literal not found at (3, '-') + + A template may have multiple fixed length markers, which allows fixed + length fields to be adjacent, but recognized separately. For example: + + >>> T = Template("MMDDYYX", "X", "MDY") + >>> T.PARSE("112489 Somebody's birthday!") + (['11', '24', '89', " Somebody's birthday!"], 27) + + Regular expression markers: + The template may have markers associated with regular expressions. + the regular expressions may be either string represenations of compiled. + For example: + >>> T = Template("v: s i", v=id, s=str, i=int) + >>> T.PARSE("this_is_an_identifier: 'a string' 12344") + (['this_is_an_identifier', "'a string'", '12344'], 39) + >>> + Here id, str, and int are regular expression conveniences provided by + this module. + + Directive markers may be mixed and matched, except that wildcards cannot precede + wildcards or single character markers. + Example: +>>> T = Template("ssnum: NNN-NN-NNNN, fn=X, ln=X, age=I, quote=Q", "X", "N", I=int, Q=str) +>>> T.PARSE("ssnum: 123-45-6789, fn=Aaron, ln=Watters, age=13, quote='do be do be do'") +(['123', '45', '6789', 'Aaron', 'Watters', '13', "'do be do be do'"], 72) +>>> + +""" + +import re, string +from types import StringType +from string import find + +# +# template parsing +# +# EG: T = Template("(NNN)NNN-NNNN X X", "X", "N") +# ([area, exch, ext, fn, ln], index) = T.PARSE("(908)949-2726 Aaron Watters") +# +class Template: + + def __init__(self, + template, + wild_card_marker=None, + single_char_marker=None, + **marker_to_regex_dict): + self.template = template + self.wild_card = wild_card_marker + self.char = single_char_marker + # determine the set of markers for this template + markers = marker_to_regex_dict.keys() + if wild_card_marker: + markers.append(wild_card_marker) + if single_char_marker: + for ch in single_char_marker: # allow multiple scm's + markers.append(ch) + self.char = single_char_primary = single_char_marker[0] + self.markers = markers + for mark in markers: + if len(mark)>1: + raise ValueError, "Marks must be single characters: "+`mark` + # compile the regular expressions if needed + self.marker_dict = marker_dict = {} + for (mark, rgex) in marker_to_regex_dict.items(): + if type(rgex) == StringType: + rgex = re.compile(rgex) + marker_dict[mark] = rgex + # determine the parse sequence + parse_seq = [] + # dummy last char + lastchar = None + index = 0 + last = len(template) + # count the number of directives encountered + ndirectives = 0 + while index s blah", s=str) + s = "' <-- a string --> ' --> 'blah blah another string blah' blah" + print T1.PARSE(s) + + T2 = Template("s --> NNNiX", "X", "N", s=str, i=int) + print T2.PARSE("'A STRING' --> 15964653alpha beta gamma") + + T3 = Template("XsXi", "X", "N", s=str, i=int) + print T3.PARSE("prefix'string'interior1234junk not parsed") + + T4 = Template("MMDDYYX", "X", "MDY") + print T4.PARSE("122961 Somebody's birthday!") + + +if __name__=="__main__": test() \ No newline at end of file diff --git a/bin/reportlab/tools/docco/yaml.py b/bin/reportlab/tools/docco/yaml.py new file mode 100644 index 00000000000..af31788622e --- /dev/null +++ b/bin/reportlab/tools/docco/yaml.py @@ -0,0 +1,201 @@ +#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/docco/yaml.py +# parses "Yet Another Markup Language" into a list of tuples. +# Each tuple says what the data is e.g. +# ('Paragraph', 'Heading1', 'Why Reportlab Rules') +# and the pattern depends on type. +""" +Parser for "Aaron's Markup Language" - a markup language +which is easier to type in than XML, yet gives us a +reasonable selection of formats. + +The general rule is that if a line begins with a '.', +it requires special processing. Otherwise lines +are concatenated to paragraphs, and blank lines +separate paragraphs. + +If the line ".foo bar bletch" is encountered, +it immediately ends and writes out any current +paragraph. + +It then looks for a parser method called 'foo'; +if found, it is called with arguments (bar, bletch). + +If this is not found, it assumes that 'foo' is a +paragraph style, and the text for the first line +of the paragraph is 'bar bletch'. It would be +up to the formatter to decide whether on not 'foo' +was a valid paragraph. + +Special commands understood at present are: +.image filename +- adds the image to the document +.beginPre Code +- begins a Preformatted object in style 'Code' +.endPre +- ends a preformatted object. +""" + + +import sys +import string +import imp +import codegrab + +#modes: +PLAIN = 1 +PREFORMATTED = 2 + +BULLETCHAR = '\267' # assumes font Symbol, but works on all platforms + +class Parser: + def __init__(self): + self.reset() + + def reset(self): + self._lineNo = 0 + self._style = 'Normal' # the default + self._results = [] + self._buf = [] + self._mode = PLAIN + + def parseFile(self, filename): + #returns list of objects + data = open(filename, 'r').readlines() + + for line in data: + #strip trailing newlines + self.readLine(line[:-1]) + self.endPara() + return self._results + + def readLine(self, line): + #this is the inner loop + self._lineNo = self._lineNo + 1 + stripped = string.lstrip(line) + if len(stripped) == 0: + if self._mode == PLAIN: + self.endPara() + else: #preformatted, append it + self._buf.append(line) + elif line[0]=='.': + # we have a command of some kind + self.endPara() + words = string.split(stripped[1:]) + cmd, args = words[0], words[1:] + + #is it a parser method? + if hasattr(self.__class__, cmd): + method = eval('self.'+cmd) + #this was very bad; any type error in the method was hidden + #we have to hack the traceback + try: + apply(method, tuple(args)) + except TypeError, err: + sys.stderr.write("Parser method: apply(%s,%s) %s at line %d\n" % (cmd, tuple(args), err, self._lineNo)) + raise + else: + # assume it is a paragraph style - + # becomes the formatter's problem + self.endPara() #end the last one + words = string.split(stripped, ' ', 1) + assert len(words)==2, "Style %s but no data at line %d" % (words[0], self._lineNo) + (styletag, data) = words + self._style = styletag[1:] + self._buf.append(data) + else: + #we have data, add to para + self._buf.append(line) + + def endPara(self): + #ends the current paragraph, or preformatted block + + text = string.join(self._buf, ' ') + if text: + if self._mode == PREFORMATTED: + #item 3 is list of lines + self._results.append(('Preformatted', self._style, + string.join(self._buf,'\n'))) + else: + self._results.append(('Paragraph', self._style, text)) + self._buf = [] + self._style = 'Normal' + + def beginPre(self, stylename): + self._mode = PREFORMATTED + self._style = stylename + + def endPre(self): + self.endPara() + self._mode = PLAIN + + def image(self, filename): + self.endPara() + self._results.append(('Image', filename)) + + def vSpace(self, points): + """Inserts a vertical spacer""" + self._results.append(('VSpace', points)) + + def pageBreak(self): + """Inserts a frame break""" + self._results.append(('PageBreak','blah')) # must be a tuple + + def custom(self, moduleName, funcName): + """Goes and gets the Python object and adds it to the story""" + self.endPara() + self._results.append(('Custom',moduleName, funcName)) + + + + def getModuleDoc(self, modulename, pathname=None): + """Documents the entire module at this point by making + paragraphs and preformatted objects""" + docco = codegrab.getObjectsDefinedIn(modulename, pathname) + if docco.doc <> None: + self._results.append(('Paragraph', 'DocString', docco.doc)) + if len(docco.functions) > 0: + for fn in docco.functions: + if fn.status == 'official': + self._results.append(('Preformatted','FunctionHeader', fn.proto)) + self._results.append(('Preformatted','DocString', fn.doc)) + + if len(docco.classes) > 0: + for cls in docco.classes: + if cls.status == 'official': + self._results.append(('Preformatted','FunctionHeader', 'Class %s:' % cls.name)) + self._results.append(('Preformatted','DocString', cls.doc)) + for mth in cls.methods: + if mth.status == 'official': + self._results.append(('Preformatted','FunctionHeader', mth.proto)) + self._results.append(('Preformatted','DocStringIndent', mth.doc)) + + + def getClassDoc(self, modulename, classname, pathname=None): + """Documents the class and its public methods""" + docco = codegrab.getObjectsDefinedIn(modulename, pathname) + found = 0 + for cls in docco.classes: + if cls.name == classname: + found = 1 + self._results.append(('Preformatted','FunctionHeader', 'Class %s:' % cls.name)) + self._results.append(('Preformatted','DocString', cls.doc)) + for mth in cls.methods: + if mth.status == 'official': + self._results.append(('Preformatted','FunctionHeader', mth.proto)) + self._results.append(('Preformatted','DocStringIndent', mth.doc)) + break + assert found, 'No Classes Defined in ' + modulename + + def nextPageTemplate(self, templateName): + self._results.append(('NextPageTemplate',templateName)) + +if __name__=='__main__': #NORUNTESTS + if len(sys.argv) <> 2: + print 'usage: yaml.py source.txt' + else: + p = Parser() + results = p.parseFile(sys.argv[1]) + import pprint + pprint.pprint(results) \ No newline at end of file diff --git a/bin/reportlab/tools/docco/yaml2pdf.py b/bin/reportlab/tools/docco/yaml2pdf.py new file mode 100644 index 00000000000..454bf53cf71 --- /dev/null +++ b/bin/reportlab/tools/docco/yaml2pdf.py @@ -0,0 +1,104 @@ +#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/docco/yaml2pdf.py +# yaml2pdf - turns stuff in Yet Another Markup Language +# into PDF documents. Very crude - it assumes a +# doc template and stylesheet (hard coded for now) +# and basically cranks out paragraphs in each style +"""yaml2pdf.py - converts Yet Another Markup Language +to reasonable PDF documents. This is ReportLab's +basic documentation tool. + +Usage: +. "yaml2pdf.py filename.ext" will create "filename.pdf" +""" + +import sys +import os +import imp + +import yaml +from rltemplate import RLDocTemplate +from reportlab.lib.styles import ParagraphStyle +from reportlab.lib.enums import * +from reportlab.lib.pagesizes import A4 +from reportlab.platypus import * +from reportlab.lib import colors +from reportlab.lib.units import inch + + +from stylesheet import getStyleSheet + + +def run(infilename, outfilename): + p = yaml.Parser() + results = p.parseFile(infilename) + + ss = getStyleSheet() + + #now make flowables from the results + story = [] + for thingy in results: + typ = thingy[0] + if typ == 'Paragraph': + (typ2, stylename, text) = thingy + if stylename == 'bu': + bulletText='\267' + else: + bulletText=None + try: + style = ss[stylename] + except KeyError: + print 'Paragraph style "%s" not found in stylesheet, using Normal instead' % stylename + style = ss['Normal'] + story.append(Paragraph(text, style, bulletText=bulletText)) + elif typ == 'Preformatted': + (typ2, stylename, text) = thingy + try: + style = ss[stylename] + except KeyError: + print 'Preformatted style "%s" not found in stylesheet, using Normal instead' % stylename + style = ss['Normal'] + story.append(Preformatted(text, style, bulletText=bulletText)) + elif typ == 'Image': + filename = thingy[1] + img = Image(filename) + story.append(img) + elif typ == 'PageBreak': + story.append(PageBreak()) + elif typ == 'VSpace': + height = thingy[1] + story.append(Spacer(0, height)) + elif typ == 'NextPageTemplate': + story.append(NextPageTemplate(thingy[1])) + elif typ == 'Custom': + # go find it + searchPath = [os.getcwd()+'\\'] + (typ2, moduleName, funcName) = thingy + found = imp.find_module(moduleName, searchPath) + assert found, "Custom object module %s not found" % moduleName + (file, pathname, description) = found + mod = imp.load_module(moduleName, file, pathname, description) + + #now get the function + func = getattr(mod, funcName) + story.append(func()) + + else: + print 'skipping',typ, 'for now' + + + #print it + doc = RLDocTemplate(outfilename, pagesize=A4) + doc.build(story) + +if __name__ == '__main__': #NORUNTESTS + if len(sys.argv) == 2: + infilename = sys.argv[1] + outfilename = os.path.splitext(infilename)[0] + '.pdf' + if os.path.isfile(infilename): + run(infilename, outfilename) + else: + print 'File not found %s' % infilename + else: + print __doc__ \ No newline at end of file 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 [ ...]" + 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" ) + 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] [ ...] + + 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= 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 new file mode 100644 index 00000000000..8655230bf43 Binary files /dev/null and b/bin/reportlab/tools/py2pdf/vertpython.jpg differ diff --git a/bin/reportlab/tools/pythonpoint/README b/bin/reportlab/tools/pythonpoint/README new file mode 100644 index 00000000000..f6d8808bdab --- /dev/null +++ b/bin/reportlab/tools/pythonpoint/README @@ -0,0 +1,29 @@ +PythonPoint is a utility for generating PDF slides from +a simple XML format - "PythonPoint Markup Language". + +This is early days. It lets you produce quite sophisticated +output, but the DTD will undoubtedly evolve and change. +However, I want people to be able to use it this summer so am +releasing now. + +It is part of the ReportLab distribution; if you have managed +to run the ReportLab tests, and have installed the Python +Imaging Library, usage should be straightforward. PIL is not +required to make slides, but the demo will have a few blanks if +you omit it. + +To use, cd to the pythonpoint directory and execute: + pythonpoint.py pythonpoint.xml +This will create pythonpoint.pdf, which is your manual. + +You can also try 'monterey.xml', which is a talk I gave +at the O'Reilly Open Source conference in Monterey +in summer 1999. + +We have issues to resolve over module load paths; +the easiest solution for now is to put the PythonPoint +directory on your path; work in a new directory; +and explicitly include the paths to any custom shapes, +and style sheets you use. + +- Andy Robinson, 6 April 2000 \ No newline at end of file diff --git a/bin/reportlab/tools/pythonpoint/__init__.py b/bin/reportlab/tools/pythonpoint/__init__.py new file mode 100644 index 00000000000..c4b994f848b --- /dev/null +++ b/bin/reportlab/tools/pythonpoint/__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/pythonpoint/__init__.py diff --git a/bin/reportlab/tools/pythonpoint/customshapes.py b/bin/reportlab/tools/pythonpoint/customshapes.py new file mode 100644 index 00000000000..779a236eec6 --- /dev/null +++ b/bin/reportlab/tools/pythonpoint/customshapes.py @@ -0,0 +1,298 @@ +#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/pythonpoint/customshapes.py +__version__=''' $Id: customshapes.py 2385 2004-06-17 15:26:05Z rgbecker $ ''' + +# xml parser stuff for PythonPoint +# PythonPoint Markup Language! + +__doc__=""" +This demonstrates a custom shape for use with the <customshape> tag. +The shape must fulfil a very simple interface, which may change in +future. + +The XML tag currently has this form: + <customshape + module="customshapes.py" + class = "MyShape" + initargs="(100,200,3)" + /> + +PythonPoint will look in the given module for the given class, +evaluate the arguments string and pass it to the constructor. +Then, it will call + + object.drawOn(canvas) + +Thus your object must be fully defined by the constructor. +For this one, we pass three argumenyts: x, y and scale. +This does a five-tile jigsaw over which words can be overlaid; +based on work done for a customer's presentation. +""" + + +import reportlab.pdfgen.canvas +from reportlab.lib import colors +from reportlab.lib.corp import RL_CorpLogo +from reportlab.graphics.shapes import Drawing + +## custom shape for use with PythonPoint. + +class Jigsaw: + """This draws a jigsaw patterm. By default it is centred on 0,0 + and has dimensions of 200 x 140; use the x/y/scale attributes + to move it around.""" + #Using my usual bulldozer coding style - I am sure a mathematician could + #derive an elegant way to draw this, but I just took a ruler, guessed at + #the control points, and reflected a few lists at the interactive prompt. + + def __init__(self, x, y, scale=1): + self.width = 200 + self.height = 140 + self.x = x + self.y = y + self.scale = scale + + + def drawOn(self, canvas): + canvas.saveState() + + canvas.setFont('Helvetica-Bold',24) + canvas.drawString(600, 100, 'A Custom Shape') + + canvas.translate(self.x, self.y) + canvas.scale(self.scale, self.scale) + self.drawBounds(canvas) + + self.drawCentre(canvas) + self.drawTopLeft(canvas) + self.drawBottomLeft(canvas) + self.drawBottomRight(canvas) + self.drawTopRight(canvas) + + canvas.restoreState() + + + def curveThrough(self, path, pointlist): + """Helper to curve through set of control points.""" + assert len(pointlist) % 3 == 1, "No. of points must be 3n+1 for integer n" + (x,y) = pointlist[0] + path.moveTo(x, y) + idx = 1 + while idx < len(pointlist)-2: + p1, p2, p3 = pointlist[idx:idx+3] + path.curveTo(p1[0], p1[1], p2[0], p2[1], p3[0], p3[1]) + idx = idx + 3 + + + def drawShape(self, canvas, controls, color): + """Utlity to draw a closed shape through a list of control points; + extends the previous proc""" + canvas.setFillColor(color) + p = canvas.beginPath() + self.curveThrough(p, controls) + p.close() + canvas.drawPath(p, stroke=1, fill=1) + + + def drawBounds(self, canvas): + """Guidelines to help me draw - not needed in production""" + canvas.setStrokeColor(colors.red) + canvas.rect(-100,-70,200,140) + canvas.line(-100,0,100,0) + canvas.line(0,70,0,-70) + canvas.setStrokeColor(colors.black) + + + def drawCentre(self, canvas): + controls = [ (0,50), #top + + #top right edge - duplicated for that corner piece + (5,50),(10,45),(10,40), + (10,35),(15,30),(20,30), + (25,30),(30,25),(30,20), + (30,15),(35,10),(40,10), + (45,10),(50,5),(50,0), + + #bottom right edge + (50, -5), (45,-10), (40,-10), + (35,-10), (30,-15), (30, -20), + (30,-25), (25,-30), (20,-30), + (15,-30), (10,-35), (10,-40), + (10,-45),(5,-50),(0,-50), + + #bottom left + (-5,-50),(-10,-45),(-10,-40), + (-10,-35),(-15,-30),(-20,-30), + (-25,-30),(-30,-25),(-30,-20), + (-30,-15),(-35,-10),(-40,-10), + (-45,-10),(-50,-5),(-50,0), + + #top left + (-50,5),(-45,10),(-40,10), + (-35,10),(-30,15),(-30,20), + (-30,25),(-25,30),(-20,30), + (-15,30),(-10,35),(-10,40), + (-10,45),(-5,50),(0,50) + + ] + + self.drawShape(canvas, controls, colors.yellow) + + + def drawTopLeft(self, canvas): + controls = [(-100,70), + (-100,69),(-100,1),(-100,0), + (-99,0),(-91,0),(-90,0), + + #jigsaw interlock - 4 sections + (-90,5),(-92,5),(-92,10), + (-92,15), (-85,15), (-80,15), + (-75,15),(-68,15),(-68,10), + (-68,5),(-70,5),(-70,0), + (-69,0),(-51,0),(-50,0), + + #five distinct curves + (-50,5),(-45,10),(-40,10), + (-35,10),(-30,15),(-30,20), + (-30,25),(-25,30),(-20,30), + (-15,30),(-10,35),(-10,40), + (-10,45),(-5,50),(0,50), + + (0,51),(0,69),(0,70), + (-1,70),(-99,70),(-100,70) + ] + self.drawShape(canvas, controls, colors.teal) + + + def drawBottomLeft(self, canvas): + + controls = [(-100,-70), + (-99,-70),(-1,-70),(0,-70), + (0,-69),(0,-51),(0,-50), + + #wavyline + (-5,-50),(-10,-45),(-10,-40), + (-10,-35),(-15,-30),(-20,-30), + (-25,-30),(-30,-25),(-30,-20), + (-30,-15),(-35,-10),(-40,-10), + (-45,-10),(-50,-5),(-50,0), + + #jigsaw interlock - 4 sections + + (-51, 0), (-69, 0), (-70, 0), + (-70, 5), (-68, 5), (-68, 10), + (-68, 15), (-75, 15), (-80, 15), + (-85, 15), (-92, 15), (-92, 10), + (-92, 5), (-90, 5), (-90, 0), + + (-91,0),(-99,0),(-100,0) + + ] + self.drawShape(canvas, controls, colors.green) + + + def drawBottomRight(self, canvas): + + controls = [ (100,-70), + (100,-69),(100,-1),(100,0), + (99,0),(91,0),(90,0), + + #jigsaw interlock - 4 sections + (90, -5), (92, -5), (92, -10), + (92, -15), (85, -15), (80, -15), + (75, -15), (68, -15), (68, -10), + (68, -5), (70, -5), (70, 0), + (69, 0), (51, 0), (50, 0), + + #wavyline + (50, -5), (45,-10), (40,-10), + (35,-10), (30,-15), (30, -20), + (30,-25), (25,-30), (20,-30), + (15,-30), (10,-35), (10,-40), + (10,-45),(5,-50),(0,-50), + + (0,-51), (0,-69), (0,-70), + (1,-70),(99,-70),(100,-70) + + ] + self.drawShape(canvas, controls, colors.navy) + + + def drawBottomLeft(self, canvas): + + controls = [(-100,-70), + (-99,-70),(-1,-70),(0,-70), + (0,-69),(0,-51),(0,-50), + + #wavyline + (-5,-50),(-10,-45),(-10,-40), + (-10,-35),(-15,-30),(-20,-30), + (-25,-30),(-30,-25),(-30,-20), + (-30,-15),(-35,-10),(-40,-10), + (-45,-10),(-50,-5),(-50,0), + + #jigsaw interlock - 4 sections + + (-51, 0), (-69, 0), (-70, 0), + (-70, 5), (-68, 5), (-68, 10), + (-68, 15), (-75, 15), (-80, 15), + (-85, 15), (-92, 15), (-92, 10), + (-92, 5), (-90, 5), (-90, 0), + + (-91,0),(-99,0),(-100,0) + + ] + self.drawShape(canvas, controls, colors.green) + + + def drawTopRight(self, canvas): + controls = [(100, 70), + (99, 70), (1, 70), (0, 70), + (0, 69), (0, 51), (0, 50), + (5, 50), (10, 45), (10, 40), + (10, 35), (15, 30), (20, 30), + (25, 30), (30, 25), (30, 20), + (30, 15), (35, 10), (40, 10), + (45, 10), (50, 5), (50, 0), + (51, 0), (69, 0), (70, 0), + (70, -5), (68, -5), (68, -10), + (68, -15), (75, -15), (80, -15), + (85, -15), (92, -15), (92, -10), + (92, -5), (90, -5), (90, 0), + (91, 0), (99, 0), (100, 0) + ] + + self.drawShape(canvas, controls, colors.magenta) + + +class Logo: + """This draws a ReportLab Logo.""" + + def __init__(self, x, y, width, height): + logo = RL_CorpLogo() + logo.x = x + logo.y = y + logo.width = width + logo.height = height + self.logo = logo + + def drawOn(self, canvas): + logo = self.logo + x, y = logo.x, logo.y + w, h = logo.width, logo.height + D = Drawing(w, h) + D.add(logo) + D.drawOn(canvas, 0, 0) + + +def run(): + c = reportlab.pdfgen.canvas.Canvas('customshape.pdf') + + J = Jigsaw(300, 540, 2) + J.drawOn(c) + c.save() + + +if __name__ == '__main__': + run() \ No newline at end of file diff --git a/bin/reportlab/tools/pythonpoint/demos/LeERC___.AFM b/bin/reportlab/tools/pythonpoint/demos/LeERC___.AFM new file mode 100644 index 00000000000..fe491e473a2 --- /dev/null +++ b/bin/reportlab/tools/pythonpoint/demos/LeERC___.AFM @@ -0,0 +1,93 @@ +StartFontMetrics 2.0 +Comment Generated by RoboFog 08-06-2001 4:26:40 PM +FontName LettErrorRobot-Chrome +FullName LettErrorRobot-Chrome +FamilyName LettErrorRobot +Weight Medium +Notice (C) 1998-2001 LettError, Just van Rossum, Erik van Blokland, http://www.letterror.com/ +ItalicAngle 0 +IsFixedPitch false +UnderlinePosition -133 +UnderlineThickness 20 +Version 001.000 +EncodingScheme AdobeStandardEncoding +FontBBox -53 -252 1047 752 +CapHeight 647 +XHeight 548 +Descender -252 +Ascender 747 +StartCharMetrics 68 +C 32 ; WX 300 ; N space ; B 0 0 0 0 ; +C 33 ; WX 300 ; N exclam ; B 48 -52 253 652 ; +C 38 ; WX 700 ; N ampersand ; B 48 -47 653 647 ; +C 42 ; WX 700 ; N asterisk ; B 48 148 653 752 ; +C 48 ; WX 700 ; N zero ; B 53 -47 648 548 ; +C 49 ; WX 500 ; N one ; B 48 -47 453 553 ; +C 50 ; WX 600 ; N two ; B 48 -47 553 548 ; +C 51 ; WX 600 ; N three ; B 48 -147 553 548 ; +C 52 ; WX 700 ; N four ; B 48 -152 653 553 ; +C 53 ; WX 600 ; N five ; B 48 -147 553 548 ; +C 54 ; WX 600 ; N six ; B 53 -47 553 647 ; +C 55 ; WX 600 ; N seven ; B 48 -152 548 548 ; +C 56 ; WX 600 ; N eight ; B 48 -47 553 647 ; +C 57 ; WX 600 ; N nine ; B 48 -147 548 548 ; +C 63 ; WX 500 ; N question ; B 48 -52 448 647 ; +C 64 ; WX 800 ; N at ; B 53 -47 748 647 ; +C 65 ; WX 700 ; N A ; B 53 -52 648 652 ; +C 66 ; WX 600 ; N B ; B 53 -47 553 647 ; +C 67 ; WX 600 ; N C ; B 53 -47 553 647 ; +C 68 ; WX 700 ; N D ; B 53 -47 648 647 ; +C 69 ; WX 600 ; N E ; B 53 -47 553 647 ; +C 70 ; WX 600 ; N F ; B 53 -52 553 647 ; +C 71 ; WX 700 ; N G ; B 53 -47 653 647 ; +C 72 ; WX 700 ; N H ; B 53 -52 648 652 ; +C 73 ; WX 300 ; N I ; B 53 -52 248 652 ; +C 74 ; WX 300 ; N J ; B -53 -252 248 652 ; +C 75 ; WX 700 ; N K ; B 53 -52 653 652 ; +C 76 ; WX 600 ; N L ; B 53 -47 553 652 ; +C 77 ; WX 900 ; N M ; B 53 -52 848 652 ; +C 78 ; WX 700 ; N N ; B 53 -52 648 652 ; +C 79 ; WX 700 ; N O ; B 53 -47 648 647 ; +C 80 ; WX 600 ; N P ; B 53 -52 548 647 ; +C 81 ; WX 700 ; N Q ; B 53 -252 653 647 ; +C 82 ; WX 600 ; N R ; B 53 -52 653 647 ; +C 83 ; WX 600 ; N S ; B 48 -47 553 647 ; +C 84 ; WX 700 ; N T ; B 48 -52 653 647 ; +C 85 ; WX 700 ; N U ; B 53 -47 648 652 ; +C 86 ; WX 700 ; N V ; B 53 -52 648 652 ; +C 87 ; WX 1100 ; N W ; B 53 -52 1047 652 ; +C 88 ; WX 700 ; N X ; B 48 -52 653 652 ; +C 89 ; WX 700 ; N Y ; B 53 -52 648 652 ; +C 90 ; WX 700 ; N Z ; B 48 -47 653 647 ; +C 97 ; WX 600 ; N a ; B 48 -47 548 548 ; +C 98 ; WX 600 ; N b ; B 53 -47 548 752 ; +C 99 ; WX 600 ; N c ; B 53 -47 553 548 ; +C 100 ; WX 600 ; N d ; B 53 -47 548 752 ; +C 101 ; WX 600 ; N e ; B 53 -47 553 548 ; +C 102 ; WX 400 ; N f ; B -53 -52 453 747 ; +C 103 ; WX 600 ; N g ; B 48 -247 548 548 ; +C 104 ; WX 600 ; N h ; B 53 -52 548 752 ; +C 105 ; WX 300 ; N i ; B 48 -52 253 752 ; +C 106 ; WX 300 ; N j ; B -53 -252 253 752 ; +C 107 ; WX 600 ; N k ; B 53 -52 553 752 ; +C 108 ; WX 300 ; N l ; B 53 -52 248 752 ; +C 109 ; WX 900 ; N m ; B 53 -52 848 548 ; +C 110 ; WX 600 ; N n ; B 53 -52 548 548 ; +C 111 ; WX 600 ; N o ; B 53 -47 548 548 ; +C 112 ; WX 600 ; N p ; B 53 -252 548 548 ; +C 113 ; WX 600 ; N q ; B 53 -252 548 548 ; +C 114 ; WX 400 ; N r ; B 53 -52 453 553 ; +C 115 ; WX 600 ; N s ; B 48 -47 553 548 ; +C 116 ; WX 400 ; N t ; B -53 -47 453 652 ; +C 117 ; WX 600 ; N u ; B 53 -47 548 553 ; +C 118 ; WX 600 ; N v ; B 53 -52 548 553 ; +C 119 ; WX 900 ; N w ; B 53 -52 848 553 ; +C 120 ; WX 700 ; N x ; B 48 -52 653 553 ; +C 121 ; WX 600 ; N y ; B 48 -252 548 553 ; +C 122 ; WX 500 ; N z ; B -53 -47 553 548 ; +EndCharMetrics +StartKernData +StartKernPairs 0 +EndKernPairs +EndKernData +EndFontMetrics diff --git a/bin/reportlab/tools/pythonpoint/demos/LeERC___.PFB b/bin/reportlab/tools/pythonpoint/demos/LeERC___.PFB new file mode 100644 index 00000000000..c1cf7ec189a Binary files /dev/null and b/bin/reportlab/tools/pythonpoint/demos/LeERC___.PFB differ diff --git a/bin/reportlab/tools/pythonpoint/demos/examples.py b/bin/reportlab/tools/pythonpoint/demos/examples.py new file mode 100644 index 00000000000..14aee2336fb --- /dev/null +++ b/bin/reportlab/tools/pythonpoint/demos/examples.py @@ -0,0 +1,841 @@ +import string + +testannotations=""" +def annotations(canvas): + from reportlab.lib.units import inch + canvas.drawString(inch, 2.5*inch, + "setAuthor, setTitle, setSubject have no visible effect") + canvas.drawString(inch, inch, "But if you are viewing this document dynamically") + canvas.drawString(inch, 0.5*inch, "please look at File/Document Info") + canvas.setAuthor("the ReportLab Team") + canvas.setTitle("ReportLab PDF Generation User Guide") + canvas.setSubject("How to Generate PDF files using the ReportLab modules") +""" + +# magic function making module + +test1 = """ +def f(a,b): + print "it worked", a, b + return a+b +""" + +test2 = """ +def g(n): + if n==0: return 1 + else: return n*g(n-1) + """ + +testhello = """ +def hello(c): + from reportlab.lib.units import inch + # move the origin up and to the left + c.translate(inch,inch) + # define a large font + c.setFont("Helvetica", 14) + # choose some colors + c.setStrokeColorRGB(0.2,0.5,0.3) + c.setFillColorRGB(1,0,1) + # draw some lines + c.line(0,0,0,1.7*inch) + c.line(0,0,1*inch,0) + # draw a rectangle + c.rect(0.2*inch,0.2*inch,1*inch,1.5*inch, fill=1) + # make text go straight up + c.rotate(90) + # change color + c.setFillColorRGB(0,0,0.77) + # say hello (note after rotate the y coord needs to be negative!) + c.drawString(0.3*inch, -inch, "Hello World") +""" + +testcoords = """ +def coords(canvas): + from reportlab.lib.units import inch + from reportlab.lib.colors import pink, black, red, blue, green + c = canvas + c.setStrokeColor(pink) + c.grid([inch, 2*inch, 3*inch, 4*inch], [0.5*inch, inch, 1.5*inch, 2*inch, 2.5*inch]) + c.setStrokeColor(black) + c.setFont("Times-Roman", 20) + c.drawString(0,0, "(0,0) the Origin") + c.drawString(2.5*inch, inch, "(2.5,1) in inches") + c.drawString(4*inch, 2.5*inch, "(4, 2.5)") + c.setFillColor(red) + c.rect(0,2*inch,0.2*inch,0.3*inch, fill=1) + c.setFillColor(green) + c.circle(4.5*inch, 0.4*inch, 0.2*inch, fill=1) +""" + +testtranslate = """ +def translate(canvas): + from reportlab.lib.units import cm + canvas.translate(2.3*cm, 0.3*cm) + coords(canvas) + """ + +testscale = """ +def scale(canvas): + canvas.scale(0.75, 0.5) + coords(canvas) +""" + +testscaletranslate = """ +def scaletranslate(canvas): + from reportlab.lib.units import inch + canvas.setFont("Courier-BoldOblique", 12) + # save the state + canvas.saveState() + # scale then translate + canvas.scale(0.3, 0.5) + canvas.translate(2.4*inch, 1.5*inch) + canvas.drawString(0, 2.7*inch, "Scale then translate") + coords(canvas) + # forget the scale and translate... + canvas.restoreState() + # translate then scale + canvas.translate(2.4*inch, 1.5*inch) + canvas.scale(0.3, 0.5) + canvas.drawString(0, 2.7*inch, "Translate then scale") + coords(canvas) +""" + +testmirror = """ +def mirror(canvas): + from reportlab.lib.units import inch + canvas.translate(5.5*inch, 0) + canvas.scale(-1.0, 1.0) + coords(canvas) +""" + +testcolors = """ +def colors(canvas): + from reportlab.lib import colors + from reportlab.lib.units import inch + black = colors.black + y = x = 0; dy=inch*3/4.0; dx=inch*5.5/5; w=h=dy/2; rdx=(dx-w)/2 + rdy=h/5.0; texty=h+2*rdy + canvas.setFont("Helvetica",10) + for [namedcolor, name] in ( + [colors.lavenderblush, "lavenderblush"], + [colors.lawngreen, "lawngreen"], + [colors.lemonchiffon, "lemonchiffon"], + [colors.lightblue, "lightblue"], + [colors.lightcoral, "lightcoral"]): + canvas.setFillColor(namedcolor) + canvas.rect(x+rdx, y+rdy, w, h, fill=1) + canvas.setFillColor(black) + canvas.drawCentredString(x+dx/2, y+texty, name) + x = x+dx + y = y + dy; x = 0 + for rgb in [(1,0,0), (0,1,0), (0,0,1), (0.5,0.3,0.1), (0.4,0.5,0.3)]: + r,g,b = rgb + canvas.setFillColorRGB(r,g,b) + canvas.rect(x+rdx, y+rdy, w, h, fill=1) + canvas.setFillColor(black) + canvas.drawCentredString(x+dx/2, y+texty, "r%s g%s b%s"%rgb) + x = x+dx + y = y + dy; x = 0 + for cmyk in [(1,0,0,0), (0,1,0,0), (0,0,1,0), (0,0,0,1), (0,0,0,0)]: + c,m,y1,k = cmyk + canvas.setFillColorCMYK(c,m,y1,k) + canvas.rect(x+rdx, y+rdy, w, h, fill=1) + canvas.setFillColor(black) + canvas.drawCentredString(x+dx/2, y+texty, "c%s m%s y%s k%s"%cmyk) + x = x+dx + y = y + dy; x = 0 + for gray in (0.0, 0.25, 0.50, 0.75, 1.0): + canvas.setFillGray(gray) + canvas.rect(x+rdx, y+rdy, w, h, fill=1) + canvas.setFillColor(black) + canvas.drawCentredString(x+dx/2, y+texty, "gray: %s"%gray) + x = x+dx +""" + +testspumoni = """ +def spumoni(canvas): + from reportlab.lib.units import inch + from reportlab.lib.colors import pink, green, brown, white + x = 0; dx = 0.4*inch + for i in range(4): + for color in (pink, green, brown): + canvas.setFillColor(color) + canvas.rect(x,0,dx,3*inch,stroke=0,fill=1) + x = x+dx + canvas.setFillColor(white) + canvas.setStrokeColor(white) + canvas.setFont("Helvetica-Bold", 85) + canvas.drawCentredString(2.75*inch, 1.3*inch, "SPUMONI") +""" + +testspumoni2 = """ +def spumoni2(canvas): + from reportlab.lib.units import inch + from reportlab.lib.colors import pink, green, brown, white, black + # draw the previous drawing + spumoni(canvas) + # now put an ice cream cone on top of it: + # first draw a triangle (ice cream cone) + p = canvas.beginPath() + xcenter = 2.75*inch + radius = 0.45*inch + p.moveTo(xcenter-radius, 1.5*inch) + p.lineTo(xcenter+radius, 1.5*inch) + p.lineTo(xcenter, 0) + canvas.setFillColor(brown) + canvas.setStrokeColor(black) + canvas.drawPath(p, fill=1) + # draw some circles (scoops) + y = 1.5*inch + for color in (pink, green, brown): + canvas.setFillColor(color) + canvas.circle(xcenter, y, radius, fill=1) + y = y+radius +""" + +testbezier = """ +def bezier(canvas): + from reportlab.lib.colors import yellow, green, red, black + from reportlab.lib.units import inch + i = inch + d = i/4 + # define the bezier curve control points + x1,y1, x2,y2, x3,y3, x4,y4 = d,1.5*i, 1.5*i,d, 3*i,d, 5.5*i-d,3*i-d + # draw a figure enclosing the control points + canvas.setFillColor(yellow) + p = canvas.beginPath() + p.moveTo(x1,y1) + for (x,y) in [(x2,y2), (x3,y3), (x4,y4)]: + p.lineTo(x,y) + canvas.drawPath(p, fill=1, stroke=0) + # draw the tangent lines + canvas.setLineWidth(inch*0.1) + canvas.setStrokeColor(green) + canvas.line(x1,y1,x2,y2) + canvas.setStrokeColor(red) + canvas.line(x3,y3,x4,y4) + # finally draw the curve + canvas.setStrokeColor(black) + canvas.bezier(x1,y1, x2,y2, x3,y3, x4,y4) +""" + +testbezier2 = """ +def bezier2(canvas): + from reportlab.lib.colors import yellow, green, red, black + from reportlab.lib.units import inch + # make a sequence of control points + xd,yd = 5.5*inch/2, 3*inch/2 + xc,yc = xd,yd + dxdy = [(0,0.33), (0.33,0.33), (0.75,1), (0.875,0.875), + (0.875,0.875), (1,0.75), (0.33,0.33), (0.33,0)] + pointlist = [] + for xoffset in (1,-1): + yoffset = xoffset + for (dx,dy) in dxdy: + px = xc + xd*xoffset*dx + py = yc + yd*yoffset*dy + pointlist.append((px,py)) + yoffset = -xoffset + for (dy,dx) in dxdy: + px = xc + xd*xoffset*dx + py = yc + yd*yoffset*dy + pointlist.append((px,py)) + # draw tangent lines and curves + canvas.setLineWidth(inch*0.1) + while pointlist: + [(x1,y1),(x2,y2),(x3,y3),(x4,y4)] = pointlist[:4] + del pointlist[:4] + canvas.setLineWidth(inch*0.1) + canvas.setStrokeColor(green) + canvas.line(x1,y1,x2,y2) + canvas.setStrokeColor(red) + canvas.line(x3,y3,x4,y4) + # finally draw the curve + canvas.setStrokeColor(black) + canvas.bezier(x1,y1, x2,y2, x3,y3, x4,y4) +""" + +testpencil = """ +def pencil(canvas, text="No.2"): + from reportlab.lib.colors import yellow, red, black,white + from reportlab.lib.units import inch + u = inch/10.0 + canvas.setStrokeColor(black) + canvas.setLineWidth(4) + # draw erasor + canvas.setFillColor(red) + canvas.circle(30*u, 5*u, 5*u, stroke=1, fill=1) + # draw all else but the tip (mainly rectangles with different fills) + canvas.setFillColor(yellow) + canvas.rect(10*u,0,20*u,10*u, stroke=1, fill=1) + canvas.setFillColor(black) + canvas.rect(23*u,0,8*u,10*u,fill=1) + canvas.roundRect(14*u, 3.5*u, 8*u, 3*u, 1.5*u, stroke=1, fill=1) + canvas.setFillColor(white) + canvas.rect(25*u,u,1.2*u,8*u, fill=1,stroke=0) + canvas.rect(27.5*u,u,1.2*u,8*u, fill=1, stroke=0) + canvas.setFont("Times-Roman", 3*u) + canvas.drawCentredString(18*u, 4*u, text) + # now draw the tip + penciltip(canvas,debug=0) + # draw broken lines across the body. + canvas.setDash([10,5,16,10],0) + canvas.line(11*u,2.5*u,22*u,2.5*u) + canvas.line(22*u,7.5*u,12*u,7.5*u) + """ + +testpenciltip = """ +def penciltip(canvas, debug=1): + from reportlab.lib.colors import tan, black, green + from reportlab.lib.units import inch + u = inch/10.0 + canvas.setLineWidth(4) + if debug: + canvas.scale(2.8,2.8) # make it big + canvas.setLineWidth(1) # small lines + canvas.setStrokeColor(black) + canvas.setFillColor(tan) + p = canvas.beginPath() + p.moveTo(10*u,0) + p.lineTo(0,5*u) + p.lineTo(10*u,10*u) + p.curveTo(11.5*u,10*u, 11.5*u,7.5*u, 10*u,7.5*u) + p.curveTo(12*u,7.5*u, 11*u,2.5*u, 9.7*u,2.5*u) + p.curveTo(10.5*u,2.5*u, 11*u,0, 10*u,0) + canvas.drawPath(p, stroke=1, fill=1) + canvas.setFillColor(black) + p = canvas.beginPath() + p.moveTo(0,5*u) + p.lineTo(4*u,3*u) + p.lineTo(5*u,4.5*u) + p.lineTo(3*u,6.5*u) + canvas.drawPath(p, stroke=1, fill=1) + if debug: + canvas.setStrokeColor(green) # put in a frame of reference + canvas.grid([0,5*u,10*u,15*u], [0,5*u,10*u]) +""" + +testnoteannotation = """ +from reportlab.platypus.flowables import Flowable +class NoteAnnotation(Flowable): + '''put a pencil in the margin.''' + def wrap(self, *args): + return (1,10) # I take up very little space! (?) + def draw(self): + canvas = self.canv + canvas.translate(-10,-10) + canvas.rotate(180) + canvas.scale(0.2,0.2) + pencil(canvas, text="NOTE") +""" + +testhandannotation = """ +from reportlab.platypus.flowables import Flowable +from reportlab.lib.colors import tan, green +class HandAnnotation(Flowable): + '''A hand flowable.''' + def __init__(self, xoffset=0, size=None, fillcolor=tan, strokecolor=green): + from reportlab.lib.units import inch + if size is None: size=4*inch + self.fillcolor, self.strokecolor = fillcolor, strokecolor + self.xoffset = xoffset + self.size = size + # normal size is 4 inches + self.scale = size/(4.0*inch) + def wrap(self, *args): + return (self.xoffset, self.size) + def draw(self): + canvas = self.canv + canvas.setLineWidth(6) + canvas.setFillColor(self.fillcolor) + canvas.setStrokeColor(self.strokecolor) + canvas.translate(self.xoffset+self.size,0) + canvas.rotate(90) + canvas.scale(self.scale, self.scale) + hand(canvas, debug=0, fill=1) +""" + +lyrics = '''\ +well she hit Net Solutions +and she registered her own .com site now +and filled it up with yahoo profile pics +she snarfed in one night now +and she made 50 million when Hugh Hefner +bought up the rights now +and she'll have fun fun fun +til her Daddy takes the keyboard away''' + +lyrics = string.split(lyrics, "\n") +testtextsize = """ +def textsize(canvas): + from reportlab.lib.units import inch + from reportlab.lib.colors import magenta, red + canvas.setFont("Times-Roman", 20) + canvas.setFillColor(red) + canvas.drawCentredString(2.75*inch, 2.5*inch, "Font size examples") + canvas.setFillColor(magenta) + size = 7 + y = 2.3*inch + x = 1.3*inch + for line in lyrics: + canvas.setFont("Helvetica", size) + canvas.drawRightString(x,y,"%s points: " % size) + canvas.drawString(x,y, line) + y = y-size*1.2 + size = size+1.5 +""" + +teststar = """ +def star(canvas, title="Title Here", aka="Comment here.", + xcenter=None, ycenter=None, nvertices=5): + from math import pi + from reportlab.lib.units import inch + radius=inch/3.0 + if xcenter is None: xcenter=2.75*inch + if ycenter is None: ycenter=1.5*inch + canvas.drawCentredString(xcenter, ycenter+1.3*radius, title) + canvas.drawCentredString(xcenter, ycenter-1.4*radius, aka) + p = canvas.beginPath() + p.moveTo(xcenter,ycenter+radius) + from math import pi, cos, sin + angle = (2*pi)*2/5.0 + startangle = pi/2.0 + for vertex in range(nvertices-1): + nextangle = angle*(vertex+1)+startangle + x = xcenter + radius*cos(nextangle) + y = ycenter + radius*sin(nextangle) + p.lineTo(x,y) + if nvertices==5: + p.close() + canvas.drawPath(p) +""" + +testjoins = """ +def joins(canvas): + from reportlab.lib.units import inch + # make lines big + canvas.setLineWidth(5) + star(canvas, "Default: mitered join", "0: pointed", xcenter = 1*inch) + canvas.setLineJoin(1) + star(canvas, "Round join", "1: rounded") + canvas.setLineJoin(2) + star(canvas, "Bevelled join", "2: square", xcenter=4.5*inch) +""" + +testcaps = """ +def caps(canvas): + from reportlab.lib.units import inch + # make lines big + canvas.setLineWidth(5) + star(canvas, "Default", "no projection",xcenter = 1*inch, + nvertices=4) + canvas.setLineCap(1) + star(canvas, "Round cap", "1: ends in half circle", nvertices=4) + canvas.setLineCap(2) + star(canvas, "Square cap", "2: projects out half a width", xcenter=4.5*inch, + nvertices=4) +""" + +testdashes = """ +def dashes(canvas): + from reportlab.lib.units import inch + # make lines big + canvas.setDash(6,3) + star(canvas, "Simple dashes", "6 points on, 3 off", xcenter = 1*inch) + canvas.setDash(1,2) + star(canvas, "Dots", "One on, two off") + canvas.setDash([1,1,3,3,1,4,4,1], 0) + star(canvas, "Complex Pattern", "[1,1,3,3,1,4,4,1]", xcenter=4.5*inch) +""" + +testcursormoves1 = """ +def cursormoves1(canvas): + from reportlab.lib.units import inch + textobject = canvas.beginText() + textobject.setTextOrigin(inch, 2.5*inch) + textobject.setFont("Helvetica-Oblique", 14) + for line in lyrics: + textobject.textLine(line) + textobject.setFillGray(0.4) + textobject.textLines(''' + With many apologies to the Beach Boys + and anyone else who finds this objectionable + ''') + canvas.drawText(textobject) +""" + +testcursormoves2 = """ +def cursormoves2(canvas): + from reportlab.lib.units import inch + textobject = canvas.beginText() + textobject.setTextOrigin(2, 2.5*inch) + textobject.setFont("Helvetica-Oblique", 14) + for line in lyrics: + textobject.textOut(line) + textobject.moveCursor(14,14) # POSITIVE Y moves down!!! + textobject.setFillColorRGB(0.4,0,1) + textobject.textLines(''' + With many apologies to the Beach Boys + and anyone else who finds this objectionable + ''') + canvas.drawText(textobject) +""" + +testcharspace = """ +def charspace(canvas): + from reportlab.lib.units import inch + textobject = canvas.beginText() + textobject.setTextOrigin(3, 2.5*inch) + textobject.setFont("Helvetica-Oblique", 10) + charspace = 0 + for line in lyrics: + textobject.setCharSpace(charspace) + textobject.textLine("%s: %s" %(charspace,line)) + charspace = charspace+0.5 + textobject.setFillGray(0.4) + textobject.textLines(''' + With many apologies to the Beach Boys + and anyone else who finds this objectionable + ''') + canvas.drawText(textobject) +""" + +testwordspace = """ +def wordspace(canvas): + from reportlab.lib.units import inch + textobject = canvas.beginText() + textobject.setTextOrigin(3, 2.5*inch) + textobject.setFont("Helvetica-Oblique", 12) + wordspace = 0 + for line in lyrics: + textobject.setWordSpace(wordspace) + textobject.textLine("%s: %s" %(wordspace,line)) + wordspace = wordspace+2.5 + textobject.setFillColorCMYK(0.4,0,0.4,0.2) + textobject.textLines(''' + With many apologies to the Beach Boys + and anyone else who finds this objectionable + ''') + canvas.drawText(textobject) +""" +testhorizontalscale = """ +def horizontalscale(canvas): + from reportlab.lib.units import inch + textobject = canvas.beginText() + textobject.setTextOrigin(3, 2.5*inch) + textobject.setFont("Helvetica-Oblique", 12) + horizontalscale = 80 # 100 is default + for line in lyrics: + textobject.setHorizScale(horizontalscale) + textobject.textLine("%s: %s" %(horizontalscale,line)) + horizontalscale = horizontalscale+10 + textobject.setFillColorCMYK(0.0,0.4,0.4,0.2) + textobject.textLines(''' + With many apologies to the Beach Boys + and anyone else who finds this objectionable + ''') + canvas.drawText(textobject) +""" +testleading = """ +def leading(canvas): + from reportlab.lib.units import inch + textobject = canvas.beginText() + textobject.setTextOrigin(3, 2.5*inch) + textobject.setFont("Helvetica-Oblique", 14) + leading = 8 + for line in lyrics: + textobject.setLeading(leading) + textobject.textLine("%s: %s" %(leading,line)) + leading = leading+2.5 + textobject.setFillColorCMYK(0.8,0,0,0.3) + textobject.textLines(''' + With many apologies to the Beach Boys + and anyone else who finds this objectionable + ''') + canvas.drawText(textobject) +""" + +testhand = """ +def hand(canvas, debug=1, fill=0): + (startx, starty) = (0,0) + curves = [ + ( 0, 2), ( 0, 4), ( 0, 8), # back of hand + ( 5, 8), ( 7,10), ( 7,14), + (10,14), (10,13), ( 7.5, 8), # thumb + (13, 8), (14, 8), (17, 8), + (19, 8), (19, 6), (17, 6), + (15, 6), (13, 6), (11, 6), # index, pointing + (12, 6), (13, 6), (14, 6), + (16, 6), (16, 4), (14, 4), + (13, 4), (12, 4), (11, 4), # middle + (11.5, 4), (12, 4), (13, 4), + (15, 4), (15, 2), (13, 2), + (12.5, 2), (11.5, 2), (11, 2), # ring + (11.5, 2), (12, 2), (12.5, 2), + (14, 2), (14, 0), (12.5, 0), + (10, 0), (8, 0), (6, 0), # pinky, then close + ] + from reportlab.lib.units import inch + if debug: canvas.setLineWidth(6) + u = inch*0.2 + p = canvas.beginPath() + p.moveTo(startx, starty) + ccopy = list(curves) + while ccopy: + [(x1,y1), (x2,y2), (x3,y3)] = ccopy[:3] + del ccopy[:3] + p.curveTo(x1*u,y1*u,x2*u,y2*u,x3*u,y3*u) + p.close() + canvas.drawPath(p, fill=fill) + if debug: + from reportlab.lib.colors import red, green + (lastx, lasty) = (startx, starty) + ccopy = list(curves) + while ccopy: + [(x1,y1), (x2,y2), (x3,y3)] = ccopy[:3] + del ccopy[:3] + canvas.setStrokeColor(red) + canvas.line(lastx*u,lasty*u, x1*u,y1*u) + canvas.setStrokeColor(green) + canvas.line(x2*u,y2*u, x3*u,y3*u) + (lastx,lasty) = (x3,y3) +""" + +testhand2 = """ +def hand2(canvas): + canvas.translate(20,10) + canvas.setLineWidth(3) + canvas.setFillColorRGB(0.1, 0.3, 0.9) + canvas.setStrokeGray(0.5) + hand(canvas, debug=0, fill=1) +""" + +testfonts = """ +def fonts(canvas): + from reportlab.lib.units import inch + text = "Now is the time for all good men to..." + x = 1.8*inch + y = 2.7*inch + for font in canvas.getAvailableFonts(): + canvas.setFont(font, 10) + canvas.drawString(x,y,text) + canvas.setFont("Helvetica", 10) + canvas.drawRightString(x-10,y, font+":") + y = y-13 +""" + +testarcs = """ +def arcs(canvas): + from reportlab.lib.units import inch + canvas.setLineWidth(4) + canvas.setStrokeColorRGB(0.8, 1, 0.6) + # draw rectangles enclosing the arcs + canvas.rect(inch, inch, 1.5*inch, inch) + canvas.rect(3*inch, inch, inch, 1.5*inch) + canvas.setStrokeColorRGB(0, 0.2, 0.4) + canvas.setFillColorRGB(1, 0.6, 0.8) + p = canvas.beginPath() + p.moveTo(0.2*inch, 0.2*inch) + p.arcTo(inch, inch, 2.5*inch,2*inch, startAng=-30, extent=135) + p.arc(3*inch, inch, 4*inch, 2.5*inch, startAng=-45, extent=270) + canvas.drawPath(p, fill=1, stroke=1) +""" +testvariousshapes = """ +def variousshapes(canvas): + from reportlab.lib.units import inch + inch = int(inch) + canvas.setStrokeGray(0.5) + canvas.grid(range(0,11*inch/2,inch/2), range(0,7*inch/2,inch/2)) + canvas.setLineWidth(4) + canvas.setStrokeColorRGB(0, 0.2, 0.7) + canvas.setFillColorRGB(1, 0.6, 0.8) + p = canvas.beginPath() + p.rect(0.5*inch, 0.5*inch, 0.5*inch, 2*inch) + p.circle(2.75*inch, 1.5*inch, 0.3*inch) + p.ellipse(3.5*inch, 0.5*inch, 1.2*inch, 2*inch) + canvas.drawPath(p, fill=1, stroke=1) +""" + +testclosingfigures = """ +def closingfigures(canvas): + from reportlab.lib.units import inch + h = inch/3.0; k = inch/2.0 + canvas.setStrokeColorRGB(0.2,0.3,0.5) + canvas.setFillColorRGB(0.8,0.6,0.2) + canvas.setLineWidth(4) + p = canvas.beginPath() + for i in (1,2,3,4): + for j in (1,2): + xc,yc = inch*i, inch*j + p.moveTo(xc,yc) + p.arcTo(xc-h, yc-k, xc+h, yc+k, startAng=0, extent=60*i) + # close only the first one, not the second one + if j==1: + p.close() + canvas.drawPath(p, fill=1, stroke=1) +""" + +testforms = """ +def forms(canvas): + #first create a form... + canvas.beginForm("SpumoniForm") + #re-use some drawing functions from earlier + spumoni(canvas) + canvas.endForm() + + #then draw it + canvas.doForm("SpumoniForm") +""" + +def doctemplateillustration(canvas): + from reportlab.lib.units import inch + canvas.setFont("Helvetica", 10) + canvas.drawString(inch/4.0, 2.75*inch, "DocTemplate") + W = 4/3.0*inch + H = 2*inch + Wd = x = inch/4.0 + Hd =y = inch/2.0 + for name in ("two column", "chapter page", "title page"): + canvas.setFillColorRGB(0.5,1.0,1.0) + canvas.rect(x,y,W,H, fill=1) + canvas.setFillColorRGB(0,0,0) + canvas.drawString(x+inch/8, y+H-Wd, "PageTemplate") + canvas.drawCentredString(x+W/2.0, y-Wd, name) + x = x+W+Wd + canvas.saveState() + d = inch/16 + dW = (W-3*d)/2.0 + hD = H -2*d-Wd + canvas.translate(Wd+d, Hd+d) + for name in ("left Frame", "right Frame"): + canvas.setFillColorRGB(1.0,0.5,1.0) + canvas.rect(0,0, dW,hD, fill=1) + canvas.setFillGray(0.7) + dd= d/2.0 + ddH = (hD-6*dd)/5.0 + ddW = dW-2*dd + yy = dd + xx = dd + for i in range(5): + canvas.rect(xx,yy,ddW,ddH, fill=1, stroke=0) + yy = yy+ddH+dd + canvas.setFillColorRGB(0,0,0) + canvas.saveState() + canvas.rotate(90) + canvas.drawString(d,-dW/2, name) + canvas.restoreState() + canvas.translate(dW+d,0) + canvas.restoreState() + canvas.setFillColorRGB(1.0, 0.5, 1.0) + mx = Wd+W+Wd+d + my = Hd+d + mW = W-2*d + mH = H-d-Hd + canvas.rect(mx, my, mW, mH, fill=1) + canvas.rect(Wd+2*(W+Wd)+d, Hd+3*d, W-2*d, H/2.0, fill=1) + canvas.setFillGray(0.7) + canvas.rect(Wd+2*(W+Wd)+d+dd, Hd+5*d, W-2*d-2*dd, H/2.0-2*d-dd, fill=1) + xx = mx+dd + yy = my+mH/5.0 + ddH = (mH-6*dd-mH/5.0)/3.0 + ddW = mW - 2*dd + for i in range(3): + canvas.setFillGray(0.7) + canvas.rect(xx,yy,ddW,ddH, fill=1, stroke=1) + canvas.setFillGray(0) + canvas.drawString(xx+dd/2.0,yy+dd/2.0, "flowable %s" %(157-i)) + yy = yy+ddH+dd + canvas.drawCentredString(3*Wd+2*W+W/2, Hd+H/2.0, "First Flowable") + canvas.setFont("Times-BoldItalic", 8) + canvas.setFillGray(0) + canvas.drawCentredString(mx+mW/2.0, my+mH+3*dd, "Chapter 6: Lubricants") + canvas.setFont("Times-BoldItalic", 10) + canvas.drawCentredString(3*Wd+2*W+W/2, Hd+H-H/4, "College Life") + +class PlatIllust: + #wrap the above for PP# + def __init__(self, x, y, scale=1): + self.x = x + self.y = y + self.scale = scale + def drawOn(self, canvas): + canvas.saveState() + canvas.translate(self.x, self.y) + canvas.scale(self.scale, self.scale) + doctemplateillustration(canvas) + canvas.restoreState() + +class PingoIllust: + #wrap the above for PP# + def __init__(self, x, y, scale=1): +## print 'Pingo illustration %f, %f, %f' % (x,y,scale) + self.x = x + self.y = y + self.scale = scale + def drawOn(self, canvas): + canvas.rect(self.x, self.y, 100,100, stroke=1, fill=1) +## from pingo import testdrawings +## from pingo import pingopdf +## drawing = testdrawings.getDrawing3() +## canvas.saveState() +## canvas.scale(self.scale, self.scale) +## pingopdf.draw(drawing, canvas, self.x, self.y) +## canvas.restoreState() + +# D = dir() +g = globals() +Dprime = {} +from types import StringType +from string import strip +for (a,b) in g.items(): + if a[:4]=="test" and type(b) is StringType: + #print 'for', a + #print b + b = strip(b) + exec(b+'\n') + +platypussetup = """ +from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer +from reportlab.lib.styles import getSampleStyleSheet +from reportlab.lib.pagesizes import DEFAULT_PAGE_SIZE +from reportlab.lib.units import inch +PAGE_HEIGHT=DEFAULT_PAGE_SIZE[1]; PAGE_WIDTH=DEFAULT_PAGE_SIZE[0] +styles = getSampleStyleSheet() +""" +platypusfirstpage = """ +Title = "Hello world" +pageinfo = "platypus example" +def myFirstPage(canvas, doc): + canvas.saveState() + canvas.setFont('Times-Bold',16) + canvas.drawCentredString(PAGE_WIDTH/2.0, PAGE_HEIGHT-108, Title) + canvas.setFont('Times-Roman',9) + canvas.drawString(inch, 0.75 * inch, "First Page / %s" % pageinfo) + canvas.restoreState() +""" +platypusnextpage = """ +def myLaterPages(canvas, doc): + canvas.saveState() + canvas.setFont('Times-Roman',9) + canvas.drawString(inch, 0.75 * inch, "Page %d %s" % (doc.page, pageinfo)) + canvas.restoreState() +""" +platypusgo = """ +def go(): + doc = SimpleDocTemplate("phello.pdf") + Story = [Spacer(1,2*inch)] + style = styles["Normal"] + for i in range(100): + bogustext = ("This is Paragraph number %s. " % i) *20 + p = Paragraph(bogustext, style) + Story.append(p) + Story.append(Spacer(1,0.2*inch)) + doc.build(Story, onFirstPage=myFirstPage, onLaterPages=myLaterPages) +""" + +if __name__=="__main__": + # then do the platypus hello world + for b in platypussetup, platypusfirstpage, platypusnextpage, platypusgo: + b = strip(b) + exec(b+'\n') + go() diff --git a/bin/reportlab/tools/pythonpoint/demos/figures.xml b/bin/reportlab/tools/pythonpoint/demos/figures.xml new file mode 100644 index 00000000000..b3314ed8298 --- /dev/null +++ b/bin/reportlab/tools/pythonpoint/demos/figures.xml @@ -0,0 +1,71 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- edited with XMLSPY v2004 rel. 3 U (http://www.xmlspy.com) by Andy Robinson (ReportLab Europe Ltd.) --> +<!DOCTYPE presentation SYSTEM "../pythonpoint.dtd"> +<presentation filename="figures.xml" pageDuration="10"> + <stylesheet module="standard" function="getParagraphStyles"/> + <title>New Feature Tests + + Andy Robinson + + + Reportlab Sample Applications + +
+ + + +
+ diff --git a/bin/reportlab/tools/pythonpoint/demos/htu.xml b/bin/reportlab/tools/pythonpoint/demos/htu.xml new file mode 100644 index 00000000000..56cb92457dc --- /dev/null +++ b/bin/reportlab/tools/pythonpoint/demos/htu.xml @@ -0,0 +1,96 @@ + + + + +
+ + + © 2002, H. Turgut UYAR + + + + + + New features in PythonPoint + H. Turgut Uyar + uyar@cs.itu.edu.tr + + + + + TrueType Support + + + PythonPoint can read UTF-8 encoded input files and produce + correct output (provided your font has all necessary + characters). That enables you, for example, to have native (in my + case, Turkish) characters in your document: + + +  0 ^  1 _ + + + + + + Effects + + + Paragraphs, images, tables and geometric shapes can now have + effectname, effectdirection, effectdimension, effectmotion and + effectduration attributes: + A paragraph + + + Col1,Col2,Col3 + Row1Col1,Row1Col2,Row1Col3 + Row2Col1,Row2Col2,Row2Col3 +
+ + + String + +
+ + + Printing + + + Be careful when using effects: A new slide is created for + each effect, so DON'T print the resulting PDF file. + new command-line option: --printout + produces printable PDF + + + + + New Paragraph Styles + + + Bullet2 - Second level bullets + Here's an example + Or an example with a longer text to see + how it wraps at the end of each line + Author and EMail + See the cover page for examples + They have to be in the style file, so either use + the htu style file or edit your style file + + + + + ToDo + + + Unicode chars in the outline + + +
+
diff --git a/bin/reportlab/tools/pythonpoint/demos/leftlogo.a85 b/bin/reportlab/tools/pythonpoint/demos/leftlogo.a85 new file mode 100644 index 00000000000..7d55afcf348 --- /dev/null +++ b/bin/reportlab/tools/pythonpoint/demos/leftlogo.a85 @@ -0,0 +1,53 @@ +BI +/W 103 /H 68 /BPC 8 /CS /RGB /F [/A85 /Fl] +ID +Gb"/*cYjIe']/l*^;)UQj:Ec+nd!,-,*G/5;0"E.+@qg.\92h2HbiE)jC5*M +$a,@:RoXB@WPjMu=[F;88l[?@OO=6qV,HNNiL#(cac+!&rF)2^DHJ?_:WQkn +1[fb`hOBr--B>FoSDXMt35Gnj6n\3XT8sf/Y%-NPLqg'pmQ+*EiV6D:,$akf +!i2MA/A-"n[R.R)S!ePi$u('8mrCG8[3:WLlB[amBYAS4hR2*f7tR;&\%]Ld +ZX5^f6Qj@\h7^FuQ(">[ml32FWhJG;Z)t2oADV4[H6GbW.mH`MDlesQZO5+Q5)]OWf2G9[ +U9W99*2'#PD+cIXnp"J"(BrY^=(rg;ITf("c#0"S<;fjK,APV+p9p+05LO&X +jQNf`8c26f]J54N'k#I9=Bt4]e&6gN'#S[nK3*VhQ!`PpXcJ0i:J_2rC@=>X\5dUbEcCZ?\6O)+F/35ZJD6R$H^-Z!R +5;8=l//9SUkC;LhQ*?di<'6e&/BNIqm(%(SDYXJ +p%%Z+J.IshkX$tLPOD'"jSV#/6iY?QI>8P-%o0k3`e#X33RJS`f'D!D2rTJ- +(4Znb03&koo>X[bKs.qpBOkU,KCCO3S$2.Gr@e8E=$On&hUiAB2[.;G@FQf7_K1&D6RJ.Ih@-56Ss&B1q:\)MgJ8P4Sa-(Fm +J"d8Z]Mf`Q$G^\tK@pQWQs3iRB[KP=+H&@+iH@XGFQ'#n!DJ$)Mc?"U#od!oYl3En7nT"7UC,,Q&Hr +9.SPlHak6s)J0;k;hQC:k82'=COeskp&dB-#@RrY1B=H^'L=,;9uJssq%XGf +h/fXmq>1G\?k6ZH*GSk&M)?&TJ?rYW.9f!gdM2&:Kr51A3^MX?GV'T@$h)Da +-RIIcpl8fhiQr=`\k[7;3`X"d!or(jb1YM>"Ppi`,67@]M"d ++mXd;WRE"IKX@n&dNNV/55(]`==+_r?K5bp?$^4Pn/[2ncL(/l#CfSYAg:iN +@3j5R$qtm;AIF7k%^ak'%4Z1354d"1VT\g]cm[^hI.Yf>DcLS\rq+As2F?LI +'gq+/_o9&kK,4@]qbdfA8FauP;!N'$5J@1P/\Th=9i^\b$/-6-kL#$E&39t% +/O[1Fm`/F?!q;%bB')DM`KY&D!,6)lG5TQ88ejHiTWKt;3_8:@M!NnP'D2s$ +2Q25"dX)7pQ%VUq+r1%.iMQiLR#&.@\:Pe96FEbkgLu`Fb;!#d,DPC-H/d?u +^']=P86?6qr/@_/T-T0?,+>brI`?=13["Y(3DYSMX7UAUk0'!l!aPN4$e\f&(i2TTrbVuOYhc05Rko:?UP09gLc(-/ni'Q6T.B&8]k +#a_e\0FTMV`IT;W7M>_'NT_f=~> +EI diff --git a/bin/reportlab/tools/pythonpoint/demos/leftlogo.gif b/bin/reportlab/tools/pythonpoint/demos/leftlogo.gif new file mode 100644 index 00000000000..2d9c990ba7a Binary files /dev/null and b/bin/reportlab/tools/pythonpoint/demos/leftlogo.gif differ diff --git a/bin/reportlab/tools/pythonpoint/demos/lj8100.jpg b/bin/reportlab/tools/pythonpoint/demos/lj8100.jpg new file mode 100644 index 00000000000..be3c6183d3b Binary files /dev/null and b/bin/reportlab/tools/pythonpoint/demos/lj8100.jpg differ diff --git a/bin/reportlab/tools/pythonpoint/demos/monterey.xml b/bin/reportlab/tools/pythonpoint/demos/monterey.xml new file mode 100644 index 00000000000..41bd979fa67 --- /dev/null +++ b/bin/reportlab/tools/pythonpoint/demos/monterey.xml @@ -0,0 +1,306 @@ + + + + + + +
+ + + + + + + + + Printing with Python + + + + Andy Robinson, Robinson Analytics Ltd. + + + O'Reilly Python Conference, Monterey, 24th August 1999 + + + + + + + Background to the project: + + + London-based consultant and corporate developer + + + want to do neat Python stuff in the daytime + + + working for many years on financial modelling + + + this is one of 6 modules in that system + + + quickest to deliver, offers very wide benefits + + + 25% of architecture done, but already very useful + + + Release early, release often! + + + + + + + + Goal: + + + A Reporting Package on the Next Curve... + + + Report on objects, not databases + + + Scalable to million page runs + + + Light enough to embed in any application + + + Allow reuse of graphical objects across reports + + + Open and extensible on several levels + + + Publication quality + + + Support all the world's languages - one day + + + + + + + Portable Document Format + + + The New PostScript + + + Free readers on all platforms + + + Better than paper - view it, email it, print it + + + 'Final Form' for documents + + + High end solution - no limits to quality + + + ...but you can't learn it in Notepad! + + + + + + + + + PDFgen and PIDDLE + + + + + + + Layer One - PDFgen + + + makes PDF documents from pure Python + + + wraps up PDF document structure + + + exposes nice effects - page transitions, outline trees (RSN!) + + + low level graphics promitives (postscript imaging model) + + + Fine control of text placement + + + Supports Asian text + + + Supports coordinate transformations and clipping + + + ...a foundation for other apps to build on + + + + + + + PDFgen Image Support + + + Python Imaging Library and zlib do all the work - many formats. + Images cached (like .pyc files) - very fast builds possible. + + + + + + + + Layer Two: PIDDLE + + + Plug In Drawing, Does Little Else + + + Easy Graphics Library + + + Abstract Canvas Interface + + + Pluggable Back Ends + + + Same code can do viewing and printing + + + Standard set of test patterns + + + Uses Python Imaging Library + + + Back ends includeTkinter, wxPython, Mac, Pythonwin, PDF, PostScript, + OpenGL, Adobe Illustrator and PIL. Really easy to add a new one! + + + + + + + Layer Three: PLATYPUS + + + "Page Layout And Typography Using Scripts" + + + Trying to work out the API now. Key Concepts: + + + Drawable objects - can 'wrap to fit' + + + Frames on page + + + Frame consumes from a list of drawables until full + + + Document Models e.g. SimpleFlowDocument + + + XSL Flow Object model may be a good target + + + + + + + Drawable Objects + + + Next layer of PIDDLE extensibility. + Each draws in its own coodinate system + + + paragraph, image, table + + + chart libraries + + + diagrams + + + Open Source - let people contribute new ones. + Anything you could have in a view can be a new + drawable type. + + + + + + + Style Sheet Driven + + + Styles use instance inheritance + + + Paragraph Styles - Style Sheet Compulsory! + + + Text Styles within a paragraph + + + Table and Table Cell Styles + + + + + + + Vision + + + XML to PDF in one step + + + Publish to web and print from same source + + + Financial and Scientific reporting tool + + + Embedded reporting engine + + + Volume reporting tool for business + + + + + + + PythonPoint + + + How I made this presentation... + + + +
+
diff --git a/bin/reportlab/tools/pythonpoint/demos/outline.gif b/bin/reportlab/tools/pythonpoint/demos/outline.gif new file mode 100644 index 00000000000..4a294b59277 Binary files /dev/null and b/bin/reportlab/tools/pythonpoint/demos/outline.gif differ diff --git a/bin/reportlab/tools/pythonpoint/demos/pplogo.gif b/bin/reportlab/tools/pythonpoint/demos/pplogo.gif new file mode 100644 index 00000000000..d0344497ca5 Binary files /dev/null and b/bin/reportlab/tools/pythonpoint/demos/pplogo.gif differ diff --git a/bin/reportlab/tools/pythonpoint/demos/python.gif b/bin/reportlab/tools/pythonpoint/demos/python.gif new file mode 100644 index 00000000000..c68e1e4ec37 Binary files /dev/null and b/bin/reportlab/tools/pythonpoint/demos/python.gif differ diff --git a/bin/reportlab/tools/pythonpoint/demos/pythonpoint.xml b/bin/reportlab/tools/pythonpoint/demos/pythonpoint.xml new file mode 100644 index 00000000000..15d1ad709b5 --- /dev/null +++ b/bin/reportlab/tools/pythonpoint/demos/pythonpoint.xml @@ -0,0 +1,1051 @@ + + + + + PythonPoint Demonstration + Andy Robinson + Reportlab Sample Applications +
+ + + +
+
diff --git a/bin/reportlab/tools/pythonpoint/demos/slidebox.py b/bin/reportlab/tools/pythonpoint/demos/slidebox.py new file mode 100644 index 00000000000..f0d9cf308e9 --- /dev/null +++ b/bin/reportlab/tools/pythonpoint/demos/slidebox.py @@ -0,0 +1,29 @@ +#Autogenerated by ReportLab guiedit do not edit +from reportlab.graphics.shapes import _DrawingEditorMixin +from reportlab.graphics.charts.slidebox import SlideBox +from rlextra.graphics.guiedit.datacharts import ODBCDataSource, CSVDataSource, DataAssociation, DataAwareDrawing + +class SlideBoxDrawing(_DrawingEditorMixin,DataAwareDrawing): + def __init__(self,width=400,height=200,*args,**kw): + apply(DataAwareDrawing.__init__,(self,width,height)+args,kw) + self._add(self,SlideBox(),name='SlideBox',validate=None,desc='The main chart') + self.height = 40 + self.width = 168 + #self.dataSource = ODBCDataSource() + self.dataSource = CSVDataSource() + self.dataSource.filename = 'slidebox.csv' + self.dataSource.integerColumns = ['chartId','value','numberOfBoxes'] + self.dataSource.sql = 'SELECT chartId,numberOfBoxes,label,value FROM generic_slidebox' + self.dataSource.associations.size = 4 + self.dataSource.associations.element00 = DataAssociation(column=0, target='chartId', assocType='scalar') + self.dataSource.associations.element01 = DataAssociation(column=1, target='SlideBox.numberOfBoxes', assocType='scalar') + self.dataSource.associations.element02 = DataAssociation(column=2, target='SlideBox.sourceLabelText', assocType='scalar') + self.dataSource.associations.element03 = DataAssociation(column=3, target='SlideBox.trianglePosition', assocType='scalar') + self.verbose = 1 + self.formats = ['eps', 'pdf'] + self.outDir = './output/' + self.fileNamePattern = 'slidebox%03d' + + +if __name__=="__main__": #NORUNTESTS + SlideBoxDrawing().go() diff --git a/bin/reportlab/tools/pythonpoint/demos/spectrum.png b/bin/reportlab/tools/pythonpoint/demos/spectrum.png new file mode 100644 index 00000000000..06747655056 Binary files /dev/null and b/bin/reportlab/tools/pythonpoint/demos/spectrum.png differ diff --git a/bin/reportlab/tools/pythonpoint/demos/vertpython.gif b/bin/reportlab/tools/pythonpoint/demos/vertpython.gif new file mode 100644 index 00000000000..5e5c60c3308 Binary files /dev/null and b/bin/reportlab/tools/pythonpoint/demos/vertpython.gif differ diff --git a/bin/reportlab/tools/pythonpoint/pythonpoint.dtd b/bin/reportlab/tools/pythonpoint/pythonpoint.dtd new file mode 100644 index 00000000000..ec876f68387 --- /dev/null +++ b/bin/reportlab/tools/pythonpoint/pythonpoint.dtd @@ -0,0 +1,275 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bin/reportlab/tools/pythonpoint/pythonpoint.py b/bin/reportlab/tools/pythonpoint/pythonpoint.py new file mode 100644 index 00000000000..c5e823de04d --- /dev/null +++ b/bin/reportlab/tools/pythonpoint/pythonpoint.py @@ -0,0 +1,1124 @@ +#!/usr/bin/env python + +""" +This is PythonPoint! + +The idea is a simple markup languages for describing presentation +slides, and other documents which run page by page. I expect most +of it will be reusable in other page layout stuff. + +Look at the sample near the top, which shows how the presentation +should be coded up. + +The parser, which is in a separate module to allow for multiple +parsers, turns the XML sample into an object tree. There is a +simple class hierarchy of items, the inner levels of which create +flowable objects to go in the frames. These know how to draw +themselves. + +The currently available 'Presentation Objects' are: + + The main hierarchy... + PPPresentation + PPSection + PPSlide + PPFrame + + PPAuthor, PPTitle and PPSubject are optional + + Things to flow within frames... + PPPara - flowing text + PPPreformatted - text with line breaks and tabs, for code.. + PPImage + PPTable - bulk formatted tabular data + PPSpacer + + Things to draw directly on the page... + PPRect + PPRoundRect + PPDrawingElement - user base class for graphics + PPLine + PPEllipse + +Features added by H. Turgut Uyar +- TrueType support (actually, just an import in the style file); + this also enables the use of Unicode symbols +- para, image, table, line, rectangle, roundrect, ellipse, polygon + and string elements can now have effect attributes + (careful: new slide for each effect!) +- added printout mode (no new slides for effects, see item above) +- added a second-level bullet: Bullet2 +- small bugfixes in handleHiddenSlides: + corrected the outlineEntry of included hidden slide + and made sure to include the last slide even if hidden + +Recently added features are: + +- file globbing +- package structure +- named colors throughout (using names from reportlab/lib/colors.py) +- handout mode with arbitrary number of columns per page +- stripped off pages hidden in the outline tree (hackish) +- new tag for speaker notes (paragraphs only) +- new tag for syntax-colorized Python code +- reformatted pythonpoint.xml and monterey.xml demos +- written/extended DTD +- arbitrary font support +- print proper speaker notes (TODO) +- fix bug with partially hidden graphics (TODO) +- save in combined presentation/handout mode (TODO) +- add pyRXP support (TODO) +""" + +import os, sys, imp, string, pprint, getopt, glob + +from reportlab import rl_config +from reportlab.lib import styles +from reportlab.lib import colors +from reportlab.lib.units import cm +from reportlab.lib.utils import getStringIO +from reportlab.lib.enums import TA_LEFT, TA_RIGHT, TA_CENTER, TA_JUSTIFY +from reportlab.pdfbase import pdfmetrics +from reportlab.pdfgen import canvas +from reportlab.platypus.doctemplate import SimpleDocTemplate +from reportlab.platypus.flowables import Flowable +from reportlab.platypus.xpreformatted import PythonPreformatted +from reportlab.platypus import Preformatted, Paragraph, Frame, \ + Image, Table, TableStyle, Spacer + + +USAGE_MESSAGE = """\ +PythonPoint - a tool for making presentations in PDF. + +Usage: + pythonpoint.py [options] file1.xml [file2.xml [...]] + + where options can be any of these: + + -h / --help prints this message + -n / --notes leave room for comments + -v / --verbose verbose mode + -s / --silent silent mode (NO output) + --handout produce handout document + --printout produce printout document + --cols specify number of columns + on handout pages (default: 2) + +To create the PythonPoint user guide, do: + pythonpoint.py pythonpoint.xml +""" + + +##################################################################### +# This should probably go into reportlab/lib/fonts.py... +##################################################################### + +class FontNameNotFoundError(Exception): + pass + + +class FontFilesNotFoundError(Exception): + pass + + +##def findFontName(path): +## "Extract a Type-1 font name from an AFM file." +## +## f = open(path) +## +## found = 0 +## while not found: +## line = f.readline()[:-1] +## if not found and line[:16] == 'StartCharMetrics': +## raise FontNameNotFoundError, path +## if line[:8] == 'FontName': +## fontName = line[9:] +## found = 1 +## +## return fontName +## +## +##def locateFilesForFontWithName(name): +## "Search known paths for AFM/PFB files describing T1 font with given name." +## +## join = os.path.join +## splitext = os.path.splitext +## +## afmFile = None +## pfbFile = None +## +## found = 0 +## while not found: +## for p in rl_config.T1SearchPath: +## afmFiles = glob.glob(join(p, '*.[aA][fF][mM]')) +## for f in afmFiles: +## T1name = findFontName(f) +## if T1name == name: +## afmFile = f +## found = 1 +## break +## if afmFile: +## break +## break +## +## if afmFile: +## pfbFile = glob.glob(join(splitext(afmFile)[0] + '.[pP][fF][bB]'))[0] +## +## return afmFile, pfbFile +## +## +##def registerFont(name): +## "Register Type-1 font for future use." +## +## rl_config.warnOnMissingFontGlyphs = 0 +## rl_config.T1SearchPath.append(r'C:\Programme\Python21\reportlab\test') +## +## afmFile, pfbFile = locateFilesForFontWithName(name) +## if not afmFile and not pfbFile: +## raise FontFilesNotFoundError +## +## T1face = pdfmetrics.EmbeddedType1Face(afmFile, pfbFile) +## T1faceName = name +## pdfmetrics.registerTypeFace(T1face) +## T1font = pdfmetrics.Font(name, T1faceName, 'WinAnsiEncoding') +## pdfmetrics.registerFont(T1font) + + +def registerFont0(sourceFile, name, path): + "Register Type-1 font for future use, simple version." + + rl_config.warnOnMissingFontGlyphs = 0 + + p = os.path.join(os.path.dirname(sourceFile), path) + afmFiles = glob.glob(p + '.[aA][fF][mM]') + pfbFiles = glob.glob(p + '.[pP][fF][bB]') + assert len(afmFiles) == len(pfbFiles) == 1, FontFilesNotFoundError + + T1face = pdfmetrics.EmbeddedType1Face(afmFiles[0], pfbFiles[0]) + T1faceName = name + pdfmetrics.registerTypeFace(T1face) + T1font = pdfmetrics.Font(name, T1faceName, 'WinAnsiEncoding') + pdfmetrics.registerFont(T1font) + +##################################################################### + + +def checkColor(col): + "Converts a color name to an RGB tuple, if possible." + + if type(col) == type('') and col in dir(colors): + col = getattr(colors, col) + col = (col.red, col.green, col.blue) + + return col + + +def handleHiddenSlides(slides): + """Filters slides from a list of slides. + + In a sequence of hidden slides all but the last one are + removed. Also, the slide before the sequence of hidden + ones is removed. + + This assumes to leave only those slides in the handout + that also appear in the outline, hoping to reduce se- + quences where each new slide only adds one new line + to a list of items... + """ + + itd = indicesToDelete = map(lambda s:s.outlineEntry == None, slides) + + for i in range(len(itd)-1): + if itd[i] == 1: + if itd[i+1] == 0: + itd[i] = 0 + if i > 0 and itd[i-1] == 0: + itd[i-1] = 1 + + itd[len(itd)-1] = 0 + + for i in range(len(itd)): + if slides[i].outlineEntry: + curOutlineEntry = slides[i].outlineEntry + if itd[i] == 1: + slides[i].delete = 1 + else: + slides[i].outlineEntry = curOutlineEntry + slides[i].delete = 0 + + slides = filter(lambda s:s.delete == 0, slides) + + return slides + + +def makeSlideTable(slides, pageSize, docWidth, numCols): + """Returns a table containing a collection of SlideWrapper flowables. + """ + + slides = handleHiddenSlides(slides) + + # Set table style. + tabStyle = TableStyle( + [('GRID', (0,0), (-1,-1), 0.25, colors.black), + ('ALIGN', (0,0), (-1,-1), 'CENTRE') + ]) + + # Build table content. + width = docWidth/numCols + height = width * pageSize[1]/pageSize[0] + matrix = [] + row = [] + for slide in slides: + sw = SlideWrapper(width, height, slide, pageSize) + if (len(row)) < numCols: + row.append(sw) + else: + matrix.append(row) + row = [] + row.append(sw) + if len(row) > 0: + for i in range(numCols-len(row)): + row.append('') + matrix.append(row) + + # Make Table flowable. + t = Table(matrix, + [width + 5]*len(matrix[0]), + [height + 5]*len(matrix)) + t.setStyle(tabStyle) + + return t + + +class SlideWrapper(Flowable): + """A Flowable wrapping a PPSlide object. + """ + + def __init__(self, width, height, slide, pageSize): + Flowable.__init__(self) + self.width = width + self.height = height + self.slide = slide + self.pageSize = pageSize + + + def __repr__(self): + return "SlideWrapper(w=%s, h=%s)" % (self.width, self.height) + + + def draw(self): + "Draw the slide in our relative coordinate system." + + slide = self.slide + pageSize = self.pageSize + canv = self.canv + + canv.saveState() + canv.scale(self.width/pageSize[0], self.height/pageSize[1]) + slide.effectName = None + slide.drawOn(self.canv) + canv.restoreState() + + +class PPPresentation: + def __init__(self): + self.sourceFilename = None + self.filename = None + self.outDir = None + self.description = None + self.title = None + self.author = None + self.subject = None + self.notes = 0 # different printing mode + self.handout = 0 # prints many slides per page + self.printout = 0 # remove hidden slides + self.cols = 0 # columns per handout page + self.slides = [] + self.effectName = None + self.showOutline = 1 #should it be displayed when opening? + self.compression = rl_config.pageCompression + self.pageDuration = None + #assume landscape + self.pageWidth = rl_config.defaultPageSize[1] + self.pageHeight = rl_config.defaultPageSize[0] + self.verbose = rl_config.verbose + + + def saveAsPresentation(self): + """Write the PDF document, one slide per page.""" + if self.verbose: + print 'saving presentation...' + pageSize = (self.pageWidth, self.pageHeight) + if self.sourceFilename: + filename = os.path.splitext(self.sourceFilename)[0] + '.pdf' + if self.outDir: filename = os.path.join(self.outDir,os.path.basename(filename)) + if self.verbose: + print filename + #canv = canvas.Canvas(filename, pagesize = pageSize) + outfile = getStringIO() + if self.notes: + #translate the page from landscape to portrait + pageSize= pageSize[1], pageSize[0] + canv = canvas.Canvas(outfile, pagesize = pageSize) + canv.setPageCompression(self.compression) + canv.setPageDuration(self.pageDuration) + if self.title: + canv.setTitle(self.title) + if self.author: + canv.setAuthor(self.author) + if self.subject: + canv.setSubject(self.subject) + + slideNo = 0 + for slide in self.slides: + #need diagnostic output if something wrong with XML + slideNo = slideNo + 1 + if self.verbose: + print 'doing slide %d, id = %s' % (slideNo, slide.id) + if self.notes: + #frame and shift the slide + #canv.scale(0.67, 0.67) + scale_amt = (min(pageSize)/float(max(pageSize)))*.95 + #canv.translate(self.pageWidth / 6.0, self.pageHeight / 3.0) + #canv.translate(self.pageWidth / 2.0, .025*self.pageHeight) + canv.translate(.025*self.pageHeight, (self.pageWidth/2.0) + 5) + #canv.rotate(90) + canv.scale(scale_amt, scale_amt) + canv.rect(0,0,self.pageWidth, self.pageHeight) + slide.drawOn(canv) + canv.showPage() + + #ensure outline visible by default + if self.showOutline: + canv.showOutline() + + canv.save() + return self.savetofile(outfile, filename) + + + def saveAsHandout(self): + """Write the PDF document, multiple slides per page.""" + + styleSheet = getSampleStyleSheet() + h1 = styleSheet['Heading1'] + bt = styleSheet['BodyText'] + + if self.sourceFilename : + filename = os.path.splitext(self.sourceFilename)[0] + '.pdf' + + outfile = getStringIO() + doc = SimpleDocTemplate(outfile, pagesize=rl_config.defaultPageSize, showBoundary=0) + doc.leftMargin = 1*cm + doc.rightMargin = 1*cm + doc.topMargin = 2*cm + doc.bottomMargin = 2*cm + multiPageWidth = rl_config.defaultPageSize[0] - doc.leftMargin - doc.rightMargin - 50 + + story = [] + orgFullPageSize = (self.pageWidth, self.pageHeight) + t = makeSlideTable(self.slides, orgFullPageSize, multiPageWidth, self.cols) + story.append(t) + +## #ensure outline visible by default +## if self.showOutline: +## doc.canv.showOutline() + + doc.build(story) + return self.savetofile(outfile, filename) + + def savetofile(self, pseudofile, filename): + """Save the pseudo file to disk and return its content as a + string of text.""" + pseudofile.flush() + content = pseudofile.getvalue() + pseudofile.close() + if filename : + outf = open(filename, "wb") + outf.write(content) + outf.close() + return content + + + + def save(self): + "Save the PDF document." + + if self.handout: + return self.saveAsHandout() + else: + return self.saveAsPresentation() + + +#class PPSection: +# """A section can hold graphics which will be drawn on all +# pages within it, before frames and other content are done. +# In other words, a background template.""" +# def __init__(self, name): +# self.name = name +# self.graphics = [] +# +# def drawOn(self, canv): +# for graphic in self.graphics: +### graphic.drawOn(canv) +# +# name = str(hash(graphic)) +# internalname = canv._doc.hasForm(name) +# +# canv.saveState() +# if not internalname: +# canv.beginForm(name) +# graphic.drawOn(canv) +# canv.endForm() +# canv.doForm(name) +# else: +# canv.doForm(name) +# canv.restoreState() + + +definedForms = {} + +class PPSection: + """A section can hold graphics which will be drawn on all + pages within it, before frames and other content are done. + In other words, a background template.""" + + def __init__(self, name): + self.name = name + self.graphics = [] + + def drawOn(self, canv): + for graphic in self.graphics: + graphic.drawOn(canv) + continue + name = str(hash(graphic)) + #internalname = canv._doc.hasForm(name) + if definedForms.has_key(name): + internalname = 1 + else: + internalname = None + definedForms[name] = 1 + if not internalname: + canv.beginForm(name) + canv.saveState() + graphic.drawOn(canv) + canv.restoreState() + canv.endForm() + canv.doForm(name) + else: + canv.doForm(name) + + +class PPNotes: + def __init__(self): + self.content = [] + + def drawOn(self, canv): + print self.content + + +class PPSlide: + def __init__(self): + self.id = None + self.title = None + self.outlineEntry = None + self.outlineLevel = 0 # can be higher for sub-headings + self.effectName = None + self.effectDirection = 0 + self.effectDimension = 'H' + self.effectMotion = 'I' + self.effectDuration = 1 + self.frames = [] + self.notes = [] + self.graphics = [] + self.section = None + + def drawOn(self, canv): + if self.effectName: + canv.setPageTransition( + effectname=self.effectName, + direction = self.effectDirection, + dimension = self.effectDimension, + motion = self.effectMotion, + duration = self.effectDuration + ) + + if self.outlineEntry: + #gets an outline automatically + self.showOutline = 1 + #put an outline entry in the left pane + tag = self.title + canv.bookmarkPage(tag) + canv.addOutlineEntry(tag, tag, self.outlineLevel) + + if self.section: + self.section.drawOn(canv) + + for graphic in self.graphics: + graphic.drawOn(canv) + + for frame in self.frames: + frame.drawOn(canv) + +## # Need to draw the notes *somewhere*... +## for note in self.notes: +## print note + + +class PPFrame: + def __init__(self, x, y, width, height): + self.x = x + self.y = y + self.width = width + self.height = height + self.content = [] + self.showBoundary = 0 + + def drawOn(self, canv): + #make a frame + frame = Frame( self.x, + self.y, + self.width, + self.height + ) + frame.showBoundary = self.showBoundary + + #build a story for the frame + story = [] + for thingy in self.content: + #ask it for any flowables + story.append(thingy.getFlowable()) + #draw it + frame.addFromList(story,canv) + + +class PPPara: + """This is a placeholder for a paragraph.""" + def __init__(self): + self.rawtext = '' + self.style = None + + def escapeAgain(self, text): + """The XML has been parsed once, so '>' became '>' + in rawtext. We need to escape this to get back to + something the Platypus parser can accept""" + pass + + def getFlowable(self): +## print 'rawText for para:' +## print repr(self.rawtext) + p = Paragraph( + self.rawtext, + getStyles()[self.style], + self.bulletText + ) + return p + + +class PPPreformattedText: + """Use this for source code, or stuff you do not want to wrap""" + def __init__(self): + self.rawtext = '' + self.style = None + + def getFlowable(self): + return Preformatted(self.rawtext, getStyles()[self.style]) + + +class PPPythonCode: + """Use this for colored Python source code""" + def __init__(self): + self.rawtext = '' + self.style = None + + def getFlowable(self): + return PythonPreformatted(self.rawtext, getStyles()[self.style]) + + +class PPImage: + """Flowing image within the text""" + def __init__(self): + self.filename = None + self.width = None + self.height = None + + def getFlowable(self): + return Image(self.filename, self.width, self.height) + + +class PPTable: + """Designed for bulk loading of data for use in presentations.""" + def __init__(self): + self.rawBlocks = [] #parser stuffs things in here... + self.fieldDelim = ',' #tag args can override + self.rowDelim = '\n' #tag args can override + self.data = None + self.style = None #tag args must specify + self.widths = None #tag args can override + self.heights = None #tag args can override + + def getFlowable(self): + self.parseData() + t = Table( + self.data, + self.widths, + self.heights) + if self.style: + t.setStyle(getStyles()[self.style]) + + return t + + def parseData(self): + """Try to make sense of the table data!""" + rawdata = string.strip(string.join(self.rawBlocks, '')) + lines = string.split(rawdata, self.rowDelim) + #clean up... + lines = map(string.strip, lines) + self.data = [] + for line in lines: + cells = string.split(line, self.fieldDelim) + self.data.append(cells) + + #get the width list if not given + if not self.widths: + self.widths = [None] * len(self.data[0]) + if not self.heights: + self.heights = [None] * len(self.data) + +## import pprint +## print 'table data:' +## print 'style=',self.style +## print 'widths=',self.widths +## print 'heights=',self.heights +## print 'fieldDelim=',repr(self.fieldDelim) +## print 'rowDelim=',repr(self.rowDelim) +## pprint.pprint(self.data) + + +class PPSpacer: + def __init__(self): + self.height = 24 #points + + def getFlowable(self): + return Spacer(72, self.height) + + + ############################################################# + # + # The following are things you can draw on a page directly. + # + ############################################################## + +##class PPDrawingElement: +## """Base class for something which you draw directly on the page.""" +## def drawOn(self, canv): +## raise "NotImplementedError", "Abstract base class!" + + +class PPFixedImage: + """You place this on the page, rather than flowing it""" + def __init__(self): + self.filename = None + self.x = 0 + self.y = 0 + self.width = None + self.height = None + + def drawOn(self, canv): + if self.filename: + x, y = self.x, self.y + w, h = self.width, self.height + canv.drawImage(self.filename, x, y, w, h) + + +class PPRectangle: + def __init__(self, x, y, width, height): + self.x = x + self.y = y + self.width = width + self.height = height + self.fillColor = None + self.strokeColor = (1,1,1) + self.lineWidth=0 + + def drawOn(self, canv): + canv.saveState() + canv.setLineWidth(self.lineWidth) + if self.fillColor: + r,g,b = checkColor(self.fillColor) + canv.setFillColorRGB(r,g,b) + if self.strokeColor: + r,g,b = checkColor(self.strokeColor) + canv.setStrokeColorRGB(r,g,b) + canv.rect(self.x, self.y, self.width, self.height, + stroke=(self.strokeColor<>None), + fill = (self.fillColor<>None) + ) + canv.restoreState() + + +class PPRoundRect: + def __init__(self, x, y, width, height, radius): + self.x = x + self.y = y + self.width = width + self.height = height + self.radius = radius + self.fillColor = None + self.strokeColor = (1,1,1) + self.lineWidth=0 + + def drawOn(self, canv): + canv.saveState() + canv.setLineWidth(self.lineWidth) + if self.fillColor: + r,g,b = checkColor(self.fillColor) + canv.setFillColorRGB(r,g,b) + if self.strokeColor: + r,g,b = checkColor(self.strokeColor) + canv.setStrokeColorRGB(r,g,b) + canv.roundRect(self.x, self.y, self.width, self.height, + self.radius, + stroke=(self.strokeColor<>None), + fill = (self.fillColor<>None) + ) + canv.restoreState() + + +class PPLine: + def __init__(self, x1, y1, x2, y2): + self.x1 = x1 + self.y1 = y1 + self.x2 = x2 + self.y2 = y2 + self.fillColor = None + self.strokeColor = (1,1,1) + self.lineWidth=0 + + def drawOn(self, canv): + canv.saveState() + canv.setLineWidth(self.lineWidth) + if self.strokeColor: + r,g,b = checkColor(self.strokeColor) + canv.setStrokeColorRGB(r,g,b) + canv.line(self.x1, self.y1, self.x2, self.y2) + canv.restoreState() + + +class PPEllipse: + def __init__(self, x1, y1, x2, y2): + self.x1 = x1 + self.y1 = y1 + self.x2 = x2 + self.y2 = y2 + self.fillColor = None + self.strokeColor = (1,1,1) + self.lineWidth=0 + + def drawOn(self, canv): + canv.saveState() + canv.setLineWidth(self.lineWidth) + if self.strokeColor: + r,g,b = checkColor(self.strokeColor) + canv.setStrokeColorRGB(r,g,b) + if self.fillColor: + r,g,b = checkColor(self.fillColor) + canv.setFillColorRGB(r,g,b) + canv.ellipse(self.x1, self.y1, self.x2, self.y2, + stroke=(self.strokeColor<>None), + fill = (self.fillColor<>None) + ) + canv.restoreState() + + +class PPPolygon: + def __init__(self, pointlist): + self.points = pointlist + self.fillColor = None + self.strokeColor = (1,1,1) + self.lineWidth=0 + + def drawOn(self, canv): + canv.saveState() + canv.setLineWidth(self.lineWidth) + if self.strokeColor: + r,g,b = checkColor(self.strokeColor) + canv.setStrokeColorRGB(r,g,b) + if self.fillColor: + r,g,b = checkColor(self.fillColor) + canv.setFillColorRGB(r,g,b) + + path = canv.beginPath() + (x,y) = self.points[0] + path.moveTo(x,y) + for (x,y) in self.points[1:]: + path.lineTo(x,y) + path.close() + canv.drawPath(path, + stroke=(self.strokeColor<>None), + fill=(self.fillColor<>None)) + canv.restoreState() + + +class PPString: + def __init__(self, x, y): + self.text = '' + self.x = x + self.y = y + self.align = TA_LEFT + self.font = 'Times-Roman' + self.size = 12 + self.color = (0,0,0) + self.hasInfo = 0 # these can have data substituted into them + + def normalizeText(self): + """It contains literal XML text typed over several lines. + We want to throw away + tabs, newlines and so on, and only accept embedded string + like '\n'""" + lines = string.split(self.text, '\n') + newtext = [] + for line in lines: + newtext.append(string.strip(line)) + #accept all the '\n' as newlines + + self.text = newtext + + def drawOn(self, canv): + # for a string in a section, this will be drawn several times; + # so any substitution into the text should be in a temporary + # variable + if self.hasInfo: + # provide a dictionary of stuff which might go into + # the string, so they can number pages, do headers + # etc. + info = {} + info['title'] = canv._doc.info.title + info['author'] = canv._doc.info.author + info['subject'] = canv._doc.info.subject + info['page'] = canv.getPageNumber() + drawText = self.text % info + else: + drawText = self.text + + if self.color is None: + return + lines = string.split(string.strip(drawText), '\\n') + canv.saveState() + + canv.setFont(self.font, self.size) + + r,g,b = checkColor(self.color) + canv.setFillColorRGB(r,g,b) + cur_y = self.y + for line in lines: + if self.align == TA_LEFT: + canv.drawString(self.x, cur_y, line) + elif self.align == TA_CENTER: + canv.drawCentredString(self.x, cur_y, line) + elif self.align == TA_RIGHT: + canv.drawRightString(self.x, cur_y, line) + cur_y = cur_y - 1.2*self.size + + canv.restoreState() + +class PPDrawing: + def __init__(self): + self.drawing = None + def getFlowable(self): + return self.drawing + +class PPFigure: + def __init__(self): + self.figure = None + def getFlowable(self): + return self.figure + +def getSampleStyleSheet(): + from reportlab.tools.pythonpoint.styles.standard import getParagraphStyles + return getParagraphStyles() + + +#make a singleton and a function to access it +_styles = None +def getStyles(): + global _styles + if not _styles: + _styles = getSampleStyleSheet() + return _styles + + +def setStyles(newStyleSheet): + global _styles + _styles = newStyleSheet + +_pyRXP_Parser = None +def validate(rawdata): + global _pyRXP_Parser + if not _pyRXP_Parser: + try: + import pyRXP + except ImportError: + return + from reportlab.lib.utils import open_and_read, _RL_DIR, rl_isfile + dtd = 'pythonpoint.dtd' + if not rl_isfile(dtd): + dtd = os.path.join(_RL_DIR,'tools','pythonpoint','pythonpoint.dtd') + if not rl_isfile(dtd): return + def eocb(URI,dtdText=open_and_read(dtd),dtd=dtd): + if os.path.basename(URI)=='pythonpoint.dtd': return dtd,dtdText + return URI + _pyRXP_Parser = pyRXP.Parser(eoCB=eocb) + return _pyRXP_Parser.parse(rawdata) + +def process(datafile, notes=0, handout=0, printout=0, cols=0, verbose=0, outDir=None, datafilename=None, fx=1): + "Process one PythonPoint source file." + if not hasattr(datafile, "read"): + if not datafilename: datafilename = datafile + datafile = open(datafile) + else: + if not datafilename: datafilename = "PseudoFile" + rawdata = datafile.read() + + #if pyRXP present, use it to check and get line numbers for errors... + validate(rawdata) + return _process(rawdata, datafilename, notes, handout, printout, cols, verbose, outDir, fx) + +def _process(rawdata, datafilename, notes=0, handout=0, printout=0, cols=0, verbose=0, outDir=None, fx=1): + #print 'inner process fx=%d' % fx + from reportlab.tools.pythonpoint.stdparser import PPMLParser + parser = PPMLParser() + parser.fx = fx + parser.sourceFilename = datafilename + parser.feed(rawdata) + pres = parser.getPresentation() + pres.sourceFilename = datafilename + pres.outDir = outDir + pres.notes = notes + pres.handout = handout + pres.printout = printout + pres.cols = cols + pres.verbose = verbose + + if printout: + pres.slides = handleHiddenSlides(pres.slides) + + #this does all the work + pdfcontent = pres.save() + + if verbose: + print 'saved presentation %s.pdf' % os.path.splitext(datafilename)[0] + parser.close() + + return pdfcontent +##class P: +## def feed(self, text): +## parser = stdparser.PPMLParser() +## d = pyRXP.parse(text) +## +## +##def process2(datafilename, notes=0, handout=0, cols=0): +## "Process one PythonPoint source file." +## +## import pyRXP, pprint +## +## rawdata = open(datafilename).read() +## d = pyRXP.parse(rawdata) +## pprint.pprint(d) + + +def handleOptions(): + # set defaults + from reportlab import rl_config + options = {'cols':2, + 'handout':0, + 'printout':0, + 'help':0, + 'notes':0, + 'fx':1, + 'verbose':rl_config.verbose, + 'silent':0, + 'outDir': None} + + args = sys.argv[1:] + args = filter(lambda x: x and x[0]=='-',args) + filter(lambda x: not x or x[0]!='-',args) + try: + shortOpts = 'hnvsx' + longOpts = string.split('cols= outdir= handout help notes printout verbose silent nofx') + optList, args = getopt.getopt(args, shortOpts, longOpts) + except getopt.error, msg: + options['help'] = 1 + + if not args and os.path.isfile('pythonpoint.xml'): + args = ['pythonpoint.xml'] + + # Remove leading dashes (max. two). + for i in range(len(optList)): + o, v = optList[i] + while o[0] == '-': + o = o[1:] + optList[i] = (o, v) + + if o == 'cols': options['cols'] = int(v) + elif o=='outdir': options['outDir'] = v + + if filter(lambda ov: ov[0] == 'handout', optList): + options['handout'] = 1 + + if filter(lambda ov: ov[0] == 'printout', optList): + options['printout'] = 1 + + if optList == [] and args == [] or \ + filter(lambda ov: ov[0] in ('h', 'help'), optList): + options['help'] = 1 + + if filter(lambda ov: ov[0] in ('n', 'notes'), optList): + options['notes'] = 1 + + if filter(lambda ov: ov[0] in ('x', 'nofx'), optList): + options['fx'] = 0 + + if filter(lambda ov: ov[0] in ('v', 'verbose'), optList): + options['verbose'] = 1 + + #takes priority over verbose. Used by our test suite etc. + #to ensure no output at all + if filter(lambda ov: ov[0] in ('s', 'silent'), optList): + options['silent'] = 1 + options['verbose'] = 0 + + + return options, args + +def main(): + options, args = handleOptions() + + if options['help']: + print USAGE_MESSAGE + sys.exit(0) + + if options['verbose'] and options['notes']: + print 'speaker notes mode' + + if options['verbose'] and options['handout']: + print 'handout mode' + + if options['verbose'] and options['printout']: + print 'printout mode' + + if not options['fx']: + print 'suppressing special effects' + for fileGlobs in args: + files = glob.glob(fileGlobs) + if not files: + print fileGlobs, "not found" + return + for datafile in files: + if os.path.isfile(datafile): + file = os.path.join(os.getcwd(), datafile) + notes, handout, printout, cols, verbose, fx = options['notes'], options['handout'], options['printout'], options['cols'], options['verbose'], options['fx'] + process(file, notes, handout, printout, cols, verbose, options['outDir'], fx=fx) + else: + print 'Data file not found:', datafile + +if __name__ == '__main__': + main() diff --git a/bin/reportlab/tools/pythonpoint/stdparser.py b/bin/reportlab/tools/pythonpoint/stdparser.py new file mode 100644 index 00000000000..aaca2df2000 --- /dev/null +++ b/bin/reportlab/tools/pythonpoint/stdparser.py @@ -0,0 +1,813 @@ +""" +Parser for PythonPoint using the xmllib.py in the standard Python +distribution. Slow, but always present. We intend to add new parsers +as Python 2.x and the XML package spread in popularity and stabilise. + +The parser has a getPresentation method; it is called from +pythonpoint.py. +""" + +import string, imp, sys, os, copy +from reportlab.lib.utils import SeqTypes +from reportlab.lib import xmllib +from reportlab.lib import colors +from reportlab.lib.enums import TA_LEFT, TA_RIGHT, TA_CENTER, TA_JUSTIFY +from reportlab.lib.utils import recursiveImport +from reportlab.tools.pythonpoint import pythonpoint +from reportlab.platypus import figures + + +def getModule(modulename,fromPath='reportlab.tools.pythonpoint.styles'): + """Get a module containing style declarations. + + Search order is: + reportlab/tools/pythonpoint/ + reportlab/tools/pythonpoint/styles/ + ./ + """ + + try: + exec 'from reportlab.tools.pythonpoint import '+modulename + return eval(modulename) + except ImportError: + try: + exec 'from reportlab.tools.pythonpoint.styles import '+modulename + return eval(modulename) + except ImportError: + exec 'import '+modulename + return eval(modulename) + + +class PPMLParser(xmllib.XMLParser): + attributes = { + #this defines the available attributes for all objects, + #and their default values. Although these don't have to + #be strings, the ones parsed from the XML do, so + #everything is a quoted string and the parser has to + #convert these to numbers where appropriate. + 'stylesheet': { + 'path':'None', + 'module':'None', + 'function':'getParagraphStyles' + }, + 'frame': { + 'x':'0', + 'y':'0', + 'width':'0', + 'height':'0', + 'border':'false', + 'leftmargin':'0', #this is ignored + 'topmargin':'0', #this is ignored + 'rightmargin':'0', #this is ignored + 'bottommargin':'0', #this is ignored + }, + 'slide': { + 'id':'None', + 'title':'None', + 'effectname':'None', # Split, Blinds, Box, Wipe, Dissolve, Glitter + 'effectdirection':'0', # 0,90,180,270 + 'effectdimension':'H', # H or V - horizontal or vertical + 'effectmotion':'I', # Inwards or Outwards + 'effectduration':'1', #seconds, + 'outlineentry':'None', + 'outlinelevel':'0' # 1 is a child, 2 is a grandchild etc. + }, + 'para': { + 'style':'Normal', + 'bullettext':'', + 'effectname':'None', + 'effectdirection':'0', + 'effectdimension':'H', + 'effectmotion':'I', + 'effectduration':'1' + }, + 'image': { + 'filename':'', + 'width':'None', + 'height':'None', + 'effectname':'None', + 'effectdirection':'0', + 'effectdimension':'H', + 'effectmotion':'I', + 'effectduration':'1' + }, + 'table': { + 'widths':'None', + 'heights':'None', + 'fieldDelim':',', + 'rowDelim':'\n', + 'style':'None', + 'effectname':'None', + 'effectdirection':'0', + 'effectdimension':'H', + 'effectmotion':'I', + 'effectduration':'1' + }, + 'rectangle': { + 'x':'0', + 'y':'0', + 'width':'100', + 'height':'100', + 'fill':'None', + 'stroke':'(0,0,0)', + 'linewidth':'0', + 'effectname':'None', + 'effectdirection':'0', + 'effectdimension':'H', + 'effectmotion':'I', + 'effectduration':'1' + }, + 'roundrect': { + 'x':'0', + 'y':'0', + 'width':'100', + 'height':'100', + 'radius':'6', + 'fill':'None', + 'stroke':'(0,0,0)', + 'linewidth':'0', + 'effectname':'None', + 'effectdirection':'0', + 'effectdimension':'H', + 'effectmotion':'I', + 'effectduration':'1' + }, + 'line': { + 'x1':'0', + 'y1':'0', + 'x2':'100', + 'y2':'100', + 'stroke':'(0,0,0)', + 'width':'0', + 'effectname':'None', + 'effectdirection':'0', + 'effectdimension':'H', + 'effectmotion':'I', + 'effectduration':'1' + }, + 'ellipse': { + 'x1':'0', + 'y1':'0', + 'x2':'100', + 'y2':'100', + 'stroke':'(0,0,0)', + 'fill':'None', + 'linewidth':'0', + 'effectname':'None', + 'effectdirection':'0', + 'effectdimension':'H', + 'effectmotion':'I', + 'effectduration':'1' + }, + 'polygon': { + 'points':'(0,0),(50,0),(25,25)', + 'stroke':'(0,0,0)', + 'linewidth':'0', + 'stroke':'(0,0,0)', + 'fill':'None', + 'effectname':'None', + 'effectdirection':'0', + 'effectdimension':'H', + 'effectmotion':'I', + 'effectduration':'1' + }, + 'string':{ + 'x':'0', + 'y':'0', + 'color':'(0,0,0)', + 'font':'Times-Roman', + 'size':'12', + 'align':'left', + 'effectname':'None', + 'effectdirection':'0', + 'effectdimension':'H', + 'effectmotion':'I', + 'effectduration':'1' + }, + 'customshape':{ + 'path':'None', + 'module':'None', + 'class':'None', + 'initargs':'None' + } + } + + def __init__(self): + self.presentations = [] + #yes, I know a generic stack would be easier... + #still, testing if we are 'in' something gives + #a degree of validation. + self._curPres = None + self._curSection = None + self._curSlide = None + self._curFrame = None + self._curPara = None #the only places we are interested in + self._curPrefmt = None + self._curPyCode = None + self._curString = None + self._curTable = None + self._curTitle = None + self._curAuthor = None + self._curSubject = None + self.fx = 1 + xmllib.XMLParser.__init__(self) + + def _arg(self,tag,args,name): + "What's this for???" + if args.has_key(name): + v = args[name] + else: + if self.attributes.has_key(tag): + v = self.attributes[tag][name] + else: + v = None + return v + + def ceval(self,tag,args,name): + if args.has_key(name): + v = args[name] + else: + if self.attributes.has_key(tag): + v = self.attributes[tag][name] + else: + return None + + # handle named colors (names from reportlab.lib.colors) + if name in ('color', 'stroke', 'fill'): + v = str(pythonpoint.checkColor(v)) + + return eval(v) + + def getPresentation(self): + return self._curPres + + + def handle_data(self, data): + #the only data should be paragraph text, preformatted para + #text, 'string text' for a fixed string on the page, + #or table data + + if self._curPara: + self._curPara.rawtext = self._curPara.rawtext + data + elif self._curPrefmt: + self._curPrefmt.rawtext = self._curPrefmt.rawtext + data + elif self._curPyCode: + self._curPyCode.rawtext = self._curPyCode.rawtext + data + elif self._curString: + self._curString.text = self._curString.text + data + elif self._curTable: + self._curTable.rawBlocks.append(data) + elif self._curTitle <> None: # need to allow empty strings, + # hence explicitly testing for None + self._curTitle = self._curTitle + data + elif self._curAuthor <> None: + self._curAuthor = self._curAuthor + data + elif self._curSubject <> None: + self._curSubject = self._curSubject + data + + def handle_cdata(self, data): + #just append to current paragraph text, so we can quote XML + if self._curPara: + self._curPara.rawtext = self._curPara.rawtext + data + elif self._curPrefmt: + self._curPrefmt.rawtext = self._curPrefmt.rawtext + data + elif self._curPyCode: + self._curPyCode.rawtext = self._curPyCode.rawtext + data + elif self._curString: + self._curString.text = self._curString.text + data + elif self._curTable: + self._curTable.rawBlocks.append(data) + elif self._curAuthor <> None: + self._curAuthor = self._curAuthor + data + elif self._curSubject <> None: + self._curSubject = self._curSubject + data + + def start_presentation(self, args): + self._curPres = pythonpoint.PPPresentation() + self._curPres.filename = self._arg('presentation',args,'filename') + self._curPres.effectName = self._arg('presentation',args,'effect') + self._curPres.pageDuration = self._arg('presentation',args,'pageDuration') + + h = self._arg('presentation',args,'pageHeight') + if h: + self._curPres.pageHeight = h + w = self._arg('presentation',args,'pageWidth') + if w: + self._curPres.pageWidth = w + #print 'page size =', self._curPres.pageSize + + def end_presentation(self): + pass +## print 'Fully parsed presentation',self._curPres.filename + + def start_title(self, args): + self._curTitle = '' + + + def end_title(self): + self._curPres.title = self._curTitle + self._curTitle = None + + def start_author(self, args): + self._curAuthor = '' + + def end_author(self): + self._curPres.author = self._curAuthor + self._curAuthor = None + + def start_subject(self, args): + self._curSubject = '' + + def end_subject(self): + self._curPres.subject = self._curSubject + self._curSubject = None + + def start_stylesheet(self, args): + #makes it the current style sheet. + path = self._arg('stylesheet',args,'path') + if path=='None': path = [] + if type(path) not in SeqTypes: path = [path] + path.append('styles') + path.append(os.getcwd()) + modulename = self._arg('stylesheet', args, 'module') + funcname = self._arg('stylesheet', args, 'function') + try: + found = imp.find_module(modulename, path) + (file, pathname, description) = found + mod = imp.load_module(modulename, file, pathname, description) + except ImportError: + #last gasp + mod = getModule(modulename) + + #now get the function + func = getattr(mod, funcname) + pythonpoint.setStyles(func()) +## print 'set global stylesheet to %s.%s()' % (modulename, funcname) + + def end_stylesheet(self): + pass + + def start_section(self, args): + name = self._arg('section',args,'name') + self._curSection = pythonpoint.PPSection(name) + + def end_section(self): + self._curSection = None + + + def start_slide(self, args): + s = pythonpoint.PPSlide() + s.id = self._arg('slide',args,'id') + s.title = self._arg('slide',args,'title') + a = self._arg('slide',args,'effectname') + if a <> 'None': + s.effectName = a + s.effectDirection = self.ceval('slide',args,'effectdirection') + s.effectDimension = self._arg('slide',args,'effectdimension') + s.effectDuration = self.ceval('slide',args,'effectduration') + s.effectMotion = self._arg('slide',args,'effectmotion') + + #HACK - may not belong here in the long run... + #by default, use the slide title for the outline entry, + #unless it is specified as an arg. + a = self._arg('slide',args,'outlineentry') + if a == "Hide": + s.outlineEntry = None + elif a <> 'None': + s.outlineEntry = a + else: + s.outlineEntry = s.title + + s.outlineLevel = self.ceval('slide',args,'outlinelevel') + + #let it know its section, which may be none + s.section = self._curSection + self._curSlide = s + + def end_slide(self): + self._curPres.slides.append(self._curSlide) + self._curSlide = None + + def start_frame(self, args): + self._curFrame = pythonpoint.PPFrame( + self.ceval('frame',args,'x'), + self.ceval('frame',args,'y'), + self.ceval('frame',args,'width'), + self.ceval('frame',args,'height') + ) + if self._arg('frame',args,'border')=='true': + self._curFrame.showBoundary = 1 + + def end_frame(self): + self._curSlide.frames.append(self._curFrame) + self._curFrame = None + + def start_notes(self, args): + name = self._arg('notes',args,'name') + self._curNotes = pythonpoint.PPNotes() + + def end_notes(self): + self._curSlide.notes.append(self._curNotes) + self._curNotes = None + + def start_registerFont(self, args): + name = self._arg('font',args,'name') + path = self._arg('font',args,'path') + pythonpoint.registerFont0(self.sourceFilename, name, path) + + + def end_registerFont(self): + pass + + + def pack_slide(self, element, args): + if self.fx: + effectName = self._arg(element,args,'effectname') + if effectName <> 'None': + curSlide = copy.deepcopy(self._curSlide) + if self._curFrame: + curFrame = copy.deepcopy(self._curFrame) + curSlide.frames.append(curFrame) + self._curPres.slides.append(curSlide) + self._curSlide.effectName = effectName + self._curSlide.effectDirection = self.ceval(element,args,'effectdirection') + self._curSlide.effectDimension = self._arg(element,args,'effectdimension') + self._curSlide.effectDuration = self.ceval(element,args,'effectduration') + self._curSlide.effectMotion = self._arg(element,args,'effectmotion') + self._curSlide.outlineEntry = None + + def start_para(self, args): + self.pack_slide('para', args) + self._curPara = pythonpoint.PPPara() + self._curPara.style = self._arg('para',args,'style') + + # hack - bullet character if bullet style + bt = self._arg('para',args,'bullettext') + if bt == '': + if self._curPara.style == 'Bullet': + bt = '\xc2\xb7' # Symbol Font bullet character, reasonable default + elif self._curPara.style == 'Bullet2': + bt = '\xc2\xb7' # second-level bullet + else: + bt = None + + self._curPara.bulletText = bt + + def end_para(self): + if self._curFrame: + self._curFrame.content.append(self._curPara) + self._curPara = None + elif self._curNotes: + self._curNotes.content.append(self._curPara) + self._curPara = None + + + def start_prefmt(self, args): + self._curPrefmt = pythonpoint.PPPreformattedText() + self._curPrefmt.style = self._arg('prefmt',args,'style') + + + def end_prefmt(self): + self._curFrame.content.append(self._curPrefmt) + self._curPrefmt = None + + + def start_pycode(self, args): + self._curPyCode = pythonpoint.PPPythonCode() + self._curPyCode.style = self._arg('pycode',args,'style') + + + def end_pycode(self): + self._curFrame.content.append(self._curPyCode) + self._curPyCode = None + + + def start_image(self, args): + self.pack_slide('image',args) + sourceFilename = self.sourceFilename # XXX + filename = self._arg('image',args,'filename') + filename = os.path.join(os.path.dirname(sourceFilename), filename) + self._curImage = pythonpoint.PPImage() + self._curImage.filename = filename + self._curImage.width = self.ceval('image',args,'width') + self._curImage.height = self.ceval('image',args,'height') + + + def end_image(self): + self._curFrame.content.append(self._curImage) + self._curImage = None + + + def start_table(self, args): + self.pack_slide('table',args) + self._curTable = pythonpoint.PPTable() + self._curTable.widths = self.ceval('table',args,'widths') + self._curTable.heights = self.ceval('table',args,'heights') + #these may contain escapes like tabs - handle with + #a bit more care. + if args.has_key('fieldDelim'): + self._curTable.fieldDelim = eval('"' + args['fieldDelim'] + '"') + if args.has_key('rowDelim'): + self._curTable.rowDelim = eval('"' + args['rowDelim'] + '"') + if args.has_key('style'): + self._curTable.style = args['style'] + + + def end_table(self): + self._curFrame.content.append(self._curTable) + self._curTable = None + + + def start_spacer(self, args): + """No contents so deal with it here.""" + sp = pythonpoint.PPSpacer() + sp.height = eval(args['height']) + self._curFrame.content.append(sp) + + + def end_spacer(self): + pass + + + ## the graphics objects - go into either the current section + ## or the current slide. + def start_fixedimage(self, args): + sourceFilename = self.sourceFilename + filename = self._arg('image',args,'filename') + filename = os.path.join(os.path.dirname(sourceFilename), filename) + img = pythonpoint.PPFixedImage() + img.filename = filename + img.x = self.ceval('fixedimage',args,'x') + img.y = self.ceval('fixedimage',args,'y') + img.width = self.ceval('fixedimage',args,'width') + img.height = self.ceval('fixedimage',args,'height') + self._curFixedImage = img + + + def end_fixedimage(self): + if self._curSlide: + self._curSlide.graphics.append(self._curFixedImage) + elif self._curSection: + self._curSection.graphics.append(self._curFixedImage) + self._curFixedImage = None + + + def start_rectangle(self, args): + self.pack_slide('rectangle', args) + rect = pythonpoint.PPRectangle( + self.ceval('rectangle',args,'x'), + self.ceval('rectangle',args,'y'), + self.ceval('rectangle',args,'width'), + self.ceval('rectangle',args,'height') + ) + rect.fillColor = self.ceval('rectangle',args,'fill') + rect.strokeColor = self.ceval('rectangle',args,'stroke') + self._curRectangle = rect + + + def end_rectangle(self): + if self._curSlide: + self._curSlide.graphics.append(self._curRectangle) + elif self._curSection: + self._curSection.graphics.append(self._curRectangle) + self._curRectangle = None + + + def start_roundrect(self, args): + self.pack_slide('roundrect', args) + rrect = pythonpoint.PPRoundRect( + self.ceval('roundrect',args,'x'), + self.ceval('roundrect',args,'y'), + self.ceval('roundrect',args,'width'), + self.ceval('roundrect',args,'height'), + self.ceval('roundrect',args,'radius') + ) + rrect.fillColor = self.ceval('roundrect',args,'fill') + rrect.strokeColor = self.ceval('roundrect',args,'stroke') + self._curRoundRect = rrect + + + def end_roundrect(self): + if self._curSlide: + self._curSlide.graphics.append(self._curRoundRect) + elif self._curSection: + self._curSection.graphics.append(self._curRoundRect) + self._curRoundRect = None + + + def start_line(self, args): + self.pack_slide('line', args) + self._curLine = pythonpoint.PPLine( + self.ceval('line',args,'x1'), + self.ceval('line',args,'y1'), + self.ceval('line',args,'x2'), + self.ceval('line',args,'y2') + ) + self._curLine.strokeColor = self.ceval('line',args,'stroke') + + + def end_line(self): + if self._curSlide: + self._curSlide.graphics.append(self._curLine) + elif self._curSection: + self._curSection.graphics.append(self._curLine) + self._curLine = None + + + def start_ellipse(self, args): + self.pack_slide('ellipse', args) + self._curEllipse = pythonpoint.PPEllipse( + self.ceval('ellipse',args,'x1'), + self.ceval('ellipse',args,'y1'), + self.ceval('ellipse',args,'x2'), + self.ceval('ellipse',args,'y2') + ) + self._curEllipse.strokeColor = self.ceval('ellipse',args,'stroke') + self._curEllipse.fillColor = self.ceval('ellipse',args,'fill') + + + def end_ellipse(self): + if self._curSlide: + self._curSlide.graphics.append(self._curEllipse) + elif self._curSection: + self._curSection.graphics.append(self._curEllipse) + self._curEllipse = None + + + def start_polygon(self, args): + self.pack_slide('polygon', args) + self._curPolygon = pythonpoint.PPPolygon(self.ceval('polygon',args,'points')) + self._curPolygon.strokeColor = self.ceval('polygon',args,'stroke') + self._curPolygon.fillColor = self.ceval('polygon',args,'fill') + + + def end_polygon(self): + if self._curSlide: + self._curSlide.graphics.append(self._curPolygon) + elif self._curSection: + self._curSection.graphics.append(self._curPolygon) + self._curEllipse = None + + + def start_string(self, args): + self.pack_slide('string', args) + self._curString = pythonpoint.PPString( + self.ceval('string',args,'x'), + self.ceval('string',args,'y') + ) + self._curString.color = self.ceval('string',args,'color') + self._curString.font = self._arg('string',args,'font') + self._curString.size = self.ceval('string',args,'size') + if args['align'] == 'left': + self._curString.align = TA_LEFT + elif args['align'] == 'center': + self._curString.align = TA_CENTER + elif args['align'] == 'right': + self._curString.align = TA_RIGHT + elif args['align'] == 'justify': + self._curString.align = TA_JUSTIFY + #text comes later within the tag + + + def end_string(self): + #controller should have set the text + if self._curSlide: + self._curSlide.graphics.append(self._curString) + elif self._curSection: + self._curSection.graphics.append(self._curString) + self._curString = None + + + def start_infostring(self, args): + # like a string, but lets them embed page no, author etc. + self.start_string(args) + self._curString.hasInfo = 1 + + + def end_infostring(self): + self.end_string() + + + def start_customshape(self, args): + #loads one + path = self._arg('customshape',args,'path') + if path=='None': + path = [] + else: + path=[path] + + # add package root folder and input file's folder to path + path.append(os.path.dirname(self.sourceFilename)) + path.append(os.path.dirname(pythonpoint.__file__)) + + modulename = self._arg('customshape',args,'module') + funcname = self._arg('customshape',args,'class') + try: + found = imp.find_module(modulename, path) + (file, pathname, description) = found + mod = imp.load_module(modulename, file, pathname, description) + except ImportError: + mod = getModule(modulename) + + #now get the function + + func = getattr(mod, funcname) + initargs = self.ceval('customshape',args,'initargs') + self._curCustomShape = apply(func, initargs) + + def end_customshape(self): + if self._curSlide: + self._curSlide.graphics.append(self._curCustomShape) + elif self._curSection: + self._curSection.graphics.append(self._curCustomShape) + self._curCustomShape = None + + def start_drawing(self, args): + #loads one + moduleName = args["module"] + funcName = args["constructor"] + showBoundary = int(args.get("showBoundary", "0")) + hAlign = args.get("hAlign", "CENTER") + + + # the path for the imports should include: + # 1. document directory + # 2. python path if baseDir not given, or + # 3. baseDir if given + try: + dirName = sdict["baseDir"] + except: + dirName = None + importPath = [os.getcwd()] + if dirName is None: + importPath.extend(sys.path) + else: + importPath.insert(0, dirName) + + modul = recursiveImport(moduleName, baseDir=importPath) + func = getattr(modul, funcName) + drawing = func() + + drawing.hAlign = hAlign + if showBoundary: + drawing._showBoundary = 1 + + self._curDrawing = pythonpoint.PPDrawing() + self._curDrawing.drawing = drawing + + def end_drawing(self): + self._curFrame.content.append(self._curDrawing) + self._curDrawing = None + + def start_pageCatcherFigure(self, args): + filename = args["filename"] + pageNo = int(args["pageNo"]) + width = float(args.get("width", "595")) + height = float(args.get("height", "842")) + + + fig = figures.PageCatcherFigureNonA4(filename, pageNo, args.get("caption", ""), width, height) + sf = args.get('scaleFactor', None) + if sf: sf = float(sf) + border = not (args.get('border', None) in ['0','no']) + + fig.scaleFactor = sf + fig.border = border + + #self.ceval('pageCatcherFigure',args,'scaleFactor'), + #initargs = self.ceval('customshape',args,'initargs') + self._curFigure = pythonpoint.PPFigure() + self._curFigure.figure = fig + + def end_pageCatcherFigure(self): + self._curFrame.content.append(self._curFigure) + self._curFigure = None + + ## intra-paragraph XML should be allowed through into PLATYPUS + def unknown_starttag(self, tag, attrs): + if self._curPara: + echo = '<%s' % tag + for (key, value) in attrs.items(): + echo = echo + ' %s="%s"' % (key, value) + echo = echo + '>' + self._curPara.rawtext = self._curPara.rawtext + echo + else: + print 'Unknown start tag %s' % tag + + + def unknown_endtag(self, tag): + if self._curPara: + self._curPara.rawtext = self._curPara.rawtext + ''% tag + else: + print 'Unknown end tag %s' % tag + + def handle_charref(self, name): + try: + if name[0]=='x': + n = int(name[1:],16) + else: + n = int(name) + except ValueError: + self.unknown_charref(name) + return + self.handle_data(unichr(n).encode('utf8')) diff --git a/bin/reportlab/tools/pythonpoint/styles/__init__.py b/bin/reportlab/tools/pythonpoint/styles/__init__.py new file mode 100644 index 00000000000..e67468fd9ee --- /dev/null +++ b/bin/reportlab/tools/pythonpoint/styles/__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/pythonpoint/styles/__init__.py diff --git a/bin/reportlab/tools/pythonpoint/styles/horrible.py b/bin/reportlab/tools/pythonpoint/styles/horrible.py new file mode 100644 index 00000000000..fa6d501b043 --- /dev/null +++ b/bin/reportlab/tools/pythonpoint/styles/horrible.py @@ -0,0 +1,101 @@ +#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/pythonpoint/styles/horrible.py +__version__=''' $Id: horrible.py 2385 2004-06-17 15:26:05Z rgbecker $ ''' +# style_modern.py +__doc__="""This is an example style sheet. You can create your own, and +have them loaded by the presentation. A style sheet is just a +dictionary, where they keys are style names and the values are +ParagraphStyle objects. + +You must provide a function called "getParagraphStyles()" to +return it. In future, we can put things like LineStyles, +TableCellStyles etc. in the same modules. + +You might wish to have two parallel style sheets, one for colour +and one for black and white, so you can switch your presentations +easily. + +A style sheet MUST define a style called 'Normal'. +""" + +from reportlab.lib import styles, enums +def getParagraphStyles(): + """Returns a dictionary of styles based on Helvetica""" + stylesheet = {} + + para = styles.ParagraphStyle('Normal', None) #the ancestor of all + para.fontName = 'Courier' + para.fontSize = 24 + para.leading = 28 + stylesheet['Normal'] = para + + para = ParagraphStyle('BodyText', stylesheet['Normal']) + para.spaceBefore = 12 + stylesheet['BodyText'] = para + + para = ParagraphStyle('BigCentered', stylesheet['Normal']) + para.spaceBefore = 12 + para.alignment = enums.TA_CENTER + stylesheet['BigCentered'] = para + + para = ParagraphStyle('Italic', stylesheet['BodyText']) + para.fontName = 'Courier-Oblique' + stylesheet['Italic'] = para + + para = ParagraphStyle('Title', stylesheet['Normal']) + para.fontName = 'Courier' + para.fontSize = 48 + para.Leading = 58 + para.spaceAfter = 36 + para.alignment = enums.TA_CENTER + stylesheet['Title'] = para + + para = ParagraphStyle('Heading1', stylesheet['Normal']) + para.fontName = 'Courier-Bold' + para.fontSize = 36 + para.leading = 44 + para.spaceAfter = 36 + para.alignment = enums.TA_CENTER + stylesheet['Heading1'] = para + + para = ParagraphStyle('Heading2', stylesheet['Normal']) + para.fontName = 'Courier-Bold' + para.fontSize = 28 + para.leading = 34 + para.spaceBefore = 24 + para.spaceAfter = 12 + stylesheet['Heading2'] = para + + para = ParagraphStyle('Heading3', stylesheet['Normal']) + para.fontName = 'Courier-BoldOblique' + para.spaceBefore = 24 + para.spaceAfter = 12 + stylesheet['Heading3'] = para + + para = ParagraphStyle('Bullet', stylesheet['Normal']) + para.firstLineIndent = -18 + para.leftIndent = 72 + para.spaceBefore = 6 + #para.bulletFontName = 'Symbol' + para.bulletFontSize = 24 + para.bulletIndent = 36 + stylesheet['Bullet'] = para + + para = ParagraphStyle('Definition', stylesheet['Normal']) + #use this for definition lists + para.firstLineIndent = 0 + para.leftIndent = 72 + para.bulletIndent = 0 + para.spaceBefore = 12 + para.bulletFontName = 'Couruer-BoldOblique' + stylesheet['Definition'] = para + + para = ParagraphStyle('Code', stylesheet['Normal']) + para.fontName = 'Courier' + para.fontSize = 16 + para.leading = 18 + para.leftIndent = 36 + stylesheet['Code'] = para + + return stylesheet \ No newline at end of file diff --git a/bin/reportlab/tools/pythonpoint/styles/htu.py b/bin/reportlab/tools/pythonpoint/styles/htu.py new file mode 100644 index 00000000000..bc79a7ba68e --- /dev/null +++ b/bin/reportlab/tools/pythonpoint/styles/htu.py @@ -0,0 +1,158 @@ +from reportlab.lib import styles +from reportlab.lib import colors +from reportlab.lib.units import cm +from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_RIGHT, TA_JUSTIFY +from reportlab.platypus import Preformatted, Paragraph, Frame, \ + Image, Table, TableStyle, Spacer +from reportlab.pdfbase import pdfmetrics +from reportlab.pdfbase.ttfonts import TTFont + + +def getParagraphStyles(): + """Returns a dictionary of styles to get you started. + + We will provide a way to specify a module of these. Note that + this just includes TableStyles as well as ParagraphStyles for any + tables you wish to use. + """ + + pdfmetrics.registerFont(TTFont('Verdana','verdana.ttf')) + pdfmetrics.registerFont(TTFont('Verdana-Bold','verdanab.ttf')) + pdfmetrics.registerFont(TTFont('Verdana-Italic','verdanai.ttf')) + pdfmetrics.registerFont(TTFont('Verdana-BoldItalic','verdanaz.ttf')) + pdfmetrics.registerFont(TTFont('Arial Narrow','arialn.ttf')) + pdfmetrics.registerFont(TTFont('Arial Narrow-Bold','arialnb.ttf')) + pdfmetrics.registerFont(TTFont('Arial Narrow-Italic','arialni.ttf')) + pdfmetrics.registerFont(TTFont('Arial Narrow-BoldItalic','arialnbi.ttf')) + + stylesheet = {} + ParagraphStyle = styles.ParagraphStyle + + para = ParagraphStyle('Normal', None) #the ancestor of all + para.fontName = 'Verdana' + para.fontSize = 28 + para.leading = 32 + para.spaceAfter = 6 + stylesheet['Normal'] = para + + #This one is spaced out a bit... + para = ParagraphStyle('BodyText', stylesheet['Normal']) + para.spaceBefore = 12 + stylesheet['BodyText'] = para + + #Indented, for lists + para = ParagraphStyle('Indent', stylesheet['Normal']) + para.leftIndent = 60 + para.firstLineIndent = 0 + stylesheet['Indent'] = para + + para = ParagraphStyle('Centered', stylesheet['Normal']) + para.alignment = TA_CENTER + stylesheet['Centered'] = para + + para = ParagraphStyle('BigCentered', stylesheet['Normal']) + para.fontSize = 32 + para.alignment = TA_CENTER + para.spaceBefore = 12 + para.spaceAfter = 12 + stylesheet['BigCentered'] = para + + para = ParagraphStyle('Italic', stylesheet['BodyText']) + para.fontName = 'Verdana-Italic' + stylesheet['Italic'] = para + + para = ParagraphStyle('Title', stylesheet['Normal']) + para.fontName = 'Arial Narrow-Bold' + para.fontSize = 48 + para.leading = 58 + para.alignment = TA_CENTER + stylesheet['Title'] = para + + para = ParagraphStyle('Heading1', stylesheet['Normal']) + para.fontName = 'Arial Narrow-Bold' + para.fontSize = 40 + para.leading = 44 + para.alignment = TA_CENTER + stylesheet['Heading1'] = para + + para = ParagraphStyle('Heading2', stylesheet['Normal']) + para.fontName = 'Verdana' + para.fontSize = 32 + para.leading = 36 + para.spaceBefore = 32 + para.spaceAfter = 12 + stylesheet['Heading2'] = para + + para = ParagraphStyle('Heading3', stylesheet['Normal']) + para.fontName = 'Verdana' + para.spaceBefore = 20 + para.spaceAfter = 6 + stylesheet['Heading3'] = para + + para = ParagraphStyle('Heading4', stylesheet['Normal']) + para.fontName = 'Verdana-BoldItalic' + para.spaceBefore = 6 + stylesheet['Heading4'] = para + + para = ParagraphStyle('Bullet', stylesheet['Normal']) + para.firstLineIndent = 0 + para.leftIndent = 56 + para.spaceBefore = 6 + para.bulletFontName = 'Symbol' + para.bulletFontSize = 24 + para.bulletIndent = 20 + stylesheet['Bullet'] = para + + para = ParagraphStyle('Bullet2', stylesheet['Normal']) + para.firstLineIndent = 0 + para.leftIndent = 80 + para.spaceBefore = 6 + para.fontSize = 24 + para.bulletFontName = 'Symbol' + para.bulletFontSize = 20 + para.bulletIndent = 60 + stylesheet['Bullet2'] = para + + para = ParagraphStyle('Definition', stylesheet['Normal']) + #use this for definition lists + para.firstLineIndent = 0 + para.leftIndent = 60 + para.bulletIndent = 0 + para.bulletFontName = 'Verdana-BoldItalic' + para.bulletFontSize = 24 + stylesheet['Definition'] = para + + para = ParagraphStyle('Code', stylesheet['Normal']) + para.fontName = 'Courier' + para.fontSize = 16 + para.leading = 18 + para.leftIndent = 36 + stylesheet['Code'] = para + + para = ParagraphStyle('PythonCode', stylesheet['Normal']) + para.fontName = 'Courier' + para.fontSize = 16 + para.leading = 18 + para.leftIndent = 36 + stylesheet['Code'] = para + + para = ParagraphStyle('Small', stylesheet['Normal']) + para.fontSize = 12 + para.leading = 14 + stylesheet['Small'] = para + + #now for a table + ts = TableStyle([ + ('FONT', (0,0), (-1,-1), 'Arial Narrow', 22), + ('LINEABOVE', (0,1), (-1,1), 2, colors.green), + ('LINEABOVE', (0,2), (-1,-1), 0.25, colors.black), + ('LINEBELOW', (0,-1), (-1,-1), 2, colors.green), + ('LINEBEFORE', (0,1), (-1,-1), 2, colors.black), + ('LINEAFTER', (0,1), (-1,-1), 2, colors.black), + ('ALIGN', (4,1), (-1,-1), 'RIGHT'), #all numeric cells right aligned + ('TEXTCOLOR', (0,2), (0,-1), colors.black), + ('BACKGROUND', (0,1), (-1,1), colors.Color(0,0.7,0.7)) + ]) + stylesheet['table1'] = ts + + return stylesheet diff --git a/bin/reportlab/tools/pythonpoint/styles/modern.py b/bin/reportlab/tools/pythonpoint/styles/modern.py new file mode 100644 index 00000000000..97583b57857 --- /dev/null +++ b/bin/reportlab/tools/pythonpoint/styles/modern.py @@ -0,0 +1,120 @@ +#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/pythonpoint/styles/modern.py +__version__=''' $Id: modern.py 2385 2004-06-17 15:26:05Z rgbecker $ ''' +# style_modern.py +__doc__="""This is an example style sheet. You can create your own, and +have them loaded by the presentation. A style sheet is just a +dictionary, where they keys are style names and the values are +ParagraphStyle objects. + +You must provide a function called "getParagraphStyles()" to +return it. In future, we can put things like LineStyles, +TableCellStyles etc. in the same modules. + +You might wish to have two parallel style sheets, one for colour +and one for black and white, so you can switch your presentations +easily. + +A style sheet MUST define a style called 'Normal'. +""" + +from reportlab.lib import styles +from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_RIGHT, TA_JUSTIFY + +def getParagraphStyles(): + """Returns a dictionary of styles based on Helvetica""" + stylesheet = {} + ParagraphStyle = styles.ParagraphStyle + + para = ParagraphStyle('Normal', None) #the ancestor of all + para.fontName = 'Helvetica' + para.fontSize = 24 + para.leading = 28 + stylesheet['Normal'] = para + + para = ParagraphStyle('BodyText', stylesheet['Normal']) + para.spaceBefore = 12 + stylesheet['BodyText'] = para + + para = ParagraphStyle('Indent', stylesheet['Normal']) + para.leftIndent = 36 + para.firstLineIndent = 0 + stylesheet['Indent'] = para + + para = ParagraphStyle('Centered', stylesheet['Normal']) + para.alignment = TA_CENTER + stylesheet['Centered'] = para + + para = ParagraphStyle('BigCentered', stylesheet['Normal']) + para.spaceBefore = 12 + para.alignment = TA_CENTER + stylesheet['BigCentered'] = para + + para = ParagraphStyle('Italic', stylesheet['BodyText']) + para.fontName = 'Helvetica-Oblique' + stylesheet['Italic'] = para + + para = ParagraphStyle('Title', stylesheet['Normal']) + para.fontName = 'Helvetica' + para.fontSize = 48 + para.Leading = 58 + para.spaceAfter = 36 + para.alignment = TA_CENTER + stylesheet['Title'] = para + + para = ParagraphStyle('Heading1', stylesheet['Normal']) + para.fontName = 'Helvetica-Bold' + para.fontSize = 36 + para.leading = 44 + para.spaceAfter = 36 + para.alignment = TA_CENTER + stylesheet['Heading1'] = para + + para = ParagraphStyle('Heading2', stylesheet['Normal']) + para.fontName = 'Helvetica-Bold' + para.fontSize = 28 + para.leading = 34 + para.spaceBefore = 24 + para.spaceAfter = 12 + stylesheet['Heading2'] = para + + para = ParagraphStyle('Heading3', stylesheet['Normal']) + para.fontName = 'Helvetica-BoldOblique' + para.spaceBefore = 24 + para.spaceAfter = 12 + stylesheet['Heading3'] = para + + para = ParagraphStyle('Bullet', stylesheet['Normal']) + para.firstLineIndent = -18 + para.leftIndent = 72 + para.spaceBefore = 6 + para.bulletFontName = 'Symbol' + para.bulletFontSize = 24 + para.bulletIndent = 36 + stylesheet['Bullet'] = para + + para = ParagraphStyle('Bullet2', stylesheet['Bullet']) + para.firstLineIndent = 0 + para.bulletIndent = 72 + para.leftIndent = 108 + stylesheet['Bullet2'] = para + + + para = ParagraphStyle('Definition', stylesheet['Normal']) + #use this for definition lists + para.firstLineIndent = 0 + para.leftIndent = 72 + para.bulletIndent = 0 + para.spaceBefore = 12 + para.bulletFontName = 'Helvetica-BoldOblique' + stylesheet['Definition'] = para + + para = ParagraphStyle('Code', stylesheet['Normal']) + para.fontName = 'Courier' + para.fontSize = 16 + para.leading = 18 + para.leftIndent = 36 + stylesheet['Code'] = para + + return stylesheet \ No newline at end of file diff --git a/bin/reportlab/tools/pythonpoint/styles/projection.py b/bin/reportlab/tools/pythonpoint/styles/projection.py new file mode 100644 index 00000000000..a3c818556d0 --- /dev/null +++ b/bin/reportlab/tools/pythonpoint/styles/projection.py @@ -0,0 +1,106 @@ +"""This is an example style sheet. You can create your own, and +have them loaded by the presentation. A style sheet is just a +dictionary, where they keys are style names and the values are +ParagraphStyle objects. + +You must provide a function called "getParagraphStyles()" to +return it. In future, we can put things like LineStyles, +TableCellStyles etc. in the same modules. + +You might wish to have two parallel style sheets, one for colour +and one for black and white, so you can switch your presentations +easily. + +A style sheet MUST define a style called 'Normal'. +""" + +from reportlab.lib import styles +from reportlab.lib.colors import * +from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_RIGHT, TA_JUSTIFY + + +def getParagraphStyles(): + """Returns a dictionary of styles based on Helvetica""" + + stylesheet = {} + ParagraphStyle = styles.ParagraphStyle + + para = ParagraphStyle('Normal', None) #the ancestor of all + para.fontName = 'Helvetica-Bold' + para.fontSize = 24 + para.leading = 28 + para.textColor = white + stylesheet['Normal'] = para + + para = ParagraphStyle('BodyText', stylesheet['Normal']) + para.spaceBefore = 12 + stylesheet['BodyText'] = para + + para = ParagraphStyle('BigCentered', stylesheet['Normal']) + para.spaceBefore = 12 + para.alignment = TA_CENTER + stylesheet['BigCentered'] = para + + para = ParagraphStyle('Italic', stylesheet['BodyText']) + para.fontName = 'Helvetica-Oblique' + para.textColor = white + stylesheet['Italic'] = para + + para = ParagraphStyle('Title', stylesheet['Normal']) + para.fontName = 'Helvetica' + para.fontSize = 48 + para.Leading = 58 + para.spaceAfter = 36 + para.alignment = TA_CENTER + stylesheet['Title'] = para + + para = ParagraphStyle('Heading1', stylesheet['Normal']) + para.fontName = 'Helvetica-Bold' + para.fontSize = 48# 36 + para.leading = 44 + para.spaceAfter = 36 + para.textColor = green + para.alignment = TA_LEFT + stylesheet['Heading1'] = para + + para = ParagraphStyle('Heading2', stylesheet['Normal']) + para.fontName = 'Helvetica-Bold' + para.fontSize = 28 + para.leading = 34 + para.spaceBefore = 24 + para.spaceAfter = 12 + stylesheet['Heading2'] = para + + para = ParagraphStyle('Heading3', stylesheet['Normal']) + para.fontName = 'Helvetica-BoldOblique' + para.spaceBefore = 24 + para.spaceAfter = 12 + stylesheet['Heading3'] = para + + para = ParagraphStyle('Bullet', stylesheet['Normal']) + para.firstLineIndent = -18 + para.leftIndent = 72 + para.spaceBefore = 6 + para.bulletFontName = 'Symbol' + para.bulletFontSize = 24 + para.bulletIndent = 36 + stylesheet['Bullet'] = para + + para = ParagraphStyle('Definition', stylesheet['Normal']) + #use this for definition lists + para.firstLineIndent = 0 + para.leftIndent = 72 + para.bulletIndent = 0 + para.spaceBefore = 12 + para.bulletFontName = 'Helvetica-BoldOblique' + stylesheet['Definition'] = para + + para = ParagraphStyle('Code', stylesheet['Normal']) + para.fontName = 'Courier-Bold' + para.fontSize = 16 + para.leading = 18 + para.leftIndent = 36 + para.textColor = chartreuse + stylesheet['Code'] = para + + return stylesheet \ No newline at end of file diff --git a/bin/reportlab/tools/pythonpoint/styles/standard.py b/bin/reportlab/tools/pythonpoint/styles/standard.py new file mode 100644 index 00000000000..c525964585d --- /dev/null +++ b/bin/reportlab/tools/pythonpoint/styles/standard.py @@ -0,0 +1,132 @@ +from reportlab.lib import styles +from reportlab.lib import colors +from reportlab.lib.units import cm +from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_RIGHT, TA_JUSTIFY +from reportlab.platypus import Preformatted, Paragraph, Frame, \ + Image, Table, TableStyle, Spacer + + +def getParagraphStyles(): + """Returns a dictionary of styles to get you started. + + We will provide a way to specify a module of these. Note that + this just includes TableStyles as well as ParagraphStyles for any + tables you wish to use. + """ + + stylesheet = {} + ParagraphStyle = styles.ParagraphStyle + + para = ParagraphStyle('Normal', None) #the ancestor of all + para.fontName = 'Times-Roman' + para.fontSize = 24 + para.leading = 28 + stylesheet['Normal'] = para + + #This one is spaced out a bit... + para = ParagraphStyle('BodyText', stylesheet['Normal']) + para.spaceBefore = 12 + stylesheet['BodyText'] = para + + #Indented, for lists + para = ParagraphStyle('Indent', stylesheet['Normal']) + para.leftIndent = 36 + para.firstLineIndent = 0 + stylesheet['Indent'] = para + + para = ParagraphStyle('Centered', stylesheet['Normal']) + para.alignment = TA_CENTER + stylesheet['Centered'] = para + + para = ParagraphStyle('BigCentered', stylesheet['Normal']) + para.spaceBefore = 12 + para.alignment = TA_CENTER + stylesheet['BigCentered'] = para + + para = ParagraphStyle('Italic', stylesheet['BodyText']) + para.fontName = 'Times-Italic' + stylesheet['Italic'] = para + + para = ParagraphStyle('Title', stylesheet['Normal']) + para.fontName = 'Times-Roman' + para.fontSize = 48 + para.leading = 58 + para.alignment = TA_CENTER + stylesheet['Title'] = para + + para = ParagraphStyle('Heading1', stylesheet['Normal']) + para.fontName = 'Times-Bold' + para.fontSize = 36 + para.leading = 44 + para.alignment = TA_CENTER + stylesheet['Heading1'] = para + + para = ParagraphStyle('Heading2', stylesheet['Normal']) + para.fontName = 'Times-Bold' + para.fontSize = 28 + para.leading = 34 + para.spaceBefore = 24 + stylesheet['Heading2'] = para + + para = ParagraphStyle('Heading3', stylesheet['Normal']) + para.fontName = 'Times-BoldItalic' + para.spaceBefore = 24 + stylesheet['Heading3'] = para + + para = ParagraphStyle('Heading4', stylesheet['Normal']) + para.fontName = 'Times-BoldItalic' + para.spaceBefore = 6 + stylesheet['Heading4'] = para + + para = ParagraphStyle('Bullet', stylesheet['Normal']) + para.firstLineIndent = 0 + para.leftIndent = 56 + para.spaceBefore = 6 + para.bulletFontName = 'Symbol' + para.bulletFontSize = 24 + para.bulletIndent = 20 + stylesheet['Bullet'] = para + + para = ParagraphStyle('Definition', stylesheet['Normal']) + #use this for definition lists + para.firstLineIndent = 0 + para.leftIndent = 72 + para.bulletIndent = 0 + para.spaceBefore = 12 + para.bulletFontName = 'Helvetica-BoldOblique' + para.bulletFontSize = 24 + stylesheet['Definition'] = para + + para = ParagraphStyle('Code', stylesheet['Normal']) + para.fontName = 'Courier' + para.fontSize = 16 + para.leading = 18 + para.leftIndent = 36 + stylesheet['Code'] = para + + para = ParagraphStyle('PythonCode', stylesheet['Normal']) + para.fontName = 'Courier' + para.fontSize = 16 + para.leading = 18 + para.leftIndent = 36 + stylesheet['PythonCode'] = para + + para = ParagraphStyle('Small', stylesheet['Normal']) + para.fontSize = 12 + para.leading = 14 + stylesheet['Small'] = para + + #now for a table + ts = TableStyle([ + ('FONT', (0,0), (-1,-1), 'Times-Roman', 24), + ('LINEABOVE', (0,0), (-1,0), 2, colors.green), + ('LINEABOVE', (0,1), (-1,-1), 0.25, colors.black), + ('LINEBELOW', (0,-1), (-1,-1), 2, colors.green), + ('LINEBEFORE', (-1,0), (-1,-1), 2, colors.black), + ('ALIGN', (1,1), (-1,-1), 'RIGHT'), #all numeric cells right aligned + ('TEXTCOLOR', (0,1), (0,-1), colors.red), + ('BACKGROUND', (0,0), (-1,0), colors.Color(0,0.7,0.7)) + ]) + stylesheet['table1'] = ts + + return stylesheet diff --git a/bin/reportlab/tools/utils/add_bleed.py b/bin/reportlab/tools/utils/add_bleed.py new file mode 100644 index 00000000000..ab4a7824628 --- /dev/null +++ b/bin/reportlab/tools/utils/add_bleed.py @@ -0,0 +1,28 @@ +#How to add bleed to a page in this case 6mm to a landscape A4 +from reportlab.lib import units, pagesizes +from reportlab.pdfgen.canvas import Canvas +import sys, os, glob, time +bleedX = 6*units.mm +bleedY = 6*units.mm +pageWidth, pageHeight = pagesizes.landscape(pagesizes.A4) +def process_pdf(c,infn,prefix='PageForms'): + from rlextra.pageCatcher import pageCatcher + names, data = pageCatcher.storeFormsInMemory(open(infn,'rb').read(),prefix=prefix,all=1) + names = pageCatcher.restoreFormsInMemory(data,c) + del data + for i in xrange(len(names)): + thisname = names[i] + c.saveState() + c.translate(bleedX,bleedY) + c.doForm(thisname) + c.restoreState() + c.showPage() + +def main(): + for infn in sys.argv[1:]: + outfn = 'bleeding_'+os.path.basename(infn) + c = Canvas(outfn,pagesize=(pageWidth+2*bleedX,pageHeight+2*bleedY)) + process_pdf(c,infn) + c.save() +if __name__=='__main__': + main() diff --git a/bin/reportlab/tools/utils/dumpttf.py b/bin/reportlab/tools/utils/dumpttf.py new file mode 100644 index 00000000000..8cb4610c5af --- /dev/null +++ b/bin/reportlab/tools/utils/dumpttf.py @@ -0,0 +1,60 @@ +__all__=('dumpttf',) +def dumpttf(fn,fontName=None, verbose=0): + '''dump out known glyphs from a ttf file''' + import os + if not os.path.isfile(fn): + raise IOError('No such file "%s"' % fn) + from reportlab.pdfbase.pdfmetrics import registerFont, stringWidth + from reportlab.pdfbase.ttfonts import TTFont + from reportlab.pdfgen.canvas import Canvas + if fontName is None: + fontName = os.path.splitext(os.path.basename(fn))[0] + dmpfn = '%s-ttf-dump.pdf' % fontName + ttf = TTFont(fontName, fn) + K = ttf.face.charToGlyph.keys() + registerFont(ttf) + c = Canvas(dmpfn) + W,H = c._pagesize + titleFontSize = 30 # title font size + titleFontName = 'Helvetica' + labelFontName = 'Courier' + fontSize = 10 + border = 36 + dx0 = stringWidth('12345: ', fontName, fontSize) + dx = dx0+20 + dy = 20 + K.sort() + y = 0 + page = 0 + for i, k in enumerate(K): + if yW-border: + x = border + y -= dy + c.showPage() + c.save() + if verbose: + print 'Font %s("%s") has %d glyphs\ndumped to "%s"' % (fontName,fn,len(K),dmpfn) + +if __name__=='__main__': + import sys, glob + if '--verbose' in sys.argv: + sys.argv.remove('--verbose') + verbose = 1 + else: + verbose = 0 + for a in sys.argv[1:]: + for fn in glob.glob(a): + dumpttf(fn, verbose=verbose)