platypus: support setting template with PageBreak & add PageBreakIfNotEmpty; version --> 3.1.40
authorrobin
Tue, 14 Oct 2014 13:54:30 +0100
changeset 4145 f14cb807aa3d
parent 4144 5f3f91424f10
child 4146 950f23d00eca
platypus: support setting template with PageBreak & add PageBreakIfNotEmpty; version --> 3.1.40
src/reportlab/__init__.py
src/reportlab/platypus/__init__.py
src/reportlab/platypus/doctemplate.py
src/reportlab/platypus/flowables.py
--- a/src/reportlab/__init__.py	Tue Oct 14 13:25:32 2014 +0100
+++ b/src/reportlab/__init__.py	Tue Oct 14 13:54:30 2014 +0100
@@ -3,7 +3,7 @@
 #history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/__init__.py
 __version__=''' $Id$ '''
 __doc__="""The Reportlab PDF generation library."""
-Version = "3.1.39"
+Version = "3.1.40"
 
 import sys, os, imp
 
--- a/src/reportlab/platypus/__init__.py	Tue Oct 14 13:25:32 2014 +0100
+++ b/src/reportlab/platypus/__init__.py	Tue Oct 14 13:54:30 2014 +0100
@@ -6,7 +6,8 @@
 
 from reportlab.platypus.flowables import Flowable, Image, Macro, PageBreak, Preformatted, Spacer, XBox, \
                         CondPageBreak, KeepTogether, TraceInfo, FailOnWrap, FailOnDraw, PTOContainer, \
-                        KeepInFrame, ParagraphAndImage, ImageAndFlowables, ListFlowable, ListItem, FrameBG
+                        KeepInFrame, ParagraphAndImage, ImageAndFlowables, ListFlowable, ListItem, FrameBG, \
+                        PageBreakIfNotEmpty
 from reportlab.platypus.paragraph import Paragraph, cleanBlockQuotedText, ParaLines
 from reportlab.platypus.paraparser import ParaFrag
 from reportlab.platypus.tables import Table, TableStyle, CellStyle, LongTable
--- a/src/reportlab/platypus/doctemplate.py	Tue Oct 14 13:25:32 2014 +0100
+++ b/src/reportlab/platypus/doctemplate.py	Tue Oct 14 13:54:30 2014 +0100
@@ -73,17 +73,23 @@
     pass
 
 class PTCycle(list):
-    def __init__(self):
+    def __new__(cls,*args,**kwds):
+        self = list.__new__(cls,*args,**kwds)
         self._restart = 0
         self._idx = 0
-        list.__init__(self)
+        return self
 
