[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">
            &#187;%(title)s, page %(page)s&#171;
        </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 &#150; 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 &#150; 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 &#150; '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 &#150; 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) &#150; define the 'inner rectangle' within
                    which content flows
                </para>
                <para style="Indent">
                    border (defaults to 'false'): whether to show
                    a frame border &#150; 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' &#150; 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 &#150; "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" &#150; Paragraphs
                </para>
                <para style="BodyText">
                    Paragraphs are used for wrapping text.  They are very simple
                    &#150; 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') &#150;
                    reference to stylesheet
                </para>
                <para style="Indent">
                    bullettext &#150;
                    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') &#150;
                    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" &#150; 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" &#150; 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) &#150; name of a ReportLab
                        tablestyle  defined in the stylesheet.
                </para>
                <para style="Indent">
                    <b>
                        colDelim
                    </b>
                    (optional) &#150; the column
                      delimiter string for bulk data; defaults to a comma.
                </para>
                <para style="Indent">
                    <b>
                        rowDelim
                    </b>
                    (optional) &#150; 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 &#150; "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 &#150; a subset of SVG &#150; 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 &#150;
                    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 &#150; 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 &#150; 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--