Added relative indentation
authorandy_robinson
Thu, 01 May 2003 22:46:17 +0000
changeset 1923 e692491f0967
parent 1922 e9cca0234f85
child 1924 f73e361db889
Added relative indentation
reportlab/platypus/doctemplate.py
reportlab/platypus/frames.py
reportlab/test/test_platypus_indents.py
--- a/reportlab/platypus/doctemplate.py	Tue Apr 29 17:12:18 2003 +0000
+++ b/reportlab/platypus/doctemplate.py	Thu May 01 22:46:17 2003 +0000
@@ -1,9 +1,9 @@
 #copyright ReportLab Inc. 2000
 #see license.txt for license details
 #history http://cvs.sourceforge.net/cgi-bin/cvsweb.cgi/reportlab/platypus/doctemplate.py?cvsroot=reportlab
-#$Header: /tmp/reportlab/reportlab/platypus/doctemplate.py,v 1.61 2003/02/02 08:46:47 andy_robinson Exp $
+#$Header: /tmp/reportlab/reportlab/platypus/doctemplate.py,v 1.62 2003/05/01 22:46:17 andy_robinson Exp $
 
-__version__=''' $Id: doctemplate.py,v 1.61 2003/02/02 08:46:47 andy_robinson Exp $ '''
+__version__=''' $Id: doctemplate.py,v 1.62 2003/05/01 22:46:17 andy_robinson Exp $ '''
 
 __doc__="""
 This module contains the core structure of platypus.
@@ -137,6 +137,21 @@
 FrameBreak = _FrameBreak('frameEnd')
 PageBegin = ActionFlowable('pageBegin')
 
+class Indenter(ActionFlowable):
+    """Increases or decreases left and right margins of frame.
+
+    This allows one to have a 'context-sensitive' indentation
+    and makes nested lists way easier.
+    """    
+
+    def __init__(self, left=0, right=0):
+        self.left = left
+        self.right = right
+
+    def apply(self, doc):
+        doc.frame._leftExtraIndent = doc.frame._leftExtraIndent + self.left
+        doc.frame._rightExtraIndent = doc.frame._rightExtraIndent + self.right
+        
 
 class NextPageTemplate(ActionFlowable):
     """When you get to the next page, use the template specified (change to two column, for example)  """
@@ -290,7 +305,10 @@
         self._onProgress = None
         self._flowableCount = 0  # so we know how far to go
 
-
+        #context sensitive margins - set by story, not from outside
+        self._leftExtraIndent = 0.0
+        self._rightExtraIndent = 0.0
+        
         self._calc()
         self.afterInit()
 
@@ -374,11 +392,17 @@
         if f._atTop:
             if self.showBoundary or self.frame.showBoundary:
                 self.frame.drawBoundary(self.canv)
+        f._leftExtraIndent = self._leftExtraIndent
+        f._rightExtraIndent = self._rightExtraIndent
 
     def handle_frameEnd(self,resume=0):
         ''' Handles the semantics of the end of a frame. This includes the selection of
             the next frame or if this is the last frame then invoke pageEnd.
         '''
+        
+        self._leftExtraIndent = self.frame._leftExtraIndent
+        self._rightExtraIndent = self.frame._rightExtraIndent
+
         if hasattr(self,'_nextFrameIndex'):
             frame = self.pageTemplate.frames[self._nextFrameIndex]
             del self._nextFrameIndex
--- a/reportlab/platypus/frames.py	Tue Apr 29 17:12:18 2003 +0000
+++ b/reportlab/platypus/frames.py	Thu May 01 22:46:17 2003 +0000
@@ -1,9 +1,9 @@
 #copyright ReportLab Inc. 2000
 #see license.txt for license details
 #history http://cvs.sourceforge.net/cgi-bin/cvsweb.cgi/reportlab/platypus/frames.py?cvsroot=reportlab
-#$Header: /tmp/reportlab/reportlab/platypus/frames.py,v 1.19 2003/02/02 08:37:33 andy_robinson Exp $
+#$Header: /tmp/reportlab/reportlab/platypus/frames.py,v 1.20 2003/05/01 22:46:17 andy_robinson Exp $
 
-__version__=''' $Id: frames.py,v 1.19 2003/02/02 08:37:33 andy_robinson Exp $ '''
+__version__=''' $Id: frames.py,v 1.20 2003/05/01 22:46:17 andy_robinson Exp $ '''
 
 __doc__="""
 """
@@ -63,6 +63,12 @@
         self.__dict__['_rightPadding'] = rightPadding
         self.__dict__['_topPadding'] = topPadding
 
+        # these two should NOT be set on a frame.
+        # they are used when Indenter flowables want
+        # to adjust edges e.g. to do nested lists
+        self._leftExtraIndent = 0.0
+        self._rightExtraIndent = 0.0
+        
         # if we want a boundary to be shown
         self.showBoundary = showBoundary
 
@@ -95,6 +101,9 @@
         self._y = self._y2 - self._topPadding
         self._atTop = 1
 
+    def _getAvailableWidth(self):
+        return self._aW - self._leftExtraIndent - self._rightExtraIndent
+    
     def _add(self, flowable, canv, trySplit=0):
         """ Draws the flowable at the current position.
         Returns 1 if successful, 0 if it would not fit.
