Re: Patch to support tables with oversize cells

Robin Becker <[email protected]> Tue, 19 Apr 2022 10:45:25 +0100
Newsgroups gmane.comp.python.reportlab.user
Message-ID <[email protected]>
Hi Lennart,

I put your data for the spanning case into my test program which is attached; I can use the following commands to see 
the behavior


python lennart-example.py --splitByRow=0 --splitInRow=1 --split=55 --pdfn=ttt.pdf --useData=1

this works and splits the column spanned row 3 (row=2 in python). The cell is split OK, but it gets styles from the 
splitfirst, splitlast styles. I'm not sure that's right for a split in the row when the split row is neither a first row 
or last. I don't really know what the correct styling should be, but I think the style that would have applied in the 
non-splitting case should be used.

If I use

python lennart-example.py --splitByRow=0 --splitInRow=1 --split=22 --pdfn=ttt.pdf --useData=1

then the split fails because it crosses the spanned row cell

On 12/04/2022 11:12, Lennart Regebro wrote:
> Hi! Have you been able to look more at this?
> 
> I'm feeling pretty good about the patch now, with added tests and better
> command handling.
> 
> 
.......


-- 
Robin Becker
lennart-example.py (text/x-python, 5.5 KB)
def main():
    from reportlab.lib import colors
    from reportlab.lib.units import inch
    from reportlab.platypus import Paragraph, Table, SimpleDocTemplate, Spacer, HRFlowable
    from reportlab.lib.styles import getSampleStyleSheet
    import os, sys, getopt
    try:
        opts, args = getopt.getopt(sys.argv[1:], '', 'splitByRow= splitInRow= split= useData= pdfn='.split())
    except getopt.GetoptError as err:
        # print help information and exit:
        print(err)
        print(f'Usage {os.path.basename(sys.argv[0])} [--splitByRow=num] [--splitInRow=num] [--split=num] [--useData=] --pdfn=')
        sys.exit(2)
    else:
        splitByRow = 1
        splitInRow = 0
        split = 36
        pdfn = None
        useData = 0
        for k, v in opts:
            if k=='--splitByRow':
                splitByRow = int(v)
            elif k=='--splitInRow':
                splitInRow = int(v)
            elif k=='--split':
                split = float(v)
                if int(split)==split: split=int(split)
            elif k=='--useData':
                useData = int(v)
            elif k=='--pdfn':
                pdfn =  v
            else:
                raise ValueError(f'Unknown option name {k}={v}')

    class IndicatorTable(Table):
        def draw(self):
            Table.draw(self)
            c = self.canv
            x = self._width
            y = self._height - split
            c.setStrokeColor(colors.toColor('red'))
            c.setLineWidth(0.5)
            c.setDash([2,2])
            c.line(0,y,x+13,y)

    story = []
    storyAdd = story.append
    styleSheet = getSampleStyleSheet()
    btStyle = styleSheet['BodyText']
    def makeTable(klass=Table):
        if useData==0:
            data=  [['00', '01', '02', '03', '04'],
                    ['10', '11', '12', '13', '14'],
                    ['20', '21', '22', '23', '24'],
                    ['30', '31', '32', '33', '34']]
            data=  [['00\n\naa', '01', '02', '03', '04'],
                    ['10', '11\nbb', '12', '13', '14'],
                    ['20', '21', '22\ncc', '23', '24'],
                    ['30', '31', '32', '33\ndd', '34']]
            sty = [
                    ('GRID',(0,0),(-1,-1),0.5,colors.grey),
                    ('GRID',(1,1),(-2,-2),1,colors.green),
                    ('BOX',(0,0),(1,-1),2,colors.red),
                    ('BOX',(0,0),(-1,-1),2,colors.black),
                    ('LINEABOVE',(1,2),(-2,2),1,colors.blue),
                    ('LINEBEFORE',(2,1),(2,-2),1,colors.pink),
                    ('BACKGROUND', (0, 0), (0, 1), colors.pink),
                    ('BACKGROUND', (1, 1), (1, 2), colors.lavender),
                    ('BACKGROUND', (2, 2), (2, 3), colors.orange),
                    ('TEXTCOLOR',(0,-1),(-2,-1),colors.green),
                    ]
        elif useData==1:
            data=  [['A', 'BBBBB', 'C', 'D', 'E'],
                    ['00', '01', '02', '03', '04'],
                    ['10\n11', ],
                    ['20', '21', '22', '23', '24'],
                    ['30', '31', '32', '33', '34']]
            sty = [
                    ('ALIGN',(0,0),(-1,-1),'CENTER'),
                    ('VALIGN',(0,0),(-1,-1),'TOP'),
                    ('GRID',(0,0),(-1,-1),1,colors.green),
                    ('BOX',(0,0),(-1,-1),2,colors.red),

                    #span 'BBBB' across middle 3 cells in top row
                    ('SPAN',(1,0),(3,0)),
                    #now color the first cell in this range only,
                    #i.e. the one we want to have spanned.  Hopefuly
                    #the range of 3 will come out khaki.
                    ('BACKGROUND',(1,0),(1,0),colors.khaki),

                    #span row 3 across all columns
                    ('SPAN',(0,2),(-1,2)),

                    #span 'AAA' down first two rows
                    ('SPAN',(0,0), (0, 1)),
                    ('BACKGROUND',(0,0),(0,0),colors.cyan),
                    ('TEXTCOLOR', (0,'splitfirst'), (-1,'splitfirst'), colors.cyan),
                    ('TEXTCOLOR', (0,'splitlast'), (-1,'splitlast'), colors.red),
                    ('BACKGROUND', (0,'splitlast'), (-1,'splitlast'), colors.pink),
                    ('LINEBELOW', (0,'splitlast'), (-1,'splitlast'), 1, colors.grey,'butt'),
                   ]
        else:
            raise ValueError(f'useData should be in [0,1] not {useData}')
        return klass(data,
                    style=sty,
                    splitInRow=splitInRow,
                    splitByRow=splitByRow,
                    )
    storyAdd(Paragraph("Illustrating splits: nosplit", btStyle))
    storyAdd(makeTable(klass=IndicatorTable))
    storyAdd(Spacer(0,6))
    def addSplitTable(size=30):
        t = makeTable()
        S = t.split(4*inch,size)
        if not S:
            storyAdd(Paragraph(f"<span color=red>Illustrating splits failed</span>: split(4in,{size}) {splitByRow=} {splitInRow=}", btStyle))
            storyAdd(Spacer(0,6))
            #print('!!!!! Failed')
        else:
            #print('##### OK')
            storyAdd(Paragraph(f"Illustrating splits: split(4in,{size}) {splitByRow=} {splitInRow=}", btStyle))
            storyAdd(Spacer(0,6))
            for s in S:
                storyAdd(s)
                storyAdd(Spacer(0,6))
    addSplitTable(split)

    pdfn = pdfn or f'{os.path.basename(os.path.splitext(sys.argv[0])[0])}-{splitByRow!s}-{splitInRow!s}-{split!s}.pdf'
    doc = SimpleDocTemplate(pdfn, showBoundary=0)
    doc.build(story)
    print(f'Saved {pdfn}')

if __name__=='__main__':
    main()