XML Tagged Paragraph parser changes
authorrgbecker
Wed, 12 Apr 2000 16:26:51 +0000
changeset 117 fd3682228853
parent 116 34033af65708
child 118 6ed552aebeb4
XML Tagged Paragraph parser changes
reportlab/demos/odyssey/fodyssey.py
reportlab/demos/odyssey/odyssey.txt
reportlab/pdfbase/pdfmetrics.py
reportlab/pdfgen/canvas.py
reportlab/platypus/layout.py
reportlab/platypus/test/testplatypus.py
--- a/reportlab/demos/odyssey/fodyssey.py	Wed Apr 12 13:34:33 2000 +0000
+++ b/reportlab/demos/odyssey/fodyssey.py	Wed Apr 12 16:26:51 2000 +0000
@@ -31,10 +31,13 @@
 #
 ###############################################################################
 #	$Log: fodyssey.py,v $
+#	Revision 1.2  2000/04/12 16:24:34  rgbecker
+#	XML Tagged Paragraph parser changes
+#
 #	Revision 1.1  2000/04/06 08:58:09  rgbecker
 #	Paragraph formatting version of odyssey.py
-#
-__version__=''' $Id: fodyssey.py,v 1.1 2000/04/06 08:58:09 rgbecker Exp $ '''
+#	
+__version__=''' $Id: fodyssey.py,v 1.2 2000/04/12 16:24:34 rgbecker Exp $ '''
 __doc__=''
 
 #REPORTLAB_TEST_SCRIPT
@@ -62,7 +65,7 @@
 	canvas.restoreState()
 	
 def go():
-	doc = layout.SimpleFlowDocument('fodyssey.pdf',layout.DEFAULT_PAGE_SIZE)
+	doc = layout.SimpleFlowDocument('fodyssey.pdf',layout.DEFAULT_PAGE_SIZE,showBoundary=0)
 	doc.onFirstPage = myFirstPage
 	doc.onNewPage = myLaterPages
 	doc.build(Elements)
@@ -81,7 +84,8 @@
 	Elements.append(layout.Paragraph(txt, style))
 	Elements.append(layout.Spacer(0.2*inch, 0.3*inch))
 
-ParaStyle = styles["Normal"]
+ParaStyle = copy.copy(styles["Normal"])
+ParaStyle.alignment = layout.TA_JUSTIFY
 
 def p(txt, style=ParaStyle):
 	Elements.append(layout.Spacer(0.2*inch, 0.1*inch))
@@ -177,6 +181,7 @@
 	go()
 	t7 = time()
 	print "saving to PDF took %.4f seconds" %(t7-t6)
+	print "Total run took %.4f seconds"%(t7-t0)
 
 for fn in ('Odyssey.full.txt','Odyssey.txt'):
 	if os.path.isfile(fn):
--- a/reportlab/demos/odyssey/odyssey.txt	Wed Apr 12 13:34:33 2000 +0000
+++ b/reportlab/demos/odyssey/odyssey.txt	Wed Apr 12 16:26:51 2000 +0000
@@ -12,11 +12,12 @@
 
 BOOK I
 
-Tell me, O muse, of that ingenious hero who travelled far and wide
-after he had sacked the famous town of Troy. Many cities did he visit,
+<font color=green><b><i>Tell</i></b></font> me, O muse, of that ingenious hero who travelled far and wide
+after he had sacked the famous town of <font color=red size=12><b>Troy</b></font>. Many cities did he visit,
 and many were the nations with whose manners and customs he was acquainted;
 moreover he suffered much by sea while trying to save his own life
-and bring his men safely home; but do what he might he could not save
+and bring his men safely home; but do what he might he could not
+save<super><font color=red>1</font></super>
 his men, for they perished through their own sheer folly in eating
 the cattle of the Sun-god Hyperion; so the god prevented them from
 ever reaching home. Tell me, too, about all these things, O daughter
@@ -32,13 +33,13 @@
 to pity him except Neptune, who still persecuted him without ceasing
 and would not let him get home. 
 
-Now Neptune had gone off to the Ethiopians, who are at the world's
+<font color=green>Now Neptune had gone off to the Ethiopians, who are at the world's
 end, and lie in two halves, the one looking West and the other East.
 He had gone there to accept a hecatomb of sheep and oxen, and was
 enjoying himself at his festival; but the other gods met in the house
 of Olympian Jove, and the sire of gods and men spoke first. At that
 moment he was thinking of Aegisthus, who had been killed by Agamemnon's
-son Orestes; so he said to the other gods: 
+son Orestes; so he said to the other gods:</font>
 
 "See now, how men lay blame upon us gods for what is after all nothing
 but their own folly. Look at Aegisthus; he must needs make love to
--- a/reportlab/pdfbase/pdfmetrics.py	Wed Apr 12 13:34:33 2000 +0000
+++ b/reportlab/pdfbase/pdfmetrics.py	Wed Apr 12 16:26:51 2000 +0000
@@ -1,11 +1,11 @@
 ###############################################################################
 #
-#	ReportLab Public License Version 1.0
+#   ReportLab Public License Version 1.0
 #
 #   Except for the change of names the spirit and intention of this
 #   license is the same as that of Python
 #
-#	(C) Copyright ReportLab Inc. 1998-2000.
+#   (C) Copyright ReportLab Inc. 1998-2000.
 #
 #
 # All Rights Reserved
@@ -30,26 +30,27 @@
 # PERFORMANCE OF THIS SOFTWARE. 
 #
 ###############################################################################
-#	$Log: pdfmetrics.py,v $
-#	Revision 1.4  2000/02/17 02:07:23  rgbecker
-#	Docstring & other fixes
+#   $Log: pdfmetrics.py,v $
+#   Revision 1.5  2000/04/12 16:24:35  rgbecker
+#   XML Tagged Paragraph parser changes
 #
-#	Revision 1.3  2000/02/15 17:55:59  rgbecker
-#	License text fixes
-#	
-#	Revision 1.2  2000/02/15 15:47:09  rgbecker
-#	Added license, __version__ and Logi comment
-#	
-__version__=''' $Id: pdfmetrics.py,v 1.4 2000/02/17 02:07:23 rgbecker Exp $ '''
+#   Revision 1.4  2000/02/17 02:07:23  rgbecker
+#   Docstring & other fixes
+#   
+#   Revision 1.3  2000/02/15 17:55:59  rgbecker
+#   License text fixes
+#   
+#   Revision 1.2  2000/02/15 15:47:09  rgbecker
+#   Added license, __version__ and Logi comment
+#   
+__version__=''' $Id: pdfmetrics.py,v 1.5 2000/04/12 16:24:35 rgbecker Exp $ '''
 __doc__="""This contains pre-canned text metrics for the PDFgen package, and may also
 be used for any other PIDDLE back ends or packages which use the standard
 Type 1 postscript fonts.
 
 Its main function is to let you work out the width of strings; it exposes a 
-single function, stringwidth(text, fontname), which works out the width of a 
-string in the given font. This is an integer defined in em-square units - each
-character is defined in a 1000 x 1000 box called the em-square - for a 1-point high
-character.  So to convert to points, multiply by 1000 and then by point size.
+single function, stringWidth(text, fontname, fontSize), which works out the width of a 
+string in the given font in points.
 
 The AFM loading stuff worked for me but is not being heavily tested, as pre-canning
 the widths for the standard 14 fonts in Acrobat Reader is so much more useful. One
@@ -64,18 +65,16 @@
 """
 import string
 
-
-
 StandardEnglishFonts = [
-	'Courier', 'Courier-Bold', 'Courier-Oblique', 'Courier-BoldOblique',  
-	'Helvetica', 'Helvetica-Bold', 'Helvetica-Oblique', 
-	'Helvetica-BoldOblique',
-	'Times-Roman', 'Times-Bold', 'Times-Italic', 'Times-BoldItalic',
-	'Symbol','ZapfDingbats']
+    'Courier', 'Courier-Bold', 'Courier-Oblique', 'Courier-BoldOblique',  
+    'Helvetica', 'Helvetica-Bold', 'Helvetica-Oblique', 
+    'Helvetica-BoldOblique',
+    'Times-Roman', 'Times-Bold', 'Times-Italic', 'Times-BoldItalic',
+    'Symbol','ZapfDingbats']
 
 ##############################################################
 #
-#			PDF Metrics
+#           PDF Metrics
 # This is a preamble to give us a stringWidth function.
 # loads and caches AFM files, but won't need to as the
 # standard fonts are there already
@@ -124,7 +123,7 @@
 
 class FontCache:
     """Loads and caches font width information on demand.  Font names
-    converted to lower case for indexing.  Public interface is stringwidth"""
+    converted to lower case for indexing.  Public interface is stringWidth"""
     def __init__(self):
         global widths
         self.__widtharrays = widths
@@ -149,13 +148,12 @@
                 print 'Font',fontname,'not found - using Courier for widths'
                 return self.getfont('courier')
     
-
-    def stringwidth(self, text, font):
+    def stringWidth(self, text, font, fontSize):
         widths = self.getfont(string.lower(font))
         w = 0
         for char in text:
             w = w + widths[ord(char)]
-        return w
+        return w*fontSize*0.001
 
     def status(self):
         #returns loaded fonts
@@ -164,6 +162,4 @@
 TheFontCache = FontCache()
 
 #expose the singleton as a single function
-stringwidth = TheFontCache.stringwidth
-
-
+stringWidth = TheFontCache.stringWidth
--- a/reportlab/pdfgen/canvas.py	Wed Apr 12 13:34:33 2000 +0000
+++ b/reportlab/pdfgen/canvas.py	Wed Apr 12 16:26:51 2000 +0000
@@ -31,10 +31,13 @@
 #
 ###############################################################################
 #	$Log: canvas.py,v $
+#	Revision 1.26  2000/04/12 16:26:51  rgbecker
+#	XML Tagged Paragraph parser changes
+#
 #	Revision 1.25  2000/04/10 09:21:21  andy_robinson
 #	Color methods in textobject and canvas now synchronised.
 #	Added 'verbosity' keyword to allow hiding of 'save myfile.pdf' messages.
-#
+#	
 #	Revision 1.24  2000/04/06 09:52:02  andy_robinson
 #	Removed some old comments; tweaks to experimental Outline methods.
 #	
@@ -103,7 +106,7 @@
 #	Revision 1.2  2000/02/15 15:47:09  rgbecker
 #	Added license, __version__ and Logi comment
 #	
-__version__=''' $Id: canvas.py,v 1.25 2000/04/10 09:21:21 andy_robinson Exp $ '''
+__version__=''' $Id: canvas.py,v 1.26 2000/04/12 16:26:51 rgbecker Exp $ '''
 __doc__=""" 
 PDFgen is a library to generate PDF files containing text and graphics.  It is the 
 foundation for a complete reporting solution in Python.  It is also the
