reportlab/platypus: <br/> tags now working
authorrgbecker
Fri, 21 Jul 2006 12:44:40 +0000
changeset 2664 c9faa3a99e93
parent 2663 927cc273c5a5
child 2665 1b1ff5f0c6cb
reportlab/platypus: <br/> tags now working
reportlab/platypus/doctemplate.py
reportlab/platypus/paragraph.py
reportlab/platypus/paraparser.py
reportlab/test/test_platypus_breaking.py
reportlab/test/test_platypus_paraparser.py
--- a/reportlab/platypus/doctemplate.py	Fri Jul 21 08:48:41 2006 +0000
+++ b/reportlab/platypus/doctemplate.py	Fri Jul 21 12:44:40 2006 +0000
@@ -586,7 +586,7 @@
         n = len(flowables)
         while i<n and flowables[i].getKeepWithNext(): i += 1
         if i:
-            if not getattr(flowables[i],'locChanger',None): i += 1
+            if i<n and not getattr(flowables[i],'locChanger',None): i += 1
             K = KeepTogether(flowables[:i])
             for f in K._content:
                 f.keepWithNext = 0
--- a/reportlab/platypus/paragraph.py	Fri Jul 21 08:48:41 2006 +0000
+++ b/reportlab/platypus/paragraph.py	Fri Jul 21 12:44:40 2006 +0000
@@ -212,7 +212,8 @@
         #if you modify this you need to modify _rl_accel RGB
         def _sameFrag(f,g):
             'returns 1 if two ParaFrags map out the same'
-            if hasattr(f,'cbDefn') or hasattr(g,'cbDefn'): return 0
+            if (hasattr(f,'cbDefn') or hasattr(g,'cbDefn')
+                    or hasattr(f,'lineBreak') or hasattr(g,'lineBreak')): return 0
             for a in ('fontName', 'fontSize', 'textColor', 'rise', 'underline', 'strike', 'link'):
                 if getattr(f,a)!=getattr(g,a): return 0
             return 1
@@ -257,10 +258,14 @@
                 n = 0
         elif hasattr(f,'cbDefn'):
             W.append((f,''))
-        elif getattr(f, 'lineBreak', False) == True:
+        elif hasattr(f, 'lineBreak'):
             #pass the frag through.  The line breaker will scan for it.
-            W.append((f,''))
-            
+            if W!=[]:
+                W.insert(0,n)
+                R.append(W)
+                W = []
+                n = 0
+            R.append([0,(f,'')])
 
     if W!=[]:
         W.insert(0,n)
@@ -638,7 +643,7 @@
                 #this underscores my feeling that Unicode throughout would be easier!
                 wordWidth = stringWidth(word, fontName, fontSize, self.encoding)
                 newWidth = currentWidth + spaceWidth + wordWidth
-                if newWidth <= maxWidth or len(cLine) == 0: 
+                if newWidth <= maxWidth or len(cLine) == 0:
                     # fit one more on this line
                     cLine.append(word)
                     currentWidth = newWidth
@@ -670,6 +675,7 @@
                 return self.blPara
             n = 0
             nSp = 0
+            lineBreakPrev = False
             for w in _getFragWords(frags):
                 spaceWidth = stringWidth(' ',w[-1][0].fontName, w[-1][0].fontSize)
 
@@ -684,9 +690,14 @@
                     newWidth = currentWidth + spaceWidth + wordWidth
                 else:
                     newWidth = currentWidth
-                #if (not getattr(f, 'lineBreak','False')) and (newWidth<=maxWidth or n==0):
-                if newWidth<=maxWidth or n==0:
-                    # fit one more word on this line
+
+                #text to see if this frag is a line break. If it is and we will only act on it
+                #if the current width is non-negative or the previous thing was a deliberate lineBreak
+                lineBreak = hasattr(f,'lineBreak')
+                endLine = (newWidth>maxWidth and n>0) or (lineBreak and (currentWidth>0 or lineBreakPrev))
+                if not endLine:
+                    lineBreakPrev = lineBreak
+                    if lineBreak: continue      #throw it away
                     n += 1
                     maxSize = max(maxSize,f.fontSize)
                     nText = w[1][1]
