reportlab/platypus/doctemplate.py
changeset 1505 e45994cb76bb
parent 1502 125b52eb0a8e
child 1530 1dedd3370a99
equal deleted inserted replaced
1504:8d637a414ec3 1505:e45994cb76bb
     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.50 2002/02/03 21:25:57 andy_robinson Exp $
     4 #$Header: /tmp/reportlab/reportlab/platypus/doctemplate.py,v 1.51 2002/02/13 16:44:29 rgbecker Exp $
     5 
     5 
     6 __version__=''' $Id: doctemplate.py,v 1.50 2002/02/03 21:25:57 andy_robinson Exp $ '''
     6 __version__=''' $Id: doctemplate.py,v 1.51 2002/02/13 16:44:29 rgbecker 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 
   286 
   287         #callback facility
   287 		#callback facility
   288         self._onPage = None
   288 		self._onPage = None
   289         self._calc()
   289 		self._calc()
   290         self.afterInit()
   290 		self.afterInit()
   291 
   291 
   292     def _calc(self):
   292 	def _calc(self):
   293         self._rightMargin = self.pagesize[0] - self.rightMargin
   293 		self._rightMargin = self.pagesize[0] - self.rightMargin
   294         self._topMargin = self.pagesize[1] - self.topMargin
   294 		self._topMargin = self.pagesize[1] - self.topMargin
   295         self.width = self._rightMargin - self.leftMargin
   295 		self.width = self._rightMargin - self.leftMargin
   296         self.height = self._topMargin - self.bottomMargin
   296 		self.height = self._topMargin - self.bottomMargin
   297 
   297 
   298     def setPageCallBack(self, func):
   298 	def setPageCallBack(self, func):
   299         'Progress monitor - func(pageNo) called on each new page'
   299 		'Progress monitor - func(pageNo) called on each new page'
   300         self._onPage = func
   300 		self._onPage = func
   301 
   301 
   302     def clean_hanging(self):
   302 	def clean_hanging(self):
   303         'handle internal postponed actions'
   303 		'handle internal postponed actions'
   304         while len(self._hanging):
   304 		while len(self._hanging):
   305             self.handle_flowable(self._hanging)
   305 			self.handle_flowable(self._hanging)
   306 
   306 
   307     def addPageTemplates(self,pageTemplates):
   307 	def addPageTemplates(self,pageTemplates):
   308         'add one or a sequence of pageTemplates'
   308 		'add one or a sequence of pageTemplates'
   309         if type(pageTemplates) not in (ListType,TupleType):
   309 		if type(pageTemplates) not in (ListType,TupleType):
   310             pageTemplates = [pageTemplates]
   310 			pageTemplates = [pageTemplates]
   311         #this test below fails due to inconsistent imports!
   311 		#this test below fails due to inconsistent imports!
   312         #assert filter(lambda x: not isinstance(x,PageTemplate), pageTemplates)==[], "pageTemplates argument error"
   312 		#assert filter(lambda x: not isinstance(x,PageTemplate), pageTemplates)==[], "pageTemplates argument error"
   313         for t in pageTemplates:
   313 		for t in pageTemplates:
   314             self.pageTemplates.append(t)
   314 			self.pageTemplates.append(t)
   315 
   315 
   316     def handle_documentBegin(self):
   316 	def handle_documentBegin(self):
   317         '''implement actions at beginning of document'''
   317 		'''implement actions at beginning of document'''
   318         self._hanging = [PageBegin]
   318 		self._hanging = [PageBegin]
   319         self.pageTemplate = self.pageTemplates[0]
   319 		self.pageTemplate = self.pageTemplates[0]
   320         self.page = 0
   320 		self.page = 0
   321         self.beforeDocument()
   321 		self.beforeDocument()
   322 
   322 
   323     def handle_pageBegin(self):
   323 	def handle_pageBegin(self):
   324         '''Perform actions required at beginning of page.
   324 		'''Perform actions required at beginning of page.
   325         shouldn't normally be called directly'''
   325 		shouldn't normally be called directly'''
   326         self.page = self.page + 1
   326 		self.page = self.page + 1
   327         self.pageTemplate.beforeDrawPage(self.canv,self)
   327 		self.pageTemplate.beforeDrawPage(self.canv,self)
   328         self.pageTemplate.checkPageSize(self.canv,self)
   328 		self.pageTemplate.checkPageSize(self.canv,self)
   329         self.pageTemplate.onPage(self.canv,self)
   329 		self.pageTemplate.onPage(self.canv,self)
   330         for f in self.pageTemplate.frames: f._reset()
   330 		for f in self.pageTemplate.frames: f._reset()
   331         self.beforePage()
   331 		self.beforePage()
   332         if hasattr(self,'_nextFrameIndex'):
   332 		if hasattr(self,'_nextFrameIndex'):
   333             del self._nextFrameIndex
   333 			del self._nextFrameIndex
   334         self.frame = self.pageTemplate.frames[0]
   334 		self.frame = self.pageTemplate.frames[0]
   335         self.handle_frameBegin()
   335 		self.handle_frameBegin()
   336 
   336 
   337     def handle_pageEnd(self):
   337 	def handle_pageEnd(self):
   338         ''' show the current page
   338 		''' show the current page
   339             check the next page template
   339 			check the next page template
   340             hang a page begin
   340 			hang a page begin
   341         '''
   341 		'''
   342         self.pageTemplate.afterDrawPage(self.canv, self)
   342 		self.pageTemplate.afterDrawPage(self.canv, self)
   343         self.pageTemplate.onPageEnd(self.canv, self)
   343 		self.pageTemplate.onPageEnd(self.canv, self)
   344         self.afterPage()
   344 		self.afterPage()
   345         self.canv.showPage()
   345 		self.canv.showPage()
   346         if hasattr(self,'_nextPageTemplateIndex'):
   346 		if hasattr(self,'_nextPageTemplateIndex'):
   347             self.pageTemplate = self.pageTemplates[self._nextPageTemplateIndex]
   347 			self.pageTemplate = self.pageTemplates[self._nextPageTemplateIndex]
   348             del self._nextPageTemplateIndex
   348 			del self._nextPageTemplateIndex
   349         self._hanging.append(PageBegin)
   349 		self._hanging.append(PageBegin)
   350 
   350 
   351     def handle_pageBreak(self):
   351 	def handle_pageBreak(self):
   352         '''some might choose not to end all the frames'''
   352 		'''some might choose not to end all the frames'''
   353         if self._pageBreakQuick:
   353 		if self._pageBreakQuick:
   354             self.handle_pageEnd()
   354 			self.handle_pageEnd()
   355         else:
   355 		else:
   356             n = len(self._hanging)
   356 			n = len(self._hanging)
   357             while len(self._hanging)==n:
   357 			while len(self._hanging)==n:
   358                 self.handle_frameEnd()
   358 				self.handle_frameEnd()
   359 
   359 
   360     def handle_frameBegin(self,resume=0):
   360 	def handle_frameBegin(self,resume=0):
   361         '''What to do at the beginning of a frame'''
   361 		'''What to do at the beginning of a frame'''
   362         f = self.frame
   362 		f = self.frame
   363         if f._atTop:
   363 		if f._atTop:
   364             if self.showBoundary or self.frame.showBoundary:
   364 			if self.showBoundary or self.frame.showBoundary:
   365                 self.frame.drawBoundary(self.canv)
   365 				self.frame.drawBoundary(self.canv)
   366 
   366 
   367     def handle_frameEnd(self,resume=0):
   367 	def handle_frameEnd(self,resume=0):
   368         ''' Handles the semantics of the end of a frame. This includes the selection of
   368 		''' Handles the semantics of the end of a frame. This includes the selection of
   369             the next frame or if this is the last frame then invoke pageEnd.
   369 			the next frame or if this is the last frame then invoke pageEnd.
   370         '''
   370 		'''
   371         if hasattr(self,'_nextFrameIndex'):
   371 		if hasattr(self,'_nextFrameIndex'):
   372             frame = self.pageTemplate.frames[self._nextFrameIndex]
   372 			frame = self.pageTemplate.frames[self._nextFrameIndex]
   373             del self._nextFrameIndex
   373 			del self._nextFrameIndex
   374             self.handle_frameBegin(resume)
   374 			self.handle_frameBegin(resume)
   375         elif hasattr(self.frame,'lastFrame') or self.frame is self.pageTemplate.frames[-1]:
   375 		elif hasattr(self.frame,'lastFrame') or self.frame is self.pageTemplate.frames[-1]:
   376             self.handle_pageEnd()
   376 			self.handle_pageEnd()
   377             self.frame = None
   377 			self.frame = None
   378         else:
   378 		else:
   379             f = self.frame
   379 			f = self.frame
   380             self.frame = self.pageTemplate.frames[self.pageTemplate.frames.index(f) + 1]
   380 			self.frame = self.pageTemplate.frames[self.pageTemplate.frames.index(f) + 1]
   381             self.handle_frameBegin()
   381 			self.handle_frameBegin()
   382 
   382 
   383     def handle_nextPageTemplate(self,pt):
   383 	def handle_nextPageTemplate(self,pt):
   384         '''On endPage chenge to the page template with name or index pt'''
   384 		'''On endPage chenge to the page template with name or index pt'''
   385         if type(pt) is StringType:
   385 		if type(pt) is StringType:
   386             for t in self.pageTemplates:
   386 			for t in self.pageTemplates:
   387                 if t.id == pt:
   387 				if t.id == pt:
   388                     self._nextPageTemplateIndex = self.pageTemplates.index(t)
   388 					self._nextPageTemplateIndex = self.pageTemplates.index(t)
   389                     return
   389 					return
   390             raise ValueError, "can't find template('%s')"%pt
   390 			raise ValueError, "can't find template('%s')"%pt
   391         elif type(pt) is IntType:
   391 		elif type(pt) is IntType:
   392             self._nextPageTemplateIndex = pt
   392 			self._nextPageTemplateIndex = pt
   393         else:
   393 		else:
   394             raise TypeError, "argument pt should be string or integer"
   394 			raise TypeError, "argument pt should be string or integer"
   395 
   395 
   396     def handle_nextFrame(self,fx):
   396 	def handle_nextFrame(self,fx):
   397         '''On endFrame chenge to the frame with name or index fx'''
   397 		'''On endFrame chenge to the frame with name or index fx'''
   398         if type(fx) is StringType:
   398 		if type(fx) is StringType:
   399             for f in self.pageTemplate.frames:
   399 			for f in self.pageTemplate.frames:
   400                 if f.id == fx:
   400 				if f.id == fx:
   401                     self._nextFrameIndex = self.pageTemplate.frames.index(f)
   401 					self._nextFrameIndex = self.pageTemplate.frames.index(f)
   402                     return
   402 					return
   403             raise ValueError, "can't find frame('%s')"%fx
   403 			raise ValueError, "can't find frame('%s')"%fx
   404         elif type(fx) is IntType:
   404 		elif type(fx) is IntType:
   405             self._nextFrameIndex = fx
   405 			self._nextFrameIndex = fx
   406         else:
   406 		else:
   407             raise TypeError, "argument fx should be string or integer"
   407 			raise TypeError, "argument fx should be string or integer"
   408 
   408 
   409     def handle_currentFrame(self,fx):
   409 	def handle_currentFrame(self,fx):
   410         '''chenge to the frame with name or index fx'''
   410 		'''chenge to the frame with name or index fx'''
   411         if type(fx) is StringType:
   411 		if type(fx) is StringType:
   412             for f in self.pageTemplate.frames:
   412 			for f in self.pageTemplate.frames:
   413                 if f.id == fx:
   413 				if f.id == fx:
   414                     self._nextFrameIndex = self.pageTemplate.frames.index(f)
   414 					self._nextFrameIndex = self.pageTemplate.frames.index(f)
   415                     return
   415 					return
   416             raise ValueError, "can't find frame('%s')"%fx
   416 			raise ValueError, "can't find frame('%s')"%fx
   417         elif type(fx) is IntType:
   417 		elif type(fx) is IntType:
   418             self._nextFrameIndex = fx
   418 			self._nextFrameIndex = fx
   419         else:
   419 		else:
   420             raise TypeError, "argument fx should be string or integer"
   420 			raise TypeError, "argument fx should be string or integer"
   421 
   421 
   422     def handle_breakBefore(self, flowables):
   422 	def handle_breakBefore(self, flowables):
   423         '''preprocessing step to allow pageBreakBefore and frameBreakBefore attributes'''
   423 		'''preprocessing step to allow pageBreakBefore and frameBreakBefore attributes'''
   424         first = flowables[0]
   424 		first = flowables[0]
   425         # if we insert a page break before, we'll process that, see it again,
   425 		# if we insert a page break before, we'll process that, see it again,
   426         # and go in an infinite loop.  So we need to set a flag on the object
   426 		# and go in an infinite loop.  So we need to set a flag on the object
   427         # saying 'skip me'.  This should be unset on the next pass
   427 		# saying 'skip me'.  This should be unset on the next pass
   428         if hasattr(first, '_skipMeNextTime'):
   428 		if hasattr(first, '_skipMeNextTime'):
   429             delattr(first, '_skipMeNextTime')
   429 			delattr(first, '_skipMeNextTime')
   430             return
   430 			return
   431         # this could all be made much quicker by putting the attributes
   431 		# this could all be made much quicker by putting the attributes
   432         # in to the flowables with a defult value of 0
   432 		# in to the flowables with a defult value of 0
   433         if hasattr(first,'pageBreakBefore') and first.pageBreakBefore == 1:
   433 		if hasattr(first,'pageBreakBefore') and first.pageBreakBefore == 1:
   434             first._skipMeNextTime = 1
   434 			first._skipMeNextTime = 1
   435             first.insert(0, PageBreak())
   435 			first.insert(0, PageBreak())
   436             return
   436 			return
   437         if hasattr(first,'style') and hasattr(first.style, 'pageBreakBefore') and first.style.pageBreakBefore == 1:
   437 		if hasattr(first,'style') and hasattr(first.style, 'pageBreakBefore') and first.style.pageBreakBefore == 1:
   438             first._skipMeNextTime = 1
   438 			first._skipMeNextTime = 1
   439             flowables.insert(0, PageBreak())
   439 			flowables.insert(0, PageBreak())
   440             return
   440 			return
   441         if hasattr(first,'frameBreakBefore') and first.frameBreakBefore == 1:
   441 		if hasattr(first,'frameBreakBefore') and first.frameBreakBefore == 1:
   442             first._skipMeNextTime = 1
   442 			first._skipMeNextTime = 1
   443             flowables.insert(0, FrameBreak())
   443 			flowables.insert(0, FrameBreak())
   444             return
   444 			return
   445         if hasattr(first,'style') and hasattr(first.style, 'frameBreakBefore') and first.style.frameBreakBefore == 1:
   445 		if hasattr(first,'style') and hasattr(first.style, 'frameBreakBefore') and first.style.frameBreakBefore == 1:
   446             first._skipMeNextTime = 1
   446 			first._skipMeNextTime = 1
   447             flowables.insert(0, FrameBreak())
   447 			flowables.insert(0, FrameBreak())
   448             return
   448 			return
   449 
   449 
   450 
   450 
   451     def handle_keepWithNext(self, flowables):
   451 	def handle_keepWithNext(self, flowables):
   452         "implements keepWithNext"
   452 		"implements keepWithNext"
   453         i = 0
   453 		i = 0
   454         n = len(flowables)
   454 		n = len(flowables)
   455         while i<n and flowables[i].getKeepWithNext(): i = i + 1
   455 		while i<n and flowables[i].getKeepWithNext(): i = i + 1
   456         if i:
   456 		if i:
   457             i = i + 1
   457 			i = i + 1
   458             K = KeepTogether(flowables[:i])
   458 			K = KeepTogether(flowables[:i])
   459             for f in K._flowables:
   459 			for f in K._flowables:
   460                 f.keepWithNext = 0
   460 				f.keepWithNext = 0
   461             del flowables[:i]
   461 			del flowables[:i]
   462             flowables.insert(0,K)
   462 			flowables.insert(0,K)
   463 
   463 
   464     def handle_flowable(self,flowables):
   464 	def handle_flowable(self,flowables):
   465         '''try to handle one flowable from the front of list flowables.'''
   465 		'''try to handle one flowable from the front of list flowables.'''
   466 
   466 
   467         #allow document a chance to look at, modify or ignore
   467 		#allow document a chance to look at, modify or ignore
   468         #the object(s) about to be processed
   468 		#the object(s) about to be processed
   469         self.filterFlowables(flowables)
   469 		self.filterFlowables(flowables)
   470 
   470 
   471         self.handle_breakBefore(flowables)
   471 		self.handle_breakBefore(flowables)
   472         self.handle_keepWithNext(flowables)
   472 		self.handle_keepWithNext(flowables)
   473         f = flowables[0]
   473 		f = flowables[0]
   474         del flowables[0]
   474 		del flowables[0]
   475         if f is None:
   475 		if f is None:
   476             return
   476 			return
   477 
   477 
   478         if isinstance(f,PageBreak):
   478 		if isinstance(f,PageBreak):
   479             self.handle_pageBreak()
   479 			self.handle_pageBreak()
   480             self.afterFlowable(f)
   480 			self.afterFlowable(f)
   481         elif isinstance(f,ActionFlowable):
   481 		elif isinstance(f,ActionFlowable):
   482             f.apply(self)
   482 			f.apply(self)
   483             self.afterFlowable(f)
   483 			self.afterFlowable(f)
   484         else:
   484 		else:
   485             #try to fit it then draw it
   485 			#try to fit it then draw it
   486             if self.frame.add(f, self.canv, trySplit=self.allowSplitting):
   486 			if self.frame.add(f, self.canv, trySplit=self.allowSplitting):
   487                 self.afterFlowable(f)
   487 				self.afterFlowable(f)
   488             else:
   488 			else:
   489                 #if isinstance(f, KeepTogether): print 'could not add it to frame'
   489 				#if isinstance(f, KeepTogether): print 'could not add it to frame'
   490                 if self.allowSplitting:
   490 				if self.allowSplitting:
   491                     # see if this is a splittable thing
   491 					# see if this is a splittable thing
   492                     S = self.frame.split(f,self.canv)
   492 					S = self.frame.split(f,self.canv)
   493                     n = len(S)
   493 					n = len(S)
   494                 else:
   494 				else:
   495                     n = 0
   495 					n = 0
   496                 #if isinstance(f, KeepTogether): print 'n=%d' % n
   496 				#if isinstance(f, KeepTogether): print 'n=%d' % n
   497                 if n:
   497 				if n:
   498                     if self.frame.add(S[0], self.canv, trySplit=0):
   498 					if self.frame.add(S[0], self.canv, trySplit=0):
   499                         self.afterFlowable(S[0])
   499 						self.afterFlowable(S[0])
   500                     else:
   500 					else:
   501                         print 'n = %d' % n
   501 						print 'n = %d' % n
   502                         raise "LayoutError", "splitting error on page %d in\n%s" % (self.page,f.identity(30))
   502 						raise "LayoutError", "splitting error on page %d in\n%s" % (self.page,f.identity(30))
   503                     del S[0]
   503 					del S[0]
   504                     for f in xrange(n-1):
   504 					for f in xrange(n-1):
   505                         flowables.insert(f,S[f])    # put split flowables back on the list
   505 						flowables.insert(f,S[f])	# put split flowables back on the list
   506                 else:
   506 				else:
   507                     # this must be cleared when they are finally drawn!
   507 					# this must be cleared when they are finally drawn!
   508 ##                  if hasattr(f,'postponed'):
   508 ##					if hasattr(f,'postponed'):
   509                     if hasattr(f,'_postponed'):
   509 					if hasattr(f,'_postponed'):
   510                         message = "Flowable %s too large on page %d" % (f.identity(30), self.page)
   510 						message = "Flowable %s too large on page %d" % (f.identity(30), self.page)
   511                         #show us, it might be handy
   511 						#show us, it might be handy
   512                         #HACK = it seems within tables we sometimes
   512 						#HACK = it seems within tables we sometimes
   513                         #get an empty paragraph that won't fit and this
   513 						#get an empty paragraph that won't fit and this
   514                         #causes it to fall over.  FIXME FIXME FIXME
   514 						#causes it to fall over.  FIXME FIXME FIXME
   515                         raise "LayoutError", message
   515 						raise "LayoutError", message
   516 ##                  f.postponed = 1
   516 ##					f.postponed = 1
   517                     f._postponed = 1
   517 					f._postponed = 1
   518                     flowables.insert(0,f)           # put the flowable back
   518 					flowables.insert(0,f)			# put the flowable back
   519                     self.handle_frameEnd()
   519 					self.handle_frameEnd()
   520 
   520 
   521     #these are provided so that deriving classes can refer to them
   521 	#these are provided so that deriving classes can refer to them
   522     _handle_documentBegin = handle_documentBegin
   522 	_handle_documentBegin = handle_documentBegin
   523     _handle_pageBegin = handle_pageBegin
   523 	_handle_pageBegin = handle_pageBegin
   524     _handle_pageEnd = handle_pageEnd
   524 	_handle_pageEnd = handle_pageEnd
   525     _handle_frameBegin = handle_frameBegin
   525 	_handle_frameBegin = handle_frameBegin
   526     _handle_frameEnd = handle_frameEnd
   526 	_handle_frameEnd = handle_frameEnd
   527     _handle_flowable = handle_flowable
   527 	_handle_flowable = handle_flowable
   528     _handle_nextPageTemplate = handle_nextPageTemplate
   528 	_handle_nextPageTemplate = handle_nextPageTemplate
   529     _handle_currentFrame = handle_currentFrame
   529 	_handle_currentFrame = handle_currentFrame
   530     _handle_nextFrame = handle_nextFrame
   530 	_handle_nextFrame = handle_nextFrame
   531 
   531 
   532     def _startBuild(self, filename=None, canvasmaker=canvas.Canvas):
   532 	def _startBuild(self, filename=None, canvasmaker=canvas.Canvas):
   533         self._calc()
   533 		self._calc()
   534         self.canv = canvasmaker(filename or self.filename,pagesize=self.pagesize)
   534 		self.canv = canvasmaker(filename or self.filename,pagesize=self.pagesize)
   535         if self._onPage:
   535 		if self._onPage:
   536             self.canv.setPageCallBack(self._onPage)
   536 			self.canv.setPageCallBack(self._onPage)
   537         self.handle_documentBegin()
   537 		self.handle_documentBegin()
   538 
   538 
   539     def _endBuild(self):
   539 	def _endBuild(self):
   540         if self._hanging!=[] and self._hanging[-1] is PageBegin:
   540 		if self._hanging!=[] and self._hanging[-1] is PageBegin:
   541             del self._hanging[-1]
   541 			del self._hanging[-1]
   542             self.clean_hanging()
   542 			self.clean_hanging()
   543         else:
   543 		else:
   544             self.clean_hanging()
   544 			self.clean_hanging()
   545             self.handle_pageBreak()
   545 			self.handle_pageBreak()
   546 
   546 
   547         self.canv.save()
   547 		if getattr(self,'_doSave',1): self.canv.save()
   548         if self._onPage:
   548 		if self._onPage: self.canv.setPageCallBack(None)
   549             self.canv.setPageCallBack(None)
   549 
   550         #AR - hack - for some reason a document did not
   550 	def build(self, flowables, filename=None, canvasmaker=canvas.Canvas):
   551         #have these:
   551 		"""Build the document from a list of flowables.
   552         #if hasattr(self, 'frame'): del self.frame
   552 		   If the filename argument is provided then that filename is used
   553         #if hasattr(self, 'pageTemplate'): del self.pageTemplate
   553 		   rather than the one provided upon initialization.
   554         #del self.frame, self.pageTemplate
   554 		   If the canvasmaker argument is provided then it will be used
   555 
   555 		   instead of the default.	For example a slideshow might use
   556     def build(self, flowables, filename=None, canvasmaker=canvas.Canvas):
   556 		   an alternate canvas which places 6 slides on a page (by
   557         """Build the document from a list of flowables.
   557 		   doing translations, scalings and redefining the page break
   558            If the filename argument is provided then that filename is used
   558 		   operations).
   559            rather than the one provided upon initialization.
   559 		"""
   560            If the canvasmaker argument is provided then it will be used
   560 		#assert filter(lambda x: not isinstance(x,Flowable), flowables)==[], "flowables argument error"
   561            instead of the default.  For example a slideshow might use
   561 		self._startBuild(filename,canvasmaker)
   562            an alternate canvas which places 6 slides on a page (by
   562 
   563            doing translations, scalings and redefining the page break
   563 		while len(flowables):
   564            operations).
   564 			self.clean_hanging()
   565         """
   565 			self.handle_flowable(flowables)
   566         #assert filter(lambda x: not isinstance(x,Flowable), flowables)==[], "flowables argument error"
   566 
   567         self._startBuild(filename,canvasmaker)
   567 		self._endBuild()
   568 
   568 
   569         while len(flowables):
   569 	def _allSatisfied(self):
   570             self.clean_hanging()
   570 		"""Called by multi-build - are all cross-references resolved?"""
   571             self.handle_flowable(flowables)
   571 		allHappy = 1
   572 
   572 		for f in self._indexingFlowables:
   573         self._endBuild()
   573 			if not f.isSatisfied():
   574 
   574 				allHappy = 0
   575     def _allSatisfied(self):
   575 				break
   576         """Called by multi-build - are all cross-references resolved?"""
   576 		return allHappy
   577         allHappy = 1
   577 
   578         for f in self._indexingFlowables:
   578 	def notify(self, kind, stuff):
   579             if not f.isSatisfied():
   579 		""""Forward to any listeners"""
   580                 allHappy = 0
   580 		for l in self._indexingFlowables:
   581                 break
   581 			l.notify(kind, stuff)
   582         return allHappy
   582 
   583 
   583 	def pageRef(self, label):
   584     def notify(self, kind, stuff):
   584 		"""hook to register a page number"""
   585         """"Forward to any listeners"""
   585 		if _verbose: print "pageRef called with label '%s' on page %d" % (
   586         for l in self._indexingFlowables:
   586 			label, self.page)
   587             l.notify(kind, stuff)
   587 		self._pageRefs[label] = self.page
   588 
   588 
   589     def pageRef(self, label):
   589 	def multiBuild(self, story,
   590         """hook to register a page number"""
   590 				   filename=None,
   591         if _verbose: print "pageRef called with label '%s' on page %d" % (
   591 				   canvasmaker=canvas.Canvas,
   592             label, self.page)
   592 				   maxPasses = 10):
   593         self._pageRefs[label] = self.page
   593 		"""Makes multiple passes until all indexing flowables
   594 
   594 		are happy."""
   595     def multiBuild(self, story,
   595 		self._indexingFlowables = []
   596                    filename=None,
   596 		#scan the story and keep a copy
   597                    canvasmaker=canvas.Canvas,
   597 		for thing in story:
   598                    maxPasses = 10):
   598 			if thing.isIndexing():
   599         """Makes multiple passes until all indexing flowables
   599 				self._indexingFlowables.append(thing)
   600         are happy."""
   600 		#print 'scanned story, found these indexing flowables:\n'
   601         self._indexingFlowables = []
   601 		#print self._indexingFlowables
   602         #scan the story and keep a copy
   602 		
   603         for thing in story:
   603 		passes = 0
   604             if thing.isIndexing():
   604 		while 1:
   605                 self._indexingFlowables.append(thing)
   605 			passes = passes + 1
   606         #print 'scanned story, found these indexing flowables:\n'
   606 			if _verbose: print 'building pass '+str(passes) + '...',
   607         #print self._indexingFlowables
   607 
   608         
   608 			for fl in self._indexingFlowables:
   609         passes = 0
   609 				fl.beforeBuild()
   610         while 1:
   610 
   611             passes = passes + 1
   611 			# work with a copy of the story, since it is consumed
   612             if _verbose: print 'building pass '+str(passes) + '...',
   612 			tempStory = story[:]
   613 
   613 			self.build(tempStory, filename, canvasmaker)
   614             for fl in self._indexingFlowables:
   614 			#self.notify('debug',None)
   615                 fl.beforeBuild()
   615 
   616 
   616 			#clean up so multi-build does not go wrong - the frame
   617             # work with a copy of the story, since it is consumed
   617 			#packer might have tacked an attribute onto some
   618             tempStory = story[:]
   618 			#paragraphs
   619             self.build(tempStory, filename, canvasmaker)
   619 			for elem in story:
   620             #self.notify('debug',None)
   620 ##				if hasattr(elem, 'postponed'):
   621 
   621 ##					del elem.postponed
   622             #clean up so multi-build does not go wrong - the frame
   622 				if hasattr(elem, '_postponed'):
   623             #packer might have tacked an attribute onto some
   623 					del elem._postponed
   624             #paragraphs
   624 
   625             for elem in story:
   625 			for fl in self._indexingFlowables:
   626 ##              if hasattr(elem, 'postponed'):
   626 				fl.afterBuild()
   627 ##                  del elem.postponed
   627 
   628                 if hasattr(elem, '_postponed'):
   628 			happy = self._allSatisfied()
   629                     del elem._postponed
   629 
   630 
   630 			if happy:
   631             for fl in self._indexingFlowables:
   631 				## print 'OK'
   632                 fl.afterBuild()
   632 				break
   633 
   633 			## else:
   634             happy = self._allSatisfied()
   634 				## print 'failed'
   635 
   635 			if passes > maxPasses:
   636             if happy:
   636 				raise IndexError, "Index entries not resolved after %d passes" % maxPasses
   637                 ## print 'OK'
   637 
   638                 break
   638 		if _verbose: print 'saved'
   639             ## else:
   639 
   640                 ## print 'failed'
   640 	#these are pure virtuals override in derived classes
   641             if passes > maxPasses:
   641 	#NB these get called at suitable places by the base class
   642                 raise IndexError, "Index entries not resolved after %d passes" % maxPasses
   642 	#so if you derive and override the handle_xxx methods
   643 
   643 	#it's up to you to ensure that they maintain the needed consistency
   644         if _verbose: print 'saved'
   644 	def afterInit(self):
   645 
   645 		"""This is called after initialisation of the base class."""
   646     #these are pure virtuals override in derived classes
   646 		pass
   647     #NB these get called at suitable places by the base class
   647 
   648     #so if you derive and override the handle_xxx methods
   648 	def beforeDocument(self):
   649     #it's up to you to ensure that they maintain the needed consistency
   649 		"""This is called before any processing is
   650     def afterInit(self):
   650 		done on the document."""
   651         """This is called after initialisation of the base class."""
   651 		pass
   652         pass
   652 
   653 
   653 	def beforePage(self):
   654     def beforeDocument(self):
   654 		"""This is called at the beginning of page
   655         """This is called before any processing is
   655 		processing, and immediately before the
   656         done on the document."""
   656 		beforeDrawPage method of the current page
   657         pass
   657 		template."""
   658 
   658 		pass
   659     def beforePage(self):
   659 
   660         """This is called at the beginning of page
   660 	def afterPage(self):
   661         processing, and immediately before the
   661 		"""This is called after page processing, and
   662         beforeDrawPage method of the current page
   662 		immediately after the afterDrawPage method
   663         template."""
   663 		of the current page template."""
   664         pass
   664 		pass
   665 
   665 
   666     def afterPage(self):
   666 	def filterFlowables(self,flowables):
   667         """This is called after page processing, and
   667 		'''called to filter flowables at the start of the main handle_flowable method.
   668         immediately after the afterDrawPage method
   668 		Upon return if flowables[0] has been set to None it is discarded and the main
   669         of the current page template."""
   669 		method returns.
   670         pass
   670 		'''
   671 
   671 		pass
   672     def filterFlowables(self,flowables):
   672 
   673         '''called to filter flowables at the start of the main handle_flowable method.
   673 	def afterFlowable(self, flowable):
   674         Upon return if flowables[0] has been set to None it is discarded and the main
   674 		'''called after a flowable has been rendered'''
   675         method returns.
   675 		pass
   676         '''
       
   677         pass
       
   678 
       
   679     def afterFlowable(self, flowable):
       
   680         '''called after a flowable has been rendered'''
       
   681         pass
       
   682 
   676 
   683 
   677 
   684 class SimpleDocTemplate(BaseDocTemplate):
   678 class SimpleDocTemplate(BaseDocTemplate):
   685     """A special case document template that will handle many simple documents.
   679 	"""A special case document template that will handle many simple documents.
   686        See documentation for BaseDocTemplate.  No pageTemplates are required
   680 	   See documentation for BaseDocTemplate.  No pageTemplates are required
   687        for this special case.   A page templates are inferred from the
   681 	   for this special case.	A page templates are inferred from the
   688        margin information and the onFirstPage, onLaterPages arguments to the build method.
   682 	   margin information and the onFirstPage, onLaterPages arguments to the build method.
   689 
   683 
   690        A document which has all pages with the same look except for the first
   684 	   A document which has all pages with the same look except for the first
   691        page may can be built using this special approach.
   685 	   page may can be built using this special approach.
   692     """
   686 	"""
   693     _invalidInitArgs = ('pageTemplates',)
   687 	_invalidInitArgs = ('pageTemplates',)
   694 
   688 
   695     def handle_pageBegin(self):
   689 	def handle_pageBegin(self):
   696         '''override base method to add a change of page template after the firstpage.
   690 		'''override base method to add a change of page template after the firstpage.
   697         '''
   691 		'''
   698         self._handle_pageBegin()
   692 		self._handle_pageBegin()
   699         self._handle_nextPageTemplate('Later')
   693 		self._handle_nextPageTemplate('Later')
   700 
   694 
   701     def build(self,flowables,onFirstPage=_doNothing, onLaterPages=_doNothing):
   695 	def build(self,flowables,onFirstPage=_doNothing, onLaterPages=_doNothing):
   702         """build the document using the flowables.  Annotate the first page using the onFirstPage
   696 		"""build the document using the flowables.	Annotate the first page using the onFirstPage
   703                function and later pages using the onLaterPages function.  The onXXX pages should follow
   697 			   function and later pages using the onLaterPages function.  The onXXX pages should follow
   704                the signature
   698 			   the signature
   705 
   699 
   706                   def myOnFirstPage(canvas, document):
   700 				  def myOnFirstPage(canvas, document):
   707                       # do annotations and modify the document
   701 					  # do annotations and modify the document
   708                       ...
   702 					  ...
   709 
   703 
   710                The functions can do things like draw logos, page numbers,
   704 			   The functions can do things like draw logos, page numbers,
   711                footers, etcetera. They can use external variables to vary
   705 			   footers, etcetera. They can use external variables to vary
   712                the look (for example providing page numbering or section names).
   706 			   the look (for example providing page numbering or section names).
   713         """
   707 		"""
   714         self._calc()    #in case we changed margins sizes etc
   708 		self._calc()	#in case we changed margins sizes etc
   715         frameT = Frame(self.leftMargin, self.bottomMargin, self.width, self.height, id='normal')
   709 		frameT = Frame(self.leftMargin, self.bottomMargin, self.width, self.height, id='normal')
   716         self.addPageTemplates([PageTemplate(id='First',frames=frameT, onPage=onFirstPage,pagesize=self.pagesize),
   710 		self.addPageTemplates([PageTemplate(id='First',frames=frameT, onPage=onFirstPage,pagesize=self.pagesize),
   717                         PageTemplate(id='Later',frames=frameT, onPage=onLaterPages,pagesize=self.pagesize)])
   711 						PageTemplate(id='Later',frames=frameT, onPage=onLaterPages,pagesize=self.pagesize)])
   718         if onFirstPage is _doNothing and hasattr(self,'onFirstPage'):
   712 		if onFirstPage is _doNothing and hasattr(self,'onFirstPage'):
   719             self.pageTemplates[0].beforeDrawPage = self.onFirstPage
   713 			self.pageTemplates[0].beforeDrawPage = self.onFirstPage
   720         if onLaterPages is _doNothing and hasattr(self,'onLaterPages'):
   714 		if onLaterPages is _doNothing and hasattr(self,'onLaterPages'):
   721             self.pageTemplates[1].beforeDrawPage = self.onLaterPages
   715 			self.pageTemplates[1].beforeDrawPage = self.onLaterPages
   722         BaseDocTemplate.build(self,flowables)
   716 		BaseDocTemplate.build(self,flowables)
   723 
   717 
   724 
   718 
   725     ##########################################################
   719 	##########################################################
   726     ##
   720 	##
   727     ##   testing
   721 	##	 testing
   728     ##
   722 	##
   729     ##########################################################
   723 	##########################################################
   730 
   724 
   731 def randomText():
   725 def randomText():
   732     #this may or may not be appropriate in your company
   726 	#this may or may not be appropriate in your company
   733     from random import randint, choice
   727 	from random import randint, choice
   734 
   728 
   735     RANDOMWORDS = ['strategic','direction','proactive',
   729 	RANDOMWORDS = ['strategic','direction','proactive',
   736     'reengineering','forecast','resources',
   730 	'reengineering','forecast','resources',
   737     'forward-thinking','profit','growth','doubletalk',
   731 	'forward-thinking','profit','growth','doubletalk',
   738     'venture capital','IPO']
   732 	'venture capital','IPO']
   739 
   733 
   740     sentences = 5
   734 	sentences = 5
   741     output = ""
   735 	output = ""
   742     for sentenceno in range(randint(1,5)):
   736 	for sentenceno in range(randint(1,5)):
   743         output = output + 'Blah'
   737 		output = output + 'Blah'
   744         for wordno in range(randint(10,25)):
   738 		for wordno in range(randint(10,25)):
   745             if randint(0,4)==0:
   739 			if randint(0,4)==0:
   746                 word = choice(RANDOMWORDS)
   740 				word = choice(RANDOMWORDS)
   747             else:
   741 			else:
   748                 word = 'blah'
   742 				word = 'blah'
   749             output = output + ' ' +word
   743 			output = output + ' ' +word
   750         output = output+'.'
   744 		output = output+'.'
   751     return output
   745 	return output
   752 
   746 
   753 
   747 
   754 if __name__ == '__main__':
   748 if __name__ == '__main__':
   755 
   749 
   756     def myFirstPage(canvas, doc):
   750 	def myFirstPage(canvas, doc):
   757         canvas.saveState()
   751 		canvas.saveState()
   758         canvas.setStrokeColor(red)
   752 		canvas.setStrokeColor(red)
   759         canvas.setLineWidth(5)
   753 		canvas.setLineWidth(5)
   760         canvas.line(66,72,66,PAGE_HEIGHT-72)
   754 		canvas.line(66,72,66,PAGE_HEIGHT-72)
   761         canvas.setFont('Times-Bold',24)
   755 		canvas.setFont('Times-Bold',24)
   762         canvas.drawString(108, PAGE_HEIGHT-108, "TABLE OF CONTENTS DEMO")
   756 		canvas.drawString(108, PAGE_HEIGHT-108, "TABLE OF CONTENTS DEMO")
   763         canvas.setFont('Times-Roman',12)
   757 		canvas.setFont('Times-Roman',12)
   764         canvas.drawString(4 * inch, 0.75 * inch, "First Page")
   758 		canvas.drawString(4 * inch, 0.75 * inch, "First Page")
   765         canvas.restoreState()
   759 		canvas.restoreState()
   766 
   760 
   767     def myLaterPages(canvas, doc):
   761 	def myLaterPages(canvas, doc):
   768         canvas.saveState()
   762 		canvas.saveState()
   769         canvas.setStrokeColor(red)
   763 		canvas.setStrokeColor(red)
   770         canvas.setLineWidth(5)
   764 		canvas.setLineWidth(5)
   771         canvas.line(66,72,66,PAGE_HEIGHT-72)
   765 		canvas.line(66,72,66,PAGE_HEIGHT-72)
   772         canvas.setFont('Times-Roman',12)
   766 		canvas.setFont('Times-Roman',12)
   773         canvas.drawString(4 * inch, 0.75 * inch, "Page %d" % doc.page)
   767 		canvas.drawString(4 * inch, 0.75 * inch, "Page %d" % doc.page)
   774         canvas.restoreState()
   768 		canvas.restoreState()
   775 
   769 
   776     def run():
   770 	def run():
   777         objects_to_draw = []
   771 		objects_to_draw = []
   778         from reportlab.lib.styles import ParagraphStyle
   772 		from reportlab.lib.styles import ParagraphStyle
   779         #from paragraph import Paragraph
   773 		#from paragraph import Paragraph
   780         from doctemplate import SimpleDocTemplate
   774 		from doctemplate import SimpleDocTemplate
   781 
   775 
   782         #need a style
   776 		#need a style
   783         normal = ParagraphStyle('normal')
   777 		normal = ParagraphStyle('normal')
   784         normal.firstLineIndent = 18
   778 		normal.firstLineIndent = 18
   785         normal.spaceBefore = 6
   779 		normal.spaceBefore = 6
   786         import random
   780 		import random
   787         for i in range(15):
   781 		for i in range(15):
   788             height = 0.5 + (2*random.random())
   782 			height = 0.5 + (2*random.random())
   789             box = XBox(6 * inch, height * inch, 'Box Number %d' % i)
   783 			box = XBox(6 * inch, height * inch, 'Box Number %d' % i)
   790             objects_to_draw.append(box)
   784 			objects_to_draw.append(box)
   791             para = Paragraph(randomText(), normal)
   785 			para = Paragraph(randomText(), normal)
   792             objects_to_draw.append(para)
   786 			objects_to_draw.append(para)
   793 
   787 
   794         SimpleDocTemplate('doctemplate.pdf').build(objects_to_draw,
   788 		SimpleDocTemplate('doctemplate.pdf').build(objects_to_draw,
   795             onFirstPage=myFirstPage,onLaterPages=myLaterPages)
   789 			onFirstPage=myFirstPage,onLaterPages=myLaterPages)
   796 
   790 
   797     run()
   791 	run()
   798 
   792