@@ -749,9 +752,9 @@
         self._leading = leading
         self._code.append('BT %s %0.1f Tf %0.1f TL ET' % (pdffontname, size, leading))
 
-    def stringWidth(self, text, fontname, fontsize):
+    def stringWidth(self, text, fontName, fontSize):
         "gets width of a string in the given font and size"
-        return pdfmetrics.stringwidth(text, fontname) * 0.001 * fontsize
+        return pdfmetrics.stringWidth(text, fontName, fontSize)
         
     # basic graphics modes
     def setLineWidth(self, width):
--- a/reportlab/platypus/layout.py	Wed Apr 12 13:34:33 2000 +0000
+++ b/reportlab/platypus/layout.py	Wed Apr 12 16:26:51 2000 +0000
@@ -2,8 +2,8 @@
 #
 #	ReportLab Public License Version 1.0
 #
-#   Except for the change of names the spirit and intention of this
-#   license is the same as that of Python
+#	Except for the change of names the spirit and intention of this
+#	license is the same as that of Python
 #
 #	(C) Copyright ReportLab Inc. 1998-2000.
 #
@@ -31,9 +31,12 @@
 #
 ###############################################################################
 #	$Log: layout.py,v $
+#	Revision 1.12  2000/04/12 16:26:51  rgbecker
+#	XML Tagged Paragraph parser changes
+#
 #	Revision 1.11  2000/04/10 14:08:19  rgbecker
 #	fixes related to offset
-#
+#	
 #	Revision 1.10  2000/04/10 12:25:10  rgbecker
 #	Typo fixes for justified paras
 #	
@@ -61,7 +64,7 @@
 #	Revision 1.2  2000/02/15 15:47:09  rgbecker
 #	Added license, __version__ and Logi comment
 #	
-__version__=''' $Id: layout.py,v 1.11 2000/04/10 14:08:19 rgbecker Exp $ '''
+__version__=''' $Id: layout.py,v 1.12 2000/04/12 16:26:51 rgbecker Exp $ '''
 __doc__="""
 Page Layout And TYPography Using Scripts
 a page layout API on top of PDFgen
@@ -69,16 +72,20 @@
 """
 
 # 200-10-13 gmcm
-#   packagizing
-#   rewrote grid stuff - now in tables.py
+#	packagizing
+#	rewrote grid stuff - now in tables.py
 
 import string
 import types
 
-from reportlab.pdfbase import pdfmetrics
+from reportlab.pdfbase.pdfmetrics import stringWidth
 from reportlab.pdfgen import canvas
 from reportlab.lib.units import inch
 from reportlab.lib.colors import white, black, red, Color
+from reportlab.platypus.paraparser import ParaParser, ParaFrag
+
+#our one and only parser
+_parser=ParaParser()
 
 DEFAULT_PAGE_SIZE = (595.27,841.89)
 PAGE_HEIGHT = DEFAULT_PAGE_SIZE[1]
@@ -88,597 +95,799 @@
 TA_JUSTIFY = 4
 
 ###########################################################
-#   Styles.  This class provides an 'instance inheritance'
+#	Styles.  This class provides an 'instance inheritance'
 # mechanism for its descendants, simpler than acquisition
 # but not as far-reaching
 ###########################################################
 class PropertySet:
-    defaults = {}
+	defaults = {}
 
-    def __init__(self, name, parent=None):
-        self.name = name
-        self.parent = parent
-        self.attributes = {}
+	def __init__(self, name, parent=None):
+		self.name = name
+		self.parent = parent
+		self.attributes = {}
 
-    def __setattr__(self, key, value):
-        if self.defaults.has_key(key):
-            self.attributes[key] = value
-        else:
-            self.__dict__[key] = value
+	def __setattr__(self, key, value):
+		if self.defaults.has_key(key):
+			self.attributes[key] = value
+		else:
+			self.__dict__[key] = value
 
-    def __getattr__(self, key):
-        if self.defaults.has_key(key):
-            if self.attributes.has_key(key):
-                found = self.attributes[key]
-            elif self.parent:
-                found = getattr(self.parent, key)
-            else:  #take the class default
-                found = self.defaults[key]
-        else:
-            found = self.__dict__[key]
-        return found
+	def __getattr__(self, key):
+		if self.defaults.has_key(key):
+			if self.attributes.has_key(key):
+				found = self.attributes[key]
+			elif self.parent:
+				found = getattr(self.parent, key)
+			else:  #take the class default
+				found = self.defaults[key]
+		else:
+			found = self.__dict__[key]
+		return found
 
-    def __repr__(self):
-        return "<%s '%s'>" % (self.__class__.__name__, self.name)
+	def __repr__(self):
+		return "<%s '%s'>" % (self.__class__.__name__, self.name)
 
-    def listAttrs(self):
-        print 'name =', self.name
-        print 'parent =', self.parent
-        keylist = self.defaults.keys()
-        keylist.sort()
-        for key in keylist:
-            value = self.attributes.get(key, None)
-            if value:
-                print '%s = %s (direct)' % (key, value)
-            else: #try for inherited
-                value = getattr(self.parent, key, None)
-                if value:
-                    print '%s = %s (inherited)' % (key, value)
-                else:
-                    value = self.defaults[key]
-                    print '%s = %s (class default)' % (key, value)
+	def listAttrs(self):
+		print 'name =', self.name
+		print 'parent =', self.parent
+		keylist = self.defaults.keys()
+		keylist.sort()
+		for key in keylist:
+			value = self.attributes.get(key, None)
+			if value:
+				print '%s = %s (direct)' % (key, value)
+			else: #try for inherited
+				value = getattr(self.parent, key, None)
+				if value:
+					print '%s = %s (inherited)' % (key, value)
+				else:
+					value = self.defaults[key]
+					print '%s = %s (class default)' % (key, value)
 
 
 
 class ParagraphStyle(PropertySet):
-    defaults = {
-        'fontName':'Times-Roman',
-        'fontSize':10,
-        'leading':12,
-        'leftIndent':0,
-        'rightIndent':0,
-        'firstLineIndent':0,
-        'alignment':TA_LEFT,
-        'spaceBefore':0,
-        'spaceAfter':0,
-        'bulletFontName':'Times-Roman',
-        'bulletFontSize':10,
-        'bulletIndent':0,
+	defaults = {
+		'fontName':'Times-Roman',
+		'fontSize':10,
+		'leading':12,
+		'leftIndent':0,
+		'rightIndent':0,
+		'firstLineIndent':0,
+		'alignment':TA_LEFT,
+		'spaceBefore':0,
+		'spaceAfter':0,
+		'bulletFontName':'Times-Roman',
+		'bulletFontSize':10,
+		'bulletIndent':0,
 		'textColor': black
-        }
+		}
 
 class LineStyle(PropertySet):
-    defaults = {
-        'width':1,
-        'color': black
-        }
-    def prepareCanvas(self, canvas):
-        """You can ask a LineStyle to set up the canvas for drawing
-        the lines."""
-        canvas.setLineWidth(1)
-        #etc. etc.
+	defaults = {
+		'width':1,
+		'color': black
+		}
+	def prepareCanvas(self, canvas):
+		"""You can ask a LineStyle to set up the canvas for drawing
+		the lines."""
+		canvas.setLineWidth(1)
+		#etc. etc.
 
 class CellStyle(PropertySet):
-    defaults = {
-        'fontname':'Times-Roman',
-        'fontsize':10,
-        'leading':12,
-        'leftPadding':6,
-        'rightPadding':6,
-        'topPadding':3,
-        'bottomPadding':3,
-        'firstLineIndent':0,
-        'color':white,
-        'alignment': 'LEFT',
-        }
+	defaults = {
+		'fontname':'Times-Roman',
+		'fontsize':10,
+		'leading':12,
+		'leftPadding':6,
+		'rightPadding':6,
+		'topPadding':3,
+		'bottomPadding':3,
+		'firstLineIndent':0,
+		'color':white,
+		'alignment': 'LEFT',
+		}
 
 def testStyles():
-    pNormal = ParagraphStyle('Normal',None)
-    pNormal.fontName = 'Times-Roman'
-    pNormal.fontSize = 12
-    pNormal.leading = 14.4
+	pNormal = ParagraphStyle('Normal',None)
+	pNormal.fontName = 'Times-Roman'
+	pNormal.fontSize = 12
+	pNormal.leading = 14.4
 
-    pNormal.listAttrs()
-    print
-    pPre = ParagraphStyle('Literal', pNormal)
-    pPre.fontName = 'Courier'
-    pPre.listAttrs()
-    return pNormal, pPre
+	pNormal.listAttrs()
+	print
+	pPre = ParagraphStyle('Literal', pNormal)
+	pPre.fontName = 'Courier'
+	pPre.listAttrs()
+	return pNormal, pPre
 
 def getSampleStyleSheet():
-    """Returns a dictionary of styles to get you started.  Should be
-    usable for fairly basic word processing tasks.  We should really have
-    a class for StyleSheets, which can list itself and avoid the
-    duplication of item names seen below."""
-    stylesheet = {}
+	"""Returns a dictionary of styles to get you started.  Should be
+	usable for fairly basic word processing tasks.	We should really have
+	a class for StyleSheets, which can list itself and avoid the
+	duplication of item names seen below."""
+	stylesheet = {}
 
