[reportlab-users] Table entries with VALIGN=MIDDLE are not correct

Tim Roberts reportlab-users@reportlab.com
Mon, 04 Oct 2004 12:49:45 -0700

There is a bug in the Platypus table handling when VALIGN is set to 
MIDDLE.  The text is offset way too high, so much so that it will 
overlap cell boundaries if they are drawn.  You can see this in this 
stripped-down version of test_platypus_tables.py:

from reportlab.platypus import Spacer, SimpleDocTemplate, Table, TableStyle
from reportlab.lib.units import inch
from reportlab.lib import colors

style = TableStyle([
    ('ALIGN', (1,1), (-1,-1), 'RIGHT'),
    ('ALIGN', (0,0), (-1,0), 'CENTRE'),
    ('VALIGN',(0,0), (0,-1), 'TOP'),
    ('VALIGN',(1,0), (1,-1), 'MIDDLE'),
    ('VALIGN',(2,0), (2,-1), 'BOTTOM'),
    ('GRID',  (0,0), (-1,-1), 0.25, colors.black)

doc = SimpleDocTemplate(
    pagesize=(8.5*inch, 11*inch),

txt = 'Line 1\nLine 2'

                ('Quarter 1',txt,txt,txt,txt),
                ('Quarter 2',txt,txt,txt,txt),
            style = style,
            rowHeights = (72,)*4

Here is the code from reportlab\platypus\tables.py that implements the 
vertical positioning, at line 965:

            if valign=='BOTTOM':
                y = rowpos + cellstyle.bottomPadding+n*leading-fontsize
            elif valign=='TOP':
                y = rowpos + rowheight - cellstyle.topPadding - fontsize
            elif valign=='MIDDLE':
                y = rowpos + (cellstyle.bottomPadding + 
                raise ValueError, "Bad valign: '%s'" % str(valign)

The problem is in the 'MIDDLE' case.  It it computing the center 
position of the text block, but it is not properly compensating for the  
baseline position of the first line of text.  I've played with about a 
dozen solutions and drawn charts on my whiteboard for 30 minutes, and I 
have come up with two solutions.  The first is the one that 
theoretically SHOULD be correct:

            elif valign=='MIDDLE':
                y = rowpos + (cellstyle.bottomPadding + 
rowheight-cellstyle.topPadding+(n-1)*leading - fontsize)/2.0

The second is the one that actually gives the most pleasing results, and 
is the one that I'm now using:

            elif valign=='MIDDLE':
                y = rowpos + (cellstyle.bottomPadding + 
rowheight-cellstyle.topPadding+n*leading)/2.0 - fontsize

- Tim Roberts, timr@probo.com
  Providenza & Boekelheide, Inc.