summaryrefslogtreecommitdiff
path: root/bin/reportlab/tools/docco
diff options
context:
space:
mode:
authorpinky <>2007-01-07 21:41:39 +0000
committerpinky <>2007-01-07 21:41:39 +0000
commit05bb5d58295cd959c34f1fbcd5b605cd6dafaacc (patch)
tree3b3651037037c1617a54ef3d216e8c11ea8cdddf /bin/reportlab/tools/docco
parent62524af2f41454a2302efb21f15a95318d257a76 (diff)
Repotrlab 2.0
bzr revid: pinky-b8333ccaab5dad8ff965b6c380fadadf30d5212a
Diffstat (limited to 'bin/reportlab/tools/docco')
-rw-r--r--bin/reportlab/tools/docco/README8
-rw-r--r--bin/reportlab/tools/docco/__init__.py3
-rw-r--r--bin/reportlab/tools/docco/codegrab.py228
-rw-r--r--bin/reportlab/tools/docco/docpy.py1247
-rw-r--r--bin/reportlab/tools/docco/examples.py851
-rw-r--r--bin/reportlab/tools/docco/graphdocpy.py980
-rw-r--r--bin/reportlab/tools/docco/rl_doc_utils.py411
-rw-r--r--bin/reportlab/tools/docco/rltemplate.py135
-rw-r--r--bin/reportlab/tools/docco/stylesheet.py165
-rw-r--r--bin/reportlab/tools/docco/t_parse.py247
-rw-r--r--bin/reportlab/tools/docco/yaml.py201
-rw-r--r--bin/reportlab/tools/docco/yaml2pdf.py104
12 files changed, 4580 insertions, 0 deletions
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, '&', '&amp;'),
+ '<', '&lt;'),
+ '>', '&gt;')
+
+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("""<TABLE WIDTH="100\%" BORDER="0">""")
+ result.append("""<TR><TD BGCOLOR="%s" VALIGN="CENTER">""" % bgcolor)
+ result.append("""<H2>%s</H2>""" % text)
+ result.append("""</TD></TR></TABLE>""")
+ 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("""<TABLE WIDTH="100\%" BORDER="0">""")
+ result.append("""<TR><TD BGCOLOR="%s" VALIGN="CENTER">""" % bgcolor)
+ result.append("""<H3><TT><FONT SIZE="+2">%s</FONT></TT></H3>""" % text)
+ result.append("""</TD></TR></TABLE>""")
+ result.append('')
+
+ return join(result, '\n')
+
+
+def makeHtmlInlineImage(text):
+ """Create HTML code for an inline image.
+ """
+
+ return """<IMG SRC="%s" ALT="%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__ == '<lambda>':
+ 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("""<!doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN">""")
+ self.outLines.append("""<html>""")
+
+
+ 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('</body></html>')
+ for line in self.outLines:
+ file.write(line + '\n')
+ file.close()
+
+
+ def beginPackage(self, name):
+ DocBuilder0.beginPackage(self, name)
+
+ self.outLines.append("""<title>%s</title>""" % name)
+ self.outLines.append("""<body bgcolor="#ffffff">""")
+ self.outLines.append("""<H1>%s</H1>""" % name)
+ self.outLines.append('')
+
+
+ def beginModule(self, name, doc, imported):
+ if not self.packageName:
+ self.outLines.append("""<title>%s</title>""" % name)
+ self.outLines.append("""<body bgcolor="#ffffff">""")
+
+ self.outLines.append("""<H1>%s</H1>""" % name)
+ self.outLines.append('')
+ for line in split(doc, '\n'):
+ self.outLines.append("""<FONT SIZE="-1">%s</FONT>""" % htmlescape(line))
+ self.outLines.append('<BR>')
+ self.outLines.append('')
+
+ if imported:
+ self.outLines.append(makeHtmlSection('Imported Modules'))
+ self.outLines.append("""<ul>""")
+ for m in imported:
+ self.outLines.append("""<li>%s</li>""" % m)
+ self.outLines.append("""</ul>""")
+
+
+ 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("""<FONT SIZE="-1">%s</FONT>""" % htmlescape(line))
+ self.outLines.append('<BR>')
+
+ self.outLines.append('')
+
+
+ def beginMethods(self, names):
+ pass
+## if names:
+## self.outLines.append('<H3>Method Interface</H3>')
+## 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("""<DL><DL><DT><TT><STRONG>%s</STRONG>%s</TT></DT>""" % (name, sig))
+ append('')
+ for line in split(doc, '\n'):
+ append("""<DD><FONT SIZE="-1">%s</FONT></DD>""" % htmlescape(line))
+ append('<BR>')
+ append('</DL></DL>')
+ 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('<font size=18>Documentation for %s "%s"</font>' % (typ, name), self.bt))
+ self.story.append(Paragraph('<font size=18>Generated by: docpy.py version %s</font>' % __version__, self.bt))
+ self.story.append(Paragraph('<font size=18>Date generated: %s</font>' % timeString, self.bt))
+ self.story.append(Paragraph('<font size=18>Format: PDF</font>', 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('<bullet>\201</bullet> %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('<bullet>\201</bullet> %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('<b>%s</b>' % self.classCompartment, bt)
+ p.style.alignment = TA_CENTER
+ rows = [(p,)]
+ # No doc strings, now...
+ # rows = rows + [(Paragraph('<i>%s</i>' % 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('<b>%s</b>%s' % (name, sig), bt1)
+ rows.append((nameAndSig,))
+ # No doc strings, now...
+ # docStr = Paragraph('<i>%s</i>' % 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('<b>%s</b>' % 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('<b>%s</b>%s' % (name, sig), bt1)
+ rows.append((nameAndSig,))
+ # No doc strings, now...
+ # docStr = Paragraph('<i>%s</i>' % 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('<font size=18>Documentation for %s "%s"</font>' % (typ, name), self.bt))
+ self.story.append(Paragraph('<font size=18>Generated by: graphdocpy.py version %s</font>' % __version__, self.bt))
+ self.story.append(Paragraph('<font size=18>Date generated: %s</font>' % timeString, self.bt))
+ self.story.append(Paragraph('<font size=18>Format: PDF</font>', 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 = '<i>%s</i>' % typ.__class__.__name__
+ else:
+ desc = '<i>None</i>'
+ self.story.append(Paragraph(
+ "<b>%s</b> %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("<i>Example</i>", 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("<i>Example</i>", 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("<i>Example</i>", 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("<i>Properties of Example Widget</i>", 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('<H2>%s</H2>' % modName)
+ self.outLines.append('<PRE>%s</PRE>' % modDoc)
+ self.shouldDisplayModule = 0
+ self.hasDisplayedModule = 1
+ if self.shouldDisplayClasses:
+ self.outLines.append('<H2>Classes</H2>')
+ 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('<H3>Example</H3>')
+
+ # Sample code
+ codeSample = inspect.getsource(function)
+ self.outLines.append('<PRE>%s</PRE>' % 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('<H3>Demo</H3>')
+ 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('<H3>Demo</H3>')
+ 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('<H3>Example Code</H3>')
+
+ # Sample code
+ codeSample = inspect.getsource(widget.__class__.demo)
+ self.outLines.append('<PRE>%s</PRE>' % 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('<H3>Properties of Example Widget</H3>')
+ self.outLines.append('<PRE>%s</PRE>' % 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<font name=Courier>%s</font>"
+QFsubst = "%s<font name=Courier><i>%s</i></font>"
+
+
+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='<bullet><seq id="list1"/>.</bullet>'+text
+ P = Paragraph(text, BU)
+ getStory().append(P)
+
+def bullet(text):
+ text='<bullet><font name="Symbol">\xe2\x80\xa2</font></bullet>' + 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 <seq id="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 <seq id="Chapter"/> ' + quickfix(text), H1)
+ getStory().append(p)
+
+def heading2(text):
+ """Used to be 'lesson'"""
+ getStory().append(CondPageBreak(inch))
+ p = Paragraph('<seq template="%(Chapter)s.%(Section+)s "/>' + 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 <seq template="%(Chapter)s-%(Figure+)s"/>: ' + 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 <seq template="%(Chapter)s-%(Figure+)s"/>: ' + 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('<![CDATA[' + text + ']]>', 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 <seq template="%(Chapter)s-%(Figure+)s"/>: ' + quickfix(caption)
+ )
+ getStory().append(p)
+
+def parabox2(text, caption):
+ p = ParaBox2(text,
+ 'Figure <seq template="%(Chapter)s-%(Figure+)s"/>: ' + 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<X>", "X")
+ >>> T.PARSE('<A HREF="index.html">go to index</A>')
+ (['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<last:
+ start = index
+ thischar = template[index]
+ # is it a wildcard?
+ if thischar == wild_card_marker:
+ if lastchar == wild_card_marker:
+ raise ValueError, "two wild cards in sequence is not allowed"
+ parse_seq.append( (wild_card_marker, None) )
+ index = index+1
+ ndirectives = ndirectives+1
+ # is it a sequence of single character markers?
+ elif single_char_marker and thischar in single_char_marker:
+ if lastchar == wild_card_marker:
+ raise ValueError, "wild card cannot precede single char marker"
+ while index<last and template[index] == thischar:
+ index = index+1
+ parse_seq.append( (single_char_primary, index-start) )
+ ndirectives = ndirectives+1
+ # is it a literal sequence?
+ elif not thischar in markers:
+ while index<last and not template[index] in markers:
+ index = index+1
+ parse_seq.append( (None, template[start:index]) )
+ # otherwise it must be a re marker
+ else:
+ rgex = marker_dict[thischar]
+ parse_seq.append( (thischar, rgex) )
+ ndirectives = ndirectives+1
+ index = index+1
+ lastchar = template[index-1]
+ self.parse_seq = parse_seq
+ self.ndirectives = ndirectives
+
+ def PARSE(self, str, start=0):
+ ndirectives = self.ndirectives
+ wild_card = self.wild_card
+ single_char = self.char
+ parse_seq = self.parse_seq
+ lparse_seq = len(parse_seq) - 1
+ # make a list long enough for substitutions for directives
+ result = [None] * ndirectives
+ current_directive_index = 0
+ currentindex = start
+ # scan through the parse sequence, recognizing
+ for parse_index in xrange(lparse_seq + 1):
+ (indicator, data) = parse_seq[parse_index]
+ # is it a literal indicator?
+ if indicator is None:
+ if find(str, data, currentindex) != currentindex:
+ raise ValueError, "literal not found at "+`(currentindex,data)`
+ currentindex = currentindex + len(data)
+ else:
+ # anything else is a directive
+ # is it a wildcard?
+ if indicator == wild_card:
+ # if it is the last directive then it matches the rest of the string
+ if parse_index == lparse_seq:
+ last = len(str)
+ # otherwise must look at next directive to find end of wildcard
+ else:
+ # next directive must be re or literal
+ (nextindicator, nextdata) = parse_seq[parse_index+1]
+ if nextindicator is None:
+ # search for literal
+ last = find(str, nextdata, currentindex)
+ if last<currentindex:
+ raise ValueError, \
+ "couldn't terminate wild with lit "+`currentindex`
+ else:
+ # data is a re, search for it
+ last = nextdata.search(str, currentindex)
+ if last<currentindex:
+ raise ValueError, \
+ "couldn't terminate wild with re "+`currentindex`
+ elif indicator == single_char:
+ # data is length to eat
+ last = currentindex + data
+ else:
+ # other directives are always regular expressions
+ last = data.match(str, currentindex) + currentindex
+ if last<currentindex:
+ raise ValueError, "couldn't match re at "+`currentindex`
+ #print "accepting", str[currentindex:last]
+ result[current_directive_index] = str[currentindex:last]
+ current_directive_index = current_directive_index+1
+ currentindex = last
+ # sanity check
+ if current_directive_index != ndirectives:
+ raise SystemError, "not enough directives found?"
+ return (result, currentindex)
+
+# some useful regular expressions
+USERNAMEREGEX = \
+ "["+string.letters+"]["+string.letters+string.digits+"_]*"
+STRINGLITREGEX = "'[^\n']*'"
+SIMPLEINTREGEX = "["+string.digits+"]+"
+id = re.compile(USERNAMEREGEX)
+str = re.compile(STRINGLITREGEX)
+int = re.compile(SIMPLEINTREGEX)
+
+def test():
+ global T, T1, T2, T3
+
+ T = Template("(NNN)NNN-NNNN X X", "X", "N")
+ print T.PARSE("(908)949-2726 Aaron Watters")
+
+ T1 = Template("s --> 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