-    para = ParagraphStyle('Normal', None)   #the ancestor of all
-    para.fontName = 'Times-Roman'
-    para.fontSize = 10
-    para.leading = 12
-    stylesheet['Normal'] = para
+	para = ParagraphStyle('Normal', None)	#the ancestor of all
+	para.fontName = 'Times-Roman'
+	para.fontSize = 10
+	para.leading = 12
+	stylesheet['Normal'] = para
+
+	para = ParagraphStyle('BodyText', stylesheet['Normal'])
+	para.spaceBefore = 6
+	stylesheet['BodyText'] = para
+
+	para = ParagraphStyle('Italic', stylesheet['BodyText'])
+	para.fontName = 'Times-Italic'
+	stylesheet['Italic'] = para
 
-    para = ParagraphStyle('BodyText', stylesheet['Normal'])
-    para.spaceBefore = 6
-    stylesheet['BodyText'] = para
+	para = ParagraphStyle('Heading1', stylesheet['Normal'])
+	para.fontName = 'Times-Bold'
+	para.fontSize = 18
+	para.spaceAfter = 6
+	stylesheet['Heading1'] = para
+
+	para = ParagraphStyle('Heading2', stylesheet['Normal'])
+	para.fontName = 'Times-Bold'
+	para.fontSize = 14
+	para.spaceBefore = 12
+	para.spaceAfter = 6
+	stylesheet['Heading2'] = para
 
-    para = ParagraphStyle('Italic', stylesheet['BodyText'])
-    para.fontName = 'Times-Italic'
-    stylesheet['Italic'] = para
+	para = ParagraphStyle('Heading3', stylesheet['Normal'])
+	para.fontName = 'Times-BoldItalic'
+	para.fontSize = 12
+	para.spaceBefore = 12
+	para.spaceAfter = 6
+	stylesheet['Heading3'] = para
 
-    para = ParagraphStyle('Heading1', stylesheet['Normal'])
-    para.fontName = 'Times-Bold'
-    para.fontSize = 18
-    para.spaceAfter = 6
-    stylesheet['Heading1'] = para
+	para = ParagraphStyle('Bullet', stylesheet['Normal'])
+	para.firstLineIndent = 36
+	para.leftIndent = 36
+	para.spaceBefore = 3
+	stylesheet['Bullet'] = para
 
-    para = ParagraphStyle('Heading2', stylesheet['Normal'])
-    para.fontName = 'Times-Bold'
-    para.fontSize = 14
-    para.spaceBefore = 12
-    para.spaceAfter = 6
-    stylesheet['Heading2'] = para
+	para = ParagraphStyle('Definition', stylesheet['Normal'])
+	#use this for definition lists
+	para.firstLineIndent = 36
+	para.leftIndent = 36
+	para.bulletIndent = 0
+	para.spaceBefore = 6
+	para.bulletFontName = 'Times-BoldItalic'
+	stylesheet['Definition'] = para
 
-    para = ParagraphStyle('Heading3', stylesheet['Normal'])
-    para.fontName = 'Times-BoldItalic'
-    para.fontSize = 12
-    para.spaceBefore = 12
-    para.spaceAfter = 6
-    stylesheet['Heading3'] = para
+	para = ParagraphStyle('Code', stylesheet['Normal'])
+	para.fontName = 'Courier'
+	para.fontSize = 8
+	para.leading = 8.8
+	para.leftIndent = 36
+	stylesheet['Code'] = para
+
+	return stylesheet
+
+def cleanBlockQuotedText(text):
+	"""This is an internal utility which takes triple-
+	quoted text form within the document and returns
+	(hopefully) the paragraph the user intended originally."""
+	stripped = string.strip(text)
+	lines = string.split(stripped, '\n')
+	trimmed_lines = map(string.lstrip, lines)
+	return string.join(trimmed_lines, ' ')
 
-    para = ParagraphStyle('Bullet', stylesheet['Normal'])
-    para.firstLineIndent = 36
-    para.leftIndent = 36
-    para.spaceBefore = 3
-    stylesheet['Bullet'] = para
+#############################################################
+#	Drawable Objects - a base class and a few examples.
+#	One is just a box to get some metrics.	We also have
+#	a paragraph, an image and a special 'page break'
+#	object which fills the space.
+class Drawable:
+	"""Abstract base class for things to be drawn.	Key concepts:
+	1. It knows its size
+	2. It draws in its own coordinate system (this requires the
+		base API to provide a translate() function.
+	"""
+	def __init__(self):
+		self.width = 0
+		self.height = 0
+		self.wrapped = 0
 
-    para = ParagraphStyle('Definition', stylesheet['Normal'])
-    #use this for definition lists
-    para.firstLineIndent = 36
-    para.leftIndent = 36
-    para.bulletIndent = 0
-    para.spaceBefore = 6
-    para.bulletFontName = 'Times-BoldItalic'
-    stylesheet['Definition'] = para
+	def drawOn(self, canvas, x, y):
+		"Tell it to draw itself on the canvas.	Do not override"
+		self.canv = canvas
+		self.canv.saveState()
+		self.canv.translate(x, y)
 
