reportlab/platypus/doctemplate.py
changeset 1502 125b52eb0a8e
parent 1440 243d35446390
child 1505 e45994cb76bb
equal deleted inserted replaced
1501:2dfe5d299bc8 1502:125b52eb0a8e
     1 #copyright ReportLab Inc. 2000
     1 #copyright ReportLab Inc. 2000
     2 #see license.txt for license details
     2 #see license.txt for license details
     3 #history http://cvs.sourceforge.net/cgi-bin/cvsweb.cgi/reportlab/platypus/doctemplate.py?cvsroot=reportlab
     3 #history http://cvs.sourceforge.net/cgi-bin/cvsweb.cgi/reportlab/platypus/doctemplate.py?cvsroot=reportlab
     4 #$Header: /tmp/reportlab/reportlab/platypus/doctemplate.py,v 1.49 2001/11/26 21:49:01 andy_robinson Exp $
     4 #$Header: /tmp/reportlab/reportlab/platypus/doctemplate.py,v 1.50 2002/02/03 21:25:57 andy_robinson Exp $
     5 
     5 
     6 __version__=''' $Id: doctemplate.py,v 1.49 2001/11/26 21:49:01 andy_robinson Exp $ '''
     6 __version__=''' $Id: doctemplate.py,v 1.50 2002/02/03 21:25:57 andy_robinson Exp $ '''
     7 
     7 
     8 __doc__="""
     8 __doc__="""
     9 This module contains the core structure of platypus.
     9 This module contains the core structure of platypus.
    10 
    10 
    11 Platypus constructs documents.	Document styles are determined by DocumentTemplates.
    11 Platypus constructs documents.  Document styles are determined by DocumentTemplates.
    12 
    12 
    13 Each DocumentTemplate contains one or more PageTemplates which defines the look of the
    13 Each DocumentTemplate contains one or more PageTemplates which defines the look of the
    14 pages of the document.
    14 pages of the document.
    15 
    15 
    16 Each PageTemplate has a procedure for drawing the "non-flowing" part of the page
    16 Each PageTemplate has a procedure for drawing the "non-flowing" part of the page
    38 from types import *
    38 from types import *
    39 import sys
    39 import sys
    40 
    40 
    41 
    41 
    42 def _doNothing(canvas, doc):
    42 def _doNothing(canvas, doc):
    43 	"Dummy callback for onPage"
    43     "Dummy callback for onPage"
    44 	pass
    44     pass
    45 
    45 
    46 
    46 
    47 class IndexingFlowable(Flowable):
    47 class IndexingFlowable(Flowable):
    48 	"""Abstract interface definition for flowables which might
    48     """Abstract interface definition for flowables which might
    49 	hold references to other pages or themselves be targets
    49     hold references to other pages or themselves be targets
    50 	of cross-references.  XRefStart, XRefDest, Table of Contents,
    50     of cross-references.  XRefStart, XRefDest, Table of Contents,
    51 	Indexes etc."""
    51     Indexes etc."""
    52 	def isIndexing(self):
    52     def isIndexing(self):
    53 		return 1
    53         return 1
    54 
    54 
    55 	def isSatisfied(self):
    55     def isSatisfied(self):
    56 		return 1
    56         return 1
    57 
    57 
    58 	def notify(self, kind, stuff):
    58     def notify(self, kind, stuff):
    59 		"""This will be called by the framework wherever 'stuff' happens.
    59         """This will be called by the framework wherever 'stuff' happens.
    60 		'kind' will be a value that can be used to decide whether to
    60         'kind' will be a value that can be used to decide whether to
    61 		pay attention or not."""
    61         pay attention or not."""
    62 		pass
    62         pass
    63 
    63 
    64 	def beforeBuild(self):
    64     def beforeBuild(self):
    65 		"""Called by multiBuild before it starts; use this to clear
    65         """Called by multiBuild before it starts; use this to clear
    66 		old contents"""
    66         old contents"""
    67 		pass
    67         pass
    68 
    68 
    69 	def afterBuild(self):
    69     def afterBuild(self):
    70 		"""Called after build ends but before isSatisfied"""
    70         """Called after build ends but before isSatisfied"""
    71 		pass
    71         pass
    72 
    72 
    73 
    73 
    74 class ActionFlowable(Flowable):
    74 class ActionFlowable(Flowable):
    75 	'''This Flowable is never drawn, it can be used for data driven controls
    75     '''This Flowable is never drawn, it can be used for data driven controls
    76 	   For example to change a page template (from one column to two, for example)
    76        For example to change a page template (from one column to two, for example)
    77 	   use NextPageTemplate which creates an ActionFlowable.
    77        use NextPageTemplate which creates an ActionFlowable.
    78 	'''
    78     '''
    79 	def __init__(self,action=()):
    79     def __init__(self,action=()):
    80 		if type(action) not in (ListType, TupleType):
    80         if type(action) not in (ListType, TupleType):
    81 			action = (action,)
    81             action = (action,)
    82 		self.action = tuple(action)
    82         self.action = tuple(action)
    83 
    83 
    84 	def wrap(self, availWidth, availHeight):
    84     def wrap(self, availWidth, availHeight):
    85 		'''Should never be called.'''
    85         '''Should never be called.'''
    86 		raise NotImplementedError
    86         raise NotImplementedError
    87 
    87 
    88 	def draw(self):
    88     def draw(self):
    89 		'''Should never be called.'''
    89         '''Should never be called.'''
    90 		raise NotImplementedError
    90         raise NotImplementedError
    91 
    91 
    92 	def apply(self,doc):
    92     def apply(self,doc):
    93 		'''
    93         '''
    94 		This is called by the doc.build processing to allow the instance to
    94         This is called by the doc.build processing to allow the instance to
    95 		implement its behaviour
    95         implement its behaviour
    96 		'''
    96         '''
    97 		action = self.action[0]
    97         action = self.action[0]
    98 		args = tuple(self.action[1:])
    98         args = tuple(self.action[1:])
    99 		arn = 'handle_'+action
    99         arn = 'handle_'+action
   100 		try:
   100         try:
   101 			apply(getattr(doc,arn), args)
   101             apply(getattr(doc,arn), args)
   102 		except AttributeError, aerr:
   102         except AttributeError, aerr:
   103 			if aerr.args[0]==arn:
   103             if aerr.args[0]==arn:
   104 				raise NotImplementedError, "Can't handle ActionFlowable(%s)" % action
   104                 raise NotImplementedError, "Can't handle ActionFlowable(%s)" % action
   105 			else:
   105             else:
   106 				raise
   106                 raise
   107 		except "bogus":
   107         except "bogus":
   108 			t, v, None = sys.exc_info()
   108             t, v, None = sys.exc_info()
   109 			raise t, "%s\n	 handle_%s args=%s"%(v,action,args)
   109             raise t, "%s\n   handle_%s args=%s"%(v,action,args)
   110 
   110 
   111 	def __call__(self):
   111     def __call__(self):
   112 		return self
   112         return self
   113 
   113 
   114 class NextFrameFlowable(ActionFlowable):
   114 class NextFrameFlowable(ActionFlowable):
   115 	def __init__(self,ix,resume=0):
   115     def __init__(self,ix,resume=0):
   116 		ActionFlowable.__init__(self,('nextFrame',ix,resume))
   116         ActionFlowable.__init__(self,('nextFrame',ix,resume))
   117 
   117 
   118 class CurrentFrameFlowable(ActionFlowable):
   118 class CurrentFrameFlowable(ActionFlowable):
   119 	def __init__(self,ix,resume=0):
   119     def __init__(self,ix,resume=0):
   120 		ActionFlowable.__init__(self,('currentFrame',ix,resume))
   120         ActionFlowable.__init__(self,('currentFrame',ix,resume))
   121 
   121 
   122 class _FrameBreak(ActionFlowable):
   122 class _FrameBreak(ActionFlowable):
   123 	'''
   123     '''
   124 	A special ActionFlowable that allows setting doc._nextFrameIndex
   124     A special ActionFlowable that allows setting doc._nextFrameIndex
   125 
   125 
   126 	eg story.append(FrameBreak('mySpecialFrame'))
   126     eg story.append(FrameBreak('mySpecialFrame'))
   127 	'''
   127     '''
   128 	def __call__(self,ix=None,resume=0):
   128     def __call__(self,ix=None,resume=0):
   129 		r = self.__class__(self.action+(resume,))
   129         r = self.__class__(self.action+(resume,))
   130 		r._ix = ix
   130         r._ix = ix
   131 		return r
   131         return r
   132 
   132 
   133 	def apply(self,doc):
   133     def apply(self,doc):
   134 		if getattr(self,'_ix',None): doc._nextFrameIndex = self._ix
   134         if getattr(self,'_ix',None): doc._nextFrameIndex = self._ix
   135 		ActionFlowable.apply(self,doc)
   135         ActionFlowable.apply(self,doc)
   136 
   136 
   137 FrameBreak = _FrameBreak('frameEnd')
   137 FrameBreak = _FrameBreak('frameEnd')
   138 PageBegin = ActionFlowable('pageBegin')
   138 PageBegin = ActionFlowable('pageBegin')
   139 
   139 
   140 
   140 
   141 class NextPageTemplate(ActionFlowable):
   141 class NextPageTemplate(ActionFlowable):
   142 	"""When you get to the next page, use the template specified (change to two column, for example)  """
   142     """When you get to the next page, use the template specified (change to two column, for example)  """
   143 	def __init__(self,pt):
   143     def __init__(self,pt):
   144 		ActionFlowable.__init__(self,('nextPageTemplate',pt))
   144         ActionFlowable.__init__(self,('nextPageTemplate',pt))
   145 
   145 
   146 
   146 
   147 class PageTemplate:
   147 class PageTemplate:
   148 	"""
   148     """
   149 	essentially a list of Frames and an onPage routine to call at the start
   149     essentially a list of Frames and an onPage routine to call at the start
   150 	of a page when this is selected. onPageEnd gets called at the end.
   150     of a page when this is selected. onPageEnd gets called at the end.
   151 	derived classes can also implement beforeDrawPage and afterDrawPage if they want
   151     derived classes can also implement beforeDrawPage and afterDrawPage if they want
   152 	"""
   152     """
   153 	def __init__(self,id=None,frames=[],onPage=_doNothing, onPageEnd=_doNothing,
   153     def __init__(self,id=None,frames=[],onPage=_doNothing, onPageEnd=_doNothing,
   154 				 pagesize=defaultPageSize):
   154                  pagesize=defaultPageSize):
   155 		if type(frames) not in (ListType,TupleType): frames = [frames]
   155         if type(frames) not in (ListType,TupleType): frames = [frames]
   156 		assert filter(lambda x: not isinstance(x,Frame), frames)==[], "frames argument error"
   156         assert filter(lambda x: not isinstance(x,Frame), frames)==[], "frames argument error"
   157 		self.id = id
   157         self.id = id
   158 		self.frames = frames
   158         self.frames = frames
   159 		self.onPage = onPage
   159         self.onPage = onPage
   160 		self.onPageEnd = onPageEnd
   160         self.onPageEnd = onPageEnd
   161 		self.pagesize = pagesize
   161         self.pagesize = pagesize
   162 
   162 
   163 	def beforeDrawPage(self,canv,doc):
   163     def beforeDrawPage(self,canv,doc):
   164 		"""Override this if you want additional functionality or prefer
   164         """Override this if you want additional functionality or prefer
   165 		a class based page routine.  Called before any flowables for
   165         a class based page routine.  Called before any flowables for
   166 		this page are processed."""
   166         this page are processed."""
   167 		pass
   167         pass
   168 
   168 
   169 	def checkPageSize(self,canv,doc):
   169     def checkPageSize(self,canv,doc):
   170 		'''This gets called by the template framework
   170         '''This gets called by the template framework
   171 		If canv size != doc size then the canv size is set to
   171         If canv size != doc size then the canv size is set to
   172 		the template size or if that's not available to the
   172         the template size or if that's not available to the
   173 		doc size.
   173         doc size.
   174 		'''
   174         '''
   175 		#### NEVER EVER EVER COMPARE FLOATS FOR EQUALITY
   175         #### NEVER EVER EVER COMPARE FLOATS FOR EQUALITY
   176 		#RGB converting pagesizes to ints means we are accurate to one point
   176         #RGB converting pagesizes to ints means we are accurate to one point
   177 		#RGB I suggest we should be aiming a little better
   177         #RGB I suggest we should be aiming a little better
   178 		cp = None
   178         cp = None
   179 		dp = None
   179         dp = None
   180 		sp = None
   180         sp = None
   181 		if canv._pagesize: cp = map(int, canv._pagesize)
   181         if canv._pagesize: cp = map(int, canv._pagesize)
   182 		if self.pagesize: sp = map(int, self.pagesize)
   182         if self.pagesize: sp = map(int, self.pagesize)
   183 		if doc.pagesize: dp = map(int, doc.pagesize)
   183         if doc.pagesize: dp = map(int, doc.pagesize)
   184 		if cp!=sp:
   184         if cp!=sp:
   185 			if sp:
   185             if sp:
   186 				canv.setPageSize(self.pagesize)
   186                 canv.setPageSize(self.pagesize)
   187 			elif cp!=dp:
   187             elif cp!=dp:
   188 				canv.setPageSize(doc.pagesize)
   188                 canv.setPageSize(doc.pagesize)
   189 
   189 
   190 	def afterDrawPage(self, canv, doc):
   190     def afterDrawPage(self, canv, doc):
   191 		"""This is called after the last flowable for the page has
   191         """This is called after the last flowable for the page has
   192 		been processed.  You might use this if the page header or
   192         been processed.  You might use this if the page header or
   193 		footer needed knowledge of what flowables were drawn on
   193         footer needed knowledge of what flowables were drawn on
   194 		this page."""
   194         this page."""
   195 		pass
   195         pass
   196 
   196 
   197 
   197 
   198 class BaseDocTemplate:
   198 class BaseDocTemplate:
   199 	"""
   199     """
   200 	First attempt at defining a document template class.
   200     First attempt at defining a document template class.
   201 
   201 
   202 	The basic idea is simple.
   202     The basic idea is simple.
   203 	0)	The document has a list of data associated with it
   203     0)  The document has a list of data associated with it
   204 		this data should derive from flowables. We'll have
   204         this data should derive from flowables. We'll have
   205 		special classes like PageBreak, FrameBreak to do things
   205         special classes like PageBreak, FrameBreak to do things
   206 		like forcing a page end etc.
   206         like forcing a page end etc.
   207 
   207 
   208 	1)	The document has one or more page templates.
   208     1)  The document has one or more page templates.
   209 
   209 
   210 	2)	Each page template has one or more frames.
   210     2)  Each page template has one or more frames.
   211 
   211 
   212 	3)	The document class provides base methods for handling the
   212     3)  The document class provides base methods for handling the
   213 		story events and some reasonable methods for getting the
   213         story events and some reasonable methods for getting the
   214 		story flowables into the frames.
   214         story flowables into the frames.
   215 
   215 
   216 	4)	The document instances can override the base handler routines.
   216     4)  The document instances can override the base handler routines.
   217 
   217 
   218 	Most of the methods for this class are not called directly by the user,
   218     Most of the methods for this class are not called directly by the user,
   219 	but in some advanced usages they may need to be overridden via subclassing.
   219     but in some advanced usages they may need to be overridden via subclassing.
   220 
   220 
   221 	EXCEPTION: doctemplate.build(...) must be called for most reasonable uses
   221     EXCEPTION: doctemplate.build(...) must be called for most reasonable uses
   222 	since it builds a document using the page template.
   222     since it builds a document using the page template.
   223 
   223 
   224 	Each document template builds exactly one document into a file specified
   224     Each document template builds exactly one document into a file specified
   225 	by the filename argument on initialization.
   225     by the filename argument on initialization.
   226 
   226 
   227 	Possible keyword arguments for the initialization:
   227     Possible keyword arguments for the initialization:
   228 
   228 
   229 	pageTemplates: A list of templates.  Must be nonempty.	Names
   229     pageTemplates: A list of templates.  Must be nonempty.  Names
   230 	  assigned to the templates are used for referring to them so no two used
   230       assigned to the templates are used for referring to them so no two used
   231 	  templates should have the same name.	For example you might want one template
   231       templates should have the same name.  For example you might want one template
   232 	  for a title page, one for a section first page, one for a first page of
   232       for a title page, one for a section first page, one for a first page of
   233 	  a chapter and two more for the interior of a chapter on odd and even pages.
   233       a chapter and two more for the interior of a chapter on odd and even pages.
   234 	  If this argument is omitted then at least one pageTemplate should be provided
   234       If this argument is omitted then at least one pageTemplate should be provided
   235 	  using the addPageTemplates method before the document is built.
   235       using the addPageTemplates method before the document is built.
   236 	showBoundary: if set draw a box around the frame boundaries.
   236     showBoundary: if set draw a box around the frame boundaries.
   237 	leftMargin:
   237     leftMargin:
   238 	rightMargin:
   238     rightMargin:
   239 	topMargin:
   239     topMargin:
   240 	bottomMargin:  Margin sizes in points (default 1 inch)
   240     bottomMargin:  Margin sizes in points (default 1 inch)
   241 	  These margins may be overridden by the pageTemplates.  They are primarily of interest
   241       These margins may be overridden by the pageTemplates.  They are primarily of interest
   242 	  for the SimpleDocumentTemplate subclass.
   242       for the SimpleDocumentTemplate subclass.
   243 	allowSplitting:  If set flowables (eg, paragraphs) may be split across frames or pages
   243     allowSplitting:  If set flowables (eg, paragraphs) may be split across frames or pages
   244 	  (default: 1)
   244       (default: 1)
   245 	title: Internal title for document (does not automatically display on any page)
   245     title: Internal title for document (does not automatically display on any page)
   246 	author: Internal author for document (does not automatically display on any page)
   246     author: Internal author for document (does not automatically display on any page)
   247 	"""
   247     """
   248 	_initArgs = {	'pagesize':defaultPageSize,
   248     _initArgs = {   'pagesize':defaultPageSize,
   249 					'pageTemplates':[],
   249                     'pageTemplates':[],
   250 					'showBoundary':0,
   250                     'showBoundary':0,
   251 					'leftMargin':inch,
   251                     'leftMargin':inch,
   252 					'rightMargin':inch,
   252                     'rightMargin':inch,
   253 					'topMargin':inch,
   253                     'topMargin':inch,
   254 					'bottomMargin':inch,
   254                     'bottomMargin':inch,
   255 					'allowSplitting':1,
   255                     'allowSplitting':1,
   256 					'title':None,
   256                     'title':None,
   257 					'author':None,
   257                     'author':None,
   258 					'_pageBreakQuick':1}
   258                     '_pageBreakQuick':1}
   259 	_invalidInitArgs = ()
   259     _invalidInitArgs = ()
   260 
   260 
   261 	def __init__(self, filename, **kw):
   261     def __init__(self, filename, **kw):
   262 		"""create a document template bound to a filename (see class documentation for keyword arguments)"""
   262         """create a document template bound to a filename (see class documentation for keyword arguments)"""
   263 		self.filename = filename
   263         self.filename = filename
   264 
   264 
   265 		for k in self._initArgs.keys():
   265         for k in self._initArgs.keys():
   266 			if not kw.has_key(k):
   266             if not kw.has_key(k):
   267 				v = self._initArgs[k]
   267                 v = self._initArgs[k]
   268 			else:
   268             else:
   269 				if k in self._invalidInitArgs:
   269                 if k in self._invalidInitArgs:
   270 					raise ValueError, "Invalid argument %s" % k
   270                     raise ValueError, "Invalid argument %s" % k
   271 				v = kw[k]
   271                 v = kw[k]
   272 			setattr(self,k,v)
   272             setattr(self,k,v)
   273 		#print "pagesize is", self.pagesize
   273         #print "pagesize is", self.pagesize
   274 
   274 
   275 		p = self.pageTemplates
   275         p = self.pageTemplates
   276 		self.pageTemplates = []
   276         self.pageTemplates = []
   277 		self.addPageTemplates(p)
   277         self.addPageTemplates(p)
   278 
   278 
   279 		# facility to assist multi-build and cross-referencing.
   279         # facility to assist multi-build and cross-referencing.
   280 		# various hooks can put things into here - key is what
   280         # various hooks can put things into here - key is what
   281 		# you want, value is a page number.  This can then be
   281         # you want, value is a page number.  This can then be
   282 		# passed to indexing flowables.
   282         # passed to indexing flowables.
   283 		self._pageRefs = {}
   283         self._pageRefs = {}
   284 		self._indexingFlowables = []
   284         self._indexingFlowables = []
   285 
   285 
   286 		self._calc()
   286 
   287 		self.afterInit()
   287         #callback facility
   288 
   288         self._onPage = None
   289 	def _calc(self):
   289         self._calc()
   290 		self._rightMargin = self.pagesize[0] - self.rightMargin
   290         self.afterInit()
   291 		self._topMargin = self.pagesize[1] - self.topMargin
   291 
   292 		self.width = self._rightMargin - self.leftMargin
   292     def _calc(self):
   293 		self.height = self._topMargin - self.bottomMargin
   293         self._rightMargin = self.pagesize[0] - self.rightMargin
   294 
   294         self._topMargin = self.pagesize[1] - self.topMargin
   295 
   295         self.width = self._rightMargin - self.leftMargin
   296 	def clean_hanging(self):
   296         self.height = self._topMargin - self.bottomMargin
   297 		'handle internal postponed actions'
   297 
   298 		while len(self._hanging):
   298     def setPageCallBack(self, func):
   299 			self.handle_flowable(self._hanging)
   299         'Progress monitor - func(pageNo) called on each new page'
   300 
   300         self._onPage = func
   301 	def addPageTemplates(self,pageTemplates):
   301 
   302 		'add one or a sequence of pageTemplates'
   302     def clean_hanging(self):
   303 		if type(pageTemplates) not in (ListType,TupleType):
   303         'handle internal postponed actions'
   304 			pageTemplates = [pageTemplates]
   304         while len(self._hanging):
   305 		#this test below fails due to inconsistent imports!
   305             self.handle_flowable(self._hanging)
   306 		#assert filter(lambda x: not isinstance(x,PageTemplate), pageTemplates)==[], "pageTemplates argument error"
   306 
   307 		for t in pageTemplates:
   307     def addPageTemplates(self,pageTemplates):
   308 			self.pageTemplates.append(t)
   308         'add one or a sequence of pageTemplates'
   309 
   309         if type(pageTemplates) not in (ListType,TupleType):
   310 	def handle_documentBegin(self):
   310             pageTemplates = [pageTemplates]
   311 		'''implement actions at beginning of document'''
   311         #this test below fails due to inconsistent imports!
   312 		self._hanging = [PageBegin]
   312         #assert filter(lambda x: not isinstance(x,PageTemplate), pageTemplates)==[], "pageTemplates argument error"
   313 		self.pageTemplate = self.pageTemplates[0]
   313         for t in pageTemplates:
   314 		self.page = 0
   314             self.pageTemplates.append(t)
   315 		self.beforeDocument()
   315 
   316 
   316     def handle_documentBegin(self):
   317 	def handle_pageBegin(self):
   317         '''implement actions at beginning of document'''
   318 		'''Perform actions required at beginning of page.
   318         self._hanging = [PageBegin]
   319 		shouldn't normally be called directly'''
   319         self.pageTemplate = self.pageTemplates[0]
   320 		self.page = self.page + 1
   320         self.page = 0
   321 		self.pageTemplate.beforeDrawPage(self.canv,self)
   321         self.beforeDocument()
   322 		self.pageTemplate.checkPageSize(self.canv,self)
   322 
   323 		self.pageTemplate.onPage(self.canv,self)
   323     def handle_pageBegin(self):
   324 		for f in self.pageTemplate.frames: f._reset()
   324         '''Perform actions required at beginning of page.
   325 		self.beforePage()
   325         shouldn't normally be called directly'''
   326 		if hasattr(self,'_nextFrameIndex'):
   326         self.page = self.page + 1
   327 			del self._nextFrameIndex
   327         self.pageTemplate.beforeDrawPage(self.canv,self)
   328 		self.frame = self.pageTemplate.frames[0]
   328         self.pageTemplate.checkPageSize(self.canv,self)
   329 		self.handle_frameBegin()
   329         self.pageTemplate.onPage(self.canv,self)
   330 
   330         for f in self.pageTemplate.frames: f._reset()
   331 	def handle_pageEnd(self):
   331         self.beforePage()
   332 		''' show the current page
   332         if hasattr(self,'_nextFrameIndex'):
   333 			check the next page template
   333             del self._nextFrameIndex
   334 			hang a page begin
   334         self.frame = self.pageTemplate.frames[0]
   335 		'''
   335         self.handle_frameBegin()
   336 		self.pageTemplate.afterDrawPage(self.canv, self)
   336 
   337 		self.pageTemplate.onPageEnd(self.canv, self)
   337     def handle_pageEnd(self):
   338 		self.afterPage()
   338         ''' show the current page
   339 		self.canv.showPage()
   339             check the next page template
   340 		if hasattr(self,'_nextPageTemplateIndex'):
   340             hang a page begin
   341 			self.pageTemplate = self.pageTemplates[self._nextPageTemplateIndex]
   341         '''
   342 			del self._nextPageTemplateIndex
   342         self.pageTemplate.afterDrawPage(self.canv, self)
   343 		self._hanging.append(PageBegin)
   343         self.pageTemplate.onPageEnd(self.canv, self)
   344 
   344         self.afterPage()
   345 	def handle_pageBreak(self):
   345         self.canv.showPage()
   346 		'''some might choose not to end all the frames'''
   346         if hasattr(self,'_nextPageTemplateIndex'):
   347 		if self._pageBreakQuick:
   347             self.pageTemplate = self.pageTemplates[self._nextPageTemplateIndex]
   348 			self.handle_pageEnd()
   348             del self._nextPageTemplateIndex
   349 		else:
   349         self._hanging.append(PageBegin)
   350 			n = len(self._hanging)
   350 
   351 			while len(self._hanging)==n:
   351     def handle_pageBreak(self):
   352 				self.handle_frameEnd()
   352         '''some might choose not to end all the frames'''
   353 
   353         if self._pageBreakQuick:
   354 	def handle_frameBegin(self,resume=0):
   354             self.handle_pageEnd()
   355 		'''What to do at the beginning of a frame'''
   355         else:
   356 		f = self.frame
   356             n = len(self._hanging)
   357 		if f._atTop:
   357             while len(self._hanging)==n:
   358 			if self.showBoundary or self.frame.showBoundary:
   358                 self.handle_frameEnd()
   359 				self.frame.drawBoundary(self.canv)
   359 
   360 
   360     def handle_frameBegin(self,resume=0):
   361 	def handle_frameEnd(self,resume=0):
   361         '''What to do at the beginning of a frame'''
   362 		''' Handles the semantics of the end of a frame. This includes the selection of
   362         f = self.frame
   363 			the next frame or if this is the last frame then invoke pageEnd.
   363         if f._atTop:
   364 		'''
   364             if self.showBoundary or self.frame.showBoundary:
   365 		if hasattr(self,'_nextFrameIndex'):
   365                 self.frame.drawBoundary(self.canv)
   366 			frame = self.pageTemplate.frames[self._nextFrameIndex]
   366 
   367 			del self._nextFrameIndex
   367     def handle_frameEnd(self,resume=0):
   368 			self.handle_frameBegin(resume)
   368         ''' Handles the semantics of the end of a frame. This includes the selection of
   369 		elif hasattr(self.frame,'lastFrame') or self.frame is self.pageTemplate.frames[-1]:
   369             the next frame or if this is the last frame then invoke pageEnd.
   370 			self.handle_pageEnd()
   370         '''
   371 			self.frame = None
   371         if hasattr(self,'_nextFrameIndex'):
   372 		else:
   372             frame = self.pageTemplate.frames[self._nextFrameIndex]
   373 			f = self.frame
   373             del self._nextFrameIndex
   374 			self.frame = self.pageTemplate.frames[self.pageTemplate.frames.index(f) + 1]
   374             self.handle_frameBegin(resume)
   375 			self.handle_frameBegin()
   375         elif hasattr(self.frame,'lastFrame') or self.frame is self.pageTemplate.frames[-1]:
   376 
   376             self.handle_pageEnd()
   377 	def handle_nextPageTemplate(self,pt):
   377             self.frame = None
   378 		'''On endPage chenge to the page template with name or index pt'''
   378         else:
   379 		if type(pt) is StringType:
   379             f = self.frame
   380 			for t in self.pageTemplates:
   380             self.frame = self.pageTemplate.frames[self.pageTemplate.frames.index(f) + 1]
   381 				if t.id == pt:
   381             self.handle_frameBegin()
   382 					self._nextPageTemplateIndex = self.pageTemplates.index(t)
   382 
   383 					return
   383     def handle_nextPageTemplate(self,pt):
   384 			raise ValueError, "can't find template('%s')"%pt
   384         '''On endPage chenge to the page template with name or index pt'''
   385 		elif type(pt) is IntType:
   385         if type(pt) is StringType:
   386 			self._nextPageTemplateIndex = pt
   386             for t in self.pageTemplates:
   387 		else:
   387                 if t.id == pt:
   388 			raise TypeError, "argument pt should be string or integer"
   388                     self._nextPageTemplateIndex = self.pageTemplates.index(t)
   389 
   389                     return
   390 	def handle_nextFrame(self,fx):
   390             raise ValueError, "can't find template('%s')"%pt
   391 		'''On endFrame chenge to the frame with name or index fx'''
   391         elif type(pt) is IntType:
   392 		if type(fx) is StringType:
   392             self._nextPageTemplateIndex = pt
   393 			for f in self.pageTemplate.frames:
   393         else:
   394 				if f.id == fx:
   394             raise TypeError, "argument pt should be string or integer"
   395 					self._nextFrameIndex = self.pageTemplate.frames.index(f)
   395 
   396 					return
   396     def handle_nextFrame(self,fx):
   397 			raise ValueError, "can't find frame('%s')"%fx
   397         '''On endFrame chenge to the frame with name or index fx'''
   398 		elif type(fx) is IntType:
   398         if type(fx) is StringType:
   399 			self._nextFrameIndex = fx
   399             for f in self.pageTemplate.frames:
   400 		else:
   400                 if f.id == fx:
   401 			raise TypeError, "argument fx should be string or integer"
   401                     self._nextFrameIndex = self.pageTemplate.frames.index(f)
   402 
   402                     return
   403 	def handle_currentFrame(self,fx):
   403             raise ValueError, "can't find frame('%s')"%fx
   404 		'''chenge to the frame with name or index fx'''
   404         elif type(fx) is IntType:
   405 		if type(fx) is StringType:
   405             self._nextFrameIndex = fx
   406 			for f in self.pageTemplate.frames:
   406         else:
   407 				if f.id == fx:
   407             raise TypeError, "argument fx should be string or integer"
   408 					self._nextFrameIndex = self.pageTemplate.frames.index(f)
   408 
   409 					return
   409     def handle_currentFrame(self,fx):
   410 			raise ValueError, "can't find frame('%s')"%fx
   410         '''chenge to the frame with name or index fx'''
   411 		elif type(fx) is IntType:
   411         if type(fx) is StringType:
   412 			self._nextFrameIndex = fx
   412             for f in self.pageTemplate.frames:
   413 		else:
   413                 if f.id == fx:
   414 			raise TypeError, "argument fx should be string or integer"
   414                     self._nextFrameIndex = self.pageTemplate.frames.index(f)
   415 
   415                     return
   416 	def handle_breakBefore(self, flowables):
   416             raise ValueError, "can't find frame('%s')"%fx
   417 		'''preprocessing step to allow pageBreakBefore and frameBreakBefore attributes'''
   417         elif type(fx) is IntType:
   418 		first = flowables[0]
   418             self._nextFrameIndex = fx
   419 		# if we insert a page break before, we'll process that, see it again,
   419         else:
   420 		# and go in an infinite loop.  So we need to set a flag on the object
   420             raise TypeError, "argument fx should be string or integer"
   421 		# saying 'skip me'.  This should be unset on the next pass
   421 
   422 		if hasattr(first, '_skipMeNextTime'):
   422     def handle_breakBefore(self, flowables):
   423 			delattr(first, '_skipMeNextTime')
   423         '''preprocessing step to allow pageBreakBefore and frameBreakBefore attributes'''
   424 			return
   424         first = flowables[0]
   425 		# this could all be made much quicker by putting the attributes
   425         # if we insert a page break before, we'll process that, see it again,
   426 		# in to the flowables with a defult value of 0
   426         # and go in an infinite loop.  So we need to set a flag on the object
   427 		if hasattr(first,'pageBreakBefore') and first.pageBreakBefore == 1:
   427         # saying 'skip me'.  This should be unset on the next pass
   428 			first._skipMeNextTime = 1
   428         if hasattr(first, '_skipMeNextTime'):
   429 			first.insert(0, PageBreak())
   429             delattr(first, '_skipMeNextTime')
   430 			return
   430             return
   431 		if hasattr(first,'style') and hasattr(first.style, 'pageBreakBefore') and first.style.pageBreakBefore == 1:
   431         # this could all be made much quicker by putting the attributes
   432 			first._skipMeNextTime = 1
   432         # in to the flowables with a defult value of 0
   433 			flowables.insert(0, PageBreak())
   433         if hasattr(first,'pageBreakBefore') and first.pageBreakBefore == 1:
   434 			return
   434             first._skipMeNextTime = 1
   435 		if hasattr(first,'frameBreakBefore') and first.frameBreakBefore == 1:
   435             first.insert(0, PageBreak())
   436 			first._skipMeNextTime = 1
   436             return
   437 			flowables.insert(0, FrameBreak())
   437         if hasattr(first,'style') and hasattr(first.style, 'pageBreakBefore') and first.style.pageBreakBefore == 1:
   438 			return
   438             first._skipMeNextTime = 1
   439 		if hasattr(first,'style') and hasattr(first.style, 'frameBreakBefore') and first.style.frameBreakBefore == 1:
   439             flowables.insert(0, PageBreak())
   440 			first._skipMeNextTime = 1
   440             return
   441 			flowables.insert(0, FrameBreak())
   441         if hasattr(first,'frameBreakBefore') and first.frameBreakBefore == 1:
   442 			return
   442             first._skipMeNextTime = 1
   443 
   443             flowables.insert(0, FrameBreak())
   444 
   444             return
   445 	def handle_keepWithNext(self, flowables):
   445         if hasattr(first,'style') and hasattr(first.style, 'frameBreakBefore') and first.style.frameBreakBefore == 1:
   446 		"implements keepWithNext"
   446             first._skipMeNextTime = 1
   447 		i = 0
   447             flowables.insert(0, FrameBreak())
   448 		n = len(flowables)
   448             return
   449 		while i<n and flowables[i].getKeepWithNext(): i = i + 1
   449 
   450 		if i:
   450 
   451 			i = i + 1
   451     def handle_keepWithNext(self, flowables):
   452 			K = KeepTogether(flowables[:i])
   452         "implements keepWithNext"
   453 			for f in K._flowables:
   453         i = 0
   454 				f.keepWithNext = 0
   454         n = len(flowables)
   455 			del flowables[:i]
   455         while i<n and flowables[i].getKeepWithNext(): i = i + 1
   456 			flowables.insert(0,K)
   456         if i:
   457 
   457             i = i + 1
   458 	def handle_flowable(self,flowables):
   458             K = KeepTogether(flowables[:i])
   459 		'''try to handle one flowable from the front of list flowables.'''
   459             for f in K._flowables:
   460 
   460                 f.keepWithNext = 0
   461 		#allow document a chance to look at, modify or ignore
   461             del flowables[:i]
   462 		#the object(s) about to be processed
   462             flowables.insert(0,K)
   463 		self.filterFlowables(flowables)
   463 
   464 
   464     def handle_flowable(self,flowables):
   465 		self.handle_breakBefore(flowables)
   465         '''try to handle one flowable from the front of list flowables.'''
   466 		self.handle_keepWithNext(flowables)
   466 
   467 		f = flowables[0]
   467         #allow document a chance to look at, modify or ignore
   468 		del flowables[0]
   468         #the object(s) about to be processed
   469 		if f is None:
   469         self.filterFlowables(flowables)
   470 			return
   470 
   471 
   471         self.handle_breakBefore(flowables)
   472 		if isinstance(f,PageBreak):
   472         self.handle_keepWithNext(flowables)
   473 			self.handle_pageBreak()
   473         f = flowables[0]
   474 			self.afterFlowable(f)
   474         del flowables[0]
   475 		elif isinstance(f,ActionFlowable):
   475         if f is None:
   476 			f.apply(self)
   476             return
   477 			self.afterFlowable(f)
   477 
   478 		else:
   478         if isinstance(f,PageBreak):
   479 			#try to fit it then draw it
   479             self.handle_pageBreak()
   480 			if self.frame.add(f, self.canv, trySplit=self.allowSplitting):
   480             self.afterFlowable(f)
   481 				self.afterFlowable(f)
   481         elif isinstance(f,ActionFlowable):
   482 			else:
   482             f.apply(self)
   483 				#if isinstance(f, KeepTogether): print 'could not add it to frame'
   483             self.afterFlowable(f)
   484 				if self.allowSplitting:
   484         else:
   485 					# see if this is a splittable thing
   485             #try to fit it then draw it
   486 					S = self.frame.split(f,self.canv)
   486             if self.frame.add(f, self.canv, trySplit=self.allowSplitting):
   487 					n = len(S)
   487                 self.afterFlowable(f)
   488 				else:
   488             else:
   489 					n = 0
   489                 #if isinstance(f, KeepTogether): print 'could not add it to frame'
   490 				#if isinstance(f, KeepTogether): print 'n=%d' % n
   490                 if self.allowSplitting:
   491 				if n:
   491                     # see if this is a splittable thing
   492 					if self.frame.add(S[0], self.canv, trySplit=0):
   492                     S = self.frame.split(f,self.canv)
   493 						self.afterFlowable(S[0])
   493                     n = len(S)
   494 					else:
   494                 else:
   495 						print 'n = %d' % n
   495                     n = 0
   496 						raise "LayoutError", "splitting error on page %d in\n%s" % (self.page,f.identity(30))
   496                 #if isinstance(f, KeepTogether): print 'n=%d' % n
   497 					del S[0]
   497                 if n:
   498 					for f in xrange(n-1):
   498                     if self.frame.add(S[0], self.canv, trySplit=0):
   499 						flowables.insert(f,S[f])	# put split flowables back on the list
   499                         self.afterFlowable(S[0])
   500 				else:
   500                     else:
   501 					# this must be cleared when they are finally drawn!
   501                         print 'n = %d' % n
   502 ##					if hasattr(f,'postponed'):
   502                         raise "LayoutError", "splitting error on page %d in\n%s" % (self.page,f.identity(30))
   503 					if hasattr(f,'_postponed'):
   503                     del S[0]
   504 						message = "Flowable %s too large on page %d" % (f.identity(30), self.page)
   504                     for f in xrange(n-1):
   505 						#show us, it might be handy
   505                         flowables.insert(f,S[f])    # put split flowables back on the list
   506 						#HACK = it seems within tables we sometimes
   506                 else:
   507 						#get an empty paragraph that won't fit and this
   507                     # this must be cleared when they are finally drawn!
   508 						#causes it to fall over.  FIXME FIXME FIXME
   508 ##                  if hasattr(f,'postponed'):
   509 						raise "LayoutError", message
   509                     if hasattr(f,'_postponed'):
   510 ##					f.postponed = 1
   510                         message = "Flowable %s too large on page %d" % (f.identity(30), self.page)
   511 					f._postponed = 1
   511                         #show us, it might be handy
   512 					flowables.insert(0,f)			# put the flowable back
   512                         #HACK = it seems within tables we sometimes
   513 					self.handle_frameEnd()
   513                         #get an empty paragraph that won't fit and this
   514 
   514                         #causes it to fall over.  FIXME FIXME FIXME
   515 	#these are provided so that deriving classes can refer to them
   515                         raise "LayoutError", message
   516 	_handle_documentBegin = handle_documentBegin
   516 ##                  f.postponed = 1
   517 	_handle_pageBegin = handle_pageBegin
   517                     f._postponed = 1
   518 	_handle_pageEnd = handle_pageEnd
   518                     flowables.insert(0,f)           # put the flowable back
   519 	_handle_frameBegin = handle_frameBegin
   519                     self.handle_frameEnd()
   520 	_handle_frameEnd = handle_frameEnd
   520 
   521 	_handle_flowable = handle_flowable
   521     #these are provided so that deriving classes can refer to them
   522 	_handle_nextPageTemplate = handle_nextPageTemplate
   522     _handle_documentBegin = handle_documentBegin
   523 	_handle_currentFrame = handle_currentFrame
   523     _handle_pageBegin = handle_pageBegin
   524 	_handle_nextFrame = handle_nextFrame
   524     _handle_pageEnd = handle_pageEnd
   525 
   525     _handle_frameBegin = handle_frameBegin
   526 	def _startBuild(self, filename=None, canvasmaker=canvas.Canvas):
   526     _handle_frameEnd = handle_frameEnd
   527 		self._calc()
   527     _handle_flowable = handle_flowable
   528 		self.canv = canvasmaker(filename or self.filename,pagesize=self.pagesize)
   528     _handle_nextPageTemplate = handle_nextPageTemplate
   529 		self.handle_documentBegin()
   529     _handle_currentFrame = handle_currentFrame
   530 
   530     _handle_nextFrame = handle_nextFrame
   531 	def _endBuild(self):
   531 
   532 		if self._hanging!=[] and self._hanging[-1] is PageBegin:
   532     def _startBuild(self, filename=None, canvasmaker=canvas.Canvas):
   533 			del self._hanging[-1]
   533         self._calc()
   534 			self.clean_hanging()
   534         self.canv = canvasmaker(filename or self.filename,pagesize=self.pagesize)
   535 		else:
   535         if self._onPage:
   536 			self.clean_hanging()
   536             self.canv.setPageCallBack(self._onPage)
   537 			self.handle_pageBreak()
   537         self.handle_documentBegin()
   538 
   538 
   539 		self.canv.save()
   539     def _endBuild(self):
   540 		#AR - hack - for some reason a document did not
   540         if self._hanging!=[] and self._hanging[-1] is PageBegin:
   541 		#have these:
   541             del self._hanging[-1]
   542 		#if hasattr(self, 'frame'): del self.frame
   542             self.clean_hanging()
   543 		#if hasattr(self, 'pageTemplate'): del self.pageTemplate
   543         else:
   544 		#del self.frame, self.pageTemplate
   544             self.clean_hanging()
   545 
   545             self.handle_pageBreak()
   546 	def build(self, flowables, filename=None, canvasmaker=canvas.Canvas):
   546 
   547 		"""Build the document from a list of flowables.
   547         self.canv.save()
   548 		   If the filename argument is provided then that filename is used
   548         if self._onPage:
   549 		   rather than the one provided upon initialization.
   549             self.canv.setPageCallBack(None)
   550 		   If the canvasmaker argument is provided then it will be used
   550         #AR - hack - for some reason a document did not
   551 		   instead of the default.	For example a slideshow might use
   551         #have these:
   552 		   an alternate canvas which places 6 slides on a page (by
   552         #if hasattr(self, 'frame'): del self.frame
   553 		   doing translations, scalings and redefining the page break
   553         #if hasattr(self, 'pageTemplate'): del self.pageTemplate
   554 		   operations).
   554         #del self.frame, self.pageTemplate
   555 		"""
   555 
   556 		#assert filter(lambda x: not isinstance(x,Flowable), flowables)==[], "flowables argument error"
   556     def build(self, flowables, filename=None, canvasmaker=canvas.Canvas):
   557 		self._startBuild(filename,canvasmaker)
   557         """Build the document from a list of flowables.
   558 
   558            If the filename argument is provided then that filename is used
   559 		while len(flowables):
   559            rather than the one provided upon initialization.
   560 			self.clean_hanging()
   560            If the canvasmaker argument is provided then it will be used
   561 			self.handle_flowable(flowables)
   561            instead of the default.  For example a slideshow might use
   562 
   562            an alternate canvas which places 6 slides on a page (by
   563 		self._endBuild()
   563            doing translations, scalings and redefining the page break
   564 
   564            operations).
   565 	def _allSatisfied(self):
   565         """
   566 		"""Called by multi-build - are all cross-references resolved?"""
   566         #assert filter(lambda x: not isinstance(x,Flowable), flowables)==[], "flowables argument error"
   567 		allHappy = 1
   567         self._startBuild(filename,canvasmaker)
   568 		for f in self._indexingFlowables:
   568 
   569 			if not f.isSatisfied():
   569         while len(flowables):
   570 				allHappy = 0
   570             self.clean_hanging()
   571 				break
   571             self.handle_flowable(flowables)
   572 		return allHappy
   572 
   573 
   573         self._endBuild()
   574 	def notify(self, kind, stuff):
   574 
   575 		""""Forward to any listeners"""
   575     def _allSatisfied(self):
   576 		for l in self._indexingFlowables:
   576         """Called by multi-build - are all cross-references resolved?"""
   577 			l.notify(kind, stuff)
   577         allHappy = 1
   578 
   578         for f in self._indexingFlowables:
   579 	def pageRef(self, label):
   579             if not f.isSatisfied():
   580 		"""hook to register a page number"""
   580                 allHappy = 0
   581 		if _verbose: print "pageRef called with label '%s' on page %d" % (
   581                 break
   582 			label, self.page)
   582         return allHappy
   583 		self._pageRefs[label] = self.page
   583 
   584 
   584     def notify(self, kind, stuff):
   585 	def multiBuild(self, story,
   585         """"Forward to any listeners"""
   586 				   filename=None,
   586         for l in self._indexingFlowables:
   587 				   canvasmaker=canvas.Canvas,
   587             l.notify(kind, stuff)
   588 				   maxPasses = 10):
   588 
   589 		"""Makes multiple passes until all indexing flowables
   589     def pageRef(self, label):
   590 		are happy."""
   590         """hook to register a page number"""
   591 		self._indexingFlowables = []
   591         if _verbose: print "pageRef called with label '%s' on page %d" % (
   592 		#scan the story and keep a copy
   592             label, self.page)
   593 		for thing in story:
   593         self._pageRefs[label] = self.page
   594 			if thing.isIndexing():
   594 
   595 				self._indexingFlowables.append(thing)
   595     def multiBuild(self, story,
   596 		#print 'scanned story, found these indexing flowables:\n'
   596                    filename=None,
   597 		#print self._indexingFlowables
   597                    canvasmaker=canvas.Canvas,
   598 		
   598                    maxPasses = 10):
   599 		passes = 0
   599         """Makes multiple passes until all indexing flowables
   600 		while 1:
   600         are happy."""
   601 			passes = passes + 1
   601         self._indexingFlowables = []
   602 			if _verbose: print 'building pass '+str(passes) + '...',
   602         #scan the story and keep a copy
   603 
   603         for thing in story:
   604 			for fl in self._indexingFlowables:
   604             if thing.isIndexing():
   605 				fl.beforeBuild()
   605                 self._indexingFlowables.append(thing)
   606 
   606         #print 'scanned story, found these indexing flowables:\n'
   607 			# work with a copy of the story, since it is consumed
   607         #print self._indexingFlowables
   608 			tempStory = story[:]
   608         
   609 			self.build(tempStory, filename, canvasmaker)
   609         passes = 0
   610 			#self.notify('debug',None)
   610         while 1:
   611 
   611             passes = passes + 1
   612 			#clean up so multi-build does not go wrong - the frame
   612             if _verbose: print 'building pass '+str(passes) + '...',
   613 			#packer might have tacked an attribute onto some
   613 
   614 			#paragraphs
   614             for fl in self._indexingFlowables:
   615 			for elem in story:
   615                 fl.beforeBuild()
   616 ##				if hasattr(elem, 'postponed'):
   616 
   617 ##					del elem.postponed
   617             # work with a copy of the story, since it is consumed
   618 				if hasattr(elem, '_postponed'):
   618             tempStory = story[:]
   619 					del elem._postponed
   619             self.build(tempStory, filename, canvasmaker)
   620 
   620             #self.notify('debug',None)
   621 			for fl in self._indexingFlowables:
   621 
   622 				fl.afterBuild()
   622             #clean up so multi-build does not go wrong - the frame
   623 
   623             #packer might have tacked an attribute onto some
   624 			happy = self._allSatisfied()
   624             #paragraphs
   625 
   625             for elem in story:
   626 			if happy:
   626 ##              if hasattr(elem, 'postponed'):
   627 				## print 'OK'
   627 ##                  del elem.postponed
   628 				break
   628                 if hasattr(elem, '_postponed'):
   629 			## else:
   629                     del elem._postponed
   630 				## print 'failed'
   630 
   631 			if passes > maxPasses:
   631             for fl in self._indexingFlowables:
   632 				raise IndexError, "Index entries not resolved after %d passes" % maxPasses
   632                 fl.afterBuild()
   633 
   633 
   634 		if _verbose: print 'saved'
   634             happy = self._allSatisfied()
   635 
   635 
   636 	#these are pure virtuals override in derived classes
   636             if happy:
   637 	#NB these get called at suitable places by the base class
   637                 ## print 'OK'
   638 	#so if you derive and override the handle_xxx methods
   638                 break
   639 	#it's up to you to ensure that they maintain the needed consistency
   639             ## else:
   640 	def afterInit(self):
   640                 ## print 'failed'
   641 		"""This is called after initialisation of the base class."""
   641             if passes > maxPasses:
   642 		pass
   642                 raise IndexError, "Index entries not resolved after %d passes" % maxPasses
   643 
   643 
   644 	def beforeDocument(self):
   644         if _verbose: print 'saved'
   645 		"""This is called before any processing is
   645 
   646 		done on the document."""
   646     #these are pure virtuals override in derived classes
   647 		pass
   647     #NB these get called at suitable places by the base class
   648 
   648     #so if you derive and override the handle_xxx methods
   649 	def beforePage(self):
   649     #it's up to you to ensure that they maintain the needed consistency
   650 		"""This is called at the beginning of page
   650     def afterInit(self):
   651 		processing, and immediately before the
   651         """This is called after initialisation of the base class."""
   652 		beforeDrawPage method of the current page
   652         pass
   653 		template."""
   653 
   654 		pass
   654     def beforeDocument(self):
   655 
   655         """This is called before any processing is
   656 	def afterPage(self):
   656         done on the document."""
   657 		"""This is called after page processing, and
   657         pass
   658 		immediately after the afterDrawPage method
   658 
   659 		of the current page template."""
   659     def beforePage(self):
   660 		pass
   660         """This is called at the beginning of page
   661 
   661         processing, and immediately before the
   662 	def filterFlowables(self,flowables):
   662         beforeDrawPage method of the current page
   663 		'''called to filter flowables at the start of the main handle_flowable method.
   663         template."""
   664 		Upon return if flowables[0] has been set to None it is discarded and the main
   664         pass
   665 		method returns.
   665 
   666 		'''
   666     def afterPage(self):
   667 		pass
   667         """This is called after page processing, and
   668 
   668         immediately after the afterDrawPage method
   669 	def afterFlowable(self, flowable):
   669         of the current page template."""
   670 		'''called after a flowable has been rendered'''
   670         pass
   671 		pass
   671 
       
   672     def filterFlowables(self,flowables):
       
   673         '''called to filter flowables at the start of the main handle_flowable method.
       
   674         Upon return if flowables[0] has been set to None it is discarded and the main
       
   675         method returns.
       
   676         '''
       
   677         pass
       
   678 
       
   679     def afterFlowable(self, flowable):
       
   680         '''called after a flowable has been rendered'''
       
   681         pass
   672 
   682 
   673 
   683 
   674 class SimpleDocTemplate(BaseDocTemplate):
   684 class SimpleDocTemplate(BaseDocTemplate):
   675 	"""A special case document template that will handle many simple documents.
   685     """A special case document template that will handle many simple documents.
   676 	   See documentation for BaseDocTemplate.  No pageTemplates are required
   686        See documentation for BaseDocTemplate.  No pageTemplates are required
   677 	   for this special case.	A page templates are inferred from the
   687        for this special case.   A page templates are inferred from the
   678 	   margin information and the onFirstPage, onLaterPages arguments to the build method.
   688        margin information and the onFirstPage, onLaterPages arguments to the build method.
   679 
   689 
   680 	   A document which has all pages with the same look except for the first
   690        A document which has all pages with the same look except for the first
   681 	   page may can be built using this special approach.
   691        page may can be built using this special approach.
   682 	"""
   692     """
   683 	_invalidInitArgs = ('pageTemplates',)
   693     _invalidInitArgs = ('pageTemplates',)
   684 
   694 
   685 	def handle_pageBegin(self):
   695     def handle_pageBegin(self):
   686 		'''override base method to add a change of page template after the firstpage.
   696         '''override base method to add a change of page template after the firstpage.
   687 		'''
   697         '''
   688 		self._handle_pageBegin()
   698         self._handle_pageBegin()
   689 		self._handle_nextPageTemplate('Later')
   699         self._handle_nextPageTemplate('Later')
   690 
   700 
   691 	def build(self,flowables,onFirstPage=_doNothing, onLaterPages=_doNothing):
   701     def build(self,flowables,onFirstPage=_doNothing, onLaterPages=_doNothing):
   692 		"""build the document using the flowables.	Annotate the first page using the onFirstPage
   702         """build the document using the flowables.  Annotate the first page using the onFirstPage
   693 			   function and later pages using the onLaterPages function.  The onXXX pages should follow
   703                function and later pages using the onLaterPages function.  The onXXX pages should follow
   694 			   the signature
   704                the signature
   695 
   705 
   696 				  def myOnFirstPage(canvas, document):
   706                   def myOnFirstPage(canvas, document):
   697 					  # do annotations and modify the document
   707                       # do annotations and modify the document
   698 					  ...
   708                       ...
   699 
   709 
   700 			   The functions can do things like draw logos, page numbers,
   710                The functions can do things like draw logos, page numbers,
   701 			   footers, etcetera. They can use external variables to vary
   711                footers, etcetera. They can use external variables to vary
   702 			   the look (for example providing page numbering or section names).
   712                the look (for example providing page numbering or section names).
   703 		"""
   713         """
   704 		self._calc()	#in case we changed margins sizes etc
   714         self._calc()    #in case we changed margins sizes etc
   705 		frameT = Frame(self.leftMargin, self.bottomMargin, self.width, self.height, id='normal')
   715         frameT = Frame(self.leftMargin, self.bottomMargin, self.width, self.height, id='normal')
   706 		self.addPageTemplates([PageTemplate(id='First',frames=frameT, onPage=onFirstPage,pagesize=self.pagesize),
   716         self.addPageTemplates([PageTemplate(id='First',frames=frameT, onPage=onFirstPage,pagesize=self.pagesize),
   707 						PageTemplate(id='Later',frames=frameT, onPage=onLaterPages,pagesize=self.pagesize)])
   717                         PageTemplate(id='Later',frames=frameT, onPage=onLaterPages,pagesize=self.pagesize)])
   708 		if onFirstPage is _doNothing and hasattr(self,'onFirstPage'):
   718         if onFirstPage is _doNothing and hasattr(self,'onFirstPage'):
   709 			self.pageTemplates[0].beforeDrawPage = self.onFirstPage
   719             self.pageTemplates[0].beforeDrawPage = self.onFirstPage
   710 		if onLaterPages is _doNothing and hasattr(self,'onLaterPages'):
   720         if onLaterPages is _doNothing and hasattr(self,'onLaterPages'):
   711 			self.pageTemplates[1].beforeDrawPage = self.onLaterPages
   721             self.pageTemplates[1].beforeDrawPage = self.onLaterPages
   712 		BaseDocTemplate.build(self,flowables)
   722         BaseDocTemplate.build(self,flowables)
   713 
   723 
   714 
   724 
   715 	##########################################################
   725     ##########################################################
   716 	##
   726     ##
   717 	##	 testing
   727     ##   testing
   718 	##
   728     ##
   719 	##########################################################
   729     ##########################################################
   720 
   730 
   721 def randomText():
   731 def randomText():
   722 	#this may or may not be appropriate in your company
   732     #this may or may not be appropriate in your company
   723 	from random import randint, choice
   733     from random import randint, choice
   724 
   734 
   725 	RANDOMWORDS = ['strategic','direction','proactive',
   735     RANDOMWORDS = ['strategic','direction','proactive',
   726 	'reengineering','forecast','resources',
   736     'reengineering','forecast','resources',
   727 	'forward-thinking','profit','growth','doubletalk',
   737     'forward-thinking','profit','growth','doubletalk',
   728 	'venture capital','IPO']
   738     'venture capital','IPO']
   729 
   739 
   730 	sentences = 5
   740     sentences = 5
   731 	output = ""
   741     output = ""
   732 	for sentenceno in range(randint(1,5)):
   742     for sentenceno in range(randint(1,5)):
   733 		output = output + 'Blah'
   743         output = output + 'Blah'
   734 		for wordno in range(randint(10,25)):
   744         for wordno in range(randint(10,25)):
   735 			if randint(0,4)==0:
   745             if randint(0,4)==0:
   736 				word = choice(RANDOMWORDS)
   746                 word = choice(RANDOMWORDS)
   737 			else:
   747             else:
   738 				word = 'blah'
   748                 word = 'blah'
   739 			output = output + ' ' +word
   749             output = output + ' ' +word
   740 		output = output+'.'
   750         output = output+'.'
   741 	return output
   751     return output
   742 
   752 
   743 
   753 
   744 if __name__ == '__main__':
   754 if __name__ == '__main__':
   745 
   755 
   746 	def myFirstPage(canvas, doc):
   756     def myFirstPage(canvas, doc):
   747 		canvas.saveState()
   757         canvas.saveState()
   748 		canvas.setStrokeColor(red)
   758         canvas.setStrokeColor(red)
   749 		canvas.setLineWidth(5)
   759         canvas.setLineWidth(5)
   750 		canvas.line(66,72,66,PAGE_HEIGHT-72)
   760         canvas.line(66,72,66,PAGE_HEIGHT-72)
   751 		canvas.setFont('Times-Bold',24)
   761         canvas.setFont('Times-Bold',24)
   752 		canvas.drawString(108, PAGE_HEIGHT-108, "TABLE OF CONTENTS DEMO")
   762         canvas.drawString(108, PAGE_HEIGHT-108, "TABLE OF CONTENTS DEMO")
   753 		canvas.setFont('Times-Roman',12)
   763         canvas.setFont('Times-Roman',12)
   754 		canvas.drawString(4 * inch, 0.75 * inch, "First Page")
   764         canvas.drawString(4 * inch, 0.75 * inch, "First Page")
   755 		canvas.restoreState()
   765         canvas.restoreState()
   756 
   766 
   757 	def myLaterPages(canvas, doc):
   767     def myLaterPages(canvas, doc):
   758 		canvas.saveState()
   768         canvas.saveState()
   759 		canvas.setStrokeColor(red)
   769         canvas.setStrokeColor(red)
   760 		canvas.setLineWidth(5)
   770         canvas.setLineWidth(5)
   761 		canvas.line(66,72,66,PAGE_HEIGHT-72)
   771         canvas.line(66,72,66,PAGE_HEIGHT-72)
   762 		canvas.setFont('Times-Roman',12)
   772         canvas.setFont('Times-Roman',12)
   763 		canvas.drawString(4 * inch, 0.75 * inch, "Page %d" % doc.page)
   773         canvas.drawString(4 * inch, 0.75 * inch, "Page %d" % doc.page)
   764 		canvas.restoreState()
   774         canvas.restoreState()
   765 
   775 
   766 	def run():
   776     def run():
   767 		objects_to_draw = []
   777         objects_to_draw = []
   768 		from reportlab.lib.styles import ParagraphStyle
   778         from reportlab.lib.styles import ParagraphStyle
   769 		#from paragraph import Paragraph
   779         #from paragraph import Paragraph
   770 		from doctemplate import SimpleDocTemplate
   780         from doctemplate import SimpleDocTemplate
   771 
   781 
   772 		#need a style
   782         #need a style
   773 		normal = ParagraphStyle('normal')
   783         normal = ParagraphStyle('normal')
   774 		normal.firstLineIndent = 18
   784         normal.firstLineIndent = 18
   775 		normal.spaceBefore = 6
   785         normal.spaceBefore = 6
   776 		import random
   786         import random
   777 		for i in range(15):
   787         for i in range(15):
   778 			height = 0.5 + (2*random.random())
   788             height = 0.5 + (2*random.random())
   779 			box = XBox(6 * inch, height * inch, 'Box Number %d' % i)
   789             box = XBox(6 * inch, height * inch, 'Box Number %d' % i)
   780 			objects_to_draw.append(box)
   790             objects_to_draw.append(box)
   781 			para = Paragraph(randomText(), normal)
   791             para = Paragraph(randomText(), normal)
   782 			objects_to_draw.append(para)
   792             objects_to_draw.append(para)
   783 
   793 
   784 		SimpleDocTemplate('doctemplate.pdf').build(objects_to_draw,
   794         SimpleDocTemplate('doctemplate.pdf').build(objects_to_draw,
   785 			onFirstPage=myFirstPage,onLaterPages=myLaterPages)
   795             onFirstPage=myFirstPage,onLaterPages=myLaterPages)
   786 
   796 
   787 	run()
   797     run()
   788 
   798