--- a/reportlab/platypus/doctemplate.py Fri Dec 15 16:58:02 2000 +0000
+++ b/reportlab/platypus/doctemplate.py Fri Dec 15 16:59:02 2000 +0000
@@ -1,12 +1,12 @@
#copyright ReportLab Inc. 2000
#see license.txt for license details
#history http://cvs.sourceforge.net/cgi-bin/cvsweb.cgi/reportlab/platypus/doctemplate.py?cvsroot=reportlab
-#$Header: /tmp/reportlab/reportlab/platypus/doctemplate.py,v 1.31 2000/11/23 14:01:58 andy_robinson Exp $
-__version__=''' $Id: doctemplate.py,v 1.31 2000/11/23 14:01:58 andy_robinson Exp $ '''
+#$Header: /tmp/reportlab/reportlab/platypus/doctemplate.py,v 1.32 2000/12/15 16:59:02 rgbecker Exp $
+__version__=''' $Id: doctemplate.py,v 1.32 2000/12/15 16:59:02 rgbecker Exp $ '''
__doc__="""
This module contains the core structure of platypus.
-Platypus constructs documents. Document styles are determined by DocumentTemplates.
+Platypus constructs documents. Document styles are determined by DocumentTemplates.
Each DocumentTemplate contains one or more PageTemplates which defines the look of the
pages of the document.
@@ -35,645 +35,648 @@
import sys
def _doNothing(canvas, doc):
- "Dummy callback for onPage"
- pass
+ "Dummy callback for onPage"
+ pass
class IndexingFlowable0(Flowable):
- """Abstract interface definition for flowables which might
- hold references to other pages or themselves be targets
- of cross-references. XRefStart, XRefDest, Table of Contents,
- Indexes etc."""
- def isIndexing(self):
- return 1
+ """Abstract interface definition for flowables which might
+ hold references to other pages or themselves be targets
+ of cross-references. XRefStart, XRefDest, Table of Contents,
+ Indexes etc."""
+ def isIndexing(self):
+ return 1
- def isSatisfied(self):
- return 1
+ def isSatisfied(self):
+ return 1
- def notify(self, kind, stuff):
- """This will be called by the framework wherever 'stuff' happens.
- 'kind' will be a value that can be used to decide whether to
- pay attention or not."""
- pass
+ def notify(self, kind, stuff):
+ """This will be called by the framework wherever 'stuff' happens.
+ 'kind' will be a value that can be used to decide whether to
+ pay attention or not."""
+ pass
- def beforeBuild(self):
- """Called by multiBuild before it starts; use this to clear
- old contents"""
- pass
-
- def afterBuild(self):
- """Called after build ends but before isSatisfied"""
- pass
+ def beforeBuild(self):
+ """Called by multiBuild before it starts; use this to clear
+ old contents"""
+ pass
+
+ def afterBuild(self):
+ """Called after build ends but before isSatisfied"""
+ pass
class ActionFlowable(Flowable):
- '''This Flowable is never drawn, it can be used for data driven controls
- For example to change a page template (from one column to two, for example)
- use NextPageTemplate which creates an ActionFlowable.
- '''
- def __init__(self,action=[]):
- if type(action) not in (ListType, TupleType):
- action = (action,)
- self.action = action
+ '''This Flowable is never drawn, it can be used for data driven controls
+ For example to change a page template (from one column to two, for example)
+ use NextPageTemplate which creates an ActionFlowable.
+ '''
+ def __init__(self,action=[]):
+ if type(action) not in (ListType, TupleType):
+ action = (action,)
+ self.action = action
- def wrap(self, availWidth, availHeight):
- '''Should never be called.'''
- raise NotImplementedError
+ def wrap(self, availWidth, availHeight):
+ '''Should never be called.'''
+ raise NotImplementedError
- def draw(self):
- '''Should never be called.'''
- raise NotImplementedError
+ def draw(self):
+ '''Should never be called.'''
+ raise NotImplementedError
- def apply(self,doc):
- '''
- This is called by the doc.build processing to allow the instance to
- implement its behaviour
- '''
- action = self.action[0]
- args = tuple(self.action[1:])
- arn = 'handle_'+action
- try:
- apply(getattr(doc,arn), args)
- except AttributeError, aerr:
- if aerr.args[0]==arn:
- raise NotImplementedError, "Can't handle ActionFlowable(%s)" % action
- else:
- raise
- except "bogus":
- t, v, None = sys.exc_info()
- raise t, "%s\n handle_%s args=%s"%(v,action,args)
+ def apply(self,doc):
+ '''
+ This is called by the doc.build processing to allow the instance to
+ implement its behaviour
+ '''
+ action = self.action[0]
+ args = tuple(self.action[1:])
+ arn = 'handle_'+action
+ try:
+ apply(getattr(doc,arn), args)
+ except AttributeError, aerr:
+ if aerr.args[0]==arn:
+ raise NotImplementedError, "Can't handle ActionFlowable(%s)" % action
+ else:
+ raise
+ except "bogus":
+ t, v, None = sys.exc_info()
+ raise t, "%s\n handle_%s args=%s"%(v,action,args)
- def __call__(self):
- return self
+ def __call__(self):
+ return self
FrameBreak = ActionFlowable('frameEnd')
PageBegin = ActionFlowable('pageBegin')
class NextPageTemplate(ActionFlowable):
- """When you get to the next page, use the template specified (change to two column, for example) """
- def __init__(self,pt):
- ActionFlowable.__init__(self,('nextPageTemplate',pt))
+ """When you get to the next page, use the template specified (change to two column, for example) """
+ def __init__(self,pt):
+ ActionFlowable.__init__(self,('nextPageTemplate',pt))
class PageTemplate:
- """
- essentially a list of Frames and an onPage routine to call at the start
- of a page when this is selected. onPageEnd gets called at the end.
- derived classes can also implement beforeDrawPage and afterDrawPage if they want
- """
- def __init__(self,id=None,frames=[],onPage=_doNothing, onPageEnd=_doNothing,
- pagesize=DEFAULT_PAGE_SIZE):
- if type(frames) not in (ListType,TupleType): frames = [frames]
- assert filter(lambda x: not isinstance(x,Frame), frames)==[], "frames argument error"
- self.id = id
- self.frames = frames
- self.onPage = onPage
- self.onPageEnd = onPageEnd
- self.pagesize = pagesize
+ """
+ essentially a list of Frames and an onPage routine to call at the start
+ of a page when this is selected. onPageEnd gets called at the end.
+ derived classes can also implement beforeDrawPage and afterDrawPage if they want
+ """
+ def __init__(self,id=None,frames=[],onPage=_doNothing, onPageEnd=_doNothing,
+ pagesize=DEFAULT_PAGE_SIZE):
+ if type(frames) not in (ListType,TupleType): frames = [frames]
+ assert filter(lambda x: not isinstance(x,Frame), frames)==[], "frames argument error"
+ self.id = id
+ self.frames = frames
+ self.onPage = onPage
+ self.onPageEnd = onPageEnd
+ self.pagesize = pagesize
- def beforeDrawPage(self,canv,doc):
- """Override this if you want additional functionality or prefer
- a class based page routine. Called before any flowables for
- this page are processed."""
- pass
+ def beforeDrawPage(self,canv,doc):
+ """Override this if you want additional functionality or prefer
+ a class based page routine. Called before any flowables for
+ this page are processed."""
+ pass
- def afterDrawPage(self, canv, doc):
- """This is called after the last flowable for the page has
- been processed. You might use this if the page header or
- footer needed knowledge of what flowables were drawn on
- this page."""
- pass
-
+ def afterDrawPage(self, canv, doc):
+ """This is called after the last flowable for the page has
+ been processed. You might use this if the page header or
+ footer needed knowledge of what flowables were drawn on
+ this page."""
+ pass
+
class BaseDocTemplate:
- """
- First attempt at defining a document template class.
+ """
+ First attempt at defining a document template class.
- The basic idea is simple.
- 0) The document has a list of data associated with it
- this data should derive from flowables. We'll have
- special classes like PageBreak, FrameBreak to do things
- like forcing a page end etc.
+ The basic idea is simple.
+ 0) The document has a list of data associated with it
+ this data should derive from flowables. We'll have
+ special classes like PageBreak, FrameBreak to do things
+ like forcing a page end etc.
- 1) The document has one or more page templates.
+ 1) The document has one or more page templates.
- 2) Each page template has one or more frames.
+ 2) Each page template has one or more frames.
- 3) The document class provides base methods for handling the
- story events and some reasonable methods for getting the
- story flowables into the frames.
+ 3) The document class provides base methods for handling the
+ story events and some reasonable methods for getting the
+ story flowables into the frames.
- 4) The document instances can override the base handler routines.
-
- Most of the methods for this class are not called directly by the user,
- but in some advanced usages they may need to be overridden via subclassing.
-
- EXCEPTION: doctemplate.build(...) must be called for most reasonable uses
- since it builds a document using the page template.
-
- Each document template builds exactly one document into a file specified
- by the filename argument on initialization.
+ 4) The document instances can override the base handler routines.
+
+ Most of the methods for this class are not called directly by the user,
+ but in some advanced usages they may need to be overridden via subclassing.
+
+ EXCEPTION: doctemplate.build(...) must be called for most reasonable uses
+ since it builds a document using the page template.
+
+ Each document template builds exactly one document into a file specified
+ by the filename argument on initialization.
- Possible keyword arguments for the initialization:
-
- pageTemplates: A list of templates. Must be nonempty. Names
- assigned to the templates are used for referring to them so no two used
- templates should have the same name. For example you might want one template
- for a title page, one for a section first page, one for a first page of
- a chapter and two more for the interior of a chapter on odd and even pages.
- If this argument is omitted then at least one pageTemplate should be provided
- using the addPageTemplates method before the document is built.
- showBoundary: if set draw a box around the frame boundaries.
- leftMargin:
- rightMargin:
- topMargin:
- bottomMargin: Margin sizes in points (default 1 inch)
- These margins may be overridden by the pageTemplates. They are primarily of interest
- for the SimpleDocumentTemplate subclass.
- allowSplitting: If set flowables (eg, paragraphs) may be split across frames or pages
- (default: 1)
- title: Internal title for document (does not automatically display on any page)
- author: Internal author for document (does not automatically display on any page)
- """
- _initArgs = { 'pagesize':DEFAULT_PAGE_SIZE,
- 'pageTemplates':[],
- 'showBoundary':0,
- 'leftMargin':inch,
- 'rightMargin':inch,
- 'topMargin':inch,
- 'bottomMargin':inch,
- 'allowSplitting':1,
- 'title':None,
- 'author':None,
- '_pageBreakQuick':1}
- _invalidInitArgs = ()
+ Possible keyword arguments for the initialization:
+
+ pageTemplates: A list of templates. Must be nonempty. Names
+ assigned to the templates are used for referring to them so no two used
+ templates should have the same name. For example you might want one template
+ for a title page, one for a section first page, one for a first page of
+ a chapter and two more for the interior of a chapter on odd and even pages.
+ If this argument is omitted then at least one pageTemplate should be provided
+ using the addPageTemplates method before the document is built.
+ showBoundary: if set draw a box around the frame boundaries.
+ leftMargin:
+ rightMargin:
+ topMargin:
+ bottomMargin: Margin sizes in points (default 1 inch)
+ These margins may be overridden by the pageTemplates. They are primarily of interest
+ for the SimpleDocumentTemplate subclass.
+ allowSplitting: If set flowables (eg, paragraphs) may be split across frames or pages
+ (default: 1)
+ title: Internal title for document (does not automatically display on any page)
+ author: Internal author for document (does not automatically display on any page)
+ """
+ _initArgs = { 'pagesize':DEFAULT_PAGE_SIZE,
+ 'pageTemplates':[],
+ 'showBoundary':0,
+ 'leftMargin':inch,
+ 'rightMargin':inch,
+ 'topMargin':inch,
+ 'bottomMargin':inch,
+ 'allowSplitting':1,
+ 'title':None,
+ 'author':None,
+ '_pageBreakQuick':1}
+ _invalidInitArgs = ()
- def __init__(self, filename, **kw):
- """create a document template bound to a filename (see class documentation for keyword arguments)"""
- self.filename = filename
+ def __init__(self, filename, **kw):
+ """create a document template bound to a filename (see class documentation for keyword arguments)"""
+ self.filename = filename
- for k in self._initArgs.keys():
- if not kw.has_key(k):
- v = self._initArgs[k]
- else:
- if k in self._invalidInitArgs:
- raise ValueError, "Invalid argument %s" % k
- v = kw[k]
- setattr(self,k,v)
+ for k in self._initArgs.keys():
+ if not kw.has_key(k):
+ v = self._initArgs[k]
+ else:
+ if k in self._invalidInitArgs:
+ raise ValueError, "Invalid argument %s" % k
+ v = kw[k]
+ setattr(self,k,v)
- p = self.pageTemplates
- self.pageTemplates = []
- self.addPageTemplates(p)
+ p = self.pageTemplates
+ self.pageTemplates = []
+ self.addPageTemplates(p)
- # facility to assist multi-build and cross-referencing.
- # various hooks can put things into here - key is what
- # you want, value is a page number. This can then be
- # passed to indexing flowables.
- self._pageRefs = {}
- self._indexingFlowables = []
-
- self._calc()
- self.afterInit()
+ # facility to assist multi-build and cross-referencing.
+ # various hooks can put things into here - key is what
+ # you want, value is a page number. This can then be
+ # passed to indexing flowables.
+ self._pageRefs = {}
+ self._indexingFlowables = []
+
+ self._calc()
+ self.afterInit()
- def _calc(self):
- self._rightMargin = self.pagesize[0] - self.rightMargin
- self._topMargin = self.pagesize[1] - self.topMargin
- self.width = self._rightMargin - self.leftMargin
- self.height = self._topMargin - self.bottomMargin
+ def _calc(self):
+ self._rightMargin = self.pagesize[0] - self.rightMargin
+ self._topMargin = self.pagesize[1] - self.topMargin
+ self.width = self._rightMargin - self.leftMargin
+ self.height = self._topMargin - self.bottomMargin
- def clean_hanging(self):
- 'handle internal postponed actions'
- while len(self._hanging):
- self.handle_flowable(self._hanging)
+ def clean_hanging(self):
+ 'handle internal postponed actions'
+ while len(self._hanging):
+ self.handle_flowable(self._hanging)
- def addPageTemplates(self,pageTemplates):
- 'add one or a sequence of pageTemplates'
- if type(pageTemplates) not in (ListType,TupleType):
- pageTemplates = [pageTemplates]
- #this test below fails due to inconsistent imports!
- #assert filter(lambda x: not isinstance(x,PageTemplate), pageTemplates)==[], "pageTemplates argument error"
- for t in pageTemplates:
- self.pageTemplates.append(t)
-
- def handle_documentBegin(self):
- '''implement actions at beginning of document'''
- self._hanging = [PageBegin]
- self.pageTemplate = self.pageTemplates[0]
- self.page = 0
- self.beforeDocument()
+ def addPageTemplates(self,pageTemplates):
+ 'add one or a sequence of pageTemplates'
+ if type(pageTemplates) not in (ListType,TupleType):
+ pageTemplates = [pageTemplates]
+ #this test below fails due to inconsistent imports!
+ #assert filter(lambda x: not isinstance(x,PageTemplate), pageTemplates)==[], "pageTemplates argument error"
+ for t in pageTemplates:
+ self.pageTemplates.append(t)
+
+ def handle_documentBegin(self):
+ '''implement actions at beginning of document'''
+ self._hanging = [PageBegin]
+ self.pageTemplate = self.pageTemplates[0]
+ self.page = 0
+ self.beforeDocument()
- def handle_pageBegin(self):
- '''Perform actions required at beginning of page.
- shouldn't normally be called directly'''
- self.page = self.page + 1
- self.pageTemplate.beforeDrawPage(self.canv,self)
- self.pageTemplate.onPage(self.canv,self)
- self.beforePage()
- if hasattr(self,'_nextFrameIndex'):
- del self._nextFrameIndex
- self.frame = self.pageTemplate.frames[0]
- self.handle_frameBegin()
+ def handle_pageBegin(self):
+ '''Perform actions required at beginning of page.
+ shouldn't normally be called directly'''
+ self.page = self.page + 1
+ self.pageTemplate.beforeDrawPage(self.canv,self)
+ self.pageTemplate.onPage(self.canv,self)
+ self.beforePage()
+ if hasattr(self,'_nextFrameIndex'):
+ del self._nextFrameIndex
+ self.frame = self.pageTemplate.frames[0]
+ self.handle_frameBegin()
- def handle_pageEnd(self):
- ''' show the current page
- check the next page template
- hang a page begin
- '''
- self.pageTemplate.afterDrawPage(self.canv, self)
- self.pageTemplate.onPageEnd(self.canv, self)
- self.afterPage()
- self.canv.showPage()
- if hasattr(self,'_nextPageTemplateIndex'):
- self.pageTemplate = self.pageTemplates[self._nextPageTemplateIndex]
- del self._nextPageTemplateIndex
- self._hanging.append(PageBegin)
+ def handle_pageEnd(self):
+ ''' show the current page
+ check the next page template
+ hang a page begin
+ '''
+ self.pageTemplate.afterDrawPage(self.canv, self)
+ self.pageTemplate.onPageEnd(self.canv, self)
+ self.afterPage()
+ self.canv.showPage()
+ if hasattr(self,'_nextPageTemplateIndex'):
+ self.pageTemplate = self.pageTemplates[self._nextPageTemplateIndex]
+ del self._nextPageTemplateIndex
+ self._hanging.append(PageBegin)
- def handle_pageBreak(self):
- '''some might choose not to end all the frames'''
- if self._pageBreakQuick:
- self.handle_pageEnd()
- else:
- n = len(self._hanging)
- while len(self._hanging)==n:
- self.handle_frameEnd()
+ def handle_pageBreak(self):
+ '''some might choose not to end all the frames'''
+ if self._pageBreakQuick:
+ self.handle_pageEnd()
+ else:
+ n = len(self._hanging)
+ while len(self._hanging)==n:
+ self.handle_frameEnd()
- def handle_frameBegin(self,*args):
- '''What to do at the beginning of a page'''
- self.frame._reset()
- if self.showBoundary or self.frame.showBoundary:
- self.frame.drawBoundary(self.canv)
+ def handle_frameBegin(self,*args):
+ '''What to do at the beginning of a page'''
+ self.frame._reset()
+ if self.showBoundary or self.frame.showBoundary:
+ self.frame.drawBoundary(self.canv)
- def handle_frameEnd(self):
- ''' Handles the semantics of the end of a frame. This includes the selection of
- the next frame or if this is the last frame then invoke pageEnd.
- '''
- if hasattr(self,'_nextFrameIndex'):
- frame = self.pageTemplate.frames[self._nextFrameIndex]
- del self._nextFrameIndex
- self.handle_frameBegin()
- elif hasattr(self.frame,'lastFrame') or self.frame is self.pageTemplate.frames[-1]:
- self.handle_pageEnd()
- self.frame = None
- else:
- f = self.frame
- self.frame = self.pageTemplate.frames[self.pageTemplate.frames.index(f) + 1]
- self.handle_frameBegin()
+ def handle_frameEnd(self):
+ ''' Handles the semantics of the end of a frame. This includes the selection of
+ the next frame or if this is the last frame then invoke pageEnd.
+ '''
+ if hasattr(self,'_nextFrameIndex'):
+ frame = self.pageTemplate.frames[self._nextFrameIndex]
+ del self._nextFrameIndex
+ self.handle_frameBegin()
+ elif hasattr(self.frame,'lastFrame') or self.frame is self.pageTemplate.frames[-1]:
+ self.handle_pageEnd()
+ self.frame = None
+ else:
+ f = self.frame
+ self.frame = self.pageTemplate.frames[self.pageTemplate.frames.index(f) + 1]
+ self.handle_frameBegin()
- def handle_nextPageTemplate(self,pt):
- '''On endPage chenge to the page template with name or index pt'''
- if type(pt) is StringType:
- for t in self.pageTemplates:
- if t.id == pt:
- self._nextPageTemplateIndex = self.pageTemplates.index(t)
- return
- raise ValueError, "can't find template('%s')"%pt
- elif type(pt) is IntType:
- self._nextPageTemplateIndex = pt
- else:
- raise TypeError, "argument pt should be string or integer"
+ def handle_nextPageTemplate(self,pt):
+ '''On endPage chenge to the page template with name or index pt'''
+ if type(pt) is StringType:
+ for t in self.pageTemplates:
+ if t.id == pt:
+ self._nextPageTemplateIndex = self.pageTemplates.index(t)
+ return
+ raise ValueError, "can't find template('%s')"%pt
+ elif type(pt) is IntType:
+ self._nextPageTemplateIndex = pt
+ else:
+ raise TypeError, "argument pt should be string or integer"
- def handle_nextFrame(self,fx):
- '''On endFrame chenge to the frame with name or index fx'''
- if type(fx) is StringType:
- for f in self.pageTemplate.frames:
- if f.id == fx:
- self._nextFrameIndex = self.pageTemplate.frames.index(f)
- return
- raise ValueError, "can't find frame('%s')"%fx
- elif type(fx) is IntType:
- self._nextFrameIndex = fx
- else:
- raise TypeError, "argument fx should be string or integer"
+ def handle_nextFrame(self,fx):
+ '''On endFrame chenge to the frame with name or index fx'''
+ if type(fx) is StringType:
+ for f in self.pageTemplate.frames:
+ if f.id == fx:
+ self._nextFrameIndex = self.pageTemplate.frames.index(f)
+ return
+ raise ValueError, "can't find frame('%s')"%fx
+ elif type(fx) is IntType:
+ self._nextFrameIndex = fx
+ else:
+ raise TypeError, "argument fx should be string or integer"
- def handle_currentFrame(self,fx):
- '''chenge to the frame with name or index fx'''
- if type(fx) is StringType:
- for f in self.pageTemplate.frames:
- if f.id == fx:
- self._nextFrameIndex = self.pageTemplate.frames.index(f)
- return
- raise ValueError, "can't find frame('%s')"%fx
- elif type(fx) is IntType:
- self._nextFrameIndex = fx
- else:
- raise TypeError, "argument fx should be string or integer"
+ def handle_currentFrame(self,fx):
+ '''chenge to the frame with name or index fx'''
+ if type(fx) is StringType:
+ for f in self.pageTemplate.frames:
+ if f.id == fx:
+ self._nextFrameIndex = self.pageTemplate.frames.index(f)
+ return
+ raise ValueError, "can't find frame('%s')"%fx
+ elif type(fx) is IntType:
+ self._nextFrameIndex = fx
+ else:
+ raise TypeError, "argument fx should be string or integer"
- def handle_flowable(self,flowables):
- '''try to handle one flowable from the front of list flowables.'''
-
- #allow document a chance to look at, modify or ignore
- #the object(s) about to be processed
- self.filterFlowables(flowables)
- f = flowables[0]
- del flowables[0]
- if f is None:
- return
+ def handle_flowable(self,flowables):
+ '''try to handle one flowable from the front of list flowables.'''
+
+ #allow document a chance to look at, modify or ignore
+ #the object(s) about to be processed
+ self.filterFlowables(flowables)
+ f = flowables[0]
+ del flowables[0]
+ if f is None:
+ return
- if isinstance(f,PageBreak):
- self.handle_pageBreak()
- self.afterFlowable(f)
- elif isinstance(f,ActionFlowable):
- f.apply(self)
- self.afterFlowable(f)
- else:
- #general case we have to do something
- if self.frame.add(f, self.canv, trySplit=self.allowSplitting):
- self.afterFlowable(f)
- else:
- if self.allowSplitting:
- # see if this is a splittable thing
- S = self.frame.split(f,self.canv)
- n = len(S)
- else:
- n = 0
+ if isinstance(f,PageBreak):
+ self.handle_pageBreak()
+ self.afterFlowable(f)
+ elif isinstance(f,ActionFlowable):
+ f.apply(self)
+ self.afterFlowable(f)
+ else:
+ #general case we have to do something
+ if self.frame.add(f, self.canv, trySplit=self.allowSplitting):
+ self.afterFlowable(f)
+ else:
+ if self.allowSplitting:
+ # see if this is a splittable thing
+ S = self.frame.split(f,self.canv)
+ n = len(S)
+ else:
+ n = 0
- if n:
- if self.frame.add(S[0], self.canv, trySplit=0):
- self.afterFlowable(f)
- else:
- raise "LayoutError", "splitting error"
- del S[0]
- for f in xrange(n-1):
- flowables.insert(f,S[f]) # put split flowables back on the list
- else:
- # this must e cleared when they are finally drawn!
- if hasattr(f,'postponed'):
- message = "Flowable %s too large on page %d" % (f, self.page)
- #show us, it might be handy
- #HACK = it seems within tables we sometimes
- #get an empty paragraph that won't fit and this
- #causes it to fall over. FIXME FIXME FIXME
- if hasattr(f, 'getPlainText'):
- print 'Offending Paragraph:'
- print f.getPlainText()
- raise "LayoutError", message
- f.postponed = 1
- flowables.insert(0,f) # put the flowable back
- self.handle_frameEnd()
+ if n:
+ if self.frame.add(S[0], self.canv, trySplit=0):
+ self.afterFlowable(f)
+ else:
+ if hasattr(f, 'getPlainText'):
+ print 'Offending Paragraph:'
+ print f.getPlainText()
+ raise "LayoutError", "splitting error type=%s" % type(f)
+ del S[0]
+ for f in xrange(n-1):
+ flowables.insert(f,S[f]) # put split flowables back on the list
+ else:
+ # this must be cleared when they are finally drawn!
+ if hasattr(f,'postponed'):
+ message = "Flowable %s too large on page %d" % (f, self.page)
+ #show us, it might be handy
+ #HACK = it seems within tables we sometimes
+ #get an empty paragraph that won't fit and this
+ #causes it to fall over. FIXME FIXME FIXME
+ if hasattr(f, 'getPlainText'):
+ print 'Offending Paragraph:'
+ print f.getPlainText()
+ raise "LayoutError", message
+ f.postponed = 1
+ flowables.insert(0,f) # put the flowable back
+ self.handle_frameEnd()
- #these are provided so that deriving classes can refer to them
- _handle_documentBegin = handle_documentBegin
- _handle_pageBegin = handle_pageBegin
- _handle_pageEnd = handle_pageEnd
- _handle_frameBegin = handle_frameBegin
- _handle_frameEnd = handle_frameEnd
- _handle_flowable = handle_flowable
- _handle_nextPageTemplate = handle_nextPageTemplate
- _handle_currentFrame = handle_currentFrame
- _handle_nextFrame = handle_nextFrame
+ #these are provided so that deriving classes can refer to them
+ _handle_documentBegin = handle_documentBegin
+ _handle_pageBegin = handle_pageBegin
+ _handle_pageEnd = handle_pageEnd
+ _handle_frameBegin = handle_frameBegin
+ _handle_frameEnd = handle_frameEnd
+ _handle_flowable = handle_flowable
+ _handle_nextPageTemplate = handle_nextPageTemplate
+ _handle_currentFrame = handle_currentFrame
+ _handle_nextFrame = handle_nextFrame
- def _startBuild(self, filename=None, canvasmaker=canvas.Canvas):
- self._calc()
- self.canv = canvasmaker(filename or self.filename,pagesize=self.pagesize)
- self.handle_documentBegin()
+ def _startBuild(self, filename=None, canvasmaker=canvas.Canvas):
+ self._calc()
+ self.canv = canvasmaker(filename or self.filename,pagesize=self.pagesize)
+ self.handle_documentBegin()
- def _endBuild(self):
- if self._hanging!=[] and self._hanging[-1] is PageBegin:
- del self._hanging[-1]
- self.clean_hanging()
- else:
- self.clean_hanging()
- self.handle_pageBreak()
+ def _endBuild(self):
+ if self._hanging!=[] and self._hanging[-1] is PageBegin:
+ del self._hanging[-1]
+ self.clean_hanging()
+ else:
+ self.clean_hanging()
+ self.handle_pageBreak()
- self.canv.save()
- #AR - hack - for some reason a document did not
- #have these:
- #if hasattr(self, 'frame'): del self.frame
- #if hasattr(self, 'pageTemplate'): del self.pageTemplate
- #del self.frame, self.pageTemplate
+ self.canv.save()
+ #AR - hack - for some reason a document did not
+ #have these:
+ #if hasattr(self, 'frame'): del self.frame
+ #if hasattr(self, 'pageTemplate'): del self.pageTemplate
+ #del self.frame, self.pageTemplate
- def build(self, flowables, filename=None, canvasmaker=canvas.Canvas):
- """Build the document from a list of flowables.
- If the filename argument is provided then that filename is used
- rather than the one provided upon initialization.
- If the canvasmaker argument is provided then it will be used
- instead of the default. For example a slideshow might use
- an alternate canvas which places 6 slides on a page (by
- doing translations, scalings and redefining the page break
- operations).
- """
- #assert filter(lambda x: not isinstance(x,Flowable), flowables)==[], "flowables argument error"
- self._startBuild(filename,canvasmaker)
+ def build(self, flowables, filename=None, canvasmaker=canvas.Canvas):
+ """Build the document from a list of flowables.
+ If the filename argument is provided then that filename is used
+ rather than the one provided upon initialization.
+ If the canvasmaker argument is provided then it will be used
+ instead of the default. For example a slideshow might use
+ an alternate canvas which places 6 slides on a page (by
+ doing translations, scalings and redefining the page break
+ operations).
+ """
+ #assert filter(lambda x: not isinstance(x,Flowable), flowables)==[], "flowables argument error"
+ self._startBuild(filename,canvasmaker)
- while len(flowables):
- self.clean_hanging()
- self.handle_flowable(flowables)
+ while len(flowables):
+ self.clean_hanging()
+ self.handle_flowable(flowables)
- self._endBuild()
+ self._endBuild()
- def _allSatisfied0(self):
- """Called by multi-build - are all cross-references resolved?"""
- allHappy = 1
- for f in self._indexingFlowables:
- if not f.isSatisfied():
- allHappy = 0
- break
- return allHappy
+ def _allSatisfied0(self):
+ """Called by multi-build - are all cross-references resolved?"""
+ allHappy = 1
+ for f in self._indexingFlowables:
+ if not f.isSatisfied():
+ allHappy = 0
+ break
+ return allHappy
- def notify0(self, kind, stuff):
- """"Forward to any listeners"""
- for l in self._indexingFlowables:
- l.notify(kind, stuff)
-
- def pageRef0(self, label):
- """hook to register a page number"""
- print "pageRef called with label '%s' on page %d" % (
- label, self.page)
- self._pageRefs[label] = self.page
-
- def multiBuild0(self, story,
- filename=None,
- canvasmaker=canvas.Canvas,
- maxPasses = 10):
- """Makes multiple passes until all indexing flowables
- are happy."""
- self._indexingFlowables = []
- #scan the story and keep a copy
- for thing in story:
- if thing.isIndexing():
- self._indexingFlowables.append(thing)
- #print 'scanned story, found these indexing flowables:\n'
- #print self._indexingFlowables
+ def notify0(self, kind, stuff):
+ """"Forward to any listeners"""
+ for l in self._indexingFlowables:
+ l.notify(kind, stuff)
+
+ def pageRef0(self, label):
+ """hook to register a page number"""
+ print "pageRef called with label '%s' on page %d" % (
+ label, self.page)
+ self._pageRefs[label] = self.page
+
+ def multiBuild0(self, story,
+ filename=None,
+ canvasmaker=canvas.Canvas,
+ maxPasses = 10):
+ """Makes multiple passes until all indexing flowables
+ are happy."""
+ self._indexingFlowables = []
+ #scan the story and keep a copy
+ for thing in story:
+ if thing.isIndexing():
+ self._indexingFlowables.append(thing)
+ #print 'scanned story, found these indexing flowables:\n'
+ #print self._indexingFlowables
- passes = 0
- while 1:
- passes = passes + 1
- print 'building pass '+str(passes) + '...',
+ passes = 0
+ while 1:
+ passes = passes + 1
+ print 'building pass '+str(passes) + '...',
- for fl in self._indexingFlowables:
- fl.beforeBuild()
+ for fl in self._indexingFlowables:
+ fl.beforeBuild()
- # work with a copy of the story, since it is consumed
- tempStory = story[:]
- self.build(tempStory, filename, canvasmaker)
- #self.notify0('debug',None)
+ # work with a copy of the story, since it is consumed
+ tempStory = story[:]
+ self.build(tempStory, filename, canvasmaker)
+ #self.notify0('debug',None)
- #clean up so multi-build does not go wrong - the frame
- #packer might have tacked an attribute onto some
- #paragraphs
- for elem in story:
- if hasattr(elem, 'postponed'):
- del elem.postponed
+ #clean up so multi-build does not go wrong - the frame
+ #packer might have tacked an attribute onto some
+ #paragraphs
+ for elem in story:
+ if hasattr(elem, 'postponed'):
+ del elem.postponed
- for fl in self._indexingFlowables:
- fl.afterBuild()
+ for fl in self._indexingFlowables:
+ fl.afterBuild()
- happy = self._allSatisfied0()
+ happy = self._allSatisfied0()
- if happy:
- print 'OK'
- break
- else:
- print 'failed'
- if passes > maxPasses:
- raise IndexError, "Index entries not resolved after %d passes" % maxPasses
-
- print 'saved', filename
+ if happy:
+ print 'OK'
+ break
+ else:
+ print 'failed'
+ if passes > maxPasses:
+ raise IndexError, "Index entries not resolved after %d passes" % maxPasses
+
+ print 'saved', filename
- #these are pure virtuals override in derived classes
- #NB these get called at suitable places by the base class
- #so if you derive and override the handle_xxx methods
- #it's up to you to ensure that they maintain the needed consistency
- def afterInit(self):
- """This is called after initialisation of the base class."""
- pass
+ #these are pure virtuals override in derived classes
+ #NB these get called at suitable places by the base class
+ #so if you derive and override the handle_xxx methods
+ #it's up to you to ensure that they maintain the needed consistency
+ def afterInit(self):
+ """This is called after initialisation of the base class."""
+ pass
- def beforeDocument(self):
- """This is called before any processing is
- done on the document."""
- pass
+ def beforeDocument(self):
+ """This is called before any processing is
+ done on the document."""
+ pass
- def beforePage(self):
- """This is called at the beginning of page
- processing, and immediately before the
- beforeDrawPage method of the current page
- template."""
- pass
+ def beforePage(self):
+ """This is called at the beginning of page
+ processing, and immediately before the
+ beforeDrawPage method of the current page
+ template."""
+ pass
- def afterPage(self):
- """This is called after page processing, and
- immediately after the afterDrawPage method
- of the current page template."""
- pass
+ def afterPage(self):
+ """This is called after page processing, and
+ immediately after the afterDrawPage method
+ of the current page template."""
+ pass
- def filterFlowables(self,flowables):
- '''called to filter flowables at the start of the main handle_flowable method.
- Upon return if flowables[0] has been set to None it is discarded and the main
- method returns.
- '''
- pass
+ def filterFlowables(self,flowables):
+ '''called to filter flowables at the start of the main handle_flowable method.
+ Upon return if flowables[0] has been set to None it is discarded and the main
+ method returns.
+ '''
+ pass
- def afterFlowable(self, flowable):
- '''called after a flowable has been rendered'''
- pass
+ def afterFlowable(self, flowable):
+ '''called after a flowable has been rendered'''
+ pass
class SimpleDocTemplate(BaseDocTemplate):
- """A special case document template that will handle many simple documents.
- See documentation for BaseDocTemplate. No pageTemplates are required
- for this special case. A page templates are inferred from the
- margin information and the onFirstPage, onLaterPages arguments to the build method.
-
- A document which has all pages with the same look except for the first
- page may can be built using this special approach.
- """
- _invalidInitArgs = ('pageTemplates',)
- def handle_pageBegin(self):
- '''override base method to add a change of page template after the firstpage.
- '''
- self._handle_pageBegin()
- self._handle_nextPageTemplate('Later')
+ """A special case document template that will handle many simple documents.
+ See documentation for BaseDocTemplate. No pageTemplates are required
+ for this special case. A page templates are inferred from the
+ margin information and the onFirstPage, onLaterPages arguments to the build method.
+
+ A document which has all pages with the same look except for the first
+ page may can be built using this special approach.
+ """
+ _invalidInitArgs = ('pageTemplates',)
+ def handle_pageBegin(self):
+ '''override base method to add a change of page template after the firstpage.
+ '''
+ self._handle_pageBegin()
+ self._handle_nextPageTemplate('Later')
- def build(self,flowables,onFirstPage=_doNothing, onLaterPages=_doNothing):
- """build the document using the flowables. Annotate the first page using the onFirstPage
- function and later pages using the onLaterPages function. The onXXX pages should follow
- the signature
-
- def myOnFirstPage(canvas, document):
- # do annotations and modify the document
- ...
-
- The functions can do things like draw logos, page numbers,
- footers, etcetera. They can use external variables to vary
- the look (for example providing page numbering or section names).
- """
- self._calc() #in case we changed margins sizes etc
- frameT = Frame(self.leftMargin, self.bottomMargin, self.width, self.height, id='normal')
- self.addPageTemplates([PageTemplate(id='First',frames=frameT, onPage=onFirstPage),
- PageTemplate(id='Later',frames=frameT, onPage=onLaterPages)])
- if onFirstPage is _doNothing and hasattr(self,'onFirstPage'):
- self.pageTemplates[0].beforeDrawPage = self.onFirstPage
- if onLaterPages is _doNothing and hasattr(self,'onLaterPages'):
- self.pageTemplates[1].beforeDrawPage = self.onLaterPages
- BaseDocTemplate.build(self,flowables)
+ def build(self,flowables,onFirstPage=_doNothing, onLaterPages=_doNothing):
+ """build the document using the flowables. Annotate the first page using the onFirstPage
+ function and later pages using the onLaterPages function. The onXXX pages should follow
+ the signature
+
+ def myOnFirstPage(canvas, document):
+ # do annotations and modify the document
+ ...
+
+ The functions can do things like draw logos, page numbers,
+ footers, etcetera. They can use external variables to vary
+ the look (for example providing page numbering or section names).
+ """
+ self._calc() #in case we changed margins sizes etc
+ frameT = Frame(self.leftMargin, self.bottomMargin, self.width, self.height, id='normal')
+ self.addPageTemplates([PageTemplate(id='First',frames=frameT, onPage=onFirstPage),
+ PageTemplate(id='Later',frames=frameT, onPage=onLaterPages)])
+ if onFirstPage is _doNothing and hasattr(self,'onFirstPage'):
+ self.pageTemplates[0].beforeDrawPage = self.onFirstPage
+ if onLaterPages is _doNothing and hasattr(self,'onLaterPages'):
+ self.pageTemplates[1].beforeDrawPage = self.onLaterPages
+ BaseDocTemplate.build(self,flowables)
- ##########################################################
- ##
- ## testing
- ##
- ##########################################################
+ ##########################################################
+ ##
+ ## testing
+ ##
+ ##########################################################
def randomText():
- #this may or may not be appropriate in your company
- from random import randint, choice
+ #this may or may not be appropriate in your company
+ from random import randint, choice
- RANDOMWORDS = ['strategic','direction','proactive',
- 'reengineering','forecast','resources',
- 'forward-thinking','profit','growth','doubletalk',
- 'venture capital','IPO']
+ RANDOMWORDS = ['strategic','direction','proactive',
+ 'reengineering','forecast','resources',
+ 'forward-thinking','profit','growth','doubletalk',
+ 'venture capital','IPO']
- sentences = 5
- output = ""
- for sentenceno in range(randint(1,5)):
- output = output + 'Blah'
- for wordno in range(randint(10,25)):
- if randint(0,4)==0:
- word = choice(RANDOMWORDS)
- else:
- word = 'blah'
- output = output + ' ' +word
- output = output+'.'
- return output
+ sentences = 5
+ output = ""
+ for sentenceno in range(randint(1,5)):
+ output = output + 'Blah'
+ for wordno in range(randint(10,25)):
+ if randint(0,4)==0:
+ word = choice(RANDOMWORDS)
+ else:
+ word = 'blah'
+ output = output + ' ' +word
+ output = output+'.'
+ return output
if __name__ == '__main__':
- def myFirstPage(canvas, doc):
- canvas.saveState()
- canvas.setStrokeColor(red)
- canvas.setLineWidth(5)
- canvas.line(66,72,66,PAGE_HEIGHT-72)
- canvas.setFont('Times-Bold',24)
- canvas.drawString(108, PAGE_HEIGHT-108, "TABLE OF CONTENTS DEMO")
- canvas.setFont('Times-Roman',12)
- canvas.drawString(4 * inch, 0.75 * inch, "First Page")
- canvas.restoreState()
+ def myFirstPage(canvas, doc):
+ canvas.saveState()
+ canvas.setStrokeColor(red)
+ canvas.setLineWidth(5)
+ canvas.line(66,72,66,PAGE_HEIGHT-72)
+ canvas.setFont('Times-Bold',24)
+ canvas.drawString(108, PAGE_HEIGHT-108, "TABLE OF CONTENTS DEMO")
+ canvas.setFont('Times-Roman',12)
+ canvas.drawString(4 * inch, 0.75 * inch, "First Page")
+ canvas.restoreState()
- def myLaterPages(canvas, doc):
- canvas.saveState()
- canvas.setStrokeColor(red)
- canvas.setLineWidth(5)
- canvas.line(66,72,66,PAGE_HEIGHT-72)
- canvas.setFont('Times-Roman',12)
- canvas.drawString(4 * inch, 0.75 * inch, "Page %d" % doc.page)
- canvas.restoreState()
+ def myLaterPages(canvas, doc):
+ canvas.saveState()
+ canvas.setStrokeColor(red)
+ canvas.setLineWidth(5)
+ canvas.line(66,72,66,PAGE_HEIGHT-72)
+ canvas.setFont('Times-Roman',12)
+ canvas.drawString(4 * inch, 0.75 * inch, "Page %d" % doc.page)
+ canvas.restoreState()
- def run():
- objects_to_draw = []
- from reportlab.lib.styles import ParagraphStyle
- #from paragraph import Paragraph
- from doctemplate import SimpleDocTemplate
+ def run():
+ objects_to_draw = []
+ from reportlab.lib.styles import ParagraphStyle
+ #from paragraph import Paragraph
+ from doctemplate import SimpleDocTemplate
- #need a style
- normal = ParagraphStyle('normal')
- normal.firstLineIndent = 18
- normal.spaceBefore = 6
- import random
- for i in range(15):
- height = 0.5 + (2*random.random())
- box = XBox(6 * inch, height * inch, 'Box Number %d' % i)
- objects_to_draw.append(box)
- para = Paragraph(randomText(), normal)
- objects_to_draw.append(para)
+ #need a style
+ normal = ParagraphStyle('normal')
+ normal.firstLineIndent = 18
+ normal.spaceBefore = 6
+ import random
+ for i in range(15):
+ height = 0.5 + (2*random.random())
+ box = XBox(6 * inch, height * inch, 'Box Number %d' % i)
+ objects_to_draw.append(box)
+ para = Paragraph(randomText(), normal)
+ objects_to_draw.append(para)
- SimpleDocTemplate('doctemplate.pdf').build(objects_to_draw,
- onFirstPage=myFirstPage,onLaterPages=myLaterPages)
+ SimpleDocTemplate('doctemplate.pdf').build(objects_to_draw,
+ onFirstPage=myFirstPage,onLaterPages=myLaterPages)
- run()
+ run()