-    para = ParagraphStyle('Code', stylesheet['Normal'])
-    para.fontName = 'Courier'
-    para.fontSize = 8
-    para.leading = 8.8
-    para.leftIndent = 36
-    stylesheet['Code'] = para
+		self.draw()   #this is the bit you overload
+
+		self.canv.restoreState()
+		del self.canv
 
 
-    return stylesheet
+	def wrap(self, availWidth, availHeight):
+		"""This will be called by the enclosing frame before objects
+		are asked their size, drawn or whatever.  It returns the
+		size actually used."""
+		return (self.width, self.height)
+
+class XBox(Drawable):
+	"""Example drawable - a box with an x through it and a caption.
+	This has a known size, so does not need to respond to wrap()."""
+	def __init__(self, width, height, text = 'A Box'):
+		Drawable.__init__(self)
+		self.width = width
+		self.height = height
+		self.text = text
+
+	def draw(self):
+		self.canv.rect(0, 0, self.width, self.height)
+		self.canv.line(0, 0, self.width, self.height)
+		self.canv.line(0, self.height, self.width, 0)
+
+		#centre the text
+		self.canv.setFont('Times-Roman',12)
+		self.canv.drawCentredString(0.5*self.width, 0.5*self.height, self.text)
+
+def	_leftDrawParaLine( tx, offset, extraspace, words, last=0):
+	tx.moveCursor(offset, 0)
+	tx._textOut(string.join(words),1)
+	tx.moveCursor(-offset, 0)
+
+def	_centerDrawParaLine( tx, offset, extraspace, words, last=0):
+	tx.moveCursor(offset + 0.5 * extraspace, 0)
+	tx._textOut(string.join(words),1)
+	tx.moveCursor(-offset + 0.5 * extraspace, 0)
+
+def	_rightDrawParaLine( tx, offset, extraspace, words, last=0):
+	tx.moveCursor(offset + extraspace, 0)
+	tx._textOut(string.join(words),1)
+	tx.moveCursor(-offset + extraspace, 0)
+
+def	_justifyDrawParaLine( tx, offset, extraspace, words, last=0):
+	tx.moveCursor(offset, 0)
+	text  = string.join(words)
+	if last:
+		#last one, left align
+		tx._textOut(text,1)
+	else:
+		tx.setWordSpace(extraspace / float(len(words)-1))
+		tx._textOut(text,1)
+		tx.setWordSpace(0)
+	tx.moveCursor(-offset, 0)
+
+def	_putFragLine(tx,words,leading):
+	for f in words:
+		if (tx._fontname,tx._fontsize)!=(f.fontName,f.fontSize):
+			tx._setFont(f.fontName, f.fontSize)
+		if tx._leading != leading: tx.setLeading(leading)
+		if tx.XtraState.textColor!=f.textColor:
+			tx.XtraState.textColor = f.textColor
+			tx.setFillColor(f.textColor)
+		if tx.XtraState.rise!=f.rise:
+			tx.XtraState.rise=f.rise
+			tx.setRise(f.rise)
+		tx._textOut(f.text,f is words[-1])	# cheap textOut
+
+def	_leftDrawParaLineX( tx, offset, line, leading, last=0):
+	tx.moveCursor(offset, 0)
+	_putFragLine(tx, line.words, leading)
+	tx.moveCursor(-offset, 0)
+
+def	_centerDrawParaLineX( tx, offset, line, leading, last=0):
+	tx.moveCursor(offset + 0.5 * line.extraSpace, 0)
+	_putFragLine(tx, line.words, leading)
+	tx.moveCursor(-offset + 0.5 * line.extraSpace, 0)
+
+def	_rightDrawParaLineX( tx, offset, line, leading, last=0):
+	tx.moveCursor(offset + line.extraSpace, 0)
+	_putFragLine(tx, line.words, leading)
+	tx.moveCursor(-offset + line.extraSpace, 0)
+
+def	_justifyDrawParaLineX( tx, offset, line, leading, last=0):
+	tx.moveCursor(offset, 0)
+	if last:
+		#last one, left align
+		tx.moveCursor(offset, 0)
+		_putFragLine(tx, line.words, leading)
+		tx.moveCursor(-offset, 0)
+	else:
+		tx.setWordSpace(line.extraSpace / float(line.wordCount-1))
+		_putFragLine(tx, line.words, leading)
+		tx.setWordSpace(0)
+	tx.moveCursor(-offset, 0)
+
+def	_sameFrag(f,g):
+	'returns 1 if two frags map out the same'
+	for a in ('fontName', 'fontSize', 'textColor', 'rise'):
+		if getattr(f,a)!=getattr(g,a): return 0
+	return 1
+
+def _getFragWords(frags):
+	'''	given a fragment list return a list of lists
+		[[size, (f00,w00), ..., (f0n,w0n)],....,[size, (fm0,wm0), ..., (f0n,wmn)]]
+		each pair f,w represents a style and some string
+		each sublist represents a word
+	'''
+	R = []
+	W = []
+	n = 0
+	for f in frags:
+		text = f.text
+		del f.text
+		if text!='':
+			S = string.split(text,' ')
+			if W!=[] and text[0] in [' ','\t']:
+				W.insert(0,n)
+				R.append(W)
+				W = []
+				n = 0
+
+			for w in S[:-1]:
+				W.append((f,w))
+				n = n + stringWidth(w, f.fontName, f.fontSize)
+				W.insert(0,n)
+				R.append(W)
+				W = []
+				n = 0
+
+			w = S[-1]
+			W.append((f,w))
+			n = n + stringWidth(w, f.fontName, f.fontSize)
+			if text[-1] in [' ','\t']:
+				W.insert(0,n)
+				R.append(W)
+				W = []
+				n = 0
+	if W!=[]:
+		W.insert(0,n)
+		R.append(W)
+
+	for r in R:
+		f = r[1][0]
+	return R
+
+class Paragraph(Drawable):
+	def __init__(self, text, style, bulletText = None):
+		text = cleanBlockQuotedText(text)
+		rv = _parser.parse(text,style)
+		if rv is None:
+			raise '''xml parser error (%s) in paragraph beginning\n'%s'''\
+					% (_parser.errors[0],text[:min(30,len(text))])
+		self.frags = rv
+		self.style = style
+		self.bulletText = bulletText
+		self.debug = 0	 #turn this on to see a pretty one with all the margins etc.
+
+	def wrap(self, availWidth, availHeight):
+		# work out widths array for breaking
+		self.width = availWidth
+		first_line_width = availWidth - self.style.firstLineIndent - self.style.rightIndent
+		later_widths = availWidth - self.style.leftIndent - self.style.rightIndent
+		self.bfrags = self.breakLines([first_line_width, later_widths])
+
+		#estimate the size
+		return (self.width, self.height)
+
+	def draw(self):
+		#call another method for historical reasons.  Besides, I
+		#suspect I will be playing with alternate drawing routines
+		#so not doing it here makes it easier to switch.
+		self.drawPara(self.debug)
+
+	def breakLines(self, width):
+		"""
+		Returns a broken line structure. There are two cases
+		
+		A) For the simple case of a single formatting input fragment the output is
+			A fragment specifier with
+				kind = 0
+				fontName, fontSize, leading, textColor
+				lines=	A list of lines
+						Each line has two items.
+						1) unused width in points
+						2) word list
+
+		B) When there is more than one input formatting fragment the out put is
+			A fragment specifier with
+				kind = 1
+				lines=	A list of fragments
+							1) extraspace (needed for justified)
+							2) fontSize
+							3) leading
+							4) words=word list
+								each word is itself a fragment with
+								various settings
+
+		This structure can be used to easily draw paragraphs with the various alignments.
+		You can supply either a single width or a list of widths; the latter will have its
+		last item repeated until necessary. A 2-element list is useful when there is a
+		different first line indent; a longer list could be created to facilitate custom wraps
+		around irregular objects."""
+
+		if type(width) <> types.ListType: maxwidths = [width]
+		else: maxwidths = width
+		lines = []
+		lineno = 0
+		maxwidth = maxwidths[lineno]
+		style = self.style
+		fFontSize = float(style.fontSize)
+		sLeading = style.leading
+
+		#for bullets, work out width and ensure we wrap the right amount onto line one
+		if self.bulletText <> None:
+			bulletWidth = stringWidth(
+				self.bulletText,
+				style.bulletFontName, style.bulletFontSize)
+			bulletRight = style.bulletIndent + bulletWidth
+			if bulletRight > style.firstLineIndent:
+				#..then it overruns, and we have less space available on line 1
+				maxwidths[0] = maxwidths[0] - (bulletRight - style.firstLineIndent)
+
+		self.height = style.spaceBefore + style.spaceAfter
+		frags = self.frags
+		nFrags= len(frags)
+		if nFrags==1:
+			f = frags[0]
+			fontSize = f.fontSize
+			fontName = f.fontName
+			words = string.split(f.text, ' ')
+			spacewidth = stringWidth(' ', fontName, fontSize)
+			cLine = []
+			currentwidth = - spacewidth   # hack to get around extra space for word 1
+			for word in words:
+				wordwidth = stringWidth(word, fontName, fontSize)
+				space_available = maxwidth - (currentwidth + spacewidth + wordwidth)
+				if	space_available > 0:
+					# fit one more on this line
+					cLine.append(word)
+					currentwidth = currentwidth + spacewidth + wordwidth
+				else:
+					#end of line
+					lines.append((maxwidth - currentwidth, cLine))
+					cLine = [word]
+					currentwidth = wordwidth
+					lineno = lineno + 1
+					try:
+						maxwidth = maxwidths[lineno]
+					except IndexError:
+						maxwidth = maxwidths[-1]  # use the last one
+
+			#deal with any leftovers on the final line
+			if cLine!=[]: lines.append((space_available, cLine))
+			leading=f.fontSize*sLeading/fFontSize
+			self.height = self.height + len(lines) * leading
+			return f.clone(kind=0, leading=leading, lines=lines)
+		elif nFrags<=0:
+			return ParaFrag(kind=0, fontSize=style.fontSize, fontName=style.fontName,
+							textColor=style.textColor, leading=sLeading, lines=[])
+		else:
+			height = 0
+			n = 0
+			for w in _getFragWords(frags):
+				spacewidth = stringWidth(' ',w[-1][0].fontName, w[-1][0].fontSize)
+
+				if n==0:
+					currentwidth = -spacewidth	 # hack to get around extra space for word 1
+					words = []
+					maxSize = 0
+
+				wordwidth = w[0]
+				f = w[1][0]
+				space_available = maxwidth - (currentwidth + spacewidth + wordwidth)
+				if	space_available > 0:
+					# fit one more on this line
+					n = n + 1
+					maxSize = max(maxSize,f.fontSize)
+					if words==[]:
+						words = [f.clone()]
+						words[-1].text = w[1][1]
+					elif not _sameFrag(words[-1],f):
+						words[-1].text = words[-1].text+' '
+						words.append(f.clone())
+						words[-1].text = w[1][1]
+					else:
+						words[-1].text = words[-1].text + ' ' + w[1][1]
+
+					for i in w[2:]:
+						f = i[0].clone()
+						f.text=i[1]
+						words.append(f)
+						maxSize = max(maxSize,f.fontSize)
+						
+					currentwidth = currentwidth + spacewidth + wordwidth
+				else:
+					#end of line
+					leading=maxSize*sLeading/fFontSize
+					lines.append(ParaFrag(extraSpace=(maxwidth - currentwidth),wordCount=n,
+										words=words, fontSize=maxSize, leading=leading))
+					height = height+leading
+
+					#start new line
+					lineno = lineno + 1
+					try:
+						maxwidth = maxwidths[lineno]
+					except IndexError:
+						maxwidth = maxwidths[-1]  # use the last one
+					currentwidth = wordwidth
+					n = 1
+					maxSize = f.fontSize
+					words = [f.clone()]
+					words[-1].text = w[1][1]
+
+					for i in w[2:]:
+						f = i[0].clone()
+						f.text=i[1]
+						words.append(f)
+						maxSize = max(maxSize,f.fontSize)
+
+			#deal with any leftovers on the final line
+			if words<>[]:
+				leading=maxSize*sLeading/fFontSize
+				lines.append(ParaFrag(extraSpace=(maxwidth - currentwidth),wordCount=n,
+									words=words, fontSize=maxSize, leading=leading))
+				height = height+leading
+
+			self.height = self.height + height
+			return ParaFrag(kind=1, lines=lines)
+
+		return lines
+
+	def drawPara(self,debug=0):
+		"""Draws a paragraph according to the given style.
+		Returns the final y position at the bottom. Not safe for
+		paragraphs without spaces e.g. Japanese; wrapping
+		algorithm will go infinite."""
+
+		#stash the key facts locally for speed
+		canvas = self.canv
+		style = self.style
+		bfrags = self.bfrags
+		lines = bfrags.lines
+
+		#work out the origin for line 1
+		cur_x = style.leftIndent
 
 
-def cleanBlockQuotedText(text):
-    """This is an internal utility which takes triple-
-    quoted text form within the document and returns
-    (hopefully) the paragraph the user intended originally."""
-    stripped = string.strip(text)
-    lines = string.split(stripped, '\n')
-    trimmed_lines = map(string.lstrip, lines)
-    return string.join(trimmed_lines, ' ')
+		if debug:
+			# This boxes and shades stuff to show how the paragraph
+			# uses its space.  Useful for self-documentation so
+			# the debug code stays!
+			# box the lot
+			canvas.rect(0, 0, self.width, self.height)
+			#left and right margins
+			canvas.saveState()
+			canvas.setFillColor(Color(0.9,0.9,0.9))
+			canvas.rect(0, 0, style.leftIndent, self.height)
+			canvas.rect(self.width - style.rightIndent, 0, style.rightIndent, self.height)
+			# shade above and below
+			canvas.setFillColor(Color(1.0,1.0,0.0))
+			canvas.rect(0, self.height - style.spaceBefore, self.width,  style.spaceBefore)
+			canvas.rect(0, 0, self.width, style.spaceAfter)
+			canvas.restoreState()
+			#self.drawLine(x + style.leftIndent, y, x + style.leftIndent, cur_y)
+
+		nLines = len(lines)
+		if nLines > 0:
+			canvas.saveState()
+			canvas.addLiteral('% textcanvas.drawParagraph()')
+			alignment = style.alignment
+			#is there a bullet?  if so, draw it first
+			offset = style.firstLineIndent - style.leftIndent
+			lim = nLines-1
+
+			if bfrags.kind==0:
+				if alignment == TA_LEFT:
+					dpl = _leftDrawParaLine
+				elif alignment == TA_CENTER:
+					dpl = _centerDrawParaLine
+				elif self.style.alignment == TA_RIGHT:
+					dpl = _rightDrawParaLine
+				elif self.style.alignment == TA_JUSTIFY:
+					dpl = _justifyDrawParaLine
+				f = bfrags
+				cur_y = self.height - f.fontSize - style.spaceBefore
+
+				if self.bulletText <> None:
+					tx2 = canvas.beginText(style.bulletIndent, cur_y)
+					tx2.setFont(style.bulletFontName, style.bulletFontSize)
+					tx2.textOut(self.bulletText)
+					bulletEnd = tx2.getX()
+					offset = max(offset, bulletEnd - style.leftIndent)
+					canvas.drawText(tx2)
+
+				#set up the font etc.
+				canvas._code.append('%s %s %s rg' % (f.textColor.red, f.textColor.green, f.textColor.blue))
+
+				tx = canvas.beginText(cur_x, cur_y)
+
+				#now the font for the rest of the paragraph
+				tx.setFont(f.fontName, f.fontSize, f.leading)
+				dpl( tx, offset, lines[0][0], lines[0][1], nLines==1)
+				#cur_y = cur_y + f.leading
+
+				#now the middle of the paragraph, aligned with the left margin which is our origin.
+				for i in range(1, nLines):
+					dpl( tx, 0, lines[i][0], lines[i][1], i==lim)
+					#cur_y = cur_y - f.leading
+			else:
+				f = lines[0]
+				cur_y = self.height - f.fontSize - style.spaceBefore
+				if alignment == TA_LEFT:
+					dpl = _leftDrawParaLineX
+				elif alignment == TA_CENTER:
+					dpl = _centerDrawParaLineX
+				elif self.style.alignment == TA_RIGHT:
+					dpl = _rightDrawParaLineX
+				elif self.style.alignment == TA_JUSTIFY:
+					dpl = _justifyDrawParaLineX
+
+				if self.bulletText <> None:
+					tx2 = canvas.beginText(style.bulletIndent, cur_y)
+					tx2.setFont(style.bulletFontName, style.bulletFontSize)
+					tx2.textOut(self.bulletText)
+					bulletEnd = tx2.getX()
+					offset = max(offset, bulletEnd - style.leftIndent)
+					canvas.drawText(tx2)
+
+				#set up the font etc.
+				tx = canvas.beginText(cur_x, cur_y)
+				tx.XtraState=ParaFrag()
+				tx.XtraState.textColor=None
+				tx.XtraState.rise=0
+				dpl( tx, offset, lines[0], nLines==1 and lines[0].leading or lines[1].leading, nLines==1)
+				#cur_y = cur_y + f.leading
+
+				#now the middle of the paragraph, aligned with the left margin which is our origin.
+				for i in range(1, nLines):
+					f = lines[i]
+					dpl( tx, 0, f, i==lim and f.leading or lines[i+1].leading, i==lim)
+					#cur_y = cur_y - f.leading
+				
+			canvas.drawText(tx)
+			canvas.restoreState()
+
+class Preformatted(Drawable):
+	"""This is like the HTML <PRE> tag.  The line breaks are exactly where you put
+	them, and it will not be wrapped.  So it is much simpler to implement!"""
+	def __init__(self, text, style, bulletText = None, dedent=0):
+		self.style = style
+		self.bulletText = bulletText
+
+		#tidy up text - carefully, it is probably code.  If people want to
+		#indent code within a source script, you can supply an arg to dedent
+		#and it will chop off that many character, otherwise it leaves
+		#left edge intact.
+
+		templines = string.split(text, '\n')
+		self.lines = []
+		for line in templines:
+			line = string.rstrip(line[dedent:])
+			self.lines.append(line)
+		#don't want the first or last to be empty
+		while string.strip(self.lines[0]) == '':
+			self.lines = self.lines[1:]
+		while string.strip(self.lines[-1]) == '':
+			self.lines = self.lines[:-1]
+
+	def wrap(self, availWidth, availHeight):
+		self.width = availWidth
+		self.height = (self.style.spaceBefore +
+					  self.style.leading * len(self.lines) +
+					  self.style.spaceAfter)
+		return (self.width, self.height)
+
+	def draw(self):
+		#call another method for historical reasons.  Besides, I
+		#suspect I will be playing with alternate drawing routines
+		#so not doing it here makes it easier to switch.
+
+		cur_x = self.style.leftIndent
+		cur_y = self.height - self.style.spaceBefore - self.style.fontSize
+		self.canv.addLiteral('%PreformattedPara')
+
+		tx = self.canv.beginText(cur_x, cur_y)
+		#set up the font etc.
+		tx.setFont(self.style.fontName,
+				   self.style.fontSize,
+				   self.style.leading)
+
+		for text in self.lines:
+			tx.textLine(text)
+		self.canv.drawText(tx)
+
+class Image(Drawable):
+	def __init__(self, filename, width=None, height=None):
+		"""If size to draw at not specified, get it from the image."""
+		import Image  #this will raise an error if they do not have PIL.
+		self.filename = filename
+		print 'Creating Image for', filename
+		img = Image.open(filename)
+		(self.imageWidth, self.imageHeight) = img.size
+		if width:
+			self.drawWidth = width
+		else:
+			self.drawWidth = self.imageWidth
+		if height:
+			self.drawHeight = height
+		else:
+			self.drawHeight = self.imageHeight
+
+	def wrap(self, availWidth, availHeight):
+		#the caller may decide it does not fit.
+		self.availWidth = availWidth
+		return (self.drawWidth, self.drawHeight)
+
+	def draw(self):
+		#center it
+		startx = 0.5 * (self.availWidth - self.drawWidth)
+		self.canv.drawInlineImage(self.filename,
+								  startx,
+								  0,
+								  self.drawWidth,
+								  self.drawHeight
+								  )
+class Spacer(Drawable):
+	"""A spacer just takes up space and doesn't draw anything - it can
+	ensure a gap between objects."""
+	def __init__(self, width, height):
+		self.width = width
+		self.height = height
+
+	def wrap(self, availWidth, availHeight):
+		return (self.width, self.height)
+
+	def draw(self):
+		pass
+
+class PageBreak(Drawable):
+	"""This works by consuming all remaining space in the frame!"""
+
+	def wrap(self, availWidth, availHeight):
+		self.width = availWidth
+		self.height = availHeight
+		return (availWidth,availHeight)  #step back a point
+
+	def draw(self):
+		pass
+		#
+		#self.canv.drawRect(0, 0, self.width, self.height)
+		#self.canv.drawLine(0, 0, self.width, self.height)
+		#self.canv.drawLine(0, self.height, self.width, 0)
+		#self.text = 'Page Break Object (%d x %d)' % (self.width, self.height)
+		##centre the text
+		#self.canv._currentFont = self.canv.defaultFont
+		#f = Font(size=24,bold=1)
+		#w = self.canv.stringWidth(self.text, f)
+		#x = 0.5 * (self.width - w)
+		#y = 0.33 * (self.height + f.size)
+
+		#self.canv.drawString(self.text, x, y, font=f)
+
+class Macro(Drawable):
+	"""This is not actually drawn (i.e. it has zero height)
+	but is executed when it would fit in the frame.  Allows direct
+	access to the canvas through the object 'canvas'"""
+	def __init__(self, command):
+		self.command = command
+	def wrap(self, availWidth, availHeight):
+		return (0,0)
+	def draw(self):
+		exec self.command in globals(), {'canvas':self.canv}
 
 
 
 
 #############################################################
 #