@@ -108,7 +117,7 @@
         h = y - p - s
         if h>0:
             flowable.canv = canv #so they can use stringWidth etc
-            w, h = flowable.wrap(self._aW, h)
+            w, h = flowable.wrap(self._getAvailableWidth(), h)
             del flowable.canv
         else:
             return 0
@@ -122,11 +131,11 @@
             #if ((h > self._aH and not trySplit) or w > self._aW):
             if (not rl_config.allowTableBoundsErrors) and (h > self._aH and not trySplit):
                 raise "LayoutError", "Flowable %s (%sx%s points) too large for frame (%sx%s points)." % (
-                    flowable.__class__, w,h, self._aW,self._aH)
+                    flowable.__class__, w,h, self._getAvailableWidth(),self._aH)
             return 0
         else:
             #now we can draw it, and update the current point.
-            flowable.drawOn(canv, self._x, y, _sW=self._aW-w)
+            flowable.drawOn(canv, self._x + self._leftExtraIndent, y, _sW=self._getAvailableWidth()-w)
             y = y - flowable.getSpaceAfter()
             if y<>self._y: self._atTop = 0
             self._y = y
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/reportlab/test/test_platypus_indents.py	Thu May 01 22:46:17 2003 +0000
@@ -0,0 +1,146 @@
+#copyright ReportLab Inc. 2000-2003
+#see license.txt for license details
+#history http://cvs.sourceforge.net/cgi-bin/cvsweb.cgi/reportlab/test/test_platypus_indents.py?cvsroot=reportlab
+#$Header: /tmp/reportlab/reportlab/test/test_platypus_indents.py,v 1.1 2003/05/01 22:46:17 andy_robinson Exp $
+"""Tests for context-dependent indentation
+"""
+
+import sys, os, tempfile, random
+from string import split, strip, join, whitespace
+from operator import truth
+from types import StringType, ListType
+
+from reportlab.test import unittest
+from reportlab.test.utils import makeSuiteForClasses
+
+from reportlab.pdfbase.pdfmetrics import stringWidth
+from reportlab.platypus.paraparser import ParaParser
+from reportlab.platypus.flowables import Flowable
+from reportlab.lib.colors import Color
+from reportlab.lib.units import cm
+from reportlab.lib.enums import TA_LEFT, TA_RIGHT, TA_CENTER, TA_JUSTIFY
+from reportlab.lib.utils import _className
+from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
+from reportlab.platypus.paragraph import Paragraph
+from reportlab.platypus.frames import Frame
+from reportlab.platypus.doctemplate \
+     import PageTemplate, BaseDocTemplate, Indenter, FrameBreak, NextPageTemplate
+from reportlab.platypus import tableofcontents
+from reportlab.platypus.tableofcontents import TableOfContents
+from reportlab.platypus.tables import TableStyle, Table
+from reportlab.platypus.paragraph import *
+from reportlab.platypus.paragraph import _getFragWords
+from reportlab.platypus.flowables import Spacer
+
+
+def myMainPageFrame(canvas, doc):
+    "The page frame used for all PDF documents."
+
+    canvas.saveState()
+
+    canvas.rect(2.5*cm, 2.5*cm, 15*cm, 25*cm)
+    canvas.setFont('Times-Roman', 12)
+    pageNumber = canvas.getPageNumber()
+    canvas.drawString(10*cm, cm, str(pageNumber))
+
+    canvas.restoreState()
+
+
+class MyDocTemplate(BaseDocTemplate):
+    _invalidInitArgs = ('pageTemplates',)
+
+    def __init__(self, filename, **kw):
+        frame1 = Frame(2.5*cm, 2.5*cm, 15*cm, 25*cm, id='F1')
+        self.allowSplitting = 0
+        apply(BaseDocTemplate.__init__, (self, filename), kw)
+        template1 = PageTemplate('normal', [frame1], myMainPageFrame)
+
+        frame2 = Frame(2.5*cm, 16*cm, 15*cm, 10*cm, id='F2', showBoundary=1)
+        frame3 = Frame(2.5*cm, 2.5*cm, 15*cm, 10*cm, id='F3', showBoundary=1)
+
+        template2 = PageTemplate('updown', [frame2, frame3])
+        self.addPageTemplates([template1, template2])
+
+
+class IndentTestCase(unittest.TestCase):
+    "Test multi-page splitting of paragraphs (eyeball-test)."
+
+    def test0(self):
+        "This makes one long multi-page paragraph."
+
+        # Build story.
+        story = []
+
+        styleSheet = getSampleStyleSheet()
+        h1 = styleSheet['Heading1']
+        h1.spaceBefore = 18
+        bt = styleSheet['BodyText']
+        bt.spaceBefore = 6
+
+        story.append(Paragraph('Test of context-relative indentation',h1))
+
+        story.append(Spacer(18,18))
+
+        story.append(Indenter(0,0))
+        story.append(Paragraph("This should be indented 0 points at each edge. " + ("spam " * 25),bt))
+        story.append(Indenter(0,0))
+
+        story.append(Indenter(36,0))
+        story.append(Paragraph("This should be indented 36 points at the left. " + ("spam " * 25),bt))
+        story.append(Indenter(-36,0))
+
+        story.append(Indenter(0,36))
+        story.append(Paragraph("This should be indented 36 points at the right. " + ("spam " * 25),bt))
+        story.append(Indenter(0,-36))
+
+        story.append(Indenter(36,36))
+        story.append(Paragraph("This should be indented 36 points at each edge. " + ("spam " * 25),bt))
+        story.append(Indenter(36,36))
+        story.append(Paragraph("This should be indented a FURTHER 36 points at each edge. " + ("spam " * 25),bt))
+        story.append(Indenter(-72,-72))
+
+        story.append(Paragraph("This should be back to normal at each edge. " + ("spam " * 25),bt))
+
+
+        story.append(Indenter(36,36))
+        story.append(Paragraph(("""This should be indented 36 points at the left
+        and right.  It should run over more than one page and the indent should
+        continue on the next page. """ + (random.randint(0,10) * 'x') + ' ') * 20 ,bt))
+        story.append(Indenter(-36,-36))
+
+        story.append(NextPageTemplate('updown'))
+        story.append(FrameBreak())
+        story.append(Paragraph('Another test of context-relative indentation',h1))
+        story.append(NextPageTemplate('normal'))  # so NEXT page is different template...
+        story.append(Paragraph("""This time we see if the indent level is continued across
+            frames...this page has 2 frames, let's see if it carries top to bottom. Then
+            onto a totally different template.""",bt))
+
+        story.append(Indenter(0,0))
+        story.append(Paragraph("This should be indented 0 points at each edge. " + ("spam " * 25),bt))
+        story.append(Indenter(0,0))
+
+        story.append(Indenter(36,72))
+        story.append(Paragraph(("""This should be indented 36 points at the left
+        and 72 at the right.  It should run over more than one frame and one page, and the indent should
+        continue on the next page. """ + (random.randint(0,10) * 'x') + ' ') * 35 ,bt))
+        story.append(Indenter(-36,-72))
+
+        story.append(Paragraph("This should be back to normal at each edge. " + ("spam " * 25),bt))
+
+
+
+
+        doc = MyDocTemplate('test_platypus_indents.pdf')
+        doc.multiBuild(story)
+
+
+#noruntests
+def makeSuite():
+    return makeSuiteForClasses(IndentTestCase)
+
+
+#noruntests
+if __name__ == "__main__":
+    unittest.TextTestRunner().run(makeSuite())
+