[reportlab-users] pythonpoint with Aaron's extended paragraph
Doug
reportlab-users@reportlab.com
Wed, 23 Apr 2003 16:55:12 -0400
This is a multi-part message in MIME format.
------=_NextPart_000_0002_01C309B9.1B4C70A0
Content-Type: text/plain;
charset="windows-1251"
Content-Transfer-Encoding: 7bit
Sorry for the delay. Besides a vacation, I decided to change the implemention.
I added a new tag to the stdparser for pythonpoint. The tag <paratype/> can have
either 'extended' or 'original' as the value of it's 'version' attribute. This version
value will determine which paragraph implementation will be used for <para/> tags
that follow.
I have also made some minor changes to Aaron's FastPara to correspond closer to
the slow one when firstLineIndent is > 0.
Attached you will find files merged to a ReportLab 1.17 version (I think that is the most current) .
I thought I better send these before I have to merge stuff again.
Thanks,
Doug
------=_NextPart_000_0002_01C309B9.1B4C70A0
Content-Type: text/plain;
name="pythonpoint.py"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
filename="pythonpoint.py"
#!/usr/bin/env python
"""
This is PythonPoint!
The idea is a simple markup languages for describing presentation
slides, and other documents which run page by page. I expect most
of it will be reusable in other page layout stuff.
Look at the sample near the top, which shows how the presentation
should be coded up.
The parser, which is in a separate module to allow for multiple
parsers, turns the XML sample into an object tree. There is a
simple class hierarchy of items, the inner levels of which create
flowable objects to go in the frames. These know how to draw
themselves.
The currently available 'Presentation Objects' are:
The main hierarchy...
PPPresentation
PPSection
PPSlide
PPFrame
PPAuthor, PPTitle and PPSubject are optional
Things to flow within frames...
PPPara - flowing text
PPPreformatted - text with line breaks and tabs, for code..
PPImage
PPTable - bulk formatted tabular data
PPSpacer
Things to draw directly on the page...
PPRect
PPRoundRect
PPDrawingElement - user base class for graphics
PPLine
PPEllipse
Features added by H. Turgut Uyar <uyar@cs.itu.edu.tr>
- TrueType support (actually, just an import in the style file);
this also enables the use of Unicode symbols
- para, image, table, line, rectangle, roundrect, ellipse, polygon
and string elements can now have effect attributes
(careful: new slide for each effect!)
- added printout mode (no new slides for effects, see item above)
- added a second-level bullet: Bullet2
- small bugfixes in handleHiddenSlides:
corrected the outlineEntry of included hidden slide
and made sure to include the last slide even if hidden
Recently added features are:
- file globbing
- package structure
- named colors throughout (using names from reportlab/lib/colors.py)
- handout mode with arbitrary number of columns per page
- stripped off pages hidden in the outline tree (hackish)
- new <notes> tag for speaker notes (paragraphs only)
- new <pycode> tag for syntax-colorized Python code
- reformatted pythonpoint.xml and monterey.xml demos
- written/extended DTD
- arbitrary font support
- print proper speaker notes (TODO)
- fix bug with partially hidden graphics (TODO)
- save in combined presentation/handout mode (TODO)
- add pyRXP support (TODO)
"""
import os, sys, imp, string, pprint, getopt, glob
from reportlab import rl_config
from reportlab.lib import styles
from reportlab.lib import colors
from reportlab.lib.units import cm
from reportlab.lib.utils import getStringIO
from reportlab.lib.enums import TA_LEFT, TA_RIGHT, TA_CENTER, TA_JUSTIFY
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfgen import canvas
from reportlab.platypus.doctemplate import SimpleDocTemplate
from reportlab.platypus.flowables import Flowable
from reportlab.platypus.xpreformatted import PythonPreformatted
from reportlab.platypus import Preformatted, Paragraph, Frame, \
Image, Table, TableStyle, Spacer
USAGE_MESSAGE =3D """\
PythonPoint - a tool for making presentations in PDF.
Usage:
pythonpoint.py [options] file1.xml [file2.xml [...]]
where options can be any of these:
-h / --help prints this message
-n / --notes leave room for comments
-v / --verbose verbose mode
-s / --silent silent mode (NO output)
--handout produce handout document
--printout produce printout document
--cols specify number of columns
on handout pages (default: 2)
To create the PythonPoint user guide, do:
pythonpoint.py pythonpoint.xml
"""
#####################################################################
# This should probably go into reportlab/lib/fonts.py...
#####################################################################
class FontNameNotFoundError(Exception):
pass
class FontFilesNotFoundError(Exception):
pass
##def findFontName(path):
## "Extract a Type-1 font name from an AFM file."
##
## f =3D open(path)
##
## found =3D 0
## while not found:
## line =3D f.readline()[:-1]
## if not found and line[:16] =3D=3D 'StartCharMetrics':
## raise FontNameNotFoundError, path
## if line[:8] =3D=3D 'FontName':
## fontName =3D line[9:]
## found =3D 1
##
## return fontName
##
##
##def locateFilesForFontWithName(name):
## "Search known paths for AFM/PFB files describing T1 font with =
given name."
##
## join =3D os.path.join
## splitext =3D os.path.splitext
##
## afmFile =3D None
## pfbFile =3D None
##
## found =3D 0
## while not found:
## for p in rl_config.T1SearchPath:
## afmFiles =3D glob.glob(join(p, '*.[aA][fF][mM]'))
## for f in afmFiles:
## T1name =3D findFontName(f)
## if T1name =3D=3D name:
## afmFile =3D f
## found =3D 1
## break
## if afmFile:
## break
## break
##
## if afmFile:
## pfbFile =3D glob.glob(join(splitext(afmFile)[0] + =
'.[pP][fF][bB]'))[0]
##
## return afmFile, pfbFile
##
##
##def registerFont(name):
## "Register Type-1 font for future use."
##
## rl_config.warnOnMissingFontGlyphs =3D 0
## =
rl_config.T1SearchPath.append(r'C:\Programme\Python21\reportlab\test')
##
## afmFile, pfbFile =3D locateFilesForFontWithName(name)
## if not afmFile and not pfbFile:
## raise FontFilesNotFoundError
##
## T1face =3D pdfmetrics.EmbeddedType1Face(afmFile, pfbFile)
## T1faceName =3D name
## pdfmetrics.registerTypeFace(T1face)
## T1font =3D pdfmetrics.Font(name, T1faceName, 'WinAnsiEncoding')
## pdfmetrics.registerFont(T1font)
def registerFont0(sourceFile, name, path):
"Register Type-1 font for future use, simple version."
rl_config.warnOnMissingFontGlyphs =3D 0
p =3D os.path.join(os.path.dirname(sourceFile), path)
afmFiles =3D glob.glob(p + '.[aA][fF][mM]')
pfbFiles =3D glob.glob(p + '.[pP][fF][bB]')
assert len(afmFiles) =3D=3D len(pfbFiles) =3D=3D 1, =
FontFilesNotFoundError
T1face =3D pdfmetrics.EmbeddedType1Face(afmFiles[0], pfbFiles[0])
T1faceName =3D name
pdfmetrics.registerTypeFace(T1face)
T1font =3D pdfmetrics.Font(name, T1faceName, 'WinAnsiEncoding')
pdfmetrics.registerFont(T1font)
#####################################################################
def checkColor(col):
"Converts a color name to an RGB tuple, if possible."
if type(col) =3D=3D type('') and col in dir(colors):
col =3D getattr(colors, col)
col =3D (col.red, col.green, col.blue)
return col
def handleHiddenSlides(slides):
"""Filters slides from a list of slides.
In a sequence of hidden slides all but the last one are
removed. Also, the slide before the sequence of hidden
ones is removed.
This assumes to leave only those slides in the handout
that also appear in the outline, hoping to reduce se-
quences where each new slide only adds one new line
to a list of items...
"""
itd =3D indicesToDelete =3D map(lambda s:s.outlineEntry =3D=3D None, =
slides)
for i in range(len(itd)-1):
if itd[i] =3D=3D 1:
if itd[i+1] =3D=3D 0:
itd[i] =3D 0
if i > 0 and itd[i-1] =3D=3D 0:
itd[i-1] =3D 1
itd[len(itd)-1] =3D 0
for i in range(len(itd)):
if slides[i].outlineEntry:
curOutlineEntry =3D slides[i].outlineEntry
if itd[i] =3D=3D 1:
slides[i].delete =3D 1
else:
slides[i].outlineEntry =3D curOutlineEntry
slides[i].delete =3D 0
slides =3D filter(lambda s:s.delete =3D=3D 0, slides)
return slides
def makeSlideTable(slides, pageSize, docWidth, numCols):
"""Returns a table containing a collection of SlideWrapper =
flowables.
"""
slides =3D handleHiddenSlides(slides)
# Set table style.
tabStyle =3D TableStyle(
[('GRID', (0,0), (-1,-1), 0.25, colors.black),
('ALIGN', (0,0), (-1,-1), 'CENTRE')
])
# Build table content.
width =3D docWidth/numCols
height =3D width * pageSize[1]/pageSize[0]
matrix =3D []
row =3D []
for slide in slides:
sw =3D SlideWrapper(width, height, slide, pageSize)
if (len(row)) < numCols:
row.append(sw)
else:
matrix.append(row)
row =3D []
row.append(sw)
if len(row) > 0:
for i in range(numCols-len(row)):
row.append('')
matrix.append(row)
# Make Table flowable.
t =3D Table(matrix,
[width + 5]*len(matrix[0]),
[height + 5]*len(matrix))
t.setStyle(tabStyle)
return t
class SlideWrapper(Flowable):
"""A Flowable wrapping a PPSlide object.
"""
def __init__(self, width, height, slide, pageSize):
Flowable.__init__(self)
self.width =3D width
self.height =3D height
self.slide =3D slide
self.pageSize =3D pageSize
def __repr__(self):
return "SlideWrapper(w=3D%s, h=3D%s)" % (self.width, =
self.height)
def draw(self):
"Draw the slide in our relative coordinate system."
slide =3D self.slide
pageSize =3D self.pageSize
canv =3D self.canv
canv.saveState()
canv.scale(self.width/pageSize[0], self.height/pageSize[1])
slide.effectName =3D None
slide.drawOn(self.canv)
canv.restoreState()
class PPPresentation:
def __init__(self):
self.sourceFilename =3D None
self.filename =3D None
self.outDir =3D None
self.description =3D None
self.title =3D None
self.author =3D None
self.subject =3D None
self.notes =3D 0 # different printing mode
self.handout =3D 0 # prints many slides per page
self.printout =3D 0 # remove hidden slides
self.cols =3D 0 # columns per handout page
self.slides =3D []
self.effectName =3D None
self.showOutline =3D 1 #should it be displayed when opening?
self.compression =3D rl_config.pageCompression
self.pageDuration =3D None
#assume landscape
self.pageWidth =3D rl_config.defaultPageSize[1]
self.pageHeight =3D rl_config.defaultPageSize[0]
self.verbose =3D rl_config.verbose
def saveAsPresentation(self):
"""Write the PDF document, one slide per page."""
if self.verbose:
print 'saving presentation...'
pageSize =3D (self.pageWidth, self.pageHeight)
if self.sourceFilename:
filename =3D os.path.splitext(self.sourceFilename)[0] + =
'.pdf'
if self.outDir: filename =3D =
os.path.join(self.outDir,os.path.basename(filename))
if self.verbose:
print filename
#canv =3D canvas.Canvas(filename, pagesize =3D pageSize)
outfile =3D getStringIO()
canv =3D canvas.Canvas(outfile, pagesize =3D pageSize)
canv.setPageCompression(self.compression)
canv.setPageDuration(self.pageDuration)
if self.title:
canv.setTitle(self.title)
if self.author:
canv.setAuthor(self.author)
if self.subject:
canv.setSubject(self.subject)
slideNo =3D 0
for slide in self.slides:
#need diagnostic output if something wrong with XML
slideNo =3D slideNo + 1
if self.verbose:
print 'doing slide %d, id =3D %s' % (slideNo, slide.id)
if self.notes:
#frame and shift the slide
canv.scale(0.67, 0.67)
canv.translate(self.pageWidth / 6.0, self.pageHeight / =
3.0)
canv.rect(0,0,self.pageWidth, self.pageHeight)
slide.drawOn(canv)
canv.showPage()
#ensure outline visible by default
if self.showOutline:
canv.showOutline()
canv.save()
return self.savetofile(outfile, filename)
def saveAsHandout(self):
"""Write the PDF document, multiple slides per page."""
styleSheet =3D getSampleStyleSheet()
h1 =3D styleSheet['Heading1']
bt =3D styleSheet['BodyText']
if self.sourceFilename :
filename =3D os.path.splitext(self.sourceFilename)[0] + =
'.pdf'
outfile =3D getStringIO()
doc =3D SimpleDocTemplate(outfile, =
pagesize=3Drl_config.defaultPageSize, showBoundary=3D0)
doc.leftMargin =3D 1*cm
doc.rightMargin =3D 1*cm
doc.topMargin =3D 2*cm
doc.bottomMargin =3D 2*cm
multiPageWidth =3D rl_config.defaultPageSize[0] - doc.leftMargin =
- doc.rightMargin - 50
story =3D []
orgFullPageSize =3D (self.pageWidth, self.pageHeight)
t =3D makeSlideTable(self.slides, orgFullPageSize, =
multiPageWidth, self.cols)
story.append(t)
## #ensure outline visible by default
## if self.showOutline:
## doc.canv.showOutline()
doc.build(story)
return self.savetofile(outfile, filename)
def savetofile(self, pseudofile, filename):
"""Save the pseudo file to disk and return its content as a
string of text."""
pseudofile.flush()
content =3D pseudofile.getvalue()
pseudofile.close()
if filename :
outf =3D open(filename, "wb")
outf.write(content)
outf.close()
return content
def save(self):
"Save the PDF document."
if self.handout:
return self.saveAsHandout()
else:
return self.saveAsPresentation()
#class PPSection:
# """A section can hold graphics which will be drawn on all
# pages within it, before frames and other content are done.
# In other words, a background template."""
# def __init__(self, name):
# self.name =3D name
# self.graphics =3D []
#
# def drawOn(self, canv):
# for graphic in self.graphics:
### graphic.drawOn(canv)
#
# name =3D str(hash(graphic))
# internalname =3D canv._doc.hasForm(name)
#
# canv.saveState()
# if not internalname:
# canv.beginForm(name)
# graphic.drawOn(canv)
# canv.endForm()
# canv.doForm(name)
# else:
# canv.doForm(name)
# canv.restoreState()
definedForms =3D {}
class PPSection:
"""A section can hold graphics which will be drawn on all
pages within it, before frames and other content are done.
In other words, a background template."""
def __init__(self, name):
self.name =3D name
self.graphics =3D []
def drawOn(self, canv):
for graphic in self.graphics:
graphic.drawOn(canv)
continue
name =3D str(hash(graphic))
#internalname =3D canv._doc.hasForm(name)
if definedForms.has_key(name):
internalname =3D 1
else:
internalname =3D None
definedForms[name] =3D 1
if not internalname:
canv.beginForm(name)
canv.saveState()
graphic.drawOn(canv)
canv.restoreState()
canv.endForm()
canv.doForm(name)
else:
canv.doForm(name)
class PPNotes:
def __init__(self):
self.content =3D []
def drawOn(self, canv):
print self.content
class PPSlide:
def __init__(self):
self.id =3D None
self.title =3D None
self.outlineEntry =3D None
self.outlineLevel =3D 0 # can be higher for sub-headings
self.effectName =3D None
self.effectDirection =3D 0
self.effectDimension =3D 'H'
self.effectMotion =3D 'I'
self.effectDuration =3D 1
self.frames =3D []
self.notes =3D []
self.graphics =3D []
self.section =3D None
def drawOn(self, canv):
if self.effectName:
canv.setPageTransition(
effectname=3Dself.effectName,
direction =3D self.effectDirection,
dimension =3D self.effectDimension,
motion =3D self.effectMotion,
duration =3D self.effectDuration
)
if self.outlineEntry:
#gets an outline automatically
self.showOutline =3D 1
#put an outline entry in the left pane
tag =3D self.title
canv.bookmarkPage(tag)
canv.addOutlineEntry(tag, tag, self.outlineLevel)
if self.section:
self.section.drawOn(canv)
for graphic in self.graphics:
graphic.drawOn(canv)
for frame in self.frames:
frame.drawOn(canv)
## # Need to draw the notes *somewhere*...
## for note in self.notes:
## print note
class PPFrame:
def __init__(self, x, y, width, height):
self.x =3D x
self.y =3D y
self.width =3D width
self.height =3D height
self.content =3D []
self.showBoundary =3D 0
def drawOn(self, canv):
#make a frame
frame =3D Frame( self.x,
self.y,
self.width,
self.height
)
frame.showBoundary =3D self.showBoundary
#build a story for the frame
story =3D []
for thingy in self.content:
#ask it for any flowables
story.append(thingy.getFlowable())
#draw it
frame.addFromList(story,canv)
class PPPara:
"""This is a placeholder for a paragraph."""
def __init__(self):
self.rawtext =3D ''
self.style =3D None
def getFlowable(self):
p =3D Paragraph(
self.rawtext,
getStyles()[self.style],
self.bulletText
)
return p
class PPParax:
"""This is a placeholder for the experimental para paragraph =
module."""
def __init__(self):
self.rawtext =3D ''
self.style =3D None
self.bulletText =3D None
self.contextlist =3D []
self.context =3D {}
def getFlowable(self):
from reportlab.platypus.para import FastPara, Para
# if there is no & or < in text then use the fast paragraph
if "&" not in self.rawtext and "<" not in self.rawtext:
return FastPara(getStyles()[self.style], =
simpletext=3Dself.rawtext)
else:
# use the fully featured one.
from reportlab.lib import rparsexml
parsedpara =3D rparsexml.parsexmlSimple(self.rawtext)
for item in self.contextlist:
self.context[item] =3D getStyles()[item]
self.context['para.defaultStyle'] =3D getStyles()['Normal']
self.context['li.defaultStyle' ] =3D getStyles()['Normal'] =
=20
return Para(getStyles()[self.style], =
parsedText=3Dparsedpara,
bulletText=3Dself.bulletText, state=3DNone, =
context=3Dself.context )
class PPPreformattedText:
"""Use this for source code, or stuff you do not want to wrap"""
def __init__(self):
self.rawtext =3D ''
self.style =3D None
def getFlowable(self):
return Preformatted(self.rawtext, getStyles()[self.style])
class PPPythonCode:
"""Use this for colored Python source code"""
def __init__(self):
self.rawtext =3D ''
self.style =3D None
def getFlowable(self):
return PythonPreformatted(self.rawtext, getStyles()[self.style])
class PPImage:
"""Flowing image within the text"""
def __init__(self):
self.filename =3D None
self.width =3D None
self.height =3D None
def getFlowable(self):
return Image(self.filename, self.width, self.height)
class PPTable:
"""Designed for bulk loading of data for use in presentations."""
def __init__(self):
self.rawBlocks =3D [] #parser stuffs things in here...
self.fieldDelim =3D ',' #tag args can override
self.rowDelim =3D '\n' #tag args can override
self.data =3D None
self.style =3D None #tag args must specify
self.widths =3D None #tag args can override
self.heights =3D None #tag args can override
def getFlowable(self):
self.parseData()
t =3D Table(
self.data,
self.widths,
self.heights)
if self.style:
t.setStyle(getStyles()[self.style])
return t
def parseData(self):
"""Try to make sense of the table data!"""
rawdata =3D string.strip(string.join(self.rawBlocks, ''))
lines =3D string.split(rawdata, self.rowDelim)
#clean up...
lines =3D map(string.strip, lines)
self.data =3D []
for line in lines:
cells =3D string.split(line, self.fieldDelim)
self.data.append(cells)
#get the width list if not given
if not self.widths:
self.widths =3D [None] * len(self.data[0])
if not self.heights:
self.heights =3D [None] * len(self.data)
## import pprint
## print 'table data:'
## print 'style=3D',self.style
## print 'widths=3D',self.widths
## print 'heights=3D',self.heights
## print 'fieldDelim=3D',repr(self.fieldDelim)
## print 'rowDelim=3D',repr(self.rowDelim)
## pprint.pprint(self.data)
class PPSpacer:
def __init__(self):
self.height =3D 24 #points
def getFlowable(self):
return Spacer(72, self.height)
#############################################################
#
# The following are things you can draw on a page directly.
#
##############################################################
##class PPDrawingElement:
## """Base class for something which you draw directly on the =
page."""
## def drawOn(self, canv):
## raise "NotImplementedError", "Abstract base class!"
class PPFixedImage:
"""You place this on the page, rather than flowing it"""
def __init__(self):
self.filename =3D None
self.x =3D 0
self.y =3D 0
self.width =3D None
self.height =3D None
def drawOn(self, canv):
if self.filename:
x, y =3D self.x, self.y
w, h =3D self.width, self.height
canv.drawImage(self.filename, x, y, w, h)
class PPRectangle:
def __init__(self, x, y, width, height):
self.x =3D x
self.y =3D y
self.width =3D width
self.height =3D height
self.fillColor =3D None
self.strokeColor =3D (1,1,1)
self.lineWidth=3D0
def drawOn(self, canv):
canv.saveState()
canv.setLineWidth(self.lineWidth)
if self.fillColor:
r,g,b =3D checkColor(self.fillColor)
canv.setFillColorRGB(r,g,b)
if self.strokeColor:
r,g,b =3D checkColor(self.strokeColor)
canv.setStrokeColorRGB(r,g,b)
canv.rect(self.x, self.y, self.width, self.height,
stroke=3D(self.strokeColor<>None),
fill =3D (self.fillColor<>None)
)
canv.restoreState()
class PPRoundRect:
def __init__(self, x, y, width, height, radius):
self.x =3D x
self.y =3D y
self.width =3D width
self.height =3D height
self.radius =3D radius
self.fillColor =3D None
self.strokeColor =3D (1,1,1)
self.lineWidth=3D0
def drawOn(self, canv):
canv.saveState()
canv.setLineWidth(self.lineWidth)
if self.fillColor:
r,g,b =3D checkColor(self.fillColor)
canv.setFillColorRGB(r,g,b)
if self.strokeColor:
r,g,b =3D checkColor(self.strokeColor)
canv.setStrokeColorRGB(r,g,b)
canv.roundRect(self.x, self.y, self.width, self.height,
self.radius,
stroke=3D(self.strokeColor<>None),
fill =3D (self.fillColor<>None)
)
canv.restoreState()
class PPLine:
def __init__(self, x1, y1, x2, y2):
self.x1 =3D x1
self.y1 =3D y1
self.x2 =3D x2
self.y2 =3D y2
self.fillColor =3D None
self.strokeColor =3D (1,1,1)
self.lineWidth=3D0
def drawOn(self, canv):
canv.saveState()
canv.setLineWidth(self.lineWidth)
if self.strokeColor:
r,g,b =3D checkColor(self.strokeColor)
canv.setStrokeColorRGB(r,g,b)
canv.line(self.x1, self.y1, self.x2, self.y2)
canv.restoreState()
class PPEllipse:
def __init__(self, x1, y1, x2, y2):
self.x1 =3D x1
self.y1 =3D y1
self.x2 =3D x2
self.y2 =3D y2
self.fillColor =3D None
self.strokeColor =3D (1,1,1)
self.lineWidth=3D0
def drawOn(self, canv):
canv.saveState()
canv.setLineWidth(self.lineWidth)
if self.strokeColor:
r,g,b =3D checkColor(self.strokeColor)
canv.setStrokeColorRGB(r,g,b)
if self.fillColor:
r,g,b =3D checkColor(self.fillColor)
canv.setFillColorRGB(r,g,b)
canv.ellipse(self.x1, self.y1, self.x2, self.y2,
stroke=3D(self.strokeColor<>None),
fill =3D (self.fillColor<>None)
)
canv.restoreState()
class PPPolygon:
def __init__(self, pointlist):
self.points =3D pointlist
self.fillColor =3D None
self.strokeColor =3D (1,1,1)
self.lineWidth=3D0
def drawOn(self, canv):
canv.saveState()
canv.setLineWidth(self.lineWidth)
if self.strokeColor:
r,g,b =3D checkColor(self.strokeColor)
canv.setStrokeColorRGB(r,g,b)
if self.fillColor:
r,g,b =3D checkColor(self.fillColor)
canv.setFillColorRGB(r,g,b)
path =3D canv.beginPath()
(x,y) =3D self.points[0]
path.moveTo(x,y)
for (x,y) in self.points[1:]:
path.lineTo(x,y)
path.close()
canv.drawPath(path,
stroke=3D(self.strokeColor<>None),
fill=3D(self.fillColor<>None))
canv.restoreState()
class PPString:
def __init__(self, x, y):
self.text =3D ''
self.x =3D x
self.y =3D y
self.align =3D TA_LEFT
self.font =3D 'Times-Roman'
self.size =3D 12
self.color =3D (0,0,0)
self.hasInfo =3D 0 # these can have data substituted into them
def normalizeText(self):
"""It contains literal XML text typed over several lines.
We want to throw away
tabs, newlines and so on, and only accept embedded string
like '\n'"""
lines =3D string.split(self.text, '\n')
newtext =3D []
for line in lines:
newtext.append(string.strip(line))
#accept all the '\n' as newlines
self.text =3D newtext
def drawOn(self, canv):
# for a string in a section, this will be drawn several times;
# so any substitution into the text should be in a temporary
# variable
if self.hasInfo:
# provide a dictionary of stuff which might go into
# the string, so they can number pages, do headers
# etc.
info =3D {}
info['title'] =3D canv._doc.info.title
info['author'] =3D canv._doc.info.author
info['subject'] =3D canv._doc.info.subject
info['page'] =3D canv.getPageNumber()
drawText =3D self.text % info
else:
drawText =3D self.text
if self.color is None:
return
lines =3D string.split(string.strip(drawText), '\\n')
canv.saveState()
canv.setFont(self.font, self.size)
r,g,b =3D checkColor(self.color)
canv.setFillColorRGB(r,g,b)
cur_y =3D self.y
for line in lines:
if self.align =3D=3D TA_LEFT:
canv.drawString(self.x, cur_y, line)
elif self.align =3D=3D TA_CENTER:
canv.drawCentredString(self.x, cur_y, line)
elif self.align =3D=3D TA_RIGHT:
canv.drawRightString(self.x, cur_y, line)
cur_y =3D cur_y - 1.2*self.size
canv.restoreState()
def getSampleStyleSheet():
from reportlab.tools.pythonpoint.styles.standard import =
getParagraphStyles
return getParagraphStyles()
#make a singleton and a function to access it
_styles =3D None
def getStyles():
global _styles
if not _styles:
_styles =3D getSampleStyleSheet()
return _styles
def setStyles(newStyleSheet):
global _styles
_styles =3D newStyleSheet
##def test():
## p =3D stdparser.PPMLParser()
## p.feed(sample)
## p.getPresentation().save()
## p.close()
def process(datafile, notes=3D0, handout=3D0, printout=3D0, cols=3D0, =
verbose=3D0, outDir=3DNone, datafilename=3DNone):
"Process one PythonPoint source file."
if not hasattr(datafile, "read"):
if not datafilename: datafilename =3D datafile
datafile =3D open(datafile)
else :
if not datafilename: datafilename =3D "PseudoFile"
rawdata =3D datafile.read()
return _process(rawdata, datafilename, notes, handout, printout, =
cols, verbose, outDir)
def _process(rawdata, datafilename, notes=3D0, handout=3D0, =
printout=3D0, cols=3D0, verbose=3D0, outDir=3DNone):
from reportlab.tools.pythonpoint.stdparser import PPMLParser
parser =3D PPMLParser()
parser.sourceFilename =3D datafilename
parser.feed(rawdata)
pres =3D parser.getPresentation()
pres.sourceFilename =3D datafilename
pres.outDir =3D outDir
pres.notes =3D notes
pres.handout =3D handout
pres.printout =3D printout
pres.cols =3D cols
pres.verbose =3D verbose
if printout:
pres.slides =3D handleHiddenSlides(pres.slides)
#this does all the work
pdfcontent =3D pres.save()
if verbose:
print 'saved presentation %s.pdf' % =
os.path.splitext(datafilename)[0]
parser.close()
return pdfcontent
##class P:
## def feed(self, text):
## parser =3D stdparser.PPMLParser()
## d =3D pyRXP.parse(text)
##
##
##def process2(datafilename, notes=3D0, handout=3D0, cols=3D0):
## "Process one PythonPoint source file."
##
## import pyRXP, pprint
##
## rawdata =3D open(datafilename).read()
## d =3D pyRXP.parse(rawdata)
## pprint.pprint(d)
def handleOptions():
# set defaults
from reportlab import rl_config
options =3D {'cols':2,
'handout':0,
'printout':0,
'help':0,
'notes':0,
'verbose':rl_config.verbose,
'silent':0,
'outDir': None}
args =3D sys.argv[1:]
args =3D filter(lambda x: x and x[0]=3D=3D'-',args) + filter(lambda =
x: not x or x[0]!=3D'-',args)
try:
shortOpts =3D 'hnvs'
longOpts =3D string.split('cols=3D outdir=3D handout help notes =
printout verbose silent')
optList, args =3D getopt.getopt(args, shortOpts, longOpts)
except getopt.error, msg:
options['help'] =3D 1
if not args and os.path.isfile('pythonpoint.xml'):
args =3D ['pythonpoint.xml']
# Remove leading dashes (max. two).
for i in range(len(optList)):
o, v =3D optList[i]
while o[0] =3D=3D '-':
o =3D o[1:]
optList[i] =3D (o, v)
if o =3D=3D 'cols': options['cols'] =3D int(v)
elif o=3D=3D'outdir': options['outDir'] =3D v
if filter(lambda ov: ov[0] =3D=3D 'handout', optList):
options['handout'] =3D 1
if filter(lambda ov: ov[0] =3D=3D 'printout', optList):
options['printout'] =3D 1
if optList =3D=3D [] and args =3D=3D [] or \
filter(lambda ov: ov[0] in ('h', 'help'), optList):
options['help'] =3D 1
if filter(lambda ov: ov[0] in ('n', 'notes'), optList):
options['notes'] =3D 1
if filter(lambda ov: ov[0] in ('v', 'verbose'), optList):
options['verbose'] =3D 1
#takes priority over verbose. Used by our test suite etc.
#to ensure no output at all
if filter(lambda ov: ov[0] in ('s', 'silent'), optList):
options['silent'] =3D 1
options['verbose'] =3D 0
return options, args
def main():
options, args =3D handleOptions()
if options['help']:
print USAGE_MESSAGE
sys.exit(0)
if options['verbose'] and options['notes']:
print 'speaker notes mode'
if options['verbose'] and options['handout']:
print 'handout mode'
if options['verbose'] and options['printout']:
print 'printout mode'
for fileGlobs in args:
files =3D glob.glob(fileGlobs)
for datafile in files:
if os.path.isfile(datafile):
file =3D os.path.join(os.getcwd(), datafile)
notes, handout, printout, cols, verbose =3D =
options['notes'], options['handout'], options['printout'], =
options['cols'], options['verbose']
process(file, notes, handout, printout, cols, verbose, =
options['outDir'])
else:
print 'Data file not found:', datafile
if __name__ =3D=3D '__main__':
main()
------=_NextPart_000_0002_01C309B9.1B4C70A0
Content-Type: text/plain;
name="stdparser.py"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
filename="stdparser.py"
"""
Parser for PythonPoint using the xmllib.py in the standard Python
distribution. Slow, but always present. We intend to add new parsers
as Python 2.x and the XML package spread in popularity and stabilise.
The parser has a getPresentation method; it is called from
pythonpoint.py.
"""
import string, imp, os, copy
from reportlab.lib import xmllib
from reportlab.lib import colors
from reportlab.lib.enums import TA_LEFT, TA_RIGHT, TA_CENTER, TA_JUSTIFY
from reportlab.tools.pythonpoint import pythonpoint
def getModule(modulename,fromPath='reportlab.tools.pythonpoint.styles'):
"""Get a module containing style declarations.
Search order is:
reportlab/tools/pythonpoint/
reportlab/tools/pythonpoint/styles/
./
"""
try:
exec 'from reportlab.tools.pythonpoint import '+modulename
return eval(modulename)
except ImportError:
try:
exec 'from reportlab.tools.pythonpoint.styles import '+modulename
return eval(modulename)
except ImportError:
exec 'import '+modulename
return eval(modulename)
class PPMLParser(xmllib.XMLParser):
attributes = {
#this defines the available attributes for all objects,
#and their default values. Although these don't have to
#be strings, the ones parsed from the XML do, so
#everything is a quoted string and the parser has to
#convert these to numbers where appropriate.
'stylesheet': {
'path':'None',
'module':'None',
'function':'getParagraphStyles'
},
'frame': {
'x':'0',
'y':'0',
'width':'0',
'height':'0',
'border':'false',
'leftmargin':'0', #this is ignored
'topmargin':'0', #this is ignored
'rightmargin':'0', #this is ignored
'bottommargin':'0', #this is ignored
},
'slide': {
'id':'None',
'title':'None',
'effectname':'None', # Split, Blinds, Box, Wipe, Dissolve, Glitter
'effectdirection':'0', # 0,90,180,270
'effectdimension':'H', # H or V - horizontal or vertical
'effectmotion':'I', # Inwards or Outwards
'effectduration':'1', #seconds,
'outlineentry':'None',
'outlinelevel':'0' # 1 is a child, 2 is a grandchild etc.
},
'para': {
'style':'Normal',
'bullettext':'',
'effectname':'None',
'effectdirection':'0',
'effectdimension':'H',
'effectmotion':'I',
'effectduration':'1'
},
'image': {
'filename':'',
'width':'None',
'height':'None',
'effectname':'None',
'effectdirection':'0',
'effectdimension':'H',
'effectmotion':'I',
'effectduration':'1'
},
'table': {
'widths':'None',
'heights':'None',
'fieldDelim':',',
'rowDelim':'\n',
'style':'None',
'effectname':'None',
'effectdirection':'0',
'effectdimension':'H',
'effectmotion':'I',
'effectduration':'1'
},
'rectangle': {
'x':'0',
'y':'0',
'width':'100',
'height':'100',
'fill':'None',
'stroke':'(0,0,0)',
'linewidth':'0',
'effectname':'None',
'effectdirection':'0',
'effectdimension':'H',
'effectmotion':'I',
'effectduration':'1'
},
'roundrect': {
'x':'0',
'y':'0',
'width':'100',
'height':'100',
'radius':'6',
'fill':'None',
'stroke':'(0,0,0)',
'linewidth':'0',
'effectname':'None',
'effectdirection':'0',
'effectdimension':'H',
'effectmotion':'I',
'effectduration':'1'
},
'line': {
'x1':'0',
'y1':'0',
'x2':'100',
'y2':'100',
'stroke':'(0,0,0)',
'width':'0',
'effectname':'None',
'effectdirection':'0',
'effectdimension':'H',
'effectmotion':'I',
'effectduration':'1'
},
'ellipse': {
'x1':'0',
'y1':'0',
'x2':'100',
'y2':'100',
'stroke':'(0,0,0)',
'fill':'None',
'linewidth':'0',
'effectname':'None',
'effectdirection':'0',
'effectdimension':'H',
'effectmotion':'I',
'effectduration':'1'
},
'polygon': {
'points':'(0,0),(50,0),(25,25)',
'stroke':'(0,0,0)',
'linewidth':'0',
'stroke':'(0,0,0)',
'fill':'None',
'effectname':'None',
'effectdirection':'0',
'effectdimension':'H',
'effectmotion':'I',
'effectduration':'1'
},
'string':{
'x':'0',
'y':'0',
'color':'(0,0,0)',
'font':'Times-Roman',
'size':'12',
'align':'left',
'effectname':'None',
'effectdirection':'0',
'effectdimension':'H',
'effectmotion':'I',
'effectduration':'1'
},
'customshape':{
'path':'None',
'module':'None',
'class':'None',
'initargs':'None'
}
}
def __init__(self):
self.presentations = []
#yes, I know a generic stack would be easier...
#still, testing if we are 'in' something gives
#a degree of validation.
self._curPres = None
self._curSection = None
self._curSlide = None
self._curFrame = None
self._curPara = None #the only places we are interested in
self._curPrefmt = None
self._curPyCode = None
self._curString = None
self._curTable = None
self._curTitle = None
self._curAuthor = None
self._curSubject = None
self._curParaType = None
self._curParaCount = 0
xmllib.XMLParser.__init__(self)
def _arg(self,tag,args,name):
if args.has_key(name):
v = args[name]
else:
if self.attributes.has_key(tag):
v = self.attributes[tag][name]
else:
v = None
return v
def ceval(self,tag,args,name):
if args.has_key(name):
v = args[name]
else:
if self.attributes.has_key(tag):
v = self.attributes[tag][name]
else:
return None
# handle named colors (names from reportlab.lib.colors)
if name in ('color', 'stroke', 'fill'):
v = str(pythonpoint.checkColor(v))
return eval(v)
def getPresentation(self):
return self._curPres
def handle_data(self, data):
#the only data should be paragraph text, preformatted para
#text, 'string text' for a fixed string on the page,
#or table data
if self._curPara:
self._curPara.rawtext = self._curPara.rawtext + data
elif self._curPrefmt:
self._curPrefmt.rawtext = self._curPrefmt.rawtext + data
elif self._curPyCode:
self._curPyCode.rawtext = self._curPyCode.rawtext + data
elif self._curString:
self._curString.text = self._curString.text + data
elif self._curTable:
self._curTable.rawBlocks.append(data)
elif self._curTitle <> None: # need to allow empty strings,
# hence explicitly testing for None
self._curTitle = self._curTitle + data
elif self._curAuthor <> None:
self._curAuthor = self._curAuthor + data
elif self._curSubject <> None:
self._curSubject = self._curSubject + data
def handle_cdata(self, data):
#just append to current paragraph text, so we can quote XML
if self._curPara:
self._curPara.rawtext = self._curPara.rawtext + data
elif self._curPrefmt:
self._curPrefmt.rawtext = self._curPrefmt.rawtext + data
elif self._curPyCode:
self._curPyCode.rawtext = self._curPyCode.rawtext + data
elif self._curString:
self._curString.text = self._curString.text + data
elif self._curTable:
self._curTable.rawBlocks.append(data)
elif self._curAuthor <> None:
self._curAuthor = self._curAuthor + data
elif self._curSubject <> None:
self._curSubject = self._curSubject + data
def start_presentation(self, args):
self._curPres = pythonpoint.PPPresentation()
self._curPres.filename = self._arg('presentation',args,'filename')
self._curPres.effectName = self._arg('presentation',args,'effect')
self._curPres.pageDuration = self._arg('presentation',args,'pageDuration')
def end_presentation(self):
pass
## print 'Fully parsed presentation',self._curPres.filename
def start_title(self, args):
self._curTitle = ''
def end_title(self):
self._curPres.title = self._curTitle
self._curTitle = None
def start_author(self, args):
self._curAuthor = ''
def end_author(self):
self._curPres.author = self._curAuthor
self._curAuthor = None
def start_subject(self, args):
self._curSubject = ''
def end_subject(self):
self._curPres.subject = self._curSubject
self._curSubject = None
def start_stylesheet(self, args):
#makes it the current style sheet.
path = self._arg('stylesheet',args,'path')
if path=='None':
path = []
path.append('styles')
modulename = self._arg('stylesheet', args, 'module')
funcname = self._arg('stylesheet', args, 'function')
try:
found = imp.find_module(modulename, path)
(file, pathname, description) = found
mod = imp.load_module(modulename, file, pathname, description)
except ImportError:
#last gasp
mod = getModule(modulename)
#now get the function
func = getattr(mod, funcname)
pythonpoint.setStyles(func())
## print 'set global stylesheet to %s.%s()' % (modulename, funcname)
def end_stylesheet(self):
pass
def start_section(self, args):
name = self._arg('section',args,'name')
self._curSection = pythonpoint.PPSection(name)
def end_section(self):
self._curSection = None
def start_slide(self, args):
s = pythonpoint.PPSlide()
s.id = self._arg('slide',args,'id')
s.title = self._arg('slide',args,'title')
a = self._arg('slide',args,'effectname')
if a <> 'None':
s.effectName = a
s.effectDirection = self.ceval('slide',args,'effectdirection')
s.effectDimension = self._arg('slide',args,'effectdimension')
s.effectDuration = self.ceval('slide',args,'effectduration')
s.effectMotion = self._arg('slide',args,'effectmotion')
#HACK - may not belong here in the long run...
#by default, use the slide title for the outline entry,
#unless it is specified as an arg.
a = self._arg('slide',args,'outlineentry')
if a == "Hide":
s.outlineEntry = None
elif a <> 'None':
s.outlineEntry = a
else:
s.outlineEntry = s.title
s.outlineLevel = self.ceval('slide',args,'outlinelevel')
#let it know its section, which may be none
s.section = self._curSection
self._curSlide = s
def end_slide(self):
self._curPres.slides.append(self._curSlide)
self._curSlide = None
def start_frame(self, args):
self._curFrame = pythonpoint.PPFrame(
self.ceval('frame',args,'x'),
self.ceval('frame',args,'y'),
self.ceval('frame',args,'width'),
self.ceval('frame',args,'height')
)
if self._arg('frame',args,'border')=='true':
self._curFrame.showBoundary = 1
def end_frame(self):
self._curSlide.frames.append(self._curFrame)
self._curFrame = None
def start_notes(self, args):
name = self._arg('notes',args,'name')
self._curNotes = pythonpoint.PPNotes()
def end_notes(self):
self._curSlide.notes.append(self._curNotes)
self._curNotes = None
def start_registerFont(self, args):
name = self._arg('font',args,'name')
path = self._arg('font',args,'path')
pythonpoint.registerFont0(self.sourceFilename, name, path)
def end_registerFont(self):
pass
def pack_slide(self, element, args):
effectName = self._arg(element,args,'effectname')
if effectName <> 'None':
curSlide = copy.deepcopy(self._curSlide)
if self._curFrame:
curFrame = copy.deepcopy(self._curFrame)
curSlide.frames.append(curFrame)
self._curPres.slides.append(curSlide)
self._curSlide.effectName = effectName
self._curSlide.effectDirection = self.ceval(element,args,'effectdirection')
self._curSlide.effectDimension = self._arg(element,args,'effectdimension')
self._curSlide.effectDuration = self.ceval(element,args,'effectduration')
self._curSlide.effectMotion = self._arg(element,args,'effectmotion')
self._curSlide.outlineEntry = None
def start_paratype(self, args):
if self._arg('paratype',args,'version')=='extended':
self._curParaType = 1
elif self._arg('paratype',args,'version')=='original':
self._curParaType = None
pass
def end_paratype(self):
pass
def start_para(self, args):
if self._curParaType:
self._curParaCount = self._curParaCount + 1
if self._curParaCount == 1:
self._curPara = pythonpoint.PPParax() #use Aaron's extended paragraph
elif self._curParaCount > 1:
self.unknown_starttag('para', args) #append embedded para tags to rawtext
self._curPara.contextlist.append(self._arg('para',args,'style'))
else:
self.pack_slide('para', args)
self._curPara = pythonpoint.PPPara()
if self._curParaCount <= 1:
self._curPara.style = self._arg('para',args,'style')
# hack - bullet character if bullet style
bt = self._arg('para',args,'bullettext')
if bt == '':
if self._curPara.style == 'Bullet':
bt = '\267' # Symbol Font bullet character, reasonable default
elif self._curPara.style == 'Bullet2':
bt = '\267' # second-level bullet
else:
bt = None
self._curPara.bulletText = bt
def end_para(self):
if self._curParaCount > 1:
self._curParaCount = self._curParaCount - 1
self.unknown_endtag('para') #append embedded para tags to rawtext
else:
self._curParaCount = 0
if self._curFrame:
self._curFrame.content.append(self._curPara)
self._curPara = None
elif self._curNotes:
self._curNotes.content.append(self._curPara)
self._curPara = None
def start_prefmt(self, args):
self._curPrefmt = pythonpoint.PPPreformattedText()
self._curPrefmt.style = self._arg('prefmt',args,'style')
def end_prefmt(self):
self._curFrame.content.append(self._curPrefmt)
self._curPrefmt = None
def start_pycode(self, args):
self._curPyCode = pythonpoint.PPPythonCode()
self._curPyCode.style = self._arg('pycode',args,'style')
def end_pycode(self):
self._curFrame.content.append(self._curPyCode)
self._curPyCode = None
def start_image(self, args):
self.pack_slide('image',args)
sourceFilename = self.sourceFilename # XXX
filename = self._arg('image',args,'filename')
filename = os.path.join(os.path.dirname(sourceFilename), filename)
self._curImage = pythonpoint.PPImage()
self._curImage.filename = filename
self._curImage.width = self.ceval('image',args,'width')
self._curImage.height = self.ceval('image',args,'height')
def end_image(self):
self._curFrame.content.append(self._curImage)
self._curImage = None
def start_table(self, args):
self.pack_slide('table',args)
self._curTable = pythonpoint.PPTable()
self._curTable.widths = self.ceval('table',args,'widths')
self._curTable.heights = self.ceval('table',args,'heights')
#these may contain escapes like tabs - handle with
#a bit more care.
if args.has_key('fieldDelim'):
self._curTable.fieldDelim = eval('"' + args['fieldDelim'] + '"')
if args.has_key('rowDelim'):
self._curTable.rowDelim = eval('"' + args['rowDelim'] + '"')
if args.has_key('style'):
self._curTable.style = args['style']
def end_table(self):
self._curFrame.content.append(self._curTable)
self._curTable = None
def start_spacer(self, args):
"""No contents so deal with it here."""
sp = pythonpoint.PPSpacer()
sp.height = eval(args['height'])
self._curFrame.content.append(sp)
def end_spacer(self):
pass
## the graphics objects - go into either the current section
## or the current slide.
def start_fixedimage(self, args):
sourceFilename = self.sourceFilename
filename = self._arg('image',args,'filename')
filename = os.path.join(os.path.dirname(sourceFilename), filename)
img = pythonpoint.PPFixedImage()
img.filename = filename
img.x = self.ceval('fixedimage',args,'x')
img.y = self.ceval('fixedimage',args,'y')
img.width = self.ceval('fixedimage',args,'width')
img.height = self.ceval('fixedimage',args,'height')
self._curFixedImage = img
def end_fixedimage(self):
if self._curSlide:
self._curSlide.graphics.append(self._curFixedImage)
elif self._curSection:
self._curSection.graphics.append(self._curFixedImage)
self._curFixedImage = None
def start_rectangle(self, args):
self.pack_slide('rectangle', args)
rect = pythonpoint.PPRectangle(
self.ceval('rectangle',args,'x'),
self.ceval('rectangle',args,'y'),
self.ceval('rectangle',args,'width'),
self.ceval('rectangle',args,'height')
)
rect.fillColor = self.ceval('rectangle',args,'fill')
rect.strokeColor = self.ceval('rectangle',args,'stroke')
self._curRectangle = rect
def end_rectangle(self):
if self._curSlide:
self._curSlide.graphics.append(self._curRectangle)
elif self._curSection:
self._curSection.graphics.append(self._curRectangle)
self._curRectangle = None
def start_roundrect(self, args):
self.pack_slide('roundrect', args)
rrect = pythonpoint.PPRoundRect(
self.ceval('roundrect',args,'x'),
self.ceval('roundrect',args,'y'),
self.ceval('roundrect',args,'width'),
self.ceval('roundrect',args,'height'),
self.ceval('roundrect',args,'radius')
)
rrect.fillColor = self.ceval('roundrect',args,'fill')
rrect.strokeColor = self.ceval('roundrect',args,'stroke')
self._curRoundRect = rrect
def end_roundrect(self):
if self._curSlide:
self._curSlide.graphics.append(self._curRoundRect)
elif self._curSection:
self._curSection.graphics.append(self._curRoundRect)
self._curRoundRect = None
def start_line(self, args):
self.pack_slide('line', args)
self._curLine = pythonpoint.PPLine(
self.ceval('line',args,'x1'),
self.ceval('line',args,'y1'),
self.ceval('line',args,'x2'),
self.ceval('line',args,'y2')
)
self._curLine.strokeColor = self.ceval('line',args,'stroke')
def end_line(self):
if self._curSlide:
self._curSlide.graphics.append(self._curLine)
elif self._curSection:
self._curSection.graphics.append(self._curLine)
self._curLine = None
def start_ellipse(self, args):
self.pack_slide('ellipse', args)
self._curEllipse = pythonpoint.PPEllipse(
self.ceval('ellipse',args,'x1'),
self.ceval('ellipse',args,'y1'),
self.ceval('ellipse',args,'x2'),
self.ceval('ellipse',args,'y2')
)
self._curEllipse.strokeColor = self.ceval('ellipse',args,'stroke')
self._curEllipse.fillColor = self.ceval('ellipse',args,'fill')
def end_ellipse(self):
if self._curSlide:
self._curSlide.graphics.append(self._curEllipse)
elif self._curSection:
self._curSection.graphics.append(self._curEllipse)
self._curEllipse = None
def start_polygon(self, args):
self.pack_slide('polygon', args)
self._curPolygon = pythonpoint.PPPolygon(self.ceval('polygon',args,'points'))
self._curPolygon.strokeColor = self.ceval('polygon',args,'stroke')
self._curPolygon.fillColor = self.ceval('polygon',args,'fill')
def end_polygon(self):
if self._curSlide:
self._curSlide.graphics.append(self._curPolygon)
elif self._curSection:
self._curSection.graphics.append(self._curPolygon)
self._curEllipse = None
def start_string(self, args):
self.pack_slide('string', args)
self._curString = pythonpoint.PPString(
self.ceval('string',args,'x'),
self.ceval('string',args,'y')
)
self._curString.color = self.ceval('string',args,'color')
self._curString.font = self._arg('string',args,'font')
self._curString.size = self.ceval('string',args,'size')
if args['align'] == 'left':
self._curString.align = TA_LEFT
elif args['align'] == 'center':
self._curString.align = TA_CENTER
elif args['align'] == 'right':
self._curString.align = TA_RIGHT
elif args['align'] == 'justify':
self._curString.align = TA_JUSTIFY
#text comes later within the tag
def end_string(self):
#controller should have set the text
if self._curSlide:
self._curSlide.graphics.append(self._curString)
elif self._curSection:
self._curSection.graphics.append(self._curString)
self._curString = None
def start_infostring(self, args):
# like a string, but lets them embed page no, author etc.
self.start_string(args)
self._curString.hasInfo = 1
def end_infostring(self):
self.end_string()
def start_customshape(self, args):
#loads one
path = self._arg('customshape',args,'path')
if path=='None':
path = []
else:
path=[path]
# add package root folder and input file's folder to path
path.append(os.path.dirname(self.sourceFilename))
path.append(os.path.dirname(pythonpoint.__file__))
modulename = self._arg('customshape',args,'module')
funcname = self._arg('customshape',args,'class')
try:
found = imp.find_module(modulename, path)
(file, pathname, description) = found
mod = imp.load_module(modulename, file, pathname, description)
except ImportError:
mod = getModule(modulename)
#now get the function
func = getattr(mod, funcname)
initargs = self.ceval('customshape',args,'initargs')
self._curCustomShape = apply(func, initargs)
def end_customshape(self):
if self._curSlide:
self._curSlide.graphics.append(self._curCustomShape)
elif self._curSection:
self._curSection.graphics.append(self._curCustomShape)
self._curCustomShape = None
## intra-paragraph XML should be allowed through into PLATYPUS
def unknown_starttag(self, tag, attrs):
if self._curPara:
if tag == 'ul' or tag == 'ol' or tag == 'dl' :
if attrs.has_key('style'):
self._curPara.contextlist.append(attrs['style'])
echo = '<%s' % tag
for (key, value) in attrs.items():
echo = echo + ' %s="%s"' % (key, value)
echo = echo + '>'
self._curPara.rawtext = self._curPara.rawtext + echo
else:
print 'Unknown start tag %s' % tag
def unknown_endtag(self, tag):
if self._curPara:
self._curPara.rawtext = self._curPara.rawtext + '</%s>'% tag
else:
print 'Unknown end tag %s' % tag
------=_NextPart_000_0002_01C309B9.1B4C70A0
Content-Type: text/xml;
name="pythonpoint.xml"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
filename="pythonpoint.xml"
<presentation filename="pythonpoint.pdf" pageDuration="3">
<stylesheet module="standard" function="getParagraphStyles"/>
<title>PythonPoint Demonstration</title>
<author>
Andy Robinson
</author>
<subject>
Reportlab Sample Applications
</subject>
<section name="Main">
<!-- any graphics in the section go on all its pages as a backdrop -->
<rectangle height="555" fill="ReportLabBlue" x="20" width="96" y="20"/>
<!--fixedimage height="64" filename="leftlogo.gif" x="20" width="96" y="510"/-->
<customshape module="customshapes" class="Logo" initargs="(20,510,96,64)"/>
<!--infostring size="14" align="right" x="800" y="36">
»%(title)s, page %(page)s«
</infostring-->
<!-- Now for the slides -->
<slide title="Introduction" id="Slide001" effectname="Wipe">
<frame height="468" x="120" y="72" rightmargin="36" width="700" leftmargin="36">
<para style="Heading1">
Welcome to PythonPoint
</para>
<para style="BodyText">
...a library for creating presentation slides.
</para>
<para style="BodyText">
<i>
PythonPoint
</i>
lets you create attractive and consistent presentation slides
on any platform. It is a demo app built on top of the PDFgen PDF library
and the PLATYPUS Page Layout library. Essentially, it converts slides
in an XML format to PDF.
</para>
<para style="BodyText">
It can be used right now to create slide shows, but will
undoubtedly change and evolve. Read on for a tutorial...
</para>
</frame>
<notes>
<para>
Smile and look them in the eye!
</para>
</notes>
</slide>
<slide title="Part 1" id="Part1" effectname="Blinds" effectdirection="0">
<frame height="468" x="120" y="72" rightmargin="36" width="700" leftmargin="36">
<para/>
<para/>
<para/>
<para style="Heading1">
Part 1 – Feature Overview
</para>
</frame>
</slide>
<slide title="XML Notation" id="Slide002" effectname="Blinds" effectdirection="0" outlinelevel="1">
<frame height="468" x="120" y="72" rightmargin="36" width="700" leftmargin="36">
<para style="Heading1">
XML Notation
</para>
<para style="BodyText">
You create slides in a text editor with a basic
XML syntax looking like this:
</para>
<prefmt style="Code"><![CDATA[
<frame x="160" y="72" width="600" height="468"
leftmargin="36" rightmargin="36">
<para style='Heading1'>
Welcome to PythonPoint
</para>
<para style='BodyText'>
...a library for creating presentation slides.
</para>
</frame> ]]>
</prefmt>
<para style="BodyText">
Pythonpoint then converts these into slides. Just enter
"pythonpoint.py myfile.xml" to create a PDF document
(usually called "myfile.pdf", but you specify that in the XML).
</para>
</frame>
</slide>
<slide title="Page Layout" id="Slide003" effectname="Box" outlinelevel="1">
<frame height="468" border="true" x="120" y="72" rightmargin="36" width="700" leftmargin="36">
<para style="Heading1">
Page Layout Model
</para>
<para style="BodyText">
The Page Layout model comes from PLATYPUS (Page Layout and Typography Using Scripts),
a key component of the toolkit. This covers concepts such as:
</para>
<para style="Bullet">
Reusable 'Drawable Objects'
</para>
<para style="Bullet">
Frames into which objects flow (like this one, around which we have drawn a border)
</para>
<para style="Bullet">
Style Sheets for text, table cells, line styles etc.
</para>
<para style="Bullet">
Wrapping, page breaking an document management logic
</para>
<para style="BodyText">
Everything is open and extensible. I hope a library of
reusable objects such as charts and diagrams will grow up.
</para>
</frame>
</slide>
<slide title="Reuse" id="Slide004" effectname="Wipe" outlinelevel="1">
<frame height="468" x="120" y="72" rightmargin="36" width="700" leftmargin="36">
<para style="Heading1">
Reuse and Consistency – Sections
</para>
<para style="BodyText">
You can create a 'section' spanning some or all tags in the presentation
and place graphics on this. The blue border and title come from the
section. Here's how we did the border:
</para>
<prefmt style="Code"><![CDATA[
<presentation filename='pythonpoint.pdf'>
<section name = 'Main'>
<!-- any graphics in the section go on all its pages as a backdrop -->
<rectangle x="20" y="510" width="800" height="65" fill="(0,0,1)"/>
<rectangle x="20" y="20" width="65" height="555" fill="(0,0,1)"/>
...all slides go here...
</section>
</presentation>
]]>
</prefmt>
<para style="BodyText">
Thus you can re-brand an entire presentation for
a new audience in seconds.
</para>
</frame>
</slide>
<slide title="Styles" id="Slide005" effectname="Dissolve" outlinelevel="1">
<frame height="468" x="120" y="72" rightmargin="36" width="700" leftmargin="36">
<para style="Heading1">
Style Sheets
</para>
<para style="BodyText">
Paragraph styles are defined externally. You may specify a filename
from which to load a stylesheet with the stylesheet tag.
</para>
<para style="BodyText">
Thus you can have different sizes and formats by switching
stylesheets, or colour and black-and-white options.
</para>
<para style="BodyText">
When they are added, tables will be driven by line and cell
styles in a similar way.
</para>
</frame>
</slide>
<slide title="Special Effects" id="Slide006" effectname="Dissolve" outlinelevel="1">
<frame height="468" x="120" y="72" rightmargin="36" width="700" leftmargin="36">
<para style="Heading1">
Special Effects
</para>
<para style="BodyText">
Acrobat Reader supports tags to define page transition effects.
If you are reading this on screen, you should have seen a selection
of these:
</para>
<para style="Bullet">
Split
</para>
<para style="Bullet">
Blinds
</para>
<para style="Bullet">
Box
</para>
<para style="Bullet">
Wipe
</para>
<para style="Bullet">
Dissolve
</para>
<para style="Bullet">
Glitter
</para>
<para style="BodyText">
Each has a range of options to fine-tune.
</para>
<para style="BodyText">
When they are added, tables will be driven by line and cell
styles in a similar way.
</para>
</frame>
</slide>
<slide title="Outlines and Hyperlinks" id="Slide007" effectname="Wipe" outlinelevel="1">
<frame height="468" x="120" y="72" rightmargin="36" width="700" leftmargin="36">
<para style="Heading1">
Outlines and Hyperlinks
</para>
<para style="BodyText">
By default, we generate an outline view in the left pane to
help you navigate. Hyperlinks within documents are also
possible.
</para>
<para style="BodyText">
As far as we know, this is the first PDF library to expose
these features.
</para>
</frame>
</slide>
<slide title="Basic Shapes" id="Slide008" effectname="Wipe" outlinelevel="1">
<frame height="468" x="120" y="72" rightmargin="36" width="700" leftmargin="36">
<para style="Heading1">
Basic Shapes
</para>
<para>
Here are some of the basic shapes available for decorating pages...
</para>
</frame>
<rectangle height="50" fill="(0,1,1)" x="200" width="100" y="300"/>
<roundrect height="50" x="350" y="300" radius="15" fill="(0,1,1)" width="100"/>
<line y1="300" y2="350" x1="500" x2="600"/>
<ellipse fill="(0,1,1)" y1="300" y2="350" x1="650" x2="750"/>
<polygon points="(200,200),(300,200),(350,180),(250,150)" fill="(0,1,1)"/>
<string size="14" color="(1,0,0)" align="center" x="500" y="200">
This is a\nmulti-line string\nplaced directly on the page.\n\nIt can be left-aligned,\ncentred,\nor right-aligned.
</string>
<customshape module="customshapes" class="Jigsaw" initargs="(700,200,1)"/>
</slide>
<slide title="Tables" id="Slide008a" effectname="Glitter" outlinelevel="1">
<frame height="468" x="120" y="72" rightmargin="36" width="700" leftmargin="36">
<para style="Heading1">
Tables
</para>
<para>
The Table tag lets you paste in bulk data and format it attractively:
</para>
<spacer height="24"/>
<table heights="(28,28,28,28,28)" style="table1" widths="(144,72,72,72,108)">
Division,Jan,Feb,Mar,Q1 Total
North,100,115,120,335
South,215,145,180,540
East,75,90,135,300
West,100,120,115,335
</table>
</frame>
</slide>
<slide title="Future Features" id="Slide009" effectname="Glitter" outlinelevel="1">
<frame height="468" x="120" y="72" rightmargin="36" width="700" leftmargin="36">
<para style="Heading1">
Features Coming Soon
</para>
<para style="BodyText">
This is the first version that runs. A lot can now be added fairly easily:
</para>
<para style="Bullet">
Preprocessor to let you enter paragraphs and
bullets as one block of text, with less tag typing!
</para>
<para style="Bullet">
PIDDLE drawings
</para>
<para style="Bullet">
PINGO drawings – 'Object Graphics' tags with grouping and coordinate transformations
</para>
<para style="Bullet">
Speaker notes and a mode to print them
</para>
<para style="Bullet">
Tools to archive slides in a database and build presentations to order
</para>
<para style="Italic">
...what else can YOU think of?
</para>
</frame>
</slide>
<slide title="Part 2" id="Part2" effectname="Blinds" effectdirection="0" outlinelevel="0">
<frame height="468" x="120" y="72" rightmargin="36" width="700" leftmargin="36">
<para/>
<para/>
<para/>
<para style="Heading1">
Part 2 – Reference
</para>
<para/>
<para style="Centered">
This section covers all command line options
and tags currently in use.
</para>
</frame>
</slide>
<slide title="Command Line Options" id="Slide200" effectname="Blinds" effectdirection="0" outlinelevel="1">
<frame height="510" x="120" y="72" rightmargin="36" width="700" leftmargin="36">
<para style="Heading2">
Command Line Options
</para>
<para>
Usage (NT, or executable Unix script):
</para>
<para>
<i>
pythonpoint.py [/notes] myslides.xml
</i>
</para>
<para>
or (Win9x or non-executable script)
</para>
<para>
<i>
python pythonpoint.py [/notes] myslides.xml
</i>
</para>
<para style="BodyText">
Notes:
</para>
<para style="Bullet">
The resulting PDF file has the same
name as the input file.
</para>
<para style="Bullet">
The Speaker Notes mode prints a shrunken canvas with
room for notes around the edge. To create notes, make
an extra frame off the page. See the source of
Pythonpoint.xml slide 001 for an example.
</para>
</frame>
</slide>
<slide title="Tag: Presentation" id="Slide201" effectname="Blinds" effectdirection="0" outlinelevel="1">
<frame height="510" x="120" y="72" rightmargin="36" width="700" leftmargin="36">
<para style="Heading2">
Tag "presentation"
</para>
<para style="BodyText">
This is the outermost tag in an XML file and is always required.
</para>
<para style="Italic">
Attributes:
</para>
<para style="Indent">
filename (required)
</para>
<para style="Italic">
Can Contain:
</para>
<para style="Indent">
section, stylesheet, slides
</para>
<para/>
<para style="Italic">
To Do:
</para>
<para style="Indent">
Support for page sizes, opening modes
</para>
</frame>
</slide>
<slide title="Tag: Stylesheet" id="Slide202" effectname="Blinds" effectdirection="0" outlinelevel="1">
<frame height="510" x="120" y="72" rightmargin="36" width="700" leftmargin="36">
<para style="Heading2">
Tag "stylesheet"
</para>
<para style="BodyText">
This defines an external style sheet full of paragraph styles.
For now, these are Python modules conforming to a common interface,
and examples are given. If not declared, a default style sheet is
used. You are strongly advised to define your own style sheet,
as the built-in one will change a few more times.
</para>
<para style="Italic">
Attributes:
</para>
<para style="Indent">
path, module, function
</para>
<para style="Italic">
Contained By:
</para>
<para style="Indent">
Presentation, Section
</para>
<para style="Italic">
Can Contain:
</para>
<para style="Indent">
nothing
</para>
<para style="Italic">
Example
</para>
<prefmt style="Code"><![CDATA[
<stylesheet module="modern" function="getParagraphStyles"/>
]]>
</prefmt>
</frame>
</slide>
<slide title="Tag: Section" id="Slide203" effectname="Blinds" effectdirection="0" outlinelevel="1">
<frame height="510" x="120" y="72" rightmargin="36" width="700" leftmargin="36">
<para style="Heading2">
Tag "section"
</para>
<para style="BodyText">
A Section spans across a number of slides and can apply an
overall background to them. Place graphics directly within
the section tag, either before or after slides. This lets
you re-brand a presentation very quickly. Documents
may contain multiple sections; nesting of sections is
not (yet) permitted.
</para>
<para style="Italic">
Attributes:
</para>
<para style="Indent">
name (required, but not used yet)
</para>
<para style="Italic">
Contained By:
</para>
<para style="Indent">
Presentation
</para>
<para style="Italic">
Can Contain:
</para>
<para style="Indent">
all graphic shapes; slides
</para>
</frame>
</slide>
<slide title="Tag: Slide" id="Slide204" effectname="Blinds" effectdirection="0" outlinelevel="1">
<frame height="510" x="120" y="72" rightmargin="36" width="700" leftmargin="36">
<para style="Heading2">
Tag "slide"
</para>
<para style="BodyText">
Defines a single slide. The presentation effects are defined
in the PDF reference; best to just try the combinations.
</para>
<para style="Italic">
Attributes (with defaults):
</para>
<para style="Indent">
id (required)
</para>
<para style="Indent">
title (required)
</para>
<para style="Indent">
effectname: one of Split, Blinds, Box, Wipe, Dissolve, Glitter
</para>
<para style="Indent">
effectdirection: '0','90','180' or '270'
</para>
<para style="Indent">
effectdimension: 'H' or 'V' (Horiz./Vert.)
</para>
<para style="Indent">
effectmotion: 'I' for inwards or 'O' for outwards
</para>
<para style="Indent">
effectduration: time in seconds
</para>
<para style="Italic">
Contained By:
</para>
<para style="Indent">
Presentation
</para>
<para style="Italic">
Can Contain:
</para>
<para style="Indent">
all graphic shapes; frames
</para>
</frame>
</slide>
<slide title="Tag: Frame" id="Slide205" effectname="Blinds" effectdirection="0" outlinelevel="1">
<frame height="510" x="120" y="72" rightmargin="36" width="700" leftmargin="36">
<para style="Heading2">
Tag "frame"
</para>
<para style="BodyText">
Defines a frame on the page which can hold content. You may have
as many frames as you like, to allow multi-column text or flow
around pictures.
</para>
<para style="Italic">
Attributes:
</para>
<para style="Indent">
x, y, width, height (all required): in points
</para>
<para style="Indent">
leftmargin, rightmargin, topmargin, bottommargin
(optional, default to zero) – define the 'inner rectangle' within
which content flows
</para>
<para style="Indent">
border (defaults to 'false'): whether to show
a frame border – useful when designing pages.
</para>
<para style="Italic">
Contained By:
</para>
<para style="Indent">
Slide
</para>
<para style="Italic">
Can Contain:
</para>
<para style="Indent">
all 'flowable objects' – paragraphs, images
</para>
</frame>
</slide>
<slide title="Flowable Objects" id="Slide206" effectname="Blinds" effectdirection="0" outlinelevel="1">
<frame height="510" x="120" y="72" rightmargin="36" width="700" leftmargin="36">
<para style="Heading2">
Tag family – "Flowable Objects"
</para>
<para style="BodyText">
Flowable Objects currently include Paragraphs, Preformatted text
(used for code printing, where the line breaks and spaces matter)
and inline Images. More will be added in future.
They negotiate with their containing frame about height and
width; paragraphs do what you would expect, while images are centred.
</para>
<para style="Italic">
Contained By:
</para>
<para style="Indent">
Slide
</para>
<para style="Italic">
Can Contain:
</para>
<para style="Indent">
The three instances so far contain nothing.
</para>
</frame>
</slide>
<slide title="Tag: para" id="Slide207" effectname="Blinds" effectdirection="0" outlinelevel="2">
<frame height="510" x="120" y="72" rightmargin="36" width="700" leftmargin="36">
<para style="Heading2">
Tag "para" – Paragraphs
</para>
<para style="BodyText">
Paragraphs are used for wrapping text. They are very simple
– they have a style attribute,
and the stylesheet defines most attributes externally.
Currently we use a hack to handle 'bullets', which may be in
a different font (such as 'ZapfDingbats, specified in style)
and offset to the left. These are used
for bullets, numbering and definition lists
This will vanish as soon as one can
switch fonts in mid-paragraph (due mid April).
</para>
<para style="Italic">
Attributes:
</para>
<para style="Indent">
style (defaults to 'Normal') –
reference to stylesheet
</para>
<para style="Indent">
bullettext –
text for the optional 'bullet' section. To be deprecated.
</para>
<para style="Italic">
Contained By:
</para>
<para style="Indent">
Frame
</para>
<para style="Italic">
Can Contain:
</para>
<para style="Indent">
Their text
</para>
</frame>
</slide>
<slide title="Tag: paratype" id="Slide207b" effectname="Blinds" effectdirection="0" outlinelevel="2">
<frame height="510" x="120" y="72" rightmargin="36" width="700" leftmargin="36">
<paratype version='extended'/>
<!--
All para tags will be evaluated according to any previous
paratype tag within the document xml. If no paratype tag
is used within the xml or a paratype tag is used without any
attributes, then the original reportlab paragraph implementation
is used to format the para data content for subsequent para tags.
The paratype tag above, using the 'extended' value for the version
attribute will cause subsequent para tags to be evaluated using
Aaron's extended paragraph. See /pdfgen/para.py
-->
<para style="Heading2">
Tag "paratype"
</para>
<para style="BodyText">
Optional, determines whether the original or extended paragraph
implementation will be used for para tags that follow.</para>
<para style="Italic">
Attributes:
</para>
<para style="Indent">
version (defaults to 'original')
values = original/extended
</para>
<para style="Italic">
Contained By:
</para>
<para style="Indent">
Frame
</para>
<para style="Italic">
Can Contain:
</para>
<para style="Indent">
None
</para>
<!--
-->
</frame>
</slide>
<slide title="Tag: paratype version='extended'" id="Slide207c" effectname="Blinds" effectdirection="0" outlinelevel="2">
<frame height="582" x="120" y="0" rightmargin="36" width="700" leftmargin="36">
<para style="Heading2">
Tag "paratype" (continued)
</para>
<para style="BodyText">
The new extended paragraph contains
many useful features in addition to those of the original
reportlab paragraph implementation, such as support
for:
<ul style= 'Definition' fontSize='20' leading='17' bulletFontSize='15' bulletIndent='24' >
<li>unordered lists</li>
<li>ordered lists</li>
<li>definition lists</li>
<li><u>underlining</u></li>
<li><a href="http://www.reportlab.com" color='magenta'>hyperlinks</a></li>
<li>paragraphs within paragraphs</li>
</ul>
</para>
<para style="BodyText">Many other features are available within this extended
paragraph implementation, see \reportlab\platypus\para.py</para>
<!--
-->
</frame>
</slide>
<slide title="Tag: prefmt" id="Slide207d" effectname="Blinds" effectdirection="0" outlinelevel="2">
<frame height="510" x="120" y="72" rightmargin="36" width="700" leftmargin="36">
<para style="Heading2">
Tag "prefmt"
</para>
<para style="BodyText">
This is used for printing code, or other text which contains
line breaks.
</para>
<para style="Italic">
Attributes:
</para>
<para style="Indent">
style (defaults to 'Normal') –
reference to stylesheet
</para>
<para style="Italic">
Contained By:
</para>
<para style="Indent">
Frame
</para>
<para style="Italic">
Can Contain:
</para>
<para style="Indent">
The text to be displayed
</para>
</frame>
</slide>
<slide title="Tag: image" id="Slide208" effectname="Blinds" effectdirection="0" outlinelevel="2">
<frame height="510" x="120" y="72" rightmargin="36" width="700" leftmargin="36">
<para style="Heading2">
Tag "image" – flowing images
</para>
<para style="BodyText">
This is used for an image to be displayed inline within
the frame. It will be drawn at a scale of 1 pixel to
1 point, and centred in the frame.
</para>
<para style="Italic">
Attributes:
</para>
<para style="Indent">
filename (required)
</para>
<para style="Italic">
Contained By:
</para>
<para style="Indent">
Frame
</para>
<para style="Italic">
Can Contain:
</para>
<para style="Indent">
Nothing
</para>
<para style="Italic">
To do
</para>
<para style="Indent">
Rename it 'flowing image'? Control
over alignment and size if needed. Image caching.
</para>
</frame>
</slide>
<slide title="Tag: table" id="Slide208a" effectname="Blinds" effectdirection="0" outlinelevel="2">
<frame height="510" x="120" y="72" rightmargin="36" width="700" leftmargin="36">
<para style="Heading2">
Tag "table" – tables
</para>
<para style="BodyText">
This lets you draw tables with a wide variety of formatting
options.
</para>
<para style="Italic">
Attributes:
</para>
<para style="Indent">
<b>
widths
</b>
(optional) in points (auto-sizes if not given)
</para>
<para style="Indent">
<b>
heights
</b>
(optional) in points (auto-sizes if not given)
</para>
<para style="Indent">
<b>
style
</b>
(optional) – name of a ReportLab
tablestyle defined in the stylesheet.
</para>
<para style="Indent">
<b>
colDelim
</b>
(optional) – the column
delimiter string for bulk data; defaults to a comma.
</para>
<para style="Indent">
<b>
rowDelim
</b>
(optional) – the line
delimiter string for bulk data; defaults to a carriage
return.
</para>
<para style="Italic">
Contained By:
</para>
<para style="Indent">
Frame
</para>
<para style="Italic">
Can Contain:
</para>
<para style="Indent">
Bulk data, with the row and column delimiters
specified
</para>
</frame>
</slide>
<slide title="Drawable Objects" id="Slide209" effectname="Blinds" effectdirection="0" outlinelevel="1">
<frame height="510" x="120" y="72" rightmargin="36" width="700" leftmargin="36">
<para style="Heading2">
Tag family – "Drawable Objects"
</para>
<para style="BodyText">
These are objects which know how to draw themselves directly on
the page (or section template). Use them for designing the backdrop,
and for custom graphics.
</para>
<para style="Italic">
Contained By:
</para>
<para style="Indent">
Slide, Section
</para>
<para style="Italic">
Can Contain:
</para>
<para style="Indent">
Varies.
</para>
<para style="Italic">
To Do:
</para>
<para style="Indent">
Will include the full PINGO object
model – a subset of SVG – allowing any vector graphics
at all.
</para>
</frame>
</slide>
<slide title="Tag: FixedImage" id="Slide210" effectname="Blinds" effectdirection="0" outlinelevel="2">
<frame height="510" x="120" y="72" rightmargin="36" width="700" leftmargin="36">
<para style="Heading2">
Tag "fixedimage"
</para>
<para style="BodyText">
This is an image draw directly at a fixed position –
for example, the logo at top left of the page.
</para>
<para style="Italic">
Attributes:
</para>
<para style="Indent">
filename: required
</para>
<para style="Indent">
x, y: required
</para>
<para style="Indent">
width, height: optional, stretches image
to fit box if present.
</para>
<para style="Italic">
Contained By:
</para>
<para style="Indent">
Slide, Section
</para>
<para style="Italic">
Can Contain:
</para>
<para style="Indent">
Nothing
</para>
</frame>
</slide>
<slide title="Tag: Rectangle" id="Slide211" effectname="Blinds" effectdirection="0" outlinelevel="2">
<frame height="510" x="120" y="72" rightmargin="36" width="700" leftmargin="36">
<para style="Heading2">
Tag "rectangle"
</para>
<para style="Italic">
Attributes:
</para>
<para style="Indent">
x, y, width, height: required
</para>
<para style="Indent">
fill, stroke: either 'None', or an
(r,g,b) tuple.
</para>
<para style="Indent">
linewidth: defaults to 0 (hairline)
</para>
<para style="Italic">
Contained By:
</para>
<para style="Indent">
Slide, Section
</para>
<para style="Italic">
Can Contain:
</para>
<para style="Indent">
Nothing
</para>
</frame>
</slide>
<slide title="Tag: RoundRect" id="Slide212" effectname="Blinds" effectdirection="0" outlinelevel="2">
<frame height="510" x="120" y="72" rightmargin="36" width="700" leftmargin="36">
<para style="Heading2">
Tag "roundrect"
</para>
<para style="BodyText">
This is exactly like Rectangle,
but with an extra 'radius' attribute defining the corner
radius in points – default is 6 points.
</para>
</frame>
</slide>
<slide title="Tag: Ellipse" id="Slide213" effectname="Blinds" effectdirection="0" outlinelevel="2">
<frame height="510" x="120" y="72" rightmargin="36" width="700" leftmargin="36">
<para style="Heading2">
Tag "ellipse"
</para>
<para style="BodyText">
Draws an ellipse, defined by its
bounding box. Note that it can
create circles if height and width are equal.
</para>
<para style="Italic">
Attributes:
</para>
<para style="Indent">
x1, y1, x2, y2: required
</para>
<para style="Indent">
fill, stroke: either 'None', or an
(r,g,b) tuple.
</para>
<para style="Indent">
linewidth: defaults to 0 (hairline)
</para>
<para style="Italic">
Contained By:
</para>
<para style="Indent">
Slide, Section
</para>
<para style="Italic">
Can Contain:
</para>
<para style="Indent">
Nothing
</para>
</frame>
</slide>
<slide title="Tag: Polygon" id="Slide214" effectname="Blinds" effectdirection="0" outlinelevel="2">
<frame height="510" x="120" y="72" rightmargin="36" width="700" leftmargin="36">
<para style="Heading2">
Tag "polygon"
</para>
<para style="BodyText">
Draws a polygon from a list
of points you provide.
</para>
<para style="Italic">
Attributes:
</para>
<para style="Indent">
points: list such as "(0,0),(50,0),(25,25)"
</para>
<para style="Indent">
fill, stroke: either 'None', or an
(r,g,b) tuple.
</para>
<para style="Indent">
linewidth: defaults to 0 (hairline)
</para>
<para style="Italic">
Contained By:
</para>
<para style="Indent">
Slide, Section
</para>
<para style="Italic">
Can Contain:
</para>
<para style="Indent">
Nothing
</para>
</frame>
</slide>
<slide title="Tag: Line" id="Slide215" effectname="Blinds" effectdirection="0" outlinelevel="2">
<frame height="510" x="120" y="72" rightmargin="36" width="700" leftmargin="36">
<para style="Heading2">
Tag "line"
</para>
<para style="BodyText">
Draws a line.
</para>
<para style="Italic">
Attributes:
</para>
<para style="Indent">
x1, y1, x2, y2
</para>
<para style="Indent">
stroke: either 'None', or an
(r,g,b) tuple.
</para>
<para style="Indent">
width: defaults to 0 (hairline)
</para>
<para style="Italic">
Contained By:
</para>
<para style="Indent">
Slide, Section
</para>
<para style="Italic">
Can Contain:
</para>
<para style="Indent">
Nothing
</para>
</frame>
</slide>
<slide title="Tag: String" id="Slide215" effectname="Blinds" effectdirection="0" outlinelevel="2">
<frame height="510" x="120" y="72" rightmargin="36" width="700" leftmargin="36">
<para style="Heading2">
Tag "string"
</para>
<para style="BodyText">
This places strings directly on the page. They may have
embedded newlines (use a '\n' in the XML), in which
case multi-line strings are printed. Left, right
and centre alignment are allowed.
</para>
<para style="Italic">
Attributes:
</para>
<para style="Indent">
x, y: required
</para>
<para style="Indent">
color: RGB colour tuple such as '(0,1,0)'
</para>
<para style="Indent">
font: default is 'Times-Roman'
</para>
<para style="Indent">
size: default 12
</para>
<para style="Indent">
align: default 'left', allows
also 'right' or 'center'
</para>
<para style="Italic">
Contained By:
</para>
<para style="Indent">
Slide, Section
</para>
<para style="Italic">
Can Contain:
</para>
<para style="Indent">
The text of the string
</para>
</frame>
</slide>
<slide title="Tag: CustomShape" id="Slide216" effectname="Blinds" effectdirection="0" outlinelevel="2">
<frame height="510" x="120" y="72" rightmargin="36" width="700" leftmargin="36">
<para style="Heading2">
Tag "customshape"
</para>
<para style="BodyText">
This looks in a specified Python module for a
'drawable object' you write, and initialises
it with arguments you provide before drawing.
This must provide a 'self.drawOn(canvas)' method.
</para>
<para style="Italic">
Attributes:
</para>
<para style="Indent">
path: where to look; searches Python path
if None
</para>
<para style="Indent">
module: module name
</para>
<para style="Indent">
class: class name to create
</para>
<para style="Indent">
initargs: tuple of arguments with which
to initialize the class.
</para>
<para style="Indent">
align: default 'left', allows
also 'right' or 'center'
</para>
<para style="Italic">
Contained By:
</para>
<para style="Indent">
Slide, Section
</para>
<para style="Italic">
Can Contain:
</para>
<para style="Indent">
Nothing
</para>
</frame>
</slide>
<slide title="To Do" id="Part3" effectname="Blinds" effectdirection="0">
<frame height="468" x="120" y="72" rightmargin="36" width="700" leftmargin="36">
<para style="Heading1">
Part 3 – To Do
</para>
<para style="Bullet">
Lots of testing
</para>
<para style="Bullet">
Text preprocessor to let you input text, styles and images
in something easier to type
</para>
<para style="Bullet">
Support for Pingo (http://pingo.sourceforge.net/) drawings using
the Scalable Vector Graphics imaging model
</para>
<para style="Bullet">
Proper caching of flowing images
</para>
<para style="Bullet">
Basic Tables and Charts
</para>
<para style="Bullet">
Use new XML parsers as wel as xmllib
</para>
<para style="Bullet">
Slide indexing and database search tools
</para>
<para style="Bullet">
Speaker Notes mode
</para>
<para>
Naturally, help is extremely welcome :-)
</para>
</frame>
</slide>
</section>
</presentation>
------=_NextPart_000_0002_01C309B9.1B4C70A0
Content-Type: text/plain;
name="para.py"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
filename="para.py"
"""new experimental paragraph implementation=0A=
=0A=
Intended to allow support for paragraphs in paragraphs, hotlinks,=0A=
embedded flowables, and underlining. The main entry point is the=0A=
function=0A=
=0A=
def Paragraph(text, style, bulletText=3DNone, frags=3DNone)=0A=
=0A=
Which is intended to be plug compatible with the "usual" platypus=0A=
paragraph except that it supports more functionality.=0A=
=0A=
In this implementation you may embed paragraphs inside paragraphs=0A=
to create hierarchically organized documents.=0A=
=0A=
This implementation adds the following paragraph-like tags (which=0A=
support the same attributes as paragraphs, for font specification, etc).=0A=
=0A=
- Unnumberred lists (ala html):=0A=
=0A=
<ul>=0A=
<li>first one</li>=0A=
<li>second one</li>=0A=
</ul>=0A=
=0A=
Also <ul type=3D"disc"> (default) or <ul type=3D"circle">, <ul =
type=3D"square">.=0A=
=0A=
- Numberred lists (ala html):=0A=
=0A=
<ol>=0A=
<li>first one</li>=0A=
<li>second one</li>=0A=
</ol>=0A=
=0A=
Also <ul type=3D"1"> (default) or <ul type=3D"a">, <ul type=3D"A">.=0A=
=0A=
- Display lists (ala HTML):=0A=
=0A=
For example=0A=
=0A=
<dl bulletFontName=3D"Helvetica-BoldOblique" spaceBefore=3D"10" =
spaceAfter=3D"10">=0A=
<dt>frogs</dt> <dd>Little green slimy things. Delicious with =
<b>garlic</b></dd>=0A=
<dt>kittens</dt> <dd>cute, furry, not edible</dd>=0A=
<dt>bunnies</dt> <dd>cute, furry,. Delicious with <b>garlic</b></dd>=0A=
</dl>=0A=
=0A=
ALSO the following additional internal paragraph markup tags are =
supported=0A=
=0A=
<u>underlined text</u>=0A=
=0A=
<a href=3D"http://www.reportlab.com">hyperlinked text</a>=0A=
<a href=3D"http://www.reportlab.com" color=3D"blue">hyperlinked text</a>=0A=
=0A=
<link destination=3D"end" >Go to the end (go to document internal =
destination)</link>=0A=
<link destination=3D"start" color=3D"cyan">Go to the beginning</link>=0A=
=0A=
<setLink destination=3D"start" color=3D"magenta">This is the document =
start=0A=
(define document destination inside paragraph, color is =
optional)</setLink>=0A=
=0A=
"""=0A=
=0A=
from reportlab.pdfbase.pdfmetrics import stringWidth=0A=
from reportlab.lib.utils import fp_str=0A=
from reportlab.platypus.flowables import Flowable=0A=
from reportlab.lib import colors=0A=
=0A=
import string=0A=
=0A=
# SET THIS TO CAUSE A VIEWING BUG WITH ACROREAD 3 (for at least one =
input)=0A=
# CAUSEERROR =3D 0=0A=
=0A=
debug =3D 0=0A=
=0A=
DUMPPROGRAM =3D 0=0A=
=0A=
TOOSMALLSPACE =3D 1e-5=0A=
=0A=
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_RIGHT, TA_JUSTIFY=0A=
=0A=
# indent changes effect the next line=0A=
# align changes effect the current line=0A=
=0A=
# need to fix spacing questions... if ends with space then space may be =
inserted=0A=
=0A=
# NEGATIVE SPACE SHOULD NEVER BE EXPANDED (IN JUSTIFICATION, EG)=0A=
=0A=
class paragraphEngine:=0A=
# text origin of 0,0 is upper left corner=0A=
def __init__(self, program =3D None):=0A=
from reportlab.lib.colors import black=0A=
if program is None:=0A=
program =3D []=0A=
self.lineOpHandlers =3D [] # for handling underlining and =
hyperlinking, etc=0A=
self.program =3D program=0A=
#self.=0A=
self.indent =3D self.rightIndent =3D 0.0=0A=
self.baseindent =3D 0.0 # adjust this to add more indentation =
for bullets, eg=0A=
self.fontName =3D "Helvetica"=0A=
self.fontSize =3D 10=0A=
self.leading =3D 12=0A=
self.fontColor =3D black=0A=
self.x =3D self.y =3D self.rise =3D 0.0=0A=
from reportlab.lib.enums import TA_LEFT=0A=
self.alignment =3D TA_LEFT=0A=
self.textStateStack =3D []=0A=
TEXT_STATE_VARIABLES =3D ("indent", "rightIndent", "fontName", =
"fontSize",=0A=
"leading", "fontColor", "lineOpHandlers", =
"rise",=0A=
"alignment")=0A=
#"textStateStack")=0A=
def pushTextState(self):=0A=
state =3D []=0A=
for var in self.TEXT_STATE_VARIABLES:=0A=
val =3D getattr(self, var)=0A=
state.append(val)=0A=
#self.textStateStack.append(state)=0A=
self.textStateStack =3D self.textStateStack+[state] # fresh copy=0A=
#print "push", self.textStateStack=0A=
#print "push", len(self.textStateStack), state=0A=
return state=0A=
def popTextState(self):=0A=
state =3D self.textStateStack[-1]=0A=
self.textStateStack =3D self.textStateStack[:-1]=0A=
#print "pop", self.textStateStack=0A=
state =3D state[:] # copy for destruction=0A=
#print "pop", len(self.textStateStack), state=0A=
#print "handlers before", self.lineOpHandlers=0A=
for var in self.TEXT_STATE_VARIABLES:=0A=
val =3D state[0]=0A=
del state[0]=0A=
setattr(self, var, val)=0A=
=0A=
def format(self, maxwidth, maxheight, program, leading=3D0):=0A=
"return program with line operations added if at least one line =
fits"=0A=
# note: a generated formatted segment should not be formatted =
again=0A=
startstate =3D self.__dict__.copy()=0A=
#remainder =3D self.cleanProgram(program)=0A=
remainder =3D program[:]=0A=
#program1 =3D remainder[:] # debug only=0A=
lineprogram =3D []=0A=
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_RIGHT, =
TA_JUSTIFY=0A=
#if maxheight<TOOSMALLSPACE:=0A=
# raise ValueError, "attempt to format inside too small a =
height! "+repr(maxheight)=0A=
heightremaining =3D maxheight-leading=0A=
room =3D 1=0A=
cursorcount =3D 0 # debug=0A=
while remainder and room: #heightremaining>=3Dself.leading and =
remainder:=0A=
#print "getting line with statestack", =
len(self.textStateStack)=0A=
#heightremaining =3D heightremaining - self.leading=0A=
indent =3D self.indent=0A=
rightIndent =3D self.rightIndent=0A=
linewidth =3D maxwidth - indent - rightIndent=0A=
beforelinestate =3D self.__dict__.copy()=0A=
if linewidth<TOOSMALLSPACE:=0A=
raise ValueError, "indents %s %s too wide for space %s" =
% (self.indent, self.rightIndent, maxwidth)=0A=
try:=0A=
(lineIsFull, line, cursor, currentLength, usedIndent, =
maxLength, justStrings) =3D self.fitLine(remainder, maxwidth)=0A=
except:=0A=
## print "failed to fit line near", cursorcount # debug=0A=
## for i in program1[max(0,cursorcount-10): cursorcount]:=0A=
## print=0A=
## print i,=0A=
## print "***" *8=0A=
## for i in program1[cursorcount:cursorcount+20]:=0A=
## print i=0A=
raise=0A=
cursorcount =3D cursorcount+cursor # debug=0A=
leading =3D self.leading=0A=
if heightremaining>leading:=0A=
heightremaining =3D heightremaining-leading=0A=
else:=0A=
room =3D 0=0A=
#self.resetState(beforelinestate)=0A=
self.__dict__.update(beforelinestate)=0A=
break # no room for this line=0A=
## if debug:=0A=
## print "line", line=0A=
## if lineIsFull: print "is full"=0A=
## else: print "is partially full"=0A=
## print "consumes", cursor, "elements"=0A=
## print "covers", currentLength, "of", maxwidth=0A=
alignment =3D self.alignment # last declared alignment for =
this line used=0A=
# recompute linewidth using the used indent=0A=
#linewidth =3D maxwidth - usedIndent - rightIndent=0A=
remainder =3D remainder[cursor:]=0A=
if not remainder:=0A=
# trim off the extra end of line=0A=
del line[-1]=0A=
# do justification if any=0A=
#line =3D self.shrinkWrap(line=0A=
if alignment=3D=3DTA_LEFT:=0A=
#if debug:=0A=
# print "ALIGN LEFT"=0A=
if justStrings:=0A=
line =3D stringLine(line, currentLength)=0A=
else:=0A=
line =3D self.shrinkWrap(line)=0A=
pass=0A=
elif alignment=3D=3DTA_CENTER:=0A=
#if debug:=0A=
# print "ALIGN CENTER"=0A=
if justStrings:=0A=
line =3D stringLine(line, currentLength)=0A=
else:=0A=
line =3D self.shrinkWrap(line)=0A=
line =3D self.centerAlign(line, currentLength, maxLength)=0A=
elif alignment=3D=3DTA_RIGHT:=0A=
#if debug:=0A=
# print "ALIGN RIGHT"=0A=
if justStrings:=0A=
line =3D stringLine(line, currentLength)=0A=
else:=0A=
line =3D self.shrinkWrap(line)=0A=
line =3D self.rightAlign(line, currentLength, maxLength)=0A=
elif alignment=3D=3DTA_JUSTIFY:=0A=
#if debug:=0A=
# print "JUSTIFY"=0A=
if remainder and lineIsFull:=0A=
if justStrings:=0A=
line =3D simpleJustifyAlign(line, currentLength, =
maxLength)=0A=
else:=0A=
line =3D self.justifyAlign(line, currentLength, =
maxLength)=0A=
else:=0A=
if justStrings:=0A=
line =3D stringLine(line, currentLength)=0A=
else:=0A=
line =3D self.shrinkWrap(line)=0A=
if debug:=0A=
print "no justify because line is not full or =
end of para"=0A=
else:=0A=
raise ValueError, "bad alignment "+repr(alignment)=0A=
if not justStrings:=0A=
line =3D self.cleanProgram(line)=0A=
lineprogram.extend(line)=0A=
laststate =3D self.__dict__.copy()=0A=
#self.resetState(startstate)=0A=
self.__dict__.update(startstate)=0A=
heightused =3D maxheight - heightremaining=0A=
return (lineprogram, remainder, laststate, heightused)=0A=
def getState(self):=0A=
# inlined=0A=
return self.__dict__.copy()=0A=
def resetState(self, state):=0A=
# primarily inlined=0A=
self.__dict__.update(state)=0A=
## def sizeOfWord(self, word):=0A=
## inlineThisFunctionForEfficiency=0A=
## return float(stringWidth(word, self.fontName, self.fontSize))=0A=
def fitLine(self, program, totalLength):=0A=
"fit words (and other things) onto a line"=0A=
# assuming word lengths and spaces have not been yet added=0A=
# fit words onto a line up to maxlength, adding spaces and =
respecting extra space=0A=
from types import StringType, TupleType, InstanceType, FloatType=0A=
from reportlab.pdfbase.pdfmetrics import stringWidth=0A=
usedIndent =3D self.indent=0A=
maxLength =3D totalLength - usedIndent - self.rightIndent=0A=
done =3D 0=0A=
line =3D []=0A=
cursor =3D 0=0A=
lineIsFull =3D 0=0A=
currentLength =3D 0=0A=
maxcursor =3D len(program)=0A=
needspace =3D 0=0A=
first =3D 1=0A=
terminated =3D None=0A=
fontName =3D self.fontName=0A=
fontSize =3D self.fontSize=0A=
spacewidth =3D stringWidth(" ", fontName, fontSize) =
#self.sizeOfWord(" ")=0A=
justStrings =3D 1=0A=
while not done and cursor<maxcursor:=0A=
opcode =3D program[cursor]=0A=
#if debug: print "opcode", cursor, opcode=0A=
topcode =3D type(opcode)=0A=
if topcode is StringType or topcode is InstanceType:=0A=
lastneedspace =3D needspace=0A=
needspace =3D 0=0A=
if topcode is InstanceType:=0A=
justStrings =3D 0=0A=
width =3D opcode.width(self)=0A=
needspace =3D 0=0A=
else:=0A=
saveopcode =3D opcode=0A=
opcode =3D opcode.strip() #string.strip(opcode)=0A=
if opcode:=0A=
width =3D stringWidth(opcode, fontName, fontSize)=0A=
else:=0A=
width =3D 0=0A=
if saveopcode and (width or currentLength):=0A=
# ignore white space at margin=0A=
needspace =3D (saveopcode[-1]=3D=3D" ")=0A=
else:=0A=
needspace =3D 0=0A=
fullwidth =3D width=0A=
if lastneedspace:=0A=
#spacewidth =3D stringWidth(" ", fontName, fontSize) =
#self.sizeOfWord(" ")=0A=
fullwidth =3D width + spacewidth=0A=
newlength =3D currentLength+fullwidth=0A=
if newlength>maxLength and not first: # always do at =
least one thing=0A=
# this word won't fit=0A=
#if debug:=0A=
# print "WORD", opcode, "wont fit, width", width, =
"fullwidth", fullwidth=0A=
# print " currentLength", currentLength, =
"newlength", newlength, "maxLength", maxLength=0A=
done =3D 1=0A=
lineIsFull =3D 1=0A=
else:=0A=
# fit the word: add a space then the word=0A=
if lastneedspace:=0A=
line.append( spacewidth ) # expandable space: =
positive=0A=
if opcode:=0A=
line.append( opcode )=0A=
if abs(width)>TOOSMALLSPACE:=0A=
line.append( -width ) # non expanding space: =
negative=0A=
currentLength =3D newlength=0A=
#print line=0A=
#stop=0A=
first =3D 0=0A=
elif topcode is FloatType:=0A=
justStrings =3D 0=0A=
aopcode =3D abs(opcode) # negative means non expanding=0A=
if aopcode>TOOSMALLSPACE:=0A=
nextLength =3D currentLength+aopcode=0A=
if nextLength>maxLength and not first: # always do =
at least one thing=0A=
#if debug: print "EXPLICIT spacer won't fit", =
maxLength, nextLength, opcode=0A=
done =3D 1=0A=
else:=0A=
if aopcode>TOOSMALLSPACE:=0A=
currentLength =3D nextLength=0A=
line.append(opcode)=0A=
first =3D 0=0A=
elif topcode is TupleType:=0A=
justStrings =3D 0=0A=
indicator =3D opcode[0]=0A=
#line.append(opcode)=0A=
if indicator=3D=3D"nextLine":=0A=
# advance to nextLine=0A=
#(i, endallmarks) =3D opcode=0A=
line.append(opcode)=0A=
cursor =3D cursor+1 # consume this element=0A=
terminated =3D done =3D 1=0A=
#if debug:=0A=
# print "nextLine encountered"=0A=
elif indicator=3D=3D"color":=0A=
# change fill color=0A=
oldcolor =3D self.fontColor=0A=
(i, colorname) =3D opcode=0A=
#print "opcode", opcode=0A=
if type(colorname) is StringType:=0A=
color =3D self.fontColor =3D getattr(colors, =
colorname)=0A=
else:=0A=
color =3D self.fontColor =3D colorname # assume =
its something sensible :)=0A=
line.append(opcode)=0A=
elif indicator=3D=3D"face":=0A=
# change font face=0A=
(i, fontname) =3D opcode=0A=
fontName =3D self.fontName =3D fontname=0A=
spacewidth =3D stringWidth(" ", fontName, fontSize) =
#self.sizeOfWord(" ")=0A=
line.append(opcode)=0A=
elif indicator=3D=3D"size":=0A=
# change font size=0A=
(i, fontsize) =3D opcode=0A=
size =3D abs(float(fontsize))=0A=
if type(fontsize) is StringType:=0A=
if fontsize[:1]=3D=3D"+":=0A=
fontSize =3D self.fontSize =3D self.fontSize =
+ size=0A=
elif fontsize[:1]=3D=3D"-":=0A=
fontSize =3D self.fontSize =3D self.fontSize =
- size=0A=
else:=0A=
fontSize =3D self.fontSize =3D size=0A=
else:=0A=
fontSize =3D self.fontSize =3D size=0A=
spacewidth =3D stringWidth(" ", fontName, fontSize) =
#self.sizeOfWord(" ")=0A=
line.append(opcode)=0A=
elif indicator=3D=3D"leading":=0A=
# change font leading=0A=
(i, leading) =3D opcode=0A=
self.leading =3D leading=0A=
line.append(opcode)=0A=
elif indicator=3D=3D"indent":=0A=
# increase the indent=0A=
(i, increment) =3D opcode=0A=
indent =3D self.indent =3D self.indent + increment=0A=
if first:=0A=
usedIndent =3D max(indent, usedIndent)=0A=
maxLength =3D totalLength - usedIndent - =
self.rightIndent=0A=
line.append(opcode)=0A=
elif indicator=3D=3D"push":=0A=
self.pushTextState()=0A=
line.append(opcode)=0A=
elif indicator=3D=3D"pop":=0A=
try:=0A=
self.popTextState()=0A=
except:=0A=
## print "stack fault near", cursor=0A=
## for i in program[max(0, cursor-10):cursor+10]:=0A=
## if i=3D=3Dcursor:=0A=
## print "***>>>",=0A=
## print i=0A=
raise=0A=
fontName =3D self.fontName=0A=
fontSize =3D self.fontSize=0A=
spacewidth =3D stringWidth(" ", fontName, fontSize) =
#self.sizeOfWord(" ")=0A=
line.append(opcode)=0A=
elif indicator=3D=3D"bullet":=0A=
(i, bullet, indent, font, size) =3D opcode=0A=
# adjust for base indent (only at format time -- =
only execute once)=0A=
indent =3D indent + self.baseindent=0A=
opcode =3D (i, bullet, indent, font, size)=0A=
if not first:=0A=
raise ValueError, "bullet not at beginning of =
line"=0A=
bulletwidth =3D float(stringWidth(bullet, font, =
size))=0A=
spacewidth =3D float(stringWidth(" ", font, size))=0A=
bulletmin =3D indent+spacewidth+bulletwidth=0A=
# decrease the line size to allow bullet=0A=
usedIndent =3D max(bulletmin, usedIndent)=0A=
if first:=0A=
maxLength =3D totalLength - usedIndent - =
self.rightIndent=0A=
line.append(opcode)=0A=
elif indicator=3D=3D"rightIndent":=0A=
# increase the right indent=0A=
(i, increment) =3D opcode=0A=
self.rightIndent =3D self.rightIndent+increment=0A=
if first:=0A=
maxLength =3D totalLength - usedIndent - =
self.rightIndent=0A=
line.append(opcode)=0A=
elif indicator=3D=3D"rise":=0A=
(i, rise) =3D opcode=0A=
newrise =3D self.rise =3D self.rise+rise=0A=
line.append(opcode)=0A=
elif indicator=3D=3D"align":=0A=
(i, alignment) =3D opcode=0A=
#if debug:=0A=
# print "SETTING ALIGNMENT", alignment=0A=
self.alignment =3D alignment=0A=
line.append(opcode)=0A=
elif indicator=3D=3D"lineOperation":=0A=
(i, handler) =3D opcode=0A=
line.append(opcode)=0A=
self.lineOpHandlers =3D self.lineOpHandlers + =
[handler] # fresh copy=0A=
elif indicator=3D=3D"endLineOperation":=0A=
(i, handler) =3D opcode=0A=
h =3D self.lineOpHandlers[:] # fresh copy=0A=
h.remove(handler)=0A=
self.lineOpHandlers =3D h=0A=
line.append(opcode)=0A=
=0A=
else:=0A=
raise ValueError, "at format time don't understand =
indicator "+repr(indicator)=0A=
else:=0A=
raise ValueError, "op must be string, float, instance, =
or tuple "+repr(opcode)=0A=
if not done:=0A=
cursor =3D cursor+1=0A=
#first =3D 0=0A=
## if debug:=0A=
## if done:=0A=
## print "DONE FLAG IS SET"=0A=
## if cursor>=3Dmaxcursor:=0A=
## print "AT END OF PROGRAM"=0A=
if not terminated:=0A=
line.append( ("nextLine", 0) )=0A=
#print "fitline", line=0A=
return (lineIsFull, line, cursor, currentLength, usedIndent, =
maxLength, justStrings)=0A=
def centerAlign(self, line, lineLength, maxLength):=0A=
diff =3D maxLength-lineLength=0A=
shift =3D diff/2.0=0A=
if shift>TOOSMALLSPACE:=0A=
return self.insertShift(line, shift)=0A=
return line=0A=
def rightAlign(self, line, lineLength, maxLength):=0A=
shift =3D maxLength-lineLength=0A=
#die=0A=
if shift>TOOSMALLSPACE:=0A=
return self.insertShift(line, shift)=0A=
return line=0A=
def insertShift(self, line, shift):=0A=
# insert shift just before first visible element in line=0A=
from types import StringType, InstanceType=0A=
result =3D []=0A=
first =3D 1=0A=
for e in line:=0A=
te =3D type(e)=0A=
if first and (te is StringType or te is InstanceType):=0A=
result.append(shift)=0A=
first =3D 0=0A=
result.append(e)=0A=
return result=0A=
def justifyAlign(self, line, lineLength, maxLength):=0A=
diff =3D maxLength-lineLength=0A=
# count EXPANDABLE SPACES AFTER THE FIRST VISIBLE=0A=
from types import InstanceType, StringType, TupleType, FloatType=0A=
spacecount =3D 0=0A=
visible =3D 0=0A=
for e in line:=0A=
te =3D type(e)=0A=
if te is FloatType and e>TOOSMALLSPACE and visible:=0A=
spacecount =3D spacecount+1=0A=
elif te is StringType or te is InstanceType:=0A=
visible =3D 1=0A=
#if debug: print "diff is", diff, "wordcount", wordcount #; die=0A=
if spacecount<1:=0A=
return line=0A=
shift =3D diff/float(spacecount)=0A=
if shift<=3DTOOSMALLSPACE:=0A=
#if debug: print "shift too small", shift=0A=
return line=0A=
first =3D 1=0A=
visible =3D 0=0A=
result =3D []=0A=
cursor =3D 0=0A=
nline =3D len(line)=0A=
while cursor<nline:=0A=
e =3D line[cursor]=0A=
te =3D type(e)=0A=
result.append(e)=0A=
if (te is InstanceType or te is StringType):=0A=
visible =3D 1=0A=
elif te is FloatType and e>TOOSMALLSPACE and visible:=0A=
expanded =3D e+shift=0A=
result[-1] =3D expanded=0A=
cursor =3D cursor+1=0A=
return result=0A=
=0A=
## if not first:=0A=
## #if debug: print "shifting", shift, e=0A=
## #result.append(shift)=0A=
## # add the shift in result before any start markers =
before e=0A=
## insertplace =3D len(result)-1=0A=
## done =3D 0=0A=
## myshift =3D shift=0A=
## while insertplace>0 and not done:=0A=
## beforeplace =3D insertplace-1=0A=
## beforething =3D result[beforeplace]=0A=
## thingtype =3D type(beforething)=0A=
## if thingtype is TupleType:=0A=
## indicator =3D beforething[0]=0A=
## if indicator=3D=3D"endLineOperation":=0A=
## done =3D 1=0A=
## elif debug:=0A=
## print "adding shift before", =
beforething=0A=
## elif thingtype is FloatType:=0A=
## myshift =3D myshift + beforething=0A=
## del result[beforeplace]=0A=
## else:=0A=
## done =3D 1=0A=
## if not done:=0A=
## insertplace =3D beforeplace=0A=
## result.insert(insertplace, myshift)=0A=
## first =3D 0=0A=
## cursor =3D cursor+1=0A=
## return result=0A=
def shrinkWrap(self, line):=0A=
# for non justified text, collapse adjacent text/shift's into =
single operations=0A=
#return line # for testing=0A=
result =3D []=0A=
index =3D 0=0A=
maxindex =3D len(line)=0A=
from types import FloatType, StringType, InstanceType=0A=
from string import join=0A=
while index<maxindex:=0A=
e =3D line[index]=0A=
te =3D type(e)=0A=
if te is StringType and index<maxindex-1:=0A=
# collect strings and floats=0A=
thestrings =3D [e]=0A=
thefloats =3D 0.0=0A=
index =3D index+1=0A=
nexte =3D line[index]=0A=
tnexte =3D type(nexte)=0A=
while index<maxindex and (tnexte is FloatType or tnexte =
is StringType):=0A=
# switch to expandable space if appropriate=0A=
if tnexte is FloatType:=0A=
if thefloats<0 and nexte>0:=0A=
thefloats =3D -thefloats=0A=
if nexte<0 and thefloats>0:=0A=
nexte =3D -nexte=0A=
thefloats =3D thefloats + nexte=0A=
elif tnexte is StringType:=0A=
thestrings.append(nexte)=0A=
index =3D index+1=0A=
if index<maxindex:=0A=
nexte =3D line[index]=0A=
tnexte =3D type(nexte)=0A=
# wrap up the result=0A=
s =3D string.join(thestrings)=0A=
result.append(s)=0A=
result.append(float(thefloats))=0A=
# back up for unhandled element=0A=
index =3D index-1=0A=
else:=0A=
result.append(e)=0A=
index =3D index+1=0A=
=0A=
return result=0A=
def cleanProgram(self, line):=0A=
"collapse adjacent spacings"=0A=
#return line # for debugging=0A=
result =3D []=0A=
last =3D 0=0A=
from types import FloatType, TupleType, StringType, InstanceType=0A=
for e in line:=0A=
if type(e) is FloatType:=0A=
# switch to expandable space if appropriate=0A=
if last<0 and e>0:=0A=
last =3D -last=0A=
if e<0 and last>0:=0A=
e =3D -e=0A=
last =3D float(last)+e=0A=
else:=0A=
if abs(last)>TOOSMALLSPACE:=0A=
result.append(last)=0A=
result.append(e)=0A=
last =3D 0=0A=
if last:=0A=
result.append(last)=0A=
# now go backwards and delete all floats occurring after all =
visible elements=0A=
## count =3D len(result)-1=0A=
## done =3D 0=0A=
## while count>0 and not done:=0A=
## e =3D result[count]=0A=
## te =3D type(e)=0A=
## if te is StringType or te is InstanceType or te is =
TupleType:=0A=
## done =3D 1=0A=
## elif te is FloatType:=0A=
## del result[count]=0A=
## count =3D count-1=0A=
# move end operations left and start operations left up to =
visibles=0A=
change =3D 1=0A=
rline =3D range(len(result)-1)=0A=
while change:=0A=
#print line=0A=
change =3D 0=0A=
for index in rline:=0A=
nextindex =3D index+1=0A=
this =3D result[index]=0A=
next =3D result[nextindex]=0A=
doswap =3D 0=0A=
tthis =3D type(this)=0A=
tnext =3D type(next)=0A=
# don't swap visibles=0A=
if tthis is StringType or tnext is StringType or this is =
InstanceType or tnext is InstanceType:=0A=
doswap =3D 0=0A=
# only swap two tuples if the second one is an end =
operation and the first is something else=0A=
elif tthis is TupleType:=0A=
thisindicator =3D this[0]=0A=
if tnext is TupleType:=0A=
nextindicator =3D next[0]=0A=
doswap =3D 0=0A=
if (nextindicator=3D=3D"endLineOperation" and =
thisindicator!=3D"endLineOperation"=0A=
and thisindicator!=3D"lineOperation"):=0A=
doswap =3D 1 # swap nonend<>end=0A=
elif tnext=3D=3DFloatType:=0A=
if thisindicator=3D=3D"lineOperation":=0A=
doswap =3D 1 # begin <> space=0A=
if doswap:=0A=
#print "swap", line[index],line[nextindex]=0A=
result[index] =3D next=0A=
result[nextindex] =3D this=0A=
change =3D 1=0A=
return result=0A=
def runOpCodes(self, program, canvas, textobject):=0A=
"render the line(s)"=0A=
#import types=0A=
from types import StringType, TupleType, InstanceType, FloatType=0A=
escape =3D canvas._escape=0A=
code =3D textobject._code=0A=
startstate =3D self.__dict__.copy()=0A=
font =3D None=0A=
size =3D None=0A=
# be sure to set them before using them (done lazily below)=0A=
#textobject.setFont(self.fontName, self.fontSize)=0A=
textobject.setFillColor(self.fontColor)=0A=
xstart =3D self.x=0A=
thislineindent =3D self.indent=0A=
thislinerightIndent =3D self.rightIndent=0A=
indented =3D 0=0A=
for opcode in program:=0A=
topcode =3D type(opcode)=0A=
if topcode is StringType or topcode is InstanceType:=0A=
if not indented:=0A=
if abs(thislineindent)>TOOSMALLSPACE:=0A=
#if debug: print "INDENTING", thislineindent=0A=
#textobject.moveCursor(thislineindent, 0)=0A=
code.append('%s Td' % fp_str(thislineindent, 0))=0A=
self.x =3D self.x + thislineindent=0A=
for handler in self.lineOpHandlers:=0A=
#handler.end_at(x, y, self, canvas, textobject) =
# finish, eg, underlining this line=0A=
handler.start_at(self.x, self.y, self, canvas, =
textobject) # start underlining the next=0A=
indented =3D 1=0A=
# lazily set font (don't do it again if not needed)=0A=
if font!=3Dself.fontName or size!=3Dself.fontSize:=0A=
font =3D self.fontName=0A=
size =3D self.fontSize=0A=
textobject.setFont(font, size)=0A=
if topcode is StringType:=0A=
#textobject.textOut(opcode)=0A=
text =3D escape(opcode)=0A=
code.append('(%s) Tj' % text)=0A=
else:=0A=
# drawable thing=0A=
opcode.execute(self, textobject, canvas)=0A=
elif topcode is FloatType:=0A=
# use abs value (ignore expandable marking)=0A=
opcode =3D abs(opcode)=0A=
if opcode>TOOSMALLSPACE:=0A=
#textobject.moveCursor(opcode, 0)=0A=
code.append('%s Td' % fp_str(opcode, 0))=0A=
self.x =3D self.x + opcode=0A=
elif topcode is TupleType:=0A=
indicator =3D opcode[0]=0A=
if indicator=3D=3D"nextLine":=0A=
# advance to nextLine=0A=
(i, endallmarks) =3D opcode=0A=
x =3D self.x=0A=
y =3D self.y=0A=
newy =3D self.y =3D self.y-self.leading=0A=
newx =3D self.x =3D xstart=0A=
thislineindent =3D self.indent=0A=
thislinerightIndent =3D self.rightIndent=0A=
indented =3D 0=0A=
for handler in self.lineOpHandlers:=0A=
handler.end_at(x, y, self, canvas, textobject) # =
finish, eg, underlining this line=0A=
#handler.start_at(newx, newy, self, canvas, =
textobject)) # start underlining the next=0A=
textobject.setTextOrigin(newx, newy)=0A=
elif indicator=3D=3D"color":=0A=
# change fill color=0A=
oldcolor =3D self.fontColor=0A=
(i, colorname) =3D opcode=0A=
#print "opcode", opcode=0A=
if type(colorname) is StringType:=0A=
color =3D self.fontColor =3D getattr(colors, =
colorname)=0A=
else:=0A=
color =3D self.fontColor =3D colorname # assume =
its something sensible :)=0A=
#if debug:=0A=
# print color.red, color.green, color.blue=0A=
# print dir(color)=0A=
#print "color is", color=0A=
#from reportlab.lib.colors import green=0A=
#if color is green: print "color is green"=0A=
if color!=3Doldcolor:=0A=
textobject.setFillColor(color)=0A=
elif indicator=3D=3D"face":=0A=
# change font face=0A=
(i, fontname) =3D opcode=0A=
self.fontName =3D fontname=0A=
#textobject.setFont(self.fontName, self.fontSize)=0A=
elif indicator=3D=3D"size":=0A=
# change font size=0A=
(i, fontsize) =3D opcode=0A=
size =3D abs(float(fontsize))=0A=
if type(fontsize) is StringType:=0A=
if fontsize[:1]=3D=3D"+":=0A=
fontSize =3D self.fontSize =3D self.fontSize =
+ size=0A=
elif fontsize[:1]=3D=3D"-":=0A=
fontSize =3D self.fontSize =3D self.fontSize =
- size=0A=
else:=0A=
fontSize =3D self.fontSize =3D size=0A=
else:=0A=
fontSize =3D self.fontSize =3D size=0A=
#(i, fontsize) =3D opcode=0A=
self.fontSize =3D fontSize=0A=
#textobject.setFont(self.fontName, self.fontSize)=0A=
elif indicator=3D=3D"leading":=0A=
# change font leading=0A=
(i, leading) =3D opcode=0A=
self.leading =3D leading=0A=
elif indicator=3D=3D"indent":=0A=
# increase the indent=0A=
(i, increment) =3D opcode=0A=
indent =3D self.indent =3D self.indent + increment=0A=
thislineindent =3D max(thislineindent, indent)=0A=
elif indicator=3D=3D"push":=0A=
self.pushTextState()=0A=
elif indicator=3D=3D"pop":=0A=
oldcolor =3D self.fontColor=0A=
oldfont =3D self.fontName=0A=
oldsize =3D self.fontSize=0A=
self.popTextState()=0A=
#if CAUSEERROR or oldfont!=3Dself.fontName or =
oldsize!=3Dself.fontSize:=0A=
# textobject.setFont(self.fontName, self.fontSize)=0A=
if oldcolor!=3Dself.fontColor:=0A=
textobject.setFillColor(self.fontColor)=0A=
elif indicator=3D=3D"wordSpacing":=0A=
(i, ws) =3D opcode=0A=
textobject.setWordSpace(ws)=0A=
elif indicator=3D=3D"bullet":=0A=
(i, bullet, indent, font, size) =3D opcode=0A=
if abs(self.x-xstart)>TOOSMALLSPACE:=0A=
raise ValueError, "bullet not at beginning of =
line"=0A=
bulletwidth =3D float(stringWidth(bullet, font, =
size))=0A=
spacewidth =3D float(stringWidth(" ", font, size))=0A=
bulletmin =3D indent+spacewidth+bulletwidth=0A=
# decrease the line size to allow bullet as needed=0A=
if bulletmin > thislineindent:=0A=
#if debug: print "BULLET IS BIG", bullet, =
bulletmin, thislineindent=0A=
thislineindent =3D bulletmin=0A=
textobject.moveCursor(indent, 0)=0A=
textobject.setFont(font, size)=0A=
textobject.textOut(bullet)=0A=
textobject.moveCursor(-indent, 0)=0A=
#textobject.textOut("M")=0A=
textobject.setFont(self.fontName, self.fontSize)=0A=
elif indicator=3D=3D"rightIndent":=0A=
# increase the right indent=0A=
(i, increment) =3D opcode=0A=
self.rightIndent =3D self.rightIndent+increment=0A=
elif indicator=3D=3D"rise":=0A=
(i, rise) =3D opcode=0A=
newrise =3D self.rise =3D self.rise+rise=0A=
textobject.setRise(newrise)=0A=
elif indicator=3D=3D"align":=0A=
(i, alignment) =3D opcode=0A=
self.alignment =3D alignment=0A=
elif indicator=3D=3D"lineOperation":=0A=
(i, handler) =3D opcode=0A=
handler.start_at(self.x, self.y, self, canvas, =
textobject)=0A=
#self.lineOpHandlers.append(handler)=0A=
#if debug: print "adding", handler, =
self.lineOpHandlers=0A=
self.lineOpHandlers =3D self.lineOpHandlers + =
[handler] # fresh copy!=0A=
elif indicator=3D=3D"endLineOperation":=0A=
(i, handler) =3D opcode=0A=
handler.end_at(self.x, self.y, self, canvas, =
textobject)=0A=
newh =3D self.lineOpHandlers =3D =
self.lineOpHandlers[:] # fresh copy=0A=
#if debug: print "removing", handler, =
self.lineOpHandlers=0A=
if handler in newh:=0A=
self.lineOpHandlers.remove(handler)=0A=
else:=0A=
pass=0A=
#print "WARNING: HANDLER", handler, "NOT IN", =
newh=0A=
else:=0A=
raise ValueError, "don't understand indicator =
"+repr(indicator)=0A=
else:=0A=
raise ValueError, "op must be string float or tuple =
"+repr(opcode)=0A=
laststate =3D self.__dict__.copy()=0A=
#self.resetState(startstate)=0A=
self.__dict__.update(startstate)=0A=
return laststate=0A=
=0A=
def stringLine(line, length):=0A=
"simple case: line with just strings and spacings which can be =
ignored"=0A=
strings =3D []=0A=
from types import StringType=0A=
for x in line:=0A=
if type(x) is StringType:=0A=
strings.append(x)=0A=
text =3D string.join(strings)=0A=
result =3D [text, float(length)]=0A=
nextlinemark =3D ("nextLine", 0)=0A=
if line and line[-1]=3D=3Dnextlinemark:=0A=
result.append( nextlinemark )=0A=
return result=0A=
=0A=
def simpleJustifyAlign(line, currentLength, maxLength):=0A=
"simple justification with only strings"=0A=
strings =3D []=0A=
from types import StringType=0A=
for x in line[:-1]:=0A=
if type(x) is StringType:=0A=
strings.append(x)=0A=
nspaces =3D len(strings)-1=0A=
slack =3D maxLength-currentLength=0A=
text =3D string.join(strings)=0A=
if nspaces>0 and slack>0:=0A=
wordspacing =3D slack/float(nspaces)=0A=
result =3D [("wordSpacing", wordspacing), text, maxLength, =
("wordSpacing", 0)]=0A=
else:=0A=
result =3D [text, currentLength, ("nextLine", 0)]=0A=
nextlinemark =3D ("nextLine", 0)=0A=
if line and line[-1]=3D=3Dnextlinemark:=0A=
result.append( nextlinemark )=0A=
return result=0A=
=0A=
from reportlab.lib.colors import black=0A=
=0A=
def readBool(text):=0A=
if string.upper(text) in ("Y", "YES", "TRUE", "1"):=0A=
return 1=0A=
elif string.upper(text) in ("N", "NO", "FALSE", "0"):=0A=
return 0=0A=
else:=0A=
raise RMLError, "true/false attribute has illegal value '%s'" % =
text=0A=
=0A=
def readAlignment(text):=0A=
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_RIGHT, =
TA_JUSTIFY=0A=
up =3D string.upper(text)=0A=
if up =3D=3D 'LEFT':=0A=
return TA_LEFT=0A=
elif up =3D=3D 'RIGHT':=0A=
return TA_RIGHT=0A=
elif up in ['CENTER', 'CENTRE']:=0A=
return TA_CENTER=0A=
elif up =3D=3D 'JUSTIFY':=0A=
return TA_JUSTIFY=0A=
=0A=
def readLength(text):=0A=
"""Read a dimension measurement: accept "3in", "5cm",=0A=
"72 pt" and so on."""=0A=
text =3D string.strip(text)=0A=
try:=0A=
return float(text)=0A=
except ValueError:=0A=
text =3D string.lower(text)=0A=
numberText, units =3D text[:-2],text[-2:]=0A=
numberText =3D string.strip(numberText)=0A=
try:=0A=
number =3D float(numberText)=0A=
except ValueError:=0A=
raise ValueError, "invalid length attribute '%s'" % text=0A=
try:=0A=
multiplier =3D {=0A=
'in':72,=0A=
'cm':28.3464566929, #72/2.54; is this accurate?=0A=
'mm':2.83464566929,=0A=
'pt':1=0A=
}[units]=0A=
except KeyError:=0A=
raise RMLError, "invalid length attribute '%s'" % text=0A=
=0A=
return number * multiplier=0A=
=0A=
def lengthSequence(s, converter=3DreadLength):=0A=
"""from "(2, 1)" or "2,1" return [2,1], for example"""=0A=
from string import split, strip=0A=
s =3D strip(s)=0A=
if s[:1]=3D=3D"(" and s[-1:]=3D=3D")":=0A=
s =3D s[1:-1]=0A=
sl =3D split(s, ",")=0A=
sl =3D map(strip, sl)=0A=
sl =3D map(converter, sl)=0A=
return sl=0A=
=0A=
=0A=
def readColor(text):=0A=
"""Read color names or tuples, RGB or CMYK, and return a Color =
object."""=0A=
if not text:=0A=
return None=0A=
from reportlab.lib import colors=0A=
if text[0] in string.letters:=0A=
return colors.__dict__[text]=0A=
tup =3D lengthSequence(text)=0A=
=0A=
msg =3D "Color tuple must have 3 (or 4) elements for RGB (or CMYC)."=0A=
assert 3 <=3D len(tup) <=3D 4, msg=0A=
msg =3D "Color tuple must have all elements <=3D 1.0."=0A=
for i in range(len(tup)):=0A=
assert tup[i] <=3D 1.0, msg=0A=
=0A=
if len(tup) =3D=3D 3:=0A=
colClass =3D colors.Color=0A=
elif len(tup) =3D=3D 4:=0A=
colClass =3D colors.CMYKColor=0A=
return apply(colClass, tup)=0A=
=0A=
class StyleAttributeConverters:=0A=
fontSize=3D[readLength]=0A=
leading=3D[readLength]=0A=
leftIndent=3D[readLength]=0A=
rightIndent=3D[readLength]=0A=
firstLineIndent=3D[readLength]=0A=
alignment=3D[readAlignment]=0A=
spaceBefore=3D[readLength]=0A=
spaceAfter=3D[readLength]=0A=
bulletFontSize=3D[readLength]=0A=
bulletIndent=3D[readLength]=0A=
textColor=3D[readColor]=0A=
backColor=3D[readColor]=0A=
=0A=
class SimpleStyle:=0A=
"simplified paragraph style without all the fancy stuff"=0A=
name =3D "basic"=0A=
fontName=3D'Times-Roman'=0A=
fontSize=3D10=0A=
leading=3D12=0A=
leftIndent=3D0=0A=
rightIndent=3D0=0A=
firstLineIndent=3D0=0A=
alignment=3DTA_LEFT=0A=
spaceBefore=3D0=0A=
spaceAfter=3D0=0A=
bulletFontName=3D'Times-Roman'=0A=
bulletFontSize=3D10=0A=
bulletIndent=3D0=0A=
textColor=3Dblack=0A=
backColor=3DNone=0A=
def __init__(self, name, parent=3DNone, **kw):=0A=
mydict =3D self.__dict__=0A=
if parent:=0A=
for (a,b) in parent.__dict__.items():=0A=
mydict[a]=3Db=0A=
for (a,b) in kw.items():=0A=
mydict[a] =3D b=0A=
def addAttributes(self, dictionary):=0A=
for key in dictionary.keys():=0A=
value =3D dictionary[key]=0A=
if value is not None:=0A=
if hasattr(StyleAttributeConverters, key):=0A=
converter =3D getattr(StyleAttributeConverters, =
key)[0]=0A=
value =3D converter(value)=0A=
setattr(self, key, value)=0A=
=0A=
=0A=
DEFAULT_ALIASES =3D {=0A=
"h1.defaultStyle": "Heading1",=0A=
"h2.defaultStyle": "Heading2",=0A=
"h3.defaultStyle": "Heading3",=0A=
"title.defaultStyle": "Title",=0A=
"para.defaultStyle": "Normal",=0A=
"pre.defaultStyle": "Code",=0A=
"li.defaultStyle": "Definition",=0A=
}=0A=
=0A=
class FastPara(Flowable):=0A=
"paragraph with no special features (not even a single ampersand!)"=0A=
def __init__(self, style, simpletext):=0A=
#if debug:=0A=
# print "FAST", id(self)=0A=
if "&" in simpletext:=0A=
raise ValueError, "no ampersands please!"=0A=
self.style =3D style=0A=
self.simpletext =3D simpletext=0A=
self.lines =3D None=0A=
=0A=
def wrap(self, availableWidth, availableHeight):=0A=
simpletext =3D self.simpletext=0A=
self.availableWidth =3D availableWidth=0A=
style =3D self.style=0A=
text =3D self.simpletext=0A=
rightIndent =3D style.rightIndent=0A=
leftIndent =3D style.leftIndent=0A=
leading =3D style.leading=0A=
font =3D style.fontName=0A=
size =3D style.fontSize=0A=
firstindent =3D style.firstLineIndent=0A=
#textcolor =3D style.textColor=0A=
words =3D string.split(simpletext)=0A=
lines =3D []=0A=
from reportlab.pdfbase.pdfmetrics import stringWidth=0A=
spacewidth =3D stringWidth(" ", font, size)=0A=
currentline =3D []=0A=
currentlength =3D 0=0A=
firstmaxlength =3D availableWidth - rightIndent - firstindent=0A=
maxlength =3D availableWidth - rightIndent - leftIndent=0A=
if maxlength<spacewidth:=0A=
return (spacewidth+rightIndent+firstindent, availableHeight) =
# need something wider than this!=0A=
if availableHeight<leading:=0A=
return (availableWidth, leading) # need something longer=0A=
if self.lines is None:=0A=
heightused =3D 0=0A=
cursor =3D 0=0A=
nwords =3D len(words)=0A=
done =3D 0=0A=
#heightused =3D leading # ???=0A=
while cursor<nwords and not done:=0A=
thismaxlength =3D maxlength=0A=
if not lines:=0A=
thismaxlength =3D firstmaxlength=0A=
thisword =3D words[cursor]=0A=
thiswordsize =3D stringWidth(thisword, font, size)=0A=
if currentlength:=0A=
thiswordsize =3D thiswordsize+spacewidth=0A=
nextlength =3D currentlength + thiswordsize=0A=
if not currentlength or nextlength<maxlength:=0A=
# add the word=0A=
cursor =3D cursor+1=0A=
currentlength =3D nextlength=0A=
currentline.append(thisword)=0A=
#print "currentline", currentline=0A=
else:=0A=
# emit the line=0A=
lines.append( (string.join(currentline), =
currentlength, len(currentline)) )=0A=
currentline =3D []=0A=
currentlength =3D 0=0A=
heightused =3D heightused+leading=0A=
if heightused+leading>availableHeight:=0A=
done =3D 1=0A=
if currentlength and not done:=0A=
lines.append( (string.join(currentline), currentlength, =
len(currentline) ))=0A=
heightused =3D heightused+leading=0A=
self.lines =3D lines=0A=
self.height =3D heightused=0A=
remainder =3D self.remainder =3D string.join(words[cursor:])=0A=
#print "lines", lines=0A=
#print "remainder is", remainder=0A=
else:=0A=
remainder =3D None=0A=
heightused =3D self.height=0A=
lines =3D self.lines=0A=
if remainder:=0A=
result =3D (availableWidth, availableHeight+leading) # need =
to split=0A=
else:=0A=
result =3D (availableWidth, heightused)=0A=
#if debug: print "wrap is", (availableWidth, availableHeight), =
result, len(lines)=0A=
return result=0A=
def split(self, availableWidth, availableHeight):=0A=
style =3D self.style=0A=
leading =3D style.leading=0A=
if availableHeight<leading:=0A=
return [] # not enough space for split=0A=
lines =3D self.lines=0A=
if lines is None:=0A=
raise ValueError, "must wrap before split"=0A=
remainder =3D self.remainder=0A=
if remainder:=0A=
next =3D FastPara(style, remainder)=0A=
return [self,next]=0A=
else:=0A=
return [self]=0A=
=0A=
def draw(self):=0A=
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_RIGHT, =
TA_JUSTIFY=0A=
style =3D self.style=0A=
lines =3D self.lines=0A=
rightIndent =3D style.rightIndent=0A=
leftIndent =3D style.leftIndent=0A=
leading =3D style.leading=0A=
font =3D style.fontName=0A=
size =3D style.fontSize=0A=
alignment =3D style.alignment=0A=
firstindent =3D style.firstLineIndent=0A=
c =3D self.canv=0A=
escape =3D c._escape=0A=
#if debug:=0A=
# print "FAST", id(self), "page number", c.getPageNumber()=0A=
height =3D self.height=0A=
#if debug:=0A=
# c.rect(0,0,-1, height-size, fill=3D1, stroke=3D1)=0A=
c.translate(0, height-size)=0A=
textobject =3D c.beginText()=0A=
code =3D textobject._code=0A=
#textobject.setTextOrigin(0,firstindent)=0A=
textobject.setFont(font, size)=0A=
if style.textColor:=0A=
textobject.setFillColor(style.textColor)=0A=
y =3D 0=0A=
basicWidth =3D self.availableWidth - rightIndent - leftIndent =
#dwh added - leftIndent=0A=
count =3D 0=0A=
nlines =3D len(lines)=0A=
while count<nlines:=0A=
(text, length, nwords) =3D lines[count]=0A=
count =3D count+1=0A=
if alignment=3D=3DTA_LEFT:=0A=
if count =3D=3D 1:=0A=
x =3D firstindent + leftIndent=0A=
else:=0A=
x =3D leftIndent=0A=
elif alignment=3D=3DTA_CENTER:=0A=
extra =3D basicWidth - length=0A=
x =3D leftIndent + extra/2.0=0A=
elif alignment=3D=3DTA_RIGHT:=0A=
extra =3D basicWidth - length=0A=
x =3D (firstindent + leftIndent) + extra=0A=
elif alignment=3D=3DTA_JUSTIFY:=0A=
if count =3D=3D 1:=0A=
x =3D (firstindent + leftIndent)=0A=
else:=0A=
x =3D leftIndent=0A=
if count<nlines and nwords>1:=0A=
# patch from doug@pennatus.com, 9 Nov 2002, no =
extraspace on last line=0A=
if count =3D=3D 1: =
#dwh=0A=
=
textobject.setWordSpace((basicWidth-firstindent-length)/(nwords-1.0)) =
#dwh=0A=
else: #dwh=0A=
=
textobject.setWordSpace((basicWidth-length)/(nwords-1.0))=0A=
else:=0A=
textobject.setWordSpace(0.0)=0A=
textobject.setTextOrigin(x,y)=0A=
text =3D escape(text)=0A=
code.append('(%s) Tj' % text)=0A=
#textobject.textOut(text)=0A=
y =3D y-leading=0A=
c.drawText(textobject)=0A=
def getSpaceBefore(self):=0A=
#if debug:=0A=
# print "got space before", self.spaceBefore=0A=
return self.style.spaceBefore=0A=
def getSpaceAfter(self):=0A=
#print "got space after", self.spaceAfter=0A=
return self.style.spaceAfter=0A=
=0A=
def defaultContext():=0A=
result =3D {}=0A=
from reportlab.lib.styles import getSampleStyleSheet=0A=
styles =3D getSampleStyleSheet()=0A=
for (stylenamekey, stylenamevalue) in DEFAULT_ALIASES.items():=0A=
result[stylenamekey] =3D styles[stylenamevalue]=0A=
return result=0A=
=0A=
class Para(Flowable):=0A=
spaceBefore =3D 0=0A=
spaceAfter =3D 0=0A=
def __init__(self, style, parsedText=3DNone, bulletText=3DNone, =
state=3DNone, context=3DNone, baseindent=3D0):=0A=
#print id(self), "para", parsedText=0A=
self.baseindent =3D baseindent=0A=
if context is None:=0A=
context =3D defaultContext()=0A=
self.context =3D context=0A=
self.parsedText =3D parsedText=0A=
self.bulletText =3D bulletText=0A=
self.style1 =3D style # make sure Flowable doesn't use this =
unless wanted! call it style1 NOT style=0A=
#self.spaceBefore =3D self.spaceAfter =3D 0=0A=
self.program =3D [] # program before layout=0A=
self.formattedProgram =3D [] # after layout=0A=
self.remainder =3D None # follow on paragraph if any=0A=
self.state =3D state # initial formatting state (for completions)=0A=
if not state:=0A=
self.spaceBefore =3D style.spaceBefore=0A=
self.spaceAfter =3D style.spaceAfter=0A=
#self.spaceBefore =3D "invalid value"=0A=
#if hasattr(self, "spaceBefore") and debug:=0A=
# print "spaceBefore is", self.spaceBefore, self.parsedText=0A=
self.bold =3D 0=0A=
self.italic =3D 0=0A=
self.face =3D "times"=0A=
self.size =3D 10=0A=
def getSpaceBefore(self):=0A=
#if debug:=0A=
# print "got space before", self.spaceBefore=0A=
return self.spaceBefore=0A=
def getSpaceAfter(self):=0A=
#print "got space after", self.spaceAfter=0A=
return self.spaceAfter=0A=
def wrap(self, availableWidth, availableHeight):=0A=
if debug:=0A=
print "WRAPPING", id(self), availableWidth, availableHeight=0A=
print " ", self.formattedProgram=0A=
print " ", self.program=0A=
self.availableHeight =3D availableHeight=0A=
self.myengine =3D p =3D paragraphEngine()=0A=
p.baseindent =3D self.baseindent # for shifting bullets as needed=0A=
parsedText =3D self.parsedText=0A=
formattedProgram =3D self.formattedProgram=0A=
state =3D self.state=0A=
if state:=0A=
leading =3D state["leading"]=0A=
else:=0A=
leading =3D self.style1.leading=0A=
program =3D self.program=0A=
self.cansplit =3D 1 # until proven otherwise=0A=
if state:=0A=
p.resetState(state)=0A=
p.x =3D 0=0A=
p.y =3D 0=0A=
needatleast =3D state["leading"]=0A=
else:=0A=
needatleast =3D self.style1.leading=0A=
if availableHeight<=3Dneedatleast:=0A=
self.cansplit =3D 0=0A=
#if debug:=0A=
# print "CANNOT COMPILE, NEED AT LEAST", needatleast, =
'AVAILABLE', availableHeight=0A=
return (availableHeight+1, availableWidth) # cannot split=0A=
if parsedText is None and program is None:=0A=
raise ValueError, "need parsedText for formatting"=0A=
if not program:=0A=
self.program =3D program =3D self.compileProgram(parsedText)=0A=
if not self.formattedProgram:=0A=
(formattedProgram, remainder, laststate, heightused) =3D =
p.format(availableWidth, availableHeight, program, leading)=0A=
self.formattedProgram =3D formattedProgram=0A=
self.height =3D heightused=0A=
self.laststate =3D laststate=0A=
self.remainderProgram =3D remainder=0A=
else:=0A=
heightused =3D self.height=0A=
remainder =3D None=0A=
# too big if there is a remainder=0A=
if remainder:=0A=
# lie about the height: it must be split anyway=0A=
#if debug:=0A=
# print "I need to split", self.formattedProgram=0A=
# print "heightused", heightused, "available", =
availableHeight, "remainder", len(remainder)=0A=
height =3D availableHeight + 1=0A=
#print "laststate is", laststate=0A=
#print "saving remainder", remainder=0A=
self.remainder =3D Para(self.style1, parsedText=3DNone, =
bulletText=3DNone, state=3Dlaststate)=0A=
self.remainder.program =3D remainder=0A=
self.remainder.spaceAfter =3D self.spaceAfter=0A=
self.spaceAfter =3D 0=0A=
else:=0A=
self.remainder =3D None # no extra=0A=
height =3D heightused=0A=
if height>availableHeight:=0A=
height =3D availableHeight-0.1=0A=
#if debug:=0A=
# print "giving height", height, "of", availableHeight, =
self.parsedText=0A=
result =3D (availableWidth, height)=0A=
if debug:=0A=
(w, h) =3D result=0A=
if abs(availableHeight-h)<0.2:=0A=
print "exact match???" + repr(availableHeight, h)=0A=
print "wrap is", (availableWidth, availableHeight), result=0A=
return result=0A=
def split(self, availableWidth, availableHeight):=0A=
#if debug:=0A=
# print "SPLITTING", id(self), availableWidth, availableHeight=0A=
if availableHeight<=3D0 or not self.cansplit:=0A=
#if debug:=0A=
# print "cannot split", availableWidth, "too small"=0A=
return [] # wrap failed to find a split=0A=
self.availableHeight =3D availableHeight=0A=
formattedProgram =3D self.formattedProgram=0A=
#print "formattedProgram is", formattedProgram=0A=
if formattedProgram is None:=0A=
raise ValueError, "must call wrap before split"=0A=
elif not formattedProgram:=0A=
# no first line in self: fail to split=0A=
return []=0A=
remainder =3D self.remainder=0A=
if remainder:=0A=
#print "SPLITTING"=0A=
result=3D [self, remainder]=0A=
else:=0A=
result=3D [self]=0A=
#if debug: print "split is", result=0A=
return result=0A=
def draw(self):=0A=
p =3D self.myengine #paragraphEngine()=0A=
formattedProgram =3D self.formattedProgram=0A=
if formattedProgram is None:=0A=
raise ValueError, "must call wrap before draw"=0A=
state =3D self.state=0A=
laststate =3D self.laststate=0A=
if state:=0A=
p.resetState(state)=0A=
p.x =3D 0=0A=
p.y =3D 0=0A=
c =3D self.canv=0A=
#if debug:=0A=
# print id(self), "page number", c.getPageNumber()=0A=
height =3D self.height=0A=
if state:=0A=
leading =3D state["leading"]=0A=
else:=0A=
leading =3D self.style1.leading=0A=
#if debug:=0A=
# c.rect(0,0,-1, height-self.size, fill=3D1, stroke=3D1)=0A=
c.translate(0, height-self.size)=0A=
t =3D c.beginText()=0A=
#t.setTextOrigin(0,0)=0A=
if DUMPPROGRAM or debug:=0A=
print "=3D"*44, "now running program"=0A=
for x in formattedProgram:=0A=
print x=0A=
print "-"*44=0A=
laststate =3D p.runOpCodes(formattedProgram, c, t)=0A=
#print laststate["x"], laststate["y"]=0A=
c.drawText(t)=0A=
=0A=
def compileProgram(self, parsedText, program=3DNone):=0A=
style =3D self.style1=0A=
# standard parameters=0A=
#program =3D self.program=0A=
if program is None:=0A=
program =3D []=0A=
a =3D program.append=0A=
fn =3D style.fontName=0A=
# add style information if there was no initial state=0A=
a( ("face", fn ) )=0A=
from reportlab.lib.fonts import ps2tt=0A=
(self.face, self.bold, self.italic) =3D ps2tt(fn)=0A=
a( ("size", style.fontSize ) )=0A=
self.size =3D style.fontSize=0A=
a( ("align", style.alignment ) )=0A=
a( ("indent", style.leftIndent ) )=0A=
if style.firstLineIndent:=0A=
a( ("indent", style.firstLineIndent ) ) # must be undone =
later=0A=
a( ("rightIndent", style.rightIndent ) )=0A=
a( ("leading", style.leading) )=0A=
if style.textColor:=0A=
a( ("color", style.textColor) )=0A=
#a( ("nextLine", 0) ) # clear for next line=0A=
if self.bulletText:=0A=
self.do_bullet(self.bulletText, program)=0A=
self.compileComponent(parsedText, program)=0A=
# now look for a place where to insert the unindent after the =
first line=0A=
if style.firstLineIndent:=0A=
count =3D 0=0A=
from types import StringType, InstanceType=0A=
for x in program:=0A=
count =3D count+1=0A=
tx =3D type(x)=0A=
if tx is StringType or tx is InstanceType:=0A=
break=0A=
program.insert( count, ("indent", -style.firstLineIndent ) ) =
# defaults to end if no visibles=0A=
#print "=3D"*8, id(self), "program is"=0A=
#for x in program:=0A=
# print x=0A=
## print "=3D"*11=0A=
## # check pushes and pops=0A=
## stackcount =3D 0=0A=
## dump =3D 0=0A=
## from types import TupleType=0A=
## for x in program:=0A=
## if dump:=0A=
## print "dump:", x=0A=
## if type(x) is TupleType:=0A=
## i =3D x[0]=0A=
## if i=3D=3D"push":=0A=
## stackcount =3D stackcount+1=0A=
## print " "*stackcount, "push", stackcount=0A=
## if i=3D=3D"pop":=0A=
## stackcount =3D stackcount-1=0A=
## print " "*stackcount, "pop", stackcount=0A=
## if stackcount<0:=0A=
## dump=3D1=0A=
## print "STACK UNDERFLOW!"=0A=
## if dump: stop=0A=
return program=0A=
def linearize(self, program =3D None, parsedText=3DNone):=0A=
#print "LINEARIZING", self=0A=
#program =3D self.program =3D []=0A=
if parsedText is None:=0A=
parsedText =3D self.parsedText=0A=
style =3D self.style1=0A=
if program is None:=0A=
program =3D []=0A=
program.append( ("push",) )=0A=
if style.spaceBefore:=0A=
program.append( ("leading", style.spaceBefore+style.leading) =
)=0A=
else:=0A=
program.append( ("leading", style.leading) )=0A=
program.append( ("nextLine", 0) )=0A=
program =3D self.compileProgram(parsedText, program=3Dprogram)=0A=
program.append( ("pop",) )=0A=
# go to old margin=0A=
program.append( ("push",) )=0A=
if style.spaceAfter:=0A=
program.append( ("leading", style.spaceAfter) )=0A=
else:=0A=
program.append( ("leading", 0) )=0A=
program.append( ("nextLine", 0) )=0A=
program.append( ("pop",) )=0A=
def compileComponent(self, parsedText, program):=0A=
import types=0A=
ttext =3D type(parsedText)=0A=
#program =3D self.program=0A=
if ttext is types.StringType:=0A=
# handle special characters here...=0A=
# short cut=0A=
if parsedText:=0A=
stext =3D parsedText.strip() #string.strip(parsedText)=0A=
if not stext:=0A=
program.append(" ") # contract whitespace to single =
space=0A=
else:=0A=
handleSpecialCharacters(self, parsedText, program)=0A=
elif ttext is types.ListType:=0A=
for e in parsedText:=0A=
self.compileComponent(e, program)=0A=
elif ttext is types.TupleType:=0A=
(tagname, attdict, content, extra) =3D parsedText=0A=
if not attdict:=0A=
attdict =3D {}=0A=
compilername =3D "compile_"+tagname=0A=
compiler =3D getattr(self, compilername, None)=0A=
if compiler is not None:=0A=
compiler(attdict, content, extra, program)=0A=
else:=0A=
# just pass the tag through=0A=
if debug:=0A=
L =3D [ "<" + tagname ]=0A=
a =3D L.append=0A=
if not attdict: attdict =3D {}=0A=
for (k, v) in attdict.items():=0A=
a(" %s=3D%s" % (k,v))=0A=
if content:=0A=
a(">")=0A=
a(str(content))=0A=
a("</%s>" % tagname)=0A=
else:=0A=
a("/>")=0A=
t =3D string.join(L, "")=0A=
handleSpecialCharacters(self, t, program)=0A=
else:=0A=
raise ValueError, "don't know how to handle tag " + =
repr(tagname)=0A=
def shiftfont(self, program, face=3DNone, bold=3DNone, =
italic=3DNone):=0A=
oldface =3D self.face=0A=
oldbold =3D self.bold=0A=
olditalic =3D self.italic=0A=
oldfontinfo =3D (oldface, oldbold, olditalic)=0A=
if face is None: face =3D oldface=0A=
if bold is None: bold =3D oldbold=0A=
if italic is None: italic =3D olditalic=0A=
self.face =3D face=0A=
self.bold =3D bold=0A=
self.italic =3D italic=0A=
from reportlab.lib.fonts import tt2ps=0A=
font =3D tt2ps(face,bold,italic)=0A=
oldfont =3D tt2ps(oldface,oldbold,olditalic)=0A=
if font!=3Doldfont:=0A=
program.append( ("face", font ) )=0A=
return oldfontinfo=0A=
=0A=
def compile_(self, attdict, content, extra, program):=0A=
# "anonymous" tag: just do the content=0A=
for e in content:=0A=
self.compileComponent(e, program)=0A=
#compile_para =3D compile_ # at least for now...=0A=
def compile_pageNumber(self, attdict, content, extra, program):=0A=
program.append(PageNumberObject())=0A=
def compile_b(self, attdict, content, extra, program):=0A=
(f,b,i) =3D self.shiftfont(program, bold=3D1)=0A=
for e in content:=0A=
self.compileComponent(e, program)=0A=
self.shiftfont(program, bold=3Db)=0A=
def compile_i(self, attdict, content, extra, program):=0A=
(f,b,i) =3D self.shiftfont(program, italic=3D1)=0A=
for e in content:=0A=
self.compileComponent(e, program)=0A=
self.shiftfont(program, italic=3Di)=0A=
def compile_u(self, attdict, content, extra, program):=0A=
# XXXX must eventually add things like alternative colors=0A=
#program =3D self.program=0A=
program.append( ('lineOperation', UNDERLINE) )=0A=
for e in content:=0A=
self.compileComponent(e, program)=0A=
program.append( ('endLineOperation', UNDERLINE) )=0A=
def compile_sub(self, attdict, content, extra, program):=0A=
size =3D self.size=0A=
self.size =3D newsize =3D size * 0.7=0A=
rise =3D size*0.5=0A=
#program =3D self.program=0A=
program.append( ('size', newsize) )=0A=
self.size =3D size=0A=
program.append( ('rise', -rise) )=0A=
for e in content:=0A=
self.compileComponent(e, program)=0A=
program.append( ('size', size) )=0A=
program.append( ('rise', rise) )=0A=
=0A=
def compile_ul(self, attdict, content, extra, program, =
tagname=3D"ul"):=0A=
# by transformation=0A=
#print "compile", tagname, attdict=0A=
atts =3D attdict.copy()=0A=
bulletmaker =3D bulletMaker(tagname, atts, self.context)=0A=
# now do each element as a separate paragraph=0A=
import types=0A=
for e in content:=0A=
te =3D type(e)=0A=
if te is types.StringType:=0A=
if e.strip(): #string.strip(e):=0A=
raise ValueError, "don't expect CDATA between list =
elements"=0A=
elif te is types.TupleType:=0A=
(tagname, attdict1, content1, extra) =3D e=0A=
if tagname!=3D"li":=0A=
raise ValueError, "don't expect %s inside list" % =
repr(tagname)=0A=
newatts =3D atts.copy()=0A=
if attdict1:=0A=
newatts.update(attdict1)=0A=
bulletmaker.makeBullet(newatts)=0A=
self.compile_para(newatts, content1, extra, program)=0A=
=0A=
def compile_ol(self, attdict, content, extra, program):=0A=
return self.compile_ul(attdict, content, extra, program, =
tagname=3D"ol")=0A=
=0A=
def compile_dl(self, attdict, content, extra, program):=0A=
# by transformation=0A=
#print "compile", tagname, attdict=0A=
atts =3D attdict.copy()=0A=
# by transformation=0A=
#print "compile", tagname, attdict=0A=
atts =3D attdict.copy()=0A=
bulletmaker =3D bulletMaker("dl", atts, self.context)=0A=
# now do each element as a separate paragraph=0A=
import types=0A=
contentcopy =3D list(content) # copy for destruction=0A=
bullet =3D ""=0A=
while contentcopy:=0A=
e =3D contentcopy[0]=0A=
del contentcopy[0]=0A=
te =3D type(e)=0A=
if te is types.StringType:=0A=
if e.strip(): #string.strip(e):=0A=
raise ValueError, "don't expect CDATA between list =
elements"=0A=
elif not contentcopy:=0A=
break # done at ending whitespace=0A=
else:=0A=
continue # ignore intermediate whitespace=0A=
elif te is types.TupleType:=0A=
(tagname, attdict1, content1, extra) =3D e=0A=
if tagname!=3D"dd" and tagname!=3D"dt":=0A=
raise ValueError, "don't expect %s here inside list, =
expect 'dd' or 'dt'" % repr(tagname)=0A=
if tagname=3D=3D"dt":=0A=
if bullet:=0A=
raise ValueError, "dt will not be displayed =
unless followed by a dd: "+repr(bullet)=0A=
if content1:=0A=
if len(content1)!=3D1:=0A=
raise ValueError, "only simple strings =
supported in dd content currently: "+repr(content1)=0A=
bullet =3D content1[0]=0A=
if type(bullet) is not types.StringType:=0A=
raise ValueError, "only simple strings =
supported in dd content currently: "+repr(content1)=0A=
elif tagname=3D=3D"dd":=0A=
newatts =3D atts.copy()=0A=
if attdict1:=0A=
newatts.update(attdict1)=0A=
bulletmaker.makeBullet(newatts, bl=3Dbullet)=0A=
self.compile_para(newatts, content1, extra, program)=0A=
bullet =3D "" # don't use this bullet again=0A=
if bullet:=0A=
raise ValueError, "dt will not be displayed unless followed =
by a dd"+repr(bullet)=0A=
=0A=
def compile_super(self, attdict, content, extra, program):=0A=
size =3D self.size=0A=
self.size =3D newsize =3D size * 0.7=0A=
rise =3D size*0.5=0A=
#program =3D self.program=0A=
program.append( ('size', newsize) )=0A=
program.append( ('rise', rise) )=0A=
for e in content:=0A=
self.compileComponent(e, program)=0A=
program.append( ('size', size) )=0A=
self.size =3D size=0A=
program.append( ('rise', -rise) )=0A=
def compile_font(self, attdict, content, extra, program):=0A=
#program =3D self.program=0A=
program.append( ("push",) ) # store current data=0A=
if attdict.has_key("face"):=0A=
face =3D attdict["face"]=0A=
from reportlab.lib.fonts import tt2ps=0A=
try:=0A=
font =3D tt2ps(face,self.bold,self.italic)=0A=
except:=0A=
font =3D face # better work!=0A=
program.append( ("face", font ) )=0A=
if attdict.has_key("color"):=0A=
colorname =3D attdict["color"]=0A=
program.append( ("color", colorname) )=0A=
if attdict.has_key("size"):=0A=
#size =3D float(attdict["size"]) # really should convert =
int, cm etc here!=0A=
size =3D attdict["size"]=0A=
program.append( ("size", size) )=0A=
for e in content:=0A=
self.compileComponent(e, program)=0A=
program.append( ("pop",) ) # restore as before=0A=
def compile_a(self, attdict, content, extra, program):=0A=
url =3D attdict["href"]=0A=
colorname =3D attdict.get("color", "blue")=0A=
#program =3D self.program=0A=
Link =3D HotLink(url)=0A=
program.append( ("push",) ) # store current data=0A=
program.append( ("color", colorname) )=0A=
program.append( ('lineOperation', Link) )=0A=
program.append( ('lineOperation', UNDERLINE) )=0A=
for e in content:=0A=
self.compileComponent(e, program)=0A=
program.append( ('endLineOperation', UNDERLINE) )=0A=
program.append( ('endLineOperation', Link) )=0A=
program.append( ("pop",) ) # restore as before=0A=
def compile_link(self, attdict, content, extra, program):=0A=
dest =3D attdict["destination"]=0A=
colorname =3D attdict.get("color", None)=0A=
#program =3D self.program=0A=
Link =3D InternalLink(dest)=0A=
program.append( ("push",) ) # store current data=0A=
if colorname:=0A=
program.append( ("color", colorname) )=0A=
program.append( ('lineOperation', Link) )=0A=
program.append( ('lineOperation', UNDERLINE) )=0A=
for e in content:=0A=
self.compileComponent(e, program)=0A=
program.append( ('endLineOperation', UNDERLINE) )=0A=
program.append( ('endLineOperation', Link) )=0A=
program.append( ("pop",) ) # restore as before=0A=
def compile_setLink(self, attdict, content, extra, program):=0A=
dest =3D attdict["destination"]=0A=
colorname =3D attdict.get("color", "blue")=0A=
#program =3D self.program=0A=
Link =3D DefDestination(dest)=0A=
program.append( ("push",) ) # store current data=0A=
if colorname:=0A=
program.append( ("color", colorname) )=0A=
program.append( ('lineOperation', Link) )=0A=
if colorname:=0A=
program.append( ('lineOperation', UNDERLINE) )=0A=
for e in content:=0A=
self.compileComponent(e, program)=0A=
if colorname:=0A=
program.append( ('endLineOperation', UNDERLINE) )=0A=
program.append( ('endLineOperation', Link) )=0A=
program.append( ("pop",) ) # restore as before=0A=
#def compile_p(self, attdict, content, extra, program):=0A=
# # have to be careful about base indent here!=0A=
# not finished=0A=
def compile_bullet(self, attdict, content, extra, program):=0A=
from types import StringType=0A=
### eventually should allow things like images and graphics in =
bullets too XXXX=0A=
if len(content)!=3D1 or type(content[0]) is not StringType:=0A=
raise ValueError, "content for bullet must be a single =
string"=0A=
text =3D content[0]=0A=
self.do_bullet(text, program)=0A=
def do_bullet(self, text, program):=0A=
style =3D self.style1=0A=
#program =3D self.program=0A=
indent =3D style.bulletIndent + self.baseindent=0A=
font =3D style.bulletFontName=0A=
size =3D style.bulletFontSize=0A=
program.append( ("bullet", text, indent, font, size) )=0A=
def compile_greek(self, attdict, content, extra, program):=0A=
self.compile_font({"face": "symbol"}, content, extra, program)=0A=
def compile_evalString(self, attdict, content, extra, program):=0A=
program.append( EvalStringObject(attdict, content, extra, =
self.context) )=0A=
def compile_name(self, attdict, content, extra, program):=0A=
program.append( NameObject(attdict, content, extra, =
self.context) )=0A=
def compile_getName(self, attdict, content, extra, program):=0A=
program.append( GetNameObject(attdict, content, extra, =
self.context) )=0A=
def compile_seq(self, attdict, content, extra, program):=0A=
program.append( SeqObject(attdict, content, extra, self.context) =
)=0A=
def compile_seqReset(self, attdict, content, extra, program):=0A=
program.append( SeqResetObject(attdict, content, extra, =
self.context) )=0A=
def compile_seqDefault(self, attdict, content, extra, program):=0A=
program.append( SeqDefaultObject(attdict, content, extra, =
self.context) )=0A=
def compile_para(self, attdict, content, extra, program, stylename =
=3D "para.defaultStyle"):=0A=
context =3D self.context=0A=
stylename =3D attdict.get("style", stylename)=0A=
style =3D context[stylename]=0A=
newstyle =3D SimpleStyle(name=3D"rml2pdf internal embedded =
style", parent=3Dstyle)=0A=
newstyle.addAttributes(attdict)=0A=
bulletText =3D attdict.get("bulletText", None)=0A=
mystyle =3D self.style1=0A=
#newstyle.bulletIndent =3D =
mystyle.leftIndent+newstyle.bulletIndent=0A=
#print "attdict", attdict=0A=
#print "leftindent, baseindent", mystyle.leftIndent=0A=
#print "bulletIndent", newstyle.bulletIndent=0A=
thepara =3D Para(newstyle, content, context=3Dcontext, =
bulletText=3DbulletText) # possible ref loop on context, break later=0A=
# now compile it and add it to the program=0A=
mybaseindent =3D self.baseindent=0A=
self.baseindent =3D thepara.baseindent =3D mystyle.leftIndent + =
self.baseindent=0A=
thepara.linearize(program=3Dprogram)=0A=
#print "program so far"=0A=
#for x in program:=0A=
# print x=0A=
program.append( ("nextLine", 0) )=0A=
self.baseindent =3D mybaseindent=0A=
def compile_tt(self, attdict, content, extra, program):=0A=
(f,b,i) =3D self.shiftfont(program, face=3D"Courier")=0A=
for e in content:=0A=
self.compileComponent(e, program)=0A=
self.shiftfont(program, face=3Df)=0A=
=0A=
=0A=
class bulletMaker:=0A=
def __init__(self, tagname, atts, context):=0A=
self.tagname =3D tagname=0A=
#print "context is", context=0A=
style =3D "li.defaultStyle"=0A=
self.style =3D style =3D atts.get("style", style)=0A=
typ =3D {"ul": "disc", "ol": "1", "dl": None}[tagname]=0A=
#print tagname, "bulletmaker type is", typ=0A=
self.typ =3Dtyp =3D atts.get("type", typ)=0A=
#print tagname, "bulletmaker type is", typ=0A=
if not atts.has_key("leftIndent"):=0A=
# get the style so you can choose an indent length=0A=
thestyle =3D context[style]=0A=
from reportlab.pdfbase.pdfmetrics import stringWidth=0A=
size =3D thestyle.fontSize=0A=
indent =3D stringWidth("XXX", "Courier", size)=0A=
atts["leftIndent"] =3D str(indent)=0A=
self.count =3D 0=0A=
def makeBullet(self, atts, bl=3DNone):=0A=
count =3D self.count =3D self.count+1=0A=
typ =3D self.typ=0A=
tagname =3D self.tagname=0A=
#print "makeBullet", tagname, typ, count=0A=
# forget space before for non-first elements=0A=
if count>1:=0A=
atts["spaceBefore"] =3D "0"=0A=
if bl is None:=0A=
if tagname=3D=3D"ul":=0A=
if typ=3D=3D"disc": bl =3D chr(109)=0A=
elif typ=3D=3D"circle": bl =3D chr(108)=0A=
elif typ=3D=3D"square": bl =3D chr(110)=0A=
else:=0A=
raise ValueError, "unordered list type %s not =
implemented" % repr(typ)=0A=
if not atts.has_key("bulletFontName"):=0A=
atts["bulletFontName"] =3D "ZapfDingbats"=0A=
elif tagname=3D=3D"ol":=0A=
if typ=3D=3D"1": bl =3D repr(count)=0A=
elif typ=3D=3D"a":=0A=
theord =3D ord("a")+count-1=0A=
bl =3D chr(theord)=0A=
elif typ=3D=3D"A":=0A=
theord =3D ord("A")+count-1=0A=
bl =3D chr(theord)=0A=
else:=0A=
raise ValueError, "ordered bullet type %s not =
implemented" % repr(typ)=0A=
else:=0A=
raise ValueError, "bad tagname "+repr(tagname)=0A=
if not atts.has_key("bulletText"):=0A=
atts["bulletText"] =3D bl=0A=
if not atts.has_key("style"):=0A=
atts["style"] =3D self.style=0A=
=0A=
class EvalStringObject:=0A=
"this will only work if rml2pdf is present"=0A=
tagname =3D "evalString"=0A=
def __init__(self, attdict, content, extra, context):=0A=
if not attdict:=0A=
attdict =3D {}=0A=
self.attdict =3D attdict=0A=
self.content =3D content=0A=
self.context =3D context=0A=
self.extra =3D extra=0A=
def getOp(self, tuple, engine):=0A=
from rlextra.rml2pdf.rml2pdf import Controller=0A=
#print "tuple", tuple=0A=
op =3D self.op =3D Controller.processTuple(tuple, self.context, =
{})=0A=
return op=0A=
def width(self, engine):=0A=
from reportlab.pdfbase.pdfmetrics import stringWidth=0A=
content =3D self.content=0A=
if not content:=0A=
content =3D []=0A=
tuple =3D (self.tagname, self.attdict, content, self.extra)=0A=
op =3D self.op =3D self.getOp(tuple, engine)=0A=
#print op.__class__=0A=
#print op.pcontent=0A=
#print self=0A=
s =3D str(op)=0A=
return stringWidth(s, engine.fontName, engine.fontSize)=0A=
def execute(self, engine, textobject, canvas):=0A=
textobject.textOut(str(self.op))=0A=
=0A=
class SeqObject(EvalStringObject):=0A=
def getOp(self, tuple, engine):=0A=
from reportlab.lib.sequencer import getSequencer=0A=
globalsequencer =3D getSequencer()=0A=
attr =3D self.attdict=0A=
#if it has a template, use that; otherwise try for id;=0A=
#otherwise take default sequence=0A=
if attr.has_key('template'):=0A=
templ =3D attr['template']=0A=
op =3D self.op =3D templ % globalsequencer=0A=
return op=0A=
elif attr.has_key('id'):=0A=
id =3D attr['id']=0A=
else:=0A=
id =3D None=0A=
op =3D self.op =3D globalsequencer.nextf(id)=0A=
return op=0A=
=0A=
class NameObject(EvalStringObject):=0A=
tagname =3D "name"=0A=
def execute(self, engine, textobject, canvas):=0A=
pass # name doesn't produce any output=0A=
=0A=
class SeqDefaultObject(NameObject):=0A=
def getOp(self, tuple, engine):=0A=
from reportlab.lib.sequencer import getSequencer=0A=
globalsequencer =3D getSequencer()=0A=
attr =3D self.attdict=0A=
try:=0A=
default =3D attr['id']=0A=
except KeyError:=0A=
default =3D None=0A=
globalsequencer.setDefaultCounter(default)=0A=
self.op =3D ""=0A=
return ""=0A=
=0A=
class SeqResetObject(NameObject):=0A=
def getOp(self, tuple, engine):=0A=
from reportlab.lib.sequencer import getSequencer=0A=
import math=0A=
globalsequencer =3D getSequencer()=0A=
attr =3D self.attdict=0A=
try:=0A=
id =3D attr['id']=0A=
except KeyError:=0A=
id =3D None=0A=
try:=0A=
base =3D math.atoi(attr['base'])=0A=
except:=0A=
base=3D0=0A=
globalsequencer.reset(id, base)=0A=
self.op =3D ""=0A=
return ""=0A=
=0A=
class GetNameObject(EvalStringObject):=0A=
tagname =3D "getName"=0A=
=0A=
class PageNumberObject:=0A=
def __init__(self, example=3D"XXX"):=0A=
self.example =3D example # XXX SHOULD ADD THE ABILITY TO PASS IN =
EXAMPLES=0A=
def width(self, engine):=0A=
from reportlab.pdfbase.pdfmetrics import stringWidth=0A=
return stringWidth(self.example, engine.fontName, =
engine.fontSize)=0A=
def execute(self, engine, textobject, canvas):=0A=
n =3D canvas.getPageNumber()=0A=
textobject.textOut(str(n))=0A=
=0A=
### this should be moved into rml2pdf=0A=
def EmbedInRml2pdf():=0A=
"make the para the default para implementation in rml2pdf"=0A=
from rlextra.rml2pdf.rml2pdf import MapNode, Controller # may not =
need to use superclass?=0A=
global paraMapper, theParaMapper, ulMapper=0A=
=0A=
class paraMapper(MapNode):=0A=
#stylename =3D "para.defaultStyle"=0A=
def translate(self, nodetuple, controller, context, overrides):=0A=
(tagname, attdict, content, extra) =3D nodetuple=0A=
stylename =3D tagname+".defaultStyle"=0A=
stylename =3D attdict.get("style", stylename)=0A=
style =3D context[stylename]=0A=
mystyle =3D SimpleStyle(name=3D"rml2pdf internal style", =
parent=3Dstyle)=0A=
mystyle.addAttributes(attdict)=0A=
bulletText =3D attdict.get("bulletText", None)=0A=
# can we use the fast implementation?=0A=
import types=0A=
result =3D None=0A=
if not bulletText and len(content)=3D=3D1:=0A=
text =3D content[0]=0A=
if type(text) is types.StringType and "&" not in text:=0A=
result =3D FastPara(mystyle, text)=0A=
if result is None:=0A=
result =3D Para(mystyle, content, context=3Dcontext, =
bulletText=3DbulletText) # possible ref loop on context, break later=0A=
return result=0A=
=0A=
theParaMapper =3D paraMapper()=0A=
=0A=
class ulMapper(MapNode):=0A=
# wrap in a default para and let the para do it=0A=
def translate(self, nodetuple, controller, context, overrides):=0A=
thepara =3D ("para", {}, [nodetuple], None)=0A=
return theParaMapper.translate(thepara, controller, context, =
overrides)=0A=
=0A=
# override rml2pdf interpreters (should be moved to rml2pdf)=0A=
theListMapper =3D ulMapper()=0A=
Controller["ul"] =3D theListMapper=0A=
Controller["ol"] =3D theListMapper=0A=
Controller["dl"] =3D theListMapper=0A=
Controller["para"] =3D theParaMapper=0A=
Controller["h1"] =3D theParaMapper=0A=
Controller["h2"] =3D theParaMapper=0A=
Controller["h3"] =3D theParaMapper=0A=
Controller["title"] =3D theParaMapper=0A=
=0A=
=0A=
testparagraph =3D """=0A=
This is Text.=0A=
<b>This is bold text.</b>=0A=
This is Text.=0A=
<i>This is italic text.</i>=0A=
=0A=
<ul>=0A=
<li> this is an element at 1=0A=
more text and even more text and on and on and so forth=0A=
more text and even more text and on and on and so forth=0A=
more text and even more text and on and on and so forth=0A=
more text and even more text and on and on and so forth=0A=
more text and even more text and on and on and so forth=0A=
=0A=
<ul>=0A=
<li> this is an element at 2=0A=
=0A=
more text and even more text and on and on and so forth=0A=
more text and even more text and on and on and so forth=0A=
=0A=
<ul>=0A=
<li> this is an element at 3=0A=
=0A=
more text and even more text and on and on and so forth=0A=
=0A=
=0A=
<dl bulletFontName=3D"Helvetica-BoldOblique" =
spaceBefore=3D"10" spaceAfter=3D"10">=0A=
<dt>frogs</dt> <dd>Little green slimy things. Delicious =
with <b>garlic</b></dd>=0A=
<dt>kittens</dt> <dd>cute, furry, not edible</dd>=0A=
<dt>bunnies</dt> <dd>cute, furry,. Delicious with =
<b>garlic</b></dd>=0A=
</dl>=0A=
=0A=
more text and even more text and on and on and so forth=0A=
=0A=
<ul>=0A=
<li> this is an element at 4=0A=
=0A=
more text and even more text and on and on and so forth=0A=
more text and even more text and on and on and so forth=0A=
=0A=
</li>=0A=
</ul>=0A=
=0A=
more text and even more text and on and on and so forth=0A=
more text and even more text and on and on and so forth=0A=
=0A=
</li>=0A=
</ul>=0A=
more text and even more text and on and on and so forth=0A=
more text and even more text and on and on and so forth=0A=
=0A=
</li>=0A=
=0A=
</ul>=0A=
<u><b>UNDERLINED</b> more text and even more text and on and on and so =
forth=0A=
more text and even more text and on and on and so forth</u>=0A=
=0A=
<ol type=3D"a">=0A=
<li>first element of the alpha list=0A=
=0A=
<ul type=3D"square">=0A=
<li>first element of the square unnumberred list</li>=0A=
=0A=
<li>second element of the unnumberred list</li>=0A=
=0A=
<li>third element of the unnumberred list=0A=
third element of the unnumberred list=0A=
third element of the unnumberred list=0A=
third element of the unnumberred list=0A=
third element of the unnumberred list=0A=
third element of the unnumberred list=0A=
third element of the unnumberred list=0A=
</li>=0A=
=0A=
<li>fourth element of the unnumberred list</li>=0A=
=0A=
</ul>=0A=
=0A=
</li>=0A=
=0A=
<li>second element of the alpha list</li>=0A=
=0A=
<li>third element of the alpha list=0A=
third element of the unnumberred list=0A=
third element of the unnumberred list=0A=
third element of the unnumberred list=0A=
third element of the unnumberred list=0A=
third element of the unnumberred list=0A=
third element of the unnumberred list=0A=
</li>=0A=
=0A=
<li>fourth element of the alpha list</li>=0A=
=0A=
</ol>=0A=
=0A=
=0A=
</li>=0A=
</ul>=0A=
=0A=
<a href=3D"http://www.reportlab.com">goto www.reportlab.com</a>.=0A=
=0A=
=0A=
<para alignment=3D"justify">=0A=
<font color=3D"red" size=3D"15">R</font>ed letter. thisisareallylongword =
andsoisthis andthisislonger=0A=
justified text paragraph example=0A=
justified text paragraph example=0A=
justified text paragraph example=0A=
</para>=0A=
=0A=
"""=0A=
=0A=
def test2(canv):=0A=
#print test_program; return=0A=
from reportlab.lib.units import inch=0A=
from reportlab.lib.styles import ParagraphStyle=0A=
from reportlab.lib import rparsexml=0A=
parsedpara =3D rparsexml.parsexmlSimple(testparagraph)=0A=
S =3D ParagraphStyle("Normal", None)=0A=
P =3D Para(S, parsedpara)=0A=
(w, h) =3D P.wrap(5*inch, 10*inch)=0A=
print "wrapped as", (h,w)=0A=
canv.translate(1*inch, 1*inch)=0A=
canv.rect(0,0,5*inch,10*inch, fill=3D0, stroke=3D1)=0A=
P.canv =3D canv=0A=
P.draw()=0A=
canv.setStrokeColorRGB(1, 0, 0)=0A=
#canv.translate(0, 3*inch)=0A=
canv.rect(0,0,w,-h, fill=3D0, stroke=3D1)=0A=
#characters not supported: epsi, Gammad, gammad, kappav, rhov, Upsi, upsi=0A=
greeks =3D {=0A=
'alpha':'a',=0A=
'beta':'b',=0A=
'chi':'c',=0A=
'Delta':'D',=0A=
'delta':'d',=0A=
'epsiv':'e',=0A=
'eta':'h',=0A=
'Gamma':'G',=0A=
'gamma':'g',=0A=
'iota':'i',=0A=
'kappa':'k',=0A=
'Lambda':'L',=0A=
'lambda':'l',=0A=
'mu':'m',=0A=
'nu':'n',=0A=
'Omega':'W',=0A=
'omega':'w',=0A=
'omicron':'x',=0A=
'Phi':'F',=0A=
'phi':'f',=0A=
'phiv':'j',=0A=
'Pi':'P',=0A=
'pi':'p',=0A=
'piv':'v',=0A=
'Psi':'Y',=0A=
'psi':'y',=0A=
'rho':'r',=0A=
'Sigma':'S',=0A=
'sigma':'s',=0A=
'sigmav':'V',=0A=
'tau':'t',=0A=
'Theta':'Q',=0A=
'theta':'q',=0A=
'thetav':'j',=0A=
'Xi':'X',=0A=
'xi':'x',=0A=
'zeta':'z',=0A=
'amp': '&',=0A=
'lt': '<',=0A=
'gt': '>',=0A=
# ... more?=0A=
}=0A=
=0A=
def handleSpecialCharacters(engine, text, program=3DNone, =
greeks=3Dgreeks):=0A=
from string import whitespace=0A=
# add space prefix if space here=0A=
if text[0:1] in whitespace:=0A=
program.append(" ")=0A=
#print "handling", repr(text)=0A=
# shortcut=0A=
if 0 and "&" not in text:=0A=
result =3D []=0A=
for x in text.split():=0A=
result.append(x+" ")=0A=
if result:=0A=
last =3D result[-1]=0A=
if text[-1:] not in whitespace:=0A=
result[-1] =3D last.strip()=0A=
program.extend(result)=0A=
return program=0A=
if program is None:=0A=
program =3D []=0A=
amptext =3D text.split("&")=0A=
first =3D 1=0A=
lastfrag =3D amptext[-1]=0A=
for fragment in amptext:=0A=
if not first:=0A=
# check for special chars=0A=
semi =3D string.find(fragment, ";")=0A=
if semi>0:=0A=
name =3D fragment[:semi]=0A=
if greeks.has_key(name):=0A=
fragment =3D fragment[semi+1:]=0A=
greeksub =3D greeks[name]=0A=
(f,b,i) =3D engine.shiftfont(program, =
face=3D"symbol")=0A=
program.append(greeksub)=0A=
engine.shiftfont(program, face=3Df)=0A=
if fragment and fragment[0] in string.whitespace:=0A=
program.append(" ") # follow the greek with a =
space=0A=
else:=0A=
# add back the &=0A=
fragment =3D "&"+fragment=0A=
else:=0A=
# add back the &=0A=
fragment =3D "&"+fragment=0A=
# add white separated components of fragment followed by space=0A=
sfragment =3D fragment.split()=0A=
for w in sfragment[:-1]:=0A=
program.append(w+" ")=0A=
# does the last one need a space?=0A=
if sfragment and fragment:=0A=
# reader 3 used to go nuts if you don't special case the =
last frag, but it's fixed?=0A=
if fragment[-1] in string.whitespace: # or =
fragment=3D=3Dlastfrag:=0A=
program.append( sfragment[-1]+" " )=0A=
else:=0A=
last =3D sfragment[-1].strip() =
#string.strip(sfragment[-1])=0A=
if last:=0A=
#print "last is", repr(last)=0A=
program.append( last )=0A=
first =3D 0=0A=
#print "HANDLED", program=0A=
return program=0A=
=0A=
def Paragraph(text, style, bulletText=3DNone, frags=3DNone):=0A=
""" Paragraph(text, style, bulletText=3DNone)=0A=
intended to be like a platypus Paragraph but better.=0A=
"""=0A=
# if there is no & or < in text then use the fast paragraph=0A=
if "&" not in text and "<" not in text:=0A=
return FastPara(style, simpletext=3Dtext)=0A=
else:=0A=
# use the fully featured one.=0A=
from reportlab.lib import rparsexml=0A=
parsedpara =3D rparsexml.parsexmlSimple(text)=0A=
return Para(style, parsedText=3Dparsedpara, =
bulletText=3DbulletText, state=3DNone)=0A=
=0A=
##class Paragraph(Para):=0A=
## """ Paragraph(text, style, bulletText=3DNone)=0A=
## intended to be like a platypus Paragraph but better.=0A=
## """=0A=
## def __init__(self, text, style, bulletText =3D None, frags=3DNone):=0A=
## # frags is dummy for compatibility=0A=
## # add a space at end for reasons I cannot fathom=0A=
## text =3D text+" "=0A=
## if debug:=0A=
## print "::: COMPILING TEXT :::"=0A=
## print text=0A=
## print ":::"=0A=
## from rlextra.radxml import rparsexml=0A=
## parsedpara =3D rparsexml.parsexmlSimple(text)=0A=
## if debug:=0A=
## print "=3D=3D=3D compiles to"=0A=
## print parsedpara=0A=
## print "=3D=3D=3D"=0A=
## Para.__init__(self, style, parsedText=3Dparsedpara, =
bulletText=3DbulletText, state=3DNone)=0A=
=0A=
class UnderLineHandler:=0A=
def __init__(self, color=3DNone):=0A=
self.color =3D color=0A=
def start_at(self, x,y, para, canvas, textobject):=0A=
self.xStart =3D x=0A=
self.yStart =3D y=0A=
def end_at(self, x, y, para, canvas, textobject):=0A=
offset =3D para.fontSize/8.0=0A=
canvas.saveState()=0A=
color =3D self.color=0A=
if self.color is None:=0A=
color =3D para.fontColor=0A=
canvas.setStrokeColor(color)=0A=
canvas.line(self.xStart, self.yStart-offset, x,y-offset)=0A=
canvas.restoreState()=0A=
=0A=
UNDERLINE =3D UnderLineHandler()=0A=
=0A=
class HotLink(UnderLineHandler):=0A=
def __init__(self, url):=0A=
self.url =3D url=0A=
def end_at(self, x, y, para, canvas, textobject):=0A=
fontsize =3D para.fontSize=0A=
rect =3D [self.xStart, self.yStart, x,y+fontsize]=0A=
if debug:=0A=
print "LINKING RECTANGLE", rect=0A=
#canvas.rect(self.xStart, self.yStart, =
x-self.xStart,y+fontsize-self.yStart, stroke=3D1)=0A=
self.link(rect, canvas)=0A=
def link(self, rect, canvas):=0A=
canvas.linkURL(self.url, rect, relative=3D1)=0A=
=0A=
class InternalLink(HotLink):=0A=
def link(self, rect, canvas):=0A=
destinationname =3D self.url=0A=
contents =3D ""=0A=
canvas.linkRect(contents, destinationname, rect, Border=3D"[0 0 =
0]")=0A=
=0A=
class DefDestination(HotLink):=0A=
defined =3D 0=0A=
def link(self, rect, canvas):=0A=
destinationname =3D self.url=0A=
if not self.defined:=0A=
[x, y, x1, y1] =3D rect=0A=
canvas.bookmarkHorizontal(destinationname, x,y1) # use the =
upper y=0A=
self.defined =3D 1=0A=
=0A=
def splitspace(text):=0A=
# split on spacing but include spaces at element ends=0A=
stext =3D string.split(text)=0A=
result =3D []=0A=
for e in stext:=0A=
result.append(e+" ")=0A=
return result=0A=
=0A=
testlink =3D HotLink("http://www.reportlab.com")=0A=
=0A=
test_program =3D [=0A=
('push',),=0A=
('indent', 100),=0A=
('rightIndent', 200),=0A=
('bullet', 'very long bullet', 50, 'Courier', 14),=0A=
('align', TA_CENTER),=0A=
('face', "Times-Roman"),=0A=
('size', 12),=0A=
('leading', 14),=0A=
] + splitspace("This is the first segment of the =
first paragraph.") + [=0A=
('lineOperation', testlink),=0A=
]+splitspace("HOTLINK This is the first segment of =
the first paragraph. This is the first segment of the first paragraph. =
This is the first segment of the first paragraph. This is the first =
segment of the first paragraph. ") + [=0A=
('endLineOperation', testlink),=0A=
('nextLine', 0),=0A=
('align', TA_LEFT),=0A=
('bullet', 'Bullet', 10, 'Courier', 8),=0A=
('face', "Times-Roman"),=0A=
('size', 12),=0A=
('leading', 14),=0A=
] + splitspace("This is the SECOND!!! segment of the =
first paragraph. This is the first segment of the first paragraph. This =
is the first segment of the first paragraph. This is the first segment =
of the first paragraph. This is the first segment of the first =
paragraph. ") + [=0A=
('nextLine', 0),=0A=
('align', TA_JUSTIFY),=0A=
('bullet', 'Bullet not quite as long this time', 50, =
'Courier', 8),=0A=
('face', "Helvetica-Oblique"),=0A=
('size', 12),=0A=
('leading', 14),=0A=
('push',),=0A=
('color', 'red'),=0A=
] + splitspace("This is the THIRD!!! segment of the =
first paragraph."=0A=
) + [=0A=
('lineOperation', UNDERLINE),=0A=
] + splitspace("This is the first segment of the =
first paragraph. This is the first segment of the first paragraph. This =
is the first segment of the first paragraph. This is the first segment =
of the first paragraph. ") + [=0A=
('endLineOperation', UNDERLINE),=0A=
('rise', 5),=0A=
"raised ", "text ",=0A=
('rise', -10),=0A=
"lowered ", "text ",=0A=
('rise', 5),=0A=
"normal ", "text ",=0A=
('pop',),=0A=
('indent', 100),=0A=
('rightIndent', 50),=0A=
('nextLine', 0),=0A=
('align', TA_RIGHT),=0A=
('bullet', 'O', 50, 'Courier', 14),=0A=
('face', "Helvetica"),=0A=
('size', 12),=0A=
('leading', 14),=0A=
] + splitspace("And this is the remainder of the =
paragraph indented further. a a a a a a a a And this is the remainder of =
the paragraph indented further. a a a a a a a a And this is the =
remainder of the paragraph indented further. a a a a a a a a And this is =
the remainder of the paragraph indented further. a a a a a a a a And =
this is the remainder of the paragraph indented further. a a a a a a a a =
And this is the remainder of the paragraph indented further. a a a a a a =
a a And this is the remainder of the paragraph indented further. a a a a =
a a a a ") + [=0A=
('pop',),=0A=
('nextLine', 0),]=0A=
=0A=
=0A=
def test():=0A=
from pprint import pprint=0A=
#print test_program; return=0A=
from reportlab.pdfgen import canvas=0A=
from reportlab.lib.units import inch=0A=
fn =3D "paratest0.pdf"=0A=
c =3D canvas.Canvas(fn)=0A=
test2(c)=0A=
c.showPage()=0A=
if 1:=0A=
remainder =3D test_program + test_program + test_program=0A=
laststate =3D {}=0A=
while remainder:=0A=
print "NEW PAGE"=0A=
c.translate(inch, 8*inch)=0A=
t =3D c.beginText()=0A=
t.setTextOrigin(0,0)=0A=
p =3D paragraphEngine()=0A=
p.resetState(laststate)=0A=
p.x =3D 0=0A=
p.y =3D 0=0A=
maxwidth =3D 7*inch=0A=
maxheight =3D 500=0A=
(formattedprogram, remainder, laststate, height) =3D =
p.format(maxwidth, maxheight, remainder)=0A=
if debug:=0A=
pprint( formattedprogram )#; return=0A=
laststate =3D p.runOpCodes(formattedprogram, c, t)=0A=
c.drawText(t)=0A=
c.showPage()=0A=
print "=3D"*30, "x=3D", laststate["x"], "y=3D", =
laststate["y"]=0A=
c.save()=0A=
print fn=0A=
=0A=
if __name__=3D=3D"__main__":=0A=
test()=0A=
------=_NextPart_000_0002_01C309B9.1B4C70A0--