-#       Drawable Objects - a base class and a few examples.
-#       One is just a box to get some metrics.  We also have
-#       a paragraph, an image and a special 'page break'
-#       object which fills the space.
-#
-#############################################################
-
-class Drawable:
-    """Abstract base class for things to be drawn.  Key concepts:
-    1. It knows its size
-    2. It draws in its own coordinate system (this requires the
-        base API to provide a translate() function.
-    """
-    def __init__(self):
-        self.width = 0
-        self.height = 0
-        self.wrapped = 0
-
-    def drawOn(self, canvas, x, y):
-        "Tell it to draw itself on the canvas.  Do not override"
-        self.canv = canvas
-        self.canv.saveState()
-        self.canv.translate(x, y)
-
-        self.draw()   #this is the bit you overload
-
-        self.canv.restoreState()
-        del self.canv
-
-
-    def wrap(self, availWidth, availHeight):
-        """This will be called by the enclosing frame before objects
-        are asked their size, drawn or whatever.  It returns the
-        size actually used."""
-        return (self.width, self.height)
-
-class XBox(Drawable):
-    """Example drawable - a box with an x through it and a caption.
-    This has a known size, so does not need to respond to wrap()."""
-    def __init__(self, width, height, text = 'A Box'):
-        Drawable.__init__(self)
-        self.width = width
-        self.height = height
-        self.text = text
-
-    def draw(self):
-        self.canv.rect(0, 0, self.width, self.height)
-        self.canv.line(0, 0, self.width, self.height)
-        self.canv.line(0, self.height, self.width, 0)
-
-        #centre the text
-        self.canv.setFont('Times-Roman',12)
-        self.canv.drawCentredString(0.5*self.width, 0.5*self.height, self.text)
-
-
-class Paragraph(Drawable):
-    def __init__(self, text, style, bulletText = None):
-        text = cleanBlockQuotedText(text)
-        self.text = text
-        self.style = style
-        self.bulletText = bulletText
-        self.debug = 0   #turn this on to see a pretty one with all the margins etc.
-
-    def wrap(self, availWidth, availHeight):
-        # work out widths array for breaking
-        self.width = availWidth
-        first_line_width = availWidth - self.style.firstLineIndent - self.style.rightIndent
-        later_widths = availWidth - self.style.leftIndent - self.style.rightIndent
-        wrap_widths = [first_line_width, later_widths]
-
-        # break it up and store the results internally
-        self.lines = self.breakLines(self.text, wrap_widths, self.style.fontName, self.style.fontSize)
-
-        linecount = len(self.lines)
-
-        #estimate the size
-        self.height = (self.style.spaceBefore +
-                  linecount * self.style.leading +
-                  self.style.spaceAfter)
-        return (self.width, self.height)
-
-    def draw(self):
-        #call another method for historical reasons.  Besides, I
-        #suspect I will be playing with alternate drawing routines
-        #so not doing it here makes it easier to switch.
-        self.drawPara(self.debug)
-
-    def breakLines(self, text, width, fontName, fontSize):
-        """Returns a structure broken into lines.  Each line has two items.  Item one
-        is the extra points of space available on that line; item two is the list of
-        words themselves.  This structure can be used to easily draw paragraphs
-        with the various alignments.  You can supply either a single width or a list
-        of widths; the latter will have its last item repeated until necessary.  A
-        2-element list is useful when there is a different first line indent; a longer
-        list could be created to facilitate custom wraps around irregular objects."""
-        if type(width) <> types.ListType:
-            maxwidths = [width]
-        else:
-            maxwidths = width
-        lines = []
-        spacewidth = pdfmetrics.stringwidth(' ', fontName) * 0.001 * fontSize
-        words = string.split(text, ' ')
-        currentline = []
-        lineno = 0
-        maxwidth = maxwidths[lineno]
-        currentwidth = - spacewidth   # hack to get around extra space for word 1
-
-        #for bullets, work out its width and ensure we wrap the right amount onto
-        #line one
-        if self.bulletText <> None:
-            bulletWidth = pdfmetrics.stringwidth(
-                self.bulletText,
-                self.style.bulletFontName) * 0.001 * self.style.bulletFontSize
-            bulletRight = self.style.bulletIndent + bulletWidth
-            if bulletRight > self.style.firstLineIndent:
-                #..then it overruns, and we have less space available on line 1
-                maxwidths[0] = maxwidths[0] - (bulletRight - self.style.firstLineIndent)
-
-        for word in words:
-            wordwidth = pdfmetrics.stringwidth(word, fontName) * 0.001 * fontSize
-            space_available = maxwidth - (currentwidth + spacewidth + wordwidth)
-##            print 'FIXME Word %s, width %0.2f, used %0.2f, remaining %0.2f' % (
-##                word, wordwidth, currentwidth, space_available)
-            if  space_available > 0:
-                # fit one more on this line
-                currentline.append(word)
-                currentwidth = currentwidth + spacewidth + wordwidth
-            else:
-                #end of line
-                lines.append((maxwidth - currentwidth, currentline))
-                currentline = [word]
-                currentwidth = wordwidth
-                lineno = lineno + 1
-                try:
-                    maxwidth = maxwidths[lineno]
-                except IndexError:
-                    maxwidth = maxwidths[-1]  # use the last one
-        #deal with any leftovers on the final line
-        if currentline <> []:
-            lines.append((space_available, currentline))
-
-        return lines
-
-
-
-    def drawPara(self,debug=0):
-        """Draws a paragraph according to the given style.
-        Returns the final y position at the bottom. Not safe for
-        paragraphs without spaces e.g. Japanese; wrapping
-        algorithm will go infinite."""
-
-        #stash the key facts locally for speed
-        canvas = self.canv
-
-        #work out the origin for line 1
-        cur_x = self.style.leftIndent
-        cur_y = self.height - self.style.fontSize - self.style.spaceBefore
-
-        if debug:
-            # This boxes and shades stuff to show how the paragraph
-            # uses its space.  Useful for self-documentation so
-            # the debug code stays!
-            # box the lot
-            canvas.rect(0, 0, self.width, self.height)
-            #left and right margins
-            canvas.saveState()
-            canvas.setFillColor(Color(0.9,0.9,0.9))
-            canvas.rect(0, 0, self.style.leftIndent, self.height)
-            canvas.rect(self.width - self.style.rightIndent, 0, self.style.rightIndent, self.height)
-            # shade above and below
-            canvas.setFillColor(Color(1.0,1.0,0.0))
-            canvas.rect(0, self.height - self.style.spaceBefore, self.width,  self.style.spaceBefore)
-            canvas.rect(0, 0, self.width, self.style.spaceAfter)
-            canvas.restoreState()
-
-            #self.drawLine(x + style.leftIndent, y, x + style.leftIndent, cur_y)
-
-
-        canvas.addLiteral('% textcanvas.drawParagraph()')
-        #set up the font etc.
-
-    ##    color = style.textColor
-    ##    r,g,b = color.red, color.green, color.blue
-    ##    self.code.append('%s %s %s rg' % (r,g,b))
-    ##
-        #is there a bullet?  if so, draw it first
-
-        if len(self.lines) > 0:
-            #begin drawing line one.  Unless the wrapping was
-            #perfect, we will have a few mm of extra space to allocate;
-            #this means moving the line origin to the right depending
-            #on whether it is centred, right-aligned or left-aligned
-            offset = self.style.firstLineIndent - self.style.leftIndent
-
-            if self.bulletText <> None:
-                tx2 = canvas.beginText(self.style.bulletIndent, cur_y)
-                tx2.setFont(self.style.bulletFontName, self.style.bulletFontSize)
-                tx2.textOut(self.bulletText)
-                bulletEnd = tx2.getX()
-                offset = max(offset, bulletEnd - self.style.leftIndent)
-                canvas.drawText(tx2)
-
-            tx = canvas.beginText(cur_x, cur_y)
-
-            #now the font for the rest of the paragraph
-            tx.setFont(self.style.fontName,
-                   self.style.fontSize,
-                   self.style.leading)
-
-            (extraspace, words) = self.lines[0]
-            text  = string.join(words)
-            if self.style.alignment == TA_LEFT:
-                tx.moveCursor(offset, 0)
-                tx.textLine(text)
-                tx.moveCursor(-offset, 0)
-            elif self.style.alignment == TA_CENTER:
-                tx.moveCursor(offset + 0.5 * extraspace, 0)
-                tx.textLine(text)
-                tx.moveCursor(-offset + 0.5 * extraspace, 0)
-            elif self.style.alignment == TA_RIGHT:
-                tx.moveCursor(offset + extraspace, 0)
-                tx.textLine(text)
-                tx.moveCursor(-offset + extraspace, 0)
-            elif self.style.alignment == TA_JUSTIFY:
-                tx.moveCursor(-offset, 0)
-                tx.setWordSpace(extraspace / float(len(words)-1))
-                tx.textLine(text)
-                tx.setWordSpace(0.0)
-                tx.moveCursor(-offset, 0)
-
-            cur_y = cur_y + self.style.leading
-
-        #now the middle of the paragraph, aligned with the left margin which is our
-        #origin.
-
-            for lineno in range(1, len(self.lines)):
-                (extraspace, words) = self.lines[lineno]
-                text = string.join(words)
-                if self.style.alignment == TA_LEFT:
-                    tx.textLine(text)
-                elif self.style.alignment == TA_CENTER:
-                    tx.moveCursor(0.5 * extraspace, 0)
-                    tx.textLine(text)
-                    tx.moveCursor(0.5 * extraspace, 0)
-                elif self.style.alignment == TA_RIGHT:
-                    tx.moveCursor(extraspace, 0)
-                    tx.textLine(text)
-                    tx.moveCursor(extraspace, 0)
-                elif self.style.alignment == TA_JUSTIFY:
-                    if lineno == len(self.lines) - 1:
-                        #last one, left align
-                        tx.textLine(text)
-                    else:
-                        tx.setWordSpace(extraspace / float(len(words)-1))
-                        tx.textLine(text)
-                        tx.setWordSpace(0.0)
-
-                cur_y = cur_y - self.style.leading
-        canvas.drawText(tx)
-        #move down a bit, correct for the extra fontSize move at the  start
-
-class Preformatted(Drawable):
-    """This is like the HTML <PRE> tag.  The line breaks are exactly where you put
-    them, and it will not be wrapped.  So it is much simpler to implement!"""
-    def __init__(self, text, style, bulletText = None, dedent=0):
-        self.style = style
-        self.bulletText = bulletText
-
-        #tidy up text - carefully, it is probably code.  If people want to
-        #indent code within a source script, you can supply an arg to dedent
-        #and it will chop off that many character, otherwise it leaves
-        #left edge intact.
-
-        templines = string.split(text, '\n')
-        self.lines = []
-        for line in templines:
-            line = string.rstrip(line[dedent:])
-            self.lines.append(line)
-        #don't want the first or last to be empty
-        while string.strip(self.lines[0]) == '':
-            self.lines = self.lines[1:]
-        while string.strip(self.lines[-1]) == '':
-            self.lines = self.lines[:-1]
-
-    def wrap(self, availWidth, availHeight):
-        self.width = availWidth
-        self.height = (self.style.spaceBefore +
-                      self.style.leading * len(self.lines) +
-                      self.style.spaceAfter)
-        return (self.width, self.height)
-
-    def draw(self):
-        #call another method for historical reasons.  Besides, I
-        #suspect I will be playing with alternate drawing routines
-        #so not doing it here makes it easier to switch.
-
-        cur_x = self.style.leftIndent
-        cur_y = self.height - self.style.spaceBefore - self.style.fontSize
-        self.canv.addLiteral('%PreformattedPara')
-
-        tx = self.canv.beginText(cur_x, cur_y)
-        #set up the font etc.
-        tx.setFont(self.style.fontName,
-                   self.style.fontSize,
-                   self.style.leading)
-
-        for text in self.lines:
-            tx.textLine(text)
-        self.canv.drawText(tx)
-
-class Image(Drawable):
-    def __init__(self, filename, width=None, height=None):
-        """If size to draw at not specified, get it from the image."""
-        import Image  #this will raise an error if they do not have PIL.
-        self.filename = filename
-        print 'Creating Image for', filename
-        img = Image.open(filename)
-        (self.imageWidth, self.imageHeight) = img.size
-        if width:
-            self.drawWidth = width
-        else:
-            self.drawWidth = self.imageWidth
-        if height:
-            self.drawHeight = height
-        else:
-            self.drawHeight = self.imageHeight
-
-    def wrap(self, availWidth, availHeight):
-        #the caller may decide it does not fit.
-        self.availWidth = availWidth
-        return (self.drawWidth, self.drawHeight)
-
-    def draw(self):
-        #center it
-        startx = 0.5 * (self.availWidth - self.drawWidth)
-        self.canv.drawInlineImage(self.filename,
-                                  startx,
-                                  0,
-                                  self.drawWidth,
-                                  self.drawHeight
-                                  )
-class Spacer(Drawable):
-    """A spacer just takes up space and doesn't draw anything - it can
-    ensure a gap between objects."""
-    def __init__(self, width, height):
-        self.width = width
-        self.height = height
-
-    def wrap(self, availWidth, availHeight):
-        return (self.width, self.height)
-
-    def draw(self):
-        pass
-
-class PageBreak(Drawable):
-    """This works by consuming all remaining space in the frame!"""
-
-    def wrap(self, availWidth, availHeight):
-        self.width = availWidth
-        self.height = availHeight
-        return (availWidth,availHeight)  #step back a point
-
-    def draw(self):
-        pass
-        #
-        #self.canv.drawRect(0, 0, self.width, self.height)
-        #self.canv.drawLine(0, 0, self.width, self.height)
-        #self.canv.drawLine(0, self.height, self.width, 0)
-        #self.text = 'Page Break Object (%d x %d)' % (self.width, self.height)
-        ##centre the text
-        #self.canv._currentFont = self.canv.defaultFont
-        #f = Font(size=24,bold=1)
-        #w = self.canv.stringWidth(self.text, f)
-        #x = 0.5 * (self.width - w)
-        #y = 0.33 * (self.height + f.size)
-
-        #self.canv.drawString(self.text, x, y, font=f)
-
-class Macro(Drawable):
-    """This is not actually drawn (i.e. it has zero height)
-    but is executed when it would fit in the frame.  Allows direct
-    access to the canvas through the object 'canvas'"""
-    def __init__(self, command):
-        self.command = command
-    def wrap(self, availWidth, availHeight):
-        return (0,0)
-    def draw(self):
-        exec self.command in globals(), {'canvas':self.canv}
-
-
-
-
-#############################################################
-#
-#       Basic paragraph-drawing routine.  Not sure where
-#       this should go, so did it as a separate function.
+#		Basic paragraph-drawing routine.  Not sure where
+#		this should go, so did it as a separate function.
 #
 #############################################################
 
 
 #############################################################
 #
