reportlab/platypus/tables.py
changeset 2289 fc3840f8294d
parent 2288 a8776318f957
child 2299 d757841f2006
--- a/reportlab/platypus/tables.py	Wed Apr 21 16:39:36 2004 +0000
+++ b/reportlab/platypus/tables.py	Thu Apr 22 17:36:41 2004 +0000
@@ -1,8 +1,8 @@
 #copyright ReportLab Inc. 2000
 #see license.txt for license details
 #history http://cvs.sourceforge.net/cgi-bin/cvsweb.cgi/reportlab/platypus/tables.py?cvsroot=reportlab
-#$Header: /tmp/reportlab/reportlab/platypus/tables.py,v 1.79 2004/04/21 16:39:36 rgbecker Exp $
-__version__=''' $Id: tables.py,v 1.79 2004/04/21 16:39:36 rgbecker Exp $ '''
+#$Header: /tmp/reportlab/reportlab/platypus/tables.py,v 1.80 2004/04/22 17:36:41 rgbecker Exp $
+__version__=''' $Id: tables.py,v 1.80 2004/04/22 17:36:41 rgbecker Exp $ '''
 
 __doc__="""
 Tables are created by passing the constructor a tuple of column widths, a tuple of row heights and the data in
@@ -18,23 +18,13 @@
 See the test output from running this module as a script for a discussion of the method for constructing
 tables and table styles.
 """
-
-from reportlab.platypus.flowables import Flowable, Image, Macro, PageBreak, Preformatted, Spacer, XBox, \
-                         CondPageBreak, KeepTogether, TraceInfo, FailOnWrap, FailOnDraw
-from reportlab.platypus.paragraph import Paragraph, cleanBlockQuotedText, ParaLines
-from reportlab.platypus.paraparser import ParaFrag
-from reportlab.platypus.frames import Frame
-from reportlab.platypus.doctemplate import BaseDocTemplate, NextPageTemplate, PageTemplate, ActionFlowable, \
-                         SimpleDocTemplate, FrameBreak, PageBegin
-from reportlab.platypus.xpreformatted import XPreformatted
+from reportlab.platypus.flowables import Flowable, Preformatted
 from reportlab import rl_config
 from reportlab.lib.styles import PropertySet, getSampleStyleSheet, ParagraphStyle
 from reportlab.lib import colors
 from reportlab.lib.utils import fp_str
 from reportlab.pdfbase import pdfmetrics
-
 import operator, string
-
 from types import TupleType, ListType, StringType
 
 class CellStyle(PropertySet):
@@ -163,7 +153,6 @@
 class Table(Flowable):
     def __init__(self, data, colWidths=None, rowHeights=None, style=None,
                 repeatRows=0, repeatCols=0, splitByRow=1, emptyTableAction=None):
-        #print "colWidths", colWidths
         self.hAlign = 'CENTER'
         self.vAlign = 'MIDDLE'
         if type(data) not in _SeqTypes:
@@ -281,18 +270,13 @@
         return w, t - V[0].getSpaceBefore()-V[-1].getSpaceAfter()
 
     def _calc_width(self,availWidth,W=None):
-        #comments added by Andy to Robin's slightly
-        #terse variable names
+        if getattr(self,'_width_calculated_once',None): return
+        #comments added by Andy to Robin's slightly terse variable names
         if not W: W = _calc_pc(self._argW,availWidth)   #widths array
-        #print 'widths array = %s' % str(self._colWidths)
-        canv = getattr(self,'canv',None)
-        saved = None
-
         if None in W:  #some column widths are not given
-            if self._spanCmds:
-                colspans = self._colSpannedCells
-            else:
-                colspans = {}
+            canv = getattr(self,'canv',None)
+            saved = None
+            colSpanCells = self._spanCmds and self._colSpanCells or ()
             if W is self._argW: W = W[:]
             while None in W:
                 j = W.index(None) #find first unspecified column
@@ -305,8 +289,7 @@
                 for v, s in map(None, V, S):
                     #if the current cell is part of a spanned region,
                     #assume a zero size.
-                    if colspans.has_key((j, i)):
-                        #print 'sizing a spanned cell (%d, %d) with content "%s"' % (j, i, str(v))
+                    if (j, i) in colSpanCells:
                         t = 0.0
                     else:#work out size
                         t = self._elementWidth(v,s)