@@ -718,6 +729,11 @@
 
                     currentWidth = newWidth
                 else:  #either it won't fit, or it's a lineBreak tag
+                    if lineBreak and not len(words):
+                        g = f.clone()
+                        del g.lineBreak
+                        words.append(g)
+
                     if currentWidth>self.width: self.width = currentWidth
                     #end of line
                     lines.append(FragLine(extraSpace=(maxWidth - currentWidth),wordCount=n,
@@ -729,6 +745,12 @@
                         maxWidth = maxWidths[lineno]
                     except IndexError:
                         maxWidth = maxWidths[-1]  # use the last one
+
+                    lineBreakPrev = lineBreak
+                    if lineBreak:
+                        n = 0
+                        continue
+
                     currentWidth = wordWidth
                     n = 1
                     maxSize = f.fontSize
--- a/reportlab/platypus/paraparser.py	Fri Jul 21 08:48:41 2006 +0000
+++ b/reportlab/platypus/paraparser.py	Fri Jul 21 12:44:40 2006 +0000
@@ -465,7 +465,7 @@
                 v = '\0'
         else:
             v = None
-            if attr: 
+            if attr:
                 self._syntax_error('<unichar/> invalid attribute %s' % attr.keys()[0])
 
         if v is not None:
@@ -483,12 +483,14 @@
 
     def start_br(self, attr):
         #just do the trick to make sure there is no content
-        self._push(_selfClosingTag='br', lineBreak=True, text='')
+        self._push(_selfClosingTag='br',lineBreak=True,text='')
 
     def end_br(self):
-        frag = self._pop(_selfClosingTag='br', lineBreak=True)
-        self.fragList.append(frag)
-
+        frag = self._stack[-1]
+        assert frag._selfClosingTag=='br' and frag.lineBreak,'Parser failure in <br/>'
+        del frag._selfClosingTag
+        self.handle_data('')
+        self._pop()
 
     def _initial_frag(self,attr,attrMap,bullet=0):
         style = self._style
@@ -760,7 +762,7 @@
             if bFragList:
                 for frag in bFragList:
                     frag.text = unicode(frag.text, self._enc)
-            
+
         return style, fragList, bFragList
 
     def _tt_parse(self,tt):
@@ -917,4 +919,4 @@
     check_text('''Here comes <font face="Courier" size="3cm">Courier 3cm</font> and normal again.''')
     #AR 14-Jul-2006: test <br/> tag
     check_text('''Before the break <br/>the middle line <br/> and the last line.''')
-    
\ No newline at end of file
+
--- a/reportlab/test/test_platypus_breaking.py	Fri Jul 21 08:48:41 2006 +0000
+++ b/reportlab/test/test_platypus_breaking.py	Fri Jul 21 12:44:40 2006 +0000
@@ -49,11 +49,27 @@
         self.addPageTemplates(template)
 
 
+_text1='''Furthermore, the fundamental error of regarding functional notions as
+categorial delimits a general convention regarding the forms of the
+grammar.  I suggested that these results would follow from the
+assumption that the descriptive power of the base component may remedy
+and, at the same time, eliminate a descriptive fact.  Thus a subset of
+English sentences interesting on quite independent grounds raises
+serious doubts about the ultimate standard that determines the accuracy
+of any proposed grammar.  Of course, the natural general principle that
+will subsume this case can be defined in such a way as to impose the
+strong generative capacity of the theory.  By combining adjunctions and
+certain deformations, the descriptive power of the base component is not
+subject to the levels of acceptability from fairly high (e.g. (99a)) to
+virtual gibberish (e.g. (98d)).
+'''
 def _test0(self):
     "This makes one long multi-page paragraph."
 
     # Build story.
     story = []
+    a = story.append
+
 
     styleSheet = getSampleStyleSheet()
     h1 = styleSheet['Heading1']
@@ -69,27 +85,38 @@
     h3.keepWithNext = 1
 
     bt = styleSheet['BodyText']
-
-    story.append(Paragraph("""
+    a(Paragraph("""
         Subsequent pages test pageBreakBefore, frameBreakBefore and
         keepTogether attributes.  Generated at %s.  The number in brackets
         at the end of each paragraph is its position in the story. (%d)""" % (
             time.ctime(time.time()), len(story)), bt))
 
     for i in range(10):
-        story.append(Paragraph('Heading 1 always starts a new page (%d)' % len(story), h1))
+        a(Paragraph('Heading 1 always starts a new page (%d)' % len(story), h1))
         for j in range(3):
-            story.append(Paragraph('Heading1 paragraphs should always'
+            a(Paragraph('Heading1 paragraphs should always'
                             'have a page break before.  Heading 2 on the other hand'
                             'should always have a FRAME break before (%d)' % len(story), bt))
-            story.append(Paragraph('Heading 2 always starts a new frame (%d)' % len(story), h2))
-            story.append(Paragraph('Heading1 paragraphs should always'
+            a(Paragraph('Heading 2 always starts a new frame (%d)' % len(story), h2))
+            a(Paragraph('Heading1 paragraphs should always'
                             'have a page break before.  Heading 2 on the other hand'
                             'should always have a FRAME break before (%d)' % len(story), bt))
             for j in range(3):
-                story.append(Paragraph(randomText(theme=PYTHON, sentences=2)+' (%d)' % len(story), bt))
-                story.append(Paragraph('I should never be at the bottom of a frame (%d)' % len(story), h3))
-                story.append(Paragraph(randomText(theme=PYTHON, sentences=1)+' (%d)' % len(story), bt))
+                a(Paragraph(randomText(theme=PYTHON, sentences=2)+' (%d)' % len(story), bt))
+                a(Paragraph('I should never be at the bottom of a frame (%d)' % len(story), h3))
+                a(Paragraph(randomText(theme=PYTHON, sentences=1)+' (%d)' % len(story), bt))
+
+    a(Paragraph('Now we do &lt;br/&gt; tests', h1))
+    a(Paragraph('First off no br tags',h3))
+    a(Paragraph(_text1,bt))
+    a(Paragraph("&lt;br/&gt; after 'the' in line 4",h3))
+    a(Paragraph(_text1.replace('forms of the','forms of the<br/>',1),bt))
+    a(Paragraph("2*&lt;br/&gt; after 'the' in line 4",h3))
+    a(Paragraph(_text1.replace('forms of the','forms of the<br/><br/>',1),bt))
+    a(Paragraph("&lt;br/&gt; after 'I suggested ' in line 5",h3))
+    a(Paragraph(_text1.replace('I suggested ','I suggested<br/>',1),bt))
+    a(Paragraph("2*&lt;br/&gt; after 'I suggested ' in line 5",h3))
+    a(Paragraph(_text1.replace('I suggested ','I suggested<br/><br/>',1),bt))
 
     doc = MyDocTemplate(outputfile('test_platypus_breaking.pdf'))
     doc.multiBuild(story)
--- a/reportlab/test/test_platypus_paraparser.py	Fri Jul 21 08:48:41 2006 +0000
+++ b/reportlab/test/test_platypus_paraparser.py	Fri Jul 21 12:44:40 2006 +0000
@@ -83,10 +83,8 @@
     def testBr(self):
         txt = u"Hello <br/> World"
         fragList = ParaParser().parse(txt, self.style)[1]
-        print fragList
-##        self.assertEquals(map(lambda x:x.text, fragList), [u'Hello ',u'Bold',u' World'])
-##        self.assertEquals(fragList[1].fontName, 'Times-Bold')
-
+        self.assertEquals(map(lambda x:x.text, fragList), [u'Hello ',u'',u' World'])
+        self.assertEquals(fragList[1].lineBreak, True)
 
 def makeSuite():
     return makeSuiteForClasses(ParaParserTestCase)