-#       A Frame, and a Document Model
+#		A Frame, and a Document Model
 #
 #############################################################
 
@@ -686,114 +895,114 @@
 LayoutError = "LayoutError"
 
 class SimpleFrame:
-    """A region into which drawable objects are to be packed.
-    Flows downwards.  A more general solution is needed which
-    will allow flows in any direction, including 'across and then
-    down' for small objects, but this is useful for
-    many languages now, as long as each object is 'full-width'
-    (i.e. a paragraph and not a word)."""
-    def __init__(self, canvas, x1, y1, width,height):
-        self.canvas = canvas
+	"""A region into which drawable objects are to be packed.
+	Flows downwards.  A more general solution is needed which
+	will allow flows in any direction, including 'across and then
+	down' for small objects, but this is useful for
+	many languages now, as long as each object is 'full-width'
+	(i.e. a paragraph and not a word)."""
+	def __init__(self, canvas, x1, y1, width,height):
+		self.canvas = canvas
 
-        #these say where it goes on the page
-        x2 = x1 + width
-        y2 = y1 + height
-        self.leftMargin = x1
-        self.bottomMargin = y1
-        self.rightMargin = x2
-        self.topMargin = y2
+		#these say where it goes on the page
+		x2 = x1 + width
+		y2 = y1 + height
+		self.leftMargin = x1
+		self.bottomMargin = y1
+		self.rightMargin = x2
+		self.topMargin = y2
 