-    def cyclicIterator(self):
-        while 1:
-            yield self[self._idx]
-            self._idx += 1
-            if self._idx>=len(self):
-                self._idx = self._restart
+    @property
+    def next_value(self):
+        v = self[self._idx]
+        self._idx += 1
+        if self._idx>=len(self):
+            self._idx = self._restart
+        return v
+
+    @property
+    def peek(self):
+        return self[self._idx]
 
 class IndexingFlowable(Flowable):
     """Abstract interface definition for flowables which might
@@ -225,12 +231,12 @@
         frame._rightExtraIndent += self.right
 
 class NotAtTopPageBreak(FrameActionFlowable):
-    def __init__(self):
-        pass
+    def __init__(self,nextTemplate=None):
+        self.nextTemplate = nextTemplate
 
     def frameAction(self,frame):
         if not frame._atTop:
-            frame.add_generated_content(PageBreak())
+            frame.add_generated_content(PageBreak(nextTemplate=self.nextTemplate))
 
 class NextPageTemplate(ActionFlowable):
     """When you get to the next page, use the template specified (change to two column, for example)  """
@@ -545,6 +551,27 @@
         self.frame._debug = self._debug
         self.handle_frameBegin()
 
+    def _setPageTemplate(self):
+        if hasattr(self,'_nextPageTemplateCycle'):
+            #they are cycling through pages'; we keep the index
+            self.pageTemplate = self._nextPageTemplateCycle.next_value
+        elif hasattr(self,'_nextPageTemplateIndex'):
+            self.pageTemplate = self.pageTemplates[self._nextPageTemplateIndex]
+            del self._nextPageTemplateIndex
+        elif self.pageTemplate.autoNextPageTemplate:
+            self.handle_nextPageTemplate(self.pageTemplate.autoNextPageTemplate)
+            self.pageTemplate = self.pageTemplates[self._nextPageTemplateIndex]
+
+    def _samePT(self,npt):
+        if isSeq(npt):
+            return getattr(self,'_nextPageTemplateCycle',[])
+        if isinstance(npt,strTypes):
+            return npt == (self.pageTemplates[self._nextPageTemplateIndex].id if hasattr(self,'_nextPageTemplateIndex') else self.pageTemplate.id)
+        if isinstance(npt,int) and 0<=npt<len(self.pageTemplates):
+            if hasattr(self,'_nextPageTemplateIndex'):
+                return npt==self._nextPageTemplateIndex
+            return npt==self.pageTemplates.find(self.pageTemplate)
+
     def handle_pageEnd(self):
         ''' show the current page
             check the next page template
@@ -575,16 +602,7 @@
             if self._debug: logger.debug("ending page %d" % self.page)
             self.canv.setPageRotation(getattr(self.pageTemplate,'rotation',self.rotation))
             self.canv.showPage()
-
-            if hasattr(self,'_nextPageTemplateCycle'):
-                #they are cycling through pages'; we keep the index
-                self.pageTemplate = next(self._nextPageTemplateCycle)
-            elif hasattr(self,'_nextPageTemplateIndex'):
-                self.pageTemplate = self.pageTemplates[self._nextPageTemplateIndex]
-                del self._nextPageTemplateIndex
-            elif self.pageTemplate.autoNextPageTemplate:
-                self.handle_nextPageTemplate(self.pageTemplate.autoNextPageTemplate)
-                self.pageTemplate = self.pageTemplates[self._nextPageTemplateIndex]
+            self._setPageTemplate()
             if self._emptyPages==0:
                 pass    #store good state here
         self._hanging.append(PageBegin)
@@ -664,7 +682,7 @@
                 raise ValueError("Invalid cycle restart position")
 
             #ensure we start on the first one
-            self._nextPageTemplateCycle = c.cyclicIterator()
+            self._nextPageTemplateCycle = c
         else:
             raise TypeError("argument pt should be string or integer or list")
 
@@ -758,6 +776,11 @@
             return
 
         if isinstance(f,PageBreak):
+            npt = f.nextTemplate
+            if npt and not self._samePT(npt):
+                npt=NextPageTemplate(npt)
+                npt.apply(self)
+                self.afterFlowable(npt)
             if isinstance(f,SlowPageBreak):
                 self.handle_pageBreak(slow=1)
             else:
@@ -884,6 +907,13 @@
         try:
             canv._doctemplate = self
             while len(flowables):
+                if self._hanging and self._hanging[-1] is PageBegin and isinstance(flowables[0],PageBreakIfNotEmpty):
+                    npt = flowables[0].nextTemplate
+                    if npt and not self._samePT(npt):
+                        npt=NextPageTemplate(npt)
+                        npt.apply(self)
+                        self._setPageTemplate()
+                    del flowables[0]
                 self.clean_hanging()
                 try:
                     first = flowables[0]
--- a/src/reportlab/platypus/flowables.py	Tue Oct 14 13:25:32 2014 +0100
+++ b/src/reportlab/platypus/flowables.py	Tue Oct 14 13:54:30 2014 +0100
@@ -41,6 +41,7 @@
         'FailOnWrap','HRFlowable','PTOContainer','KeepInFrame','UseUpSpace',
         'ListFlowable','ListItem','DDIndenter','LIIndenter',
         'DocAssign', 'DocExec', 'DocAssert', 'DocPara', 'DocIf', 'DocWhile',
+        'PageBreakIfNotEmpty',
         )
 class TraceInfo:
     "Holder for info about where an object originated"
@@ -533,10 +534,15 @@
 class PageBreak(UseUpSpace):
     """Move on to the next page in the document.
        This works by consuming all remaining space in the frame!"""
+    def __init__(self,nextTemplate=None):
+        self.nextTemplate = nextTemplate
 
 class SlowPageBreak(PageBreak):
     pass
 
+class PageBreakIfNotEmpty(PageBreak):
+    pass
+
 class CondPageBreak(Spacer):
     """use up a frame if not enough vertical space effectively CondFrameBreak"""
     def __init__(self, height):