@@ -316,19 +299,17 @@
                     if t>w: w = t   #record a new maximum
                     i = i + 1
 
-                #print 'max width for column %d is %0.2f' % (j, w)
                 W[j] = w
 
         self._colWidths = W
         width = 0
         self._colpositions = [0]        #index -1 is right side boundary; we skip when processing cells
         for w in W:
-            #print w, width
             width = width + w
             self._colpositions.append(width)
-        #print "final width", width
 
         self._width = width
+        self._width_calculated_once = 1
 
     def _elementWidth(self,v,s):
         t = type(v)
@@ -351,20 +332,20 @@
         H = self._argH
         if not W: W = _calc_pc(self._argW,availWidth)   #widths array
 
-        canv = getattr(self,'canv',None)
-        saved = None
-
-        #get a handy list of any cells which span rows.
-        #these should be ignored for sizing
-        if self._spanCmds:
-            spans = self._rowSpannedCells
-        else:
-            spans = {}
-
         hmax = lim = len(H)
         longTable = getattr(self,'_longTableOptimize',None)
 
         if None in H:
+            canv = getattr(self,'canv',None)
+            saved = None
+            #get a handy list of any cells which span rows. should be ignored for sizing
+            if self._spanCmds:
+                rowSpanCells = self._rowSpanCells
+                colSpanCells = self._colSpanCells
+                spanRanges = self._spanRanges
+                colpositions = self._colpositions
+            else:
+                rowSpanCells = colSpanCells = ()
             if canv: saved = canv._fontname, canv._fontsize, canv._leading
             H = H[:]    #make a copy as we'll change it
             self._rowHeights = H
@@ -380,7 +361,8 @@
                 h = 0
                 j = 0
                 for v, s, w in map(None, V, S, W): # value, style, width (lengths must match)
-                    if spans.has_key((j, i)):
+                    ji = j,i
+                    if ji in rowSpanCells:
                         t = 0.0  # don't count it, it's either occluded or unreliable
                     else:
                         t = type(v)
@@ -389,9 +371,11 @@
                             if w is None:
                                 raise ValueError, "Flowable %s in cell(%d,%d) can't have auto width in\n%s" % (v[0].identity(30),i,j,self.identity(30))
                             if canv: canv._fontname, canv._fontsize, canv._leading = s.fontname, s.fontsize, s.leading or 1.2*s.fontsize
+                            if ji in colSpanCells:
+                                t = spanRanges[ji]
+                                w = max(colpositions[t[2]+1]-colpositions[t[0]],w)
                             dW,t = self._listCellGeom(v,w,s)
                             if canv: canv._fontname, canv._fontsize, canv._leading = saved
-                            #print "leftpadding, rightpadding", s.leftPadding, s.rightPadding
                             dW = dW + s.leftPadding + s.rightPadding
                             if not rl_config.allowTableBoundsErrors and dW>w:
                                 raise "LayoutError", "Flowable %s (%sx%s points) too wide for cell(%d,%d) (%sx* points) in\n%s" % (v[0].identity(30),fp_str(dW),fp_str(t),i,j, fp_str(w), self.identity(30))
@@ -407,7 +391,6 @@
             if None not in H: hmax = lim
 
         height = self._height = reduce(operator.add, H[:hmax], 0)
-        #print "height, H", height, H
         self._rowpositions = [height]    # index 0 is actually topline; we skip when processing cells
         for h in H[:hmax]:
             height = height - h
@@ -432,22 +415,17 @@
         # in sizing
         if self._spanCmds:
             self._calcSpanRanges()
+            if None in self._argH:
+                self._calc_width(availWidth,W=W)
 
         # calculate the full table height
-        #print 'during calc, self._colWidths=', self._colWidths
         self._calc_height(availHeight,availWidth,W=W)
 
-        # if the width has already been calculated, don't calculate again
-        # there's surely a better, more pythonic way to short circuit this FIXME FIXME
-        if hasattr(self,'_width_calculated_once'): return
-        self._width_calculated_once = 1
-
         # calculate the full table width
         self._calc_width(availWidth,W=W)
 
         if self._spanCmds:
-            #now work out the actual rect for each spanned cell
-            #from the underlying grid
+            #now work out the actual rect for each spanned cell from the underlying grid
             self._calcSpanRects()
 
     def _hasVariWidthElements(self, upToRow=None):