-        #these create some padding.
-        self.leftPadding = 6
-        self.bottomPadding = 6
-        self.rightPadding = 6
-        self.topPadding = 6
+		#these create some padding.
+		self.leftPadding = 6
+		self.bottomPadding = 6
+		self.rightPadding = 6
+		self.topPadding = 6
 
-        #work out the available space
-        self.width = x2 - x1 - self.leftPadding - self.rightPadding
-        self.height = y2 - y1 - self.topPadding - self.bottomPadding
-        self.objects = []   #it keeps a list of objects
-        self.showBoundary = 0
-        #drawing starts at top left
-        self.x = x1 + self.leftPadding
-        self.y = y2 - self.topPadding
+		#work out the available space
+		self.width = x2 - x1 - self.leftPadding - self.rightPadding
+		self.height = y2 - y1 - self.topPadding - self.bottomPadding
+		self.objects = []	#it keeps a list of objects
+		self.showBoundary = 0
+		#drawing starts at top left
+		self.x = x1 + self.leftPadding
+		self.y = y2 - self.topPadding
 
 
-    def add(self, drawable):
-        """ Draws the object at the current position.
-        Returns 1 if successful, 0 if it would not fit.
-        Raises a LayoutError if the object is too wide,
-        or if it is too high for a totally empty frame,
-        to avoid infinite loops"""
-        w, h = drawable.wrap(self.width, self.y - self.bottomMargin - self.bottomPadding)
+	def add(self, drawable):
+		""" Draws the object at the current position.
+		Returns 1 if successful, 0 if it would not fit.
+		Raises a LayoutError if the object is too wide,
+		or if it is too high for a totally empty frame,
+		to avoid infinite loops"""
+		w, h = drawable.wrap(self.width, self.y - self.bottomMargin - self.bottomPadding)
 
-        if h > self.height:
-            raise "LayoutError", "Object (%d points) too high for frame (%d points)." % (h, self.height)
-        if w > self.width:
-            raise "LayoutError", "Object (%d points) too wide for frame (%d points)." % (w, self.width)
-        if self.y - h < (self.bottomMargin - self.bottomPadding):
-            return 0
-        else:
-            #now we can draw it, and update the current point.
-            drawable.drawOn(self.canvas, self.x, self.y - h)
-            self.y = self.y - h
-            self.objects.append(drawable)
-            return 1
+		if h > self.height:
+			raise "LayoutError", "Object (%d points) too high for frame (%d points)." % (h, self.height)
+		if w > self.width:
+			raise "LayoutError", "Object (%d points) too wide for frame (%d points)." % (w, self.width)
+		if self.y - h < (self.bottomMargin - self.bottomPadding):
+			return 0
+		else:
+			#now we can draw it, and update the current point.
+			drawable.drawOn(self.canvas, self.x, self.y - h)
+			self.y = self.y - h
+			self.objects.append(drawable)
+			return 1
 
-    def addFromList(self, drawlist):
-        """Consumes objects from the front of the list until the
-        frame is full.  If it cannot fit one object, raises
-        an exception."""
+	def addFromList(self, drawlist):
+		"""Consumes objects from the front of the list until the
+		frame is full.	If it cannot fit one object, raises
+		an exception."""
 
-        if self.showBoundary:
-            self.canvas.rect(
-                        self.leftMargin,
-                        self.bottomMargin,
-                        self.rightMargin - self.leftMargin,
-                        self.topMargin - self.bottomMargin
-                        )
+		if self.showBoundary:
+			self.canvas.rect(
+						self.leftMargin,
+						self.bottomMargin,
+						self.rightMargin - self.leftMargin,
+						self.topMargin - self.bottomMargin
+						)
 
-        while len(drawlist) > 0:
-            head = drawlist[0]
-            if self.add(head):
-                del drawlist[0]
-            else:
-                #leave it in the list for later
-                break
+		while len(drawlist) > 0:
+			head = drawlist[0]
+			if self.add(head):
+				del drawlist[0]
+			else:
+				#leave it in the list for later
+				break
 class Sequencer:
-    """Something to make it easy to number paragraphs, sections,
-    images and anything else. Usage:
-        >>> seq = layout.Sequencer()
-        >>> seq.next('Bullets')
-        1
-        >>> seq.next('Bullets')
-        2
-        >>> seq.next('Bullets')
-        3
-        >>> seq.reset('Bullets')
-        >>> seq.next('Bullets')
-        1
-        >>> seq.next('Figures')
-        1
-        >>>
-    I plan to add multi-level linkages, so that Head2 could be reet
-    """
-    def __init__(self):
-        self.dict = {}
+	"""Something to make it easy to number paragraphs, sections,
+	images and anything else. Usage:
+		>>> seq = layout.Sequencer()
+		>>> seq.next('Bullets')
+		1
+		>>> seq.next('Bullets')
+		2
+		>>> seq.next('Bullets')
+		3
+		>>> seq.reset('Bullets')
+		>>> seq.next('Bullets')
+		1
+		>>> seq.next('Figures')
+		1
+		>>>
+	I plan to add multi-level linkages, so that Head2 could be reet
+	"""
+	def __init__(self):
+		self.dict = {}
 
-    def next(self, category):
-        if self.dict.has_key(category):
-            self.dict[category] = self.dict[category] + 1
-        else:
-            self.dict[category] = 1
-        return self.dict[category]
+	def next(self, category):
+		if self.dict.has_key(category):
+			self.dict[category] = self.dict[category] + 1
+		else:
+			self.dict[category] = 1
+		return self.dict[category]
 
-    def reset(self, category):
-        self.dict[category] = 0
+	def reset(self, category):
+		self.dict[category] = 0
 
 def _doNothing(drawables, doc):
-    "Dummy callback for onFirstPage and onNewPage"
-    pass
+	"Dummy callback for onFirstPage and onNewPage"
+	pass
 
 ##########################################################
 #
@@ -801,136 +1010,136 @@
 #
 ##########################################################
 class SimpleFlowDocument:
-    """A sample document that uses a single frame on each page.
-    The intention is for programmers to create their own document
-    models as needed.  This one accepts a list of drawables. You
-    can provide callbacks to decorate the first page and
-    subsequent pages; these should do headers, footers, sidebars
-    as needed."""
-    def __init__(self, filename, pagesize, showBoundary=0):
-        self.filename = filename
-        self.pagesize = pagesize
-        self.showBoundary=showBoundary
-        #sensibel defaults; override if you wish
-        self.leftMargin =  inch
-        self.bottomMargin = inch
-        self.rightMargin = self.pagesize[0] - inch
-        self.topMargin = self.pagesize[1] - inch
+	"""A sample document that uses a single frame on each page.
+	The intention is for programmers to create their own document
+	models as needed.  This one accepts a list of drawables. You
+	can provide callbacks to decorate the first page and
+	subsequent pages; these should do headers, footers, sidebars
+	as needed."""
+	def __init__(self, filename, pagesize, showBoundary=0):
+		self.filename = filename
+		self.pagesize = pagesize
+		self.showBoundary=showBoundary
+		#sensibel defaults; override if you wish
+		self.leftMargin =  inch
+		self.bottomMargin = inch
+		self.rightMargin = self.pagesize[0] - inch
+		self.topMargin = self.pagesize[1] - inch
 
-        # 1-based counting is friendlier for readers
-        self.page = 1
-        #set these to drawing procedures of your own
-        self.onFirstPage = _doNothing
-        self.onNewPage = _doNothing
+		# 1-based counting is friendlier for readers
+		self.page = 1
+		#set these to drawing procedures of your own
+		self.onFirstPage = _doNothing
+		self.onNewPage = _doNothing
 
 
-    def build(self, drawables):
-        canv = canvas.Canvas(self.filename)
-        #canv.setPageTransition('Dissolve')
+	def build(self, drawables):
+		canv = canvas.Canvas(self.filename)
+		#canv.setPageTransition('Dissolve')
 
-        # do page 1
-        self.onFirstPage(canv, self)
-        frame1 = SimpleFrame(
-                    canv,
-                    self.leftMargin,
-                    self.bottomMargin,
-                    self.rightMargin - self.leftMargin,
-                    self.topMargin - inch - self.bottomMargin
-                            )
-        frame1.showBoundary = self.showBoundary
-        frame1.addFromList(drawables)
-        #print 'drew page %d, %d objects remaining' % (self.page, len(drawables))
-        # do subsequent pages
-        while len(drawables) > 0:
-            canv.showPage()
-            self.page = self.page + 1
-            self.onNewPage(canv, self)
-            frame = SimpleFrame(
-                    canv,
-                    self.leftMargin,
-                    self.bottomMargin,
-                    self.rightMargin - self.leftMargin,
-                    self.topMargin - self.bottomMargin
-                            )
-            frame.showBoundary = self.showBoundary
-            frame.addFromList(drawables)
+		# do page 1
+		self.onFirstPage(canv, self)
+		frame1 = SimpleFrame(
+					canv,
+					self.leftMargin,
+					self.bottomMargin,
+					self.rightMargin - self.leftMargin,
+					self.topMargin - inch - self.bottomMargin
+							)
+		frame1.showBoundary = self.showBoundary
+		frame1.addFromList(drawables)
+		#print 'drew page %d, %d objects remaining' % (self.page, len(drawables))
+		# do subsequent pages
+		while len(drawables) > 0:
+			canv.showPage()
+			self.page = self.page + 1
+			self.onNewPage(canv, self)
+			frame = SimpleFrame(
+					canv,
+					self.leftMargin,
+					self.bottomMargin,
+					self.rightMargin - self.leftMargin,
+					self.topMargin - self.bottomMargin
+							)
+			frame.showBoundary = self.showBoundary
+			frame.addFromList(drawables)
 
-            #print 'drew page %d, %d objects remaining' % (self.page, len(drawables))
+			#print 'drew page %d, %d objects remaining' % (self.page, len(drawables))
 
-        canv.save()
-    ##########################################################
-    ##
-    ##   testing
-    ##
-    ##########################################################
+		canv.save()
+	##########################################################
+	##
+	##	 testing
+	##
+	##########################################################
 
 def randomText():
-    #this may or may not be appropriate in your company
-    from random import randint, choice
+	#this may or may not be appropriate in your company
+	from random import randint, choice
 
-    RANDOMWORDS = ['strategic','direction','proactive',
-    'reengineering','forecast','resources',
-    'forward-thinking','profit','growth','doubletalk',
-    'venture capital','IPO']
+	RANDOMWORDS = ['strategic','direction','proactive',
+	'reengineering','forecast','resources',
+	'forward-thinking','profit','growth','doubletalk',
+	'venture capital','IPO']
 
-    sentences = 5
-    output = ""
-    for sentenceno in range(randint(1,5)):
-        output = output + 'Blah'
-        for wordno in range(randint(10,25)):
-            if randint(0,4)==0:
-                word = choice(RANDOMWORDS)
-            else:
-                word = 'blah'
-            output = output + ' ' +word
-        output = output+'.'
-    return output
+	sentences = 5
+	output = ""
+	for sentenceno in range(randint(1,5)):
+		output = output + 'Blah'
+		for wordno in range(randint(10,25)):
+			if randint(0,4)==0:
+				word = choice(RANDOMWORDS)
+			else:
+				word = 'blah'
+			output = output + ' ' +word
+		output = output+'.'
+	return output
 
 
 def myFirstPage(canvas, doc):
-    canvas.saveState()
-    canvas.setStrokeColor(red)
-    canvas.setLineWidth(5)
-    canvas.line(66,72,66,PAGE_HEIGHT-72)
-    canvas.setFont('Times-Bold',24)
-    canvas.drawString(108, PAGE_HEIGHT-108, "PLATYPUS")
-    canvas.setFont('Times-Roman',12)
-    canvas.drawString(4 * inch, 0.75 * inch, "First Page")
-    canvas.restoreState()
+	canvas.saveState()
+	canvas.setStrokeColor(red)
+	canvas.setLineWidth(5)
+	canvas.line(66,72,66,PAGE_HEIGHT-72)
+	canvas.setFont('Times-Bold',24)
+	canvas.drawString(108, PAGE_HEIGHT-108, "PLATYPUS")
+	canvas.setFont('Times-Roman',12)
+	canvas.drawString(4 * inch, 0.75 * inch, "First Page")
+	canvas.restoreState()
 
 def myLaterPages(canvas, doc):
-    #canvas.drawImage("snkanim.gif", 36, 36)
-    canvas.saveState()
-    canvas.setStrokeColor(red)
-    canvas.setLineWidth(5)
-    canvas.line(66,72,66,PAGE_HEIGHT-72)
-    canvas.setFont('Times-Roman',12)
-    canvas.drawString(4 * inch, 0.75 * inch, "Page %d" % doc.page)
-    canvas.restoreState()
+	#canvas.drawImage("snkanim.gif", 36, 36)
+	canvas.saveState()
+	canvas.setStrokeColor(red)
+	canvas.setLineWidth(5)
+	canvas.line(66,72,66,PAGE_HEIGHT-72)
+	canvas.setFont('Times-Roman',12)
+	canvas.drawString(4 * inch, 0.75 * inch, "Page %d" % doc.page)
+	canvas.restoreState()
 
 
 
 def run():
-    objects_to_draw = []
+	objects_to_draw = []
 
-    #need a style
-    normal = ParagraphStyle('normal')
-    normal.firstLineIndent = 18
-    normal.spaceBefore = 6
-    import random
-    for i in range(15):
-        height = 0.5 + (2*random.random())
-        box = XBox(6 * inch, height * inch, 'Box Number %d' % i)
-        objects_to_draw.append(box)
-        para = Paragraph(randomText(), normal)
-        objects_to_draw.append(para)
+	#need a style
+	normal = ParagraphStyle('normal')
+	normal.firstLineIndent = 18
+	normal.spaceBefore = 6
+	import random
+	for i in range(15):
+		height = 0.5 + (2*random.random())
+		box = XBox(6 * inch, height * inch, 'Box Number %d' % i)
+		objects_to_draw.append(box)
+		para = Paragraph(randomText(), normal)
+		objects_to_draw.append(para)
 
 
-    doc = SimpleFlowDocument('platypus.pdf',DEFAULT_PAGE_SIZE)
-    doc.onFirstPage = myFirstPage
-    doc.onNewPage = myLaterPages
-    doc.build(objects_to_draw)
+	doc = SimpleFlowDocument('platypus.pdf',DEFAULT_PAGE_SIZE)
+	doc.onFirstPage = myFirstPage
+	doc.onNewPage = myLaterPages
+	doc.build(objects_to_draw)
 
 
 if __name__ == '__main__':
-    run()
+	run()
--- a/reportlab/platypus/test/testplatypus.py	Wed Apr 12 13:34:33 2000 +0000
+++ b/reportlab/platypus/test/testplatypus.py	Wed Apr 12 16:26:51 2000 +0000
@@ -32,9 +32,12 @@
 #
 ###############################################################################
 #	$Log: testplatypus.py,v $
+#	Revision 1.8  2000/04/12 16:26:51  rgbecker
+#	XML Tagged Paragraph parser changes
+#
 #	Revision 1.7  2000/03/08 13:06:39  andy_robinson
 #	Moved inch and cm definitions to reportlab.lib.units and amended all demos
-#
+#	
 #	Revision 1.6  2000/02/17 02:09:05  rgbecker
 #	Docstring & other fixes
 #	
@@ -50,7 +53,7 @@
 #	Revision 1.2  2000/02/15 15:47:10  rgbecker
 #	Added license, __version__ and Logi comment
 #	
-__version__=''' $Id: testplatypus.py,v 1.7 2000/03/08 13:06:39 andy_robinson Exp $ '''
+__version__=''' $Id: testplatypus.py,v 1.8 2000/04/12 16:26:51 rgbecker Exp $ '''
 
 #tests and documents Page Layout API
 __doc__="""This is not obvious so here's a brief explanation.  This module is both
@@ -61,7 +64,7 @@
 builds a special "document model" in which the frames are added to each page
 and drawn into.
 """
-import string
+import string, copy
 from reportlab.pdfgen import canvas
 from reportlab.platypus import layout, tables
 from reportlab.lib.units import inch, cm
@@ -341,6 +344,22 @@
     p.debug = 1   #show me the borders
     story.append(p)
 
+    story.append(layout.Paragraph("""Same but with justification""", styleSheet['BodyText']))
+    styJ = copy.deepcopy(sty)
+    styJ.alignment = layout.TA_JUSTIFY
+    p = layout.Paragraph("""
+        <font color=red>Platypus</font> is all about fitting objects into frames on the page.  You
+        are looking at a fairly simple Platypus paragraph in Debug mode.
+        It has some gridlines drawn around it to show the left and right indents,
+        and the space before and after, all of which are attributes set in
+        the style sheet.  To be specific, this paragraph has left and
+        right indents of 18 points, a first line indent of 36 points,
+        and 6 points of space before and after itself.  A paragraph
+        object fills the width of the enclosing frame, as you would expect.""", styJ)
+
+    p.debug = 1   #show me the borders
+    story.append(p)
+
     story.append(layout.XBox(4*inch, 1*inch,
             'This is a box with a fixed size'))