@@ -550,11 +528,14 @@
 
         Any cell not in the key is not part of a spanned region
         """
-        spanRanges = {}
-        for row in range(self._nrows):
-            for col in range(self._ncols):
-                spanRanges[(col, row)] = (col, row, col, row)
-
+        self._spanRanges = spanRanges = {}
+        for x in xrange(self._ncols):
+            for y in xrange(self._nrows):
+                spanRanges[x,y] = (x, y, x, y)
+        self._colSpanCells = []
+        self._rowSpanCells = []
+        csa = self._colSpanCells.append
+        rsa = self._rowSpanCells.append
         for (cmd, start, stop) in self._spanCmds:
             x0, y0 = start
             x1, y1 = stop
@@ -564,50 +545,26 @@
             if x1 < 0: x1 = x1 + self._ncols
             if y0 < 0: y0 = y0 + self._nrows
             if y1 < 0: y1 = y1 + self._nrows
-
-            if x0 > x1:
-                x0, x1 = x1, x0
-            if y0 > y1:
-                y0, y1 = y1, y0
-
-            # unset/clobber all the ones it overwrites
-            for y in xrange(y0, y1+1):
-                for x in xrange(x0, x1+1):
-                    spanRanges[x, y] = None
-
-            # set the main entry
-            spanRanges[x0,y0] = (x0, y0, x1, y1)
-##            from pprint import pprint as pp
-##            pp(spanRanges)
-        self._spanRanges = spanRanges
+            if x0 > x1: x0, x1 = x1, x0
+            if y0 > y1: y0, y1 = y1, y0
 
-        #now produce a "listing" of all cells which
-        #are part of a spanned region, so the normal
-        #sizing algorithm can not bother sizing such cells
-        colSpannedCells = {}
-        for (key, value) in spanRanges.items():
-            if value is None:
-                colSpannedCells[key] = 1
-            elif len(value) == 4:
-                if value[0] == value[2]:
-                    #not colspanned
-                    pass
-                else:
-                    colSpannedCells[key] = 1
-        self._colSpannedCells = colSpannedCells
-        #ditto for row-spanned ones.
-        rowSpannedCells = {}
-        for (key, value) in spanRanges.items():
-            if value is None:
-                rowSpannedCells[key] = 1
-            elif len(value) == 4:
-                if value[1] == value[3]:
-                    #not rowspanned
-                    pass
-                else:
-                    rowSpannedCells[key] = 1
-        self._rowSpannedCells = rowSpannedCells
+            if x0!=x1 or y0!=y1:
+                #column span
+                if x0!=x1:
+                    for y in xrange(y0, y1+1):
+                        for x in xrange(x0,x1+1):
+                            csa((x,y))
+                #row span
+                if y0!=y1:
+                    for y in xrange(y0, y1+1):
+                        for x in xrange(x0,x1+1):
+                            rsa((x,y))
 
+                for y in xrange(y0, y1+1):
+                    for x in xrange(x0,x1+1):
+                        spanRanges[x,y] = None
+                # set the main entry
+                spanRanges[x0,y0] = (x0, y0, x1, y1)
 
     def _calcSpanRects(self):
         """Work out rects for tables which do row and column spanning.
@@ -620,6 +577,7 @@
         for each cell.  Any cell which 'does not exist' as another
         has spanned over it will get a None entry on the right
         """
+        if getattr(self,'_spanRects',None): return
         spanRects = {}
         for (coord, value) in self._spanRanges.items():
             if value is None:
@@ -635,7 +593,6 @@
 
         self._spanRects = spanRects
 
-
     def setStyle(self, tblstyle):
         if type(tblstyle) is not TableStyleType:
             tblstyle = TableStyle(tblstyle)
@@ -1148,6 +1105,9 @@
 
 def test():
     from reportlab.lib.units import inch, cm
+    from reportlab.platypus.flowables import Image, PageBreak, Spacer, XBox
+    from reportlab.platypus.paragraph import Paragraph
+    from reportlab.platypus.doctemplate import SimpleDocTemplate
     rowheights = (24, 16, 16, 16, 16)
     rowheights2 = (24, 16, 16, 16, 30)
     colwidths = (50, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32)