在Reportlab中将表格分跨多页

2 投票
1 回答
5325 浏览
提问于 2025-04-17 20:29

我正在尝试把一个“全宽”的表格分成两页甚至更多。我使用的是ReportLab的Platypus库和BaseDocTemplate类。

我有一个“全宽”的表格,里面有很多元素,这个表格应该在第一页的一个框架里显示。如果表格的行数足够多,它应该在第二页继续显示。我的问题是,第一页的框架高度和位置与其他页面不同,因为在第一页的顶部我需要显示更多的信息(没错,我说的就是发票或订单)。

经过无数次尝试,我得到的结果是一个只有一页的PDF,里面只有8个项目/行,这正好是第一页所需的空间。但是如果表格的行数超过8行,我得到的PDF就只有一页,而且表格没有显示(这意味着框架是空的,尽管我在日志中能看到所有数据)。

我使用了split()和wrap()这些方法,但可能用错了,因为我对ReportLab还不太熟悉。我给你展示一下我代码的最新版本:

from django.http import HttpResponse
from reportlab.pdfgen import canvas
from reportlab.lib.units import mm
from reportlab.lib import colors
from reportlab.lib.pagesizes import A4
from reportlab.platypus import BaseDocTemplate, PageTemplate, Table, Spacer, Frame, TableStyle,\
                               NextPageTemplate, PageBreak, FrameBreak

PAGE_WIDTH = A4[0]
PAGE_HEIGHT = A4[1]
MARGIN = 10*mm

class ThingPDF(BaseDocTemplate):
    def header(self, canvas, subheader=True):
        # print 'header()'
        data = [('AAAA', 'Thing %s' % (self.thing.name)), ]

        s = []
        t = Table(data, colWidths=[95 * mm, 95 * mm], rowHeights=None, style=None, splitByRow=1,
                  repeatRows=0, repeatCols=0)
        t.setStyle(TableStyle([
                   ('BACKGROUND', (0, 0), (0, 0), colors.red),
                   ('BACKGROUND', (1, 0), (1, 0), colors.blue),
                   ('ALIGN', (1, 0), (1, 0), 'RIGHT'),
        ]))
        s.append(t)

        # if subheader:
        #     print 'subheader'

        self.head.addFromList(s, canvas)


    def data_table(self, canvas, items):
        # print 'data_table()'
        d = [[u'col0', u'col1', u'col2', u'col3', u'col4', ],]

        for item in items:
            d.append([item.col0, item.col1, item.col2, item.col3, item.col4])

        s = []
        t = Table(d, colWidths=[20*mm, 100*mm, 20*mm, 20*mm, 30*mm], rowHeights=20*mm, style=None,\
                  splitByRow=1, repeatRows=0, repeatCols=0)

        t.setStyle([('BACKGROUND', (0,0), (-1,0), ('#eeeeee'))])


        h=187*mm #TODO
        w=A4[0] - (2*MARGIN)

        splitter = t.split(w, h)
        # print '\n\nresult of splitting: ', len(splitter)

        for i in splitter:
            print 'i: ', i
            self.dataframeX.addFromList(s, canvas)

        s.append(t)
        self.dataframe0.addFromList(s, canvas)

    def on_first_page(self, canvas, doc):
        canvas.saveState()
        self.header(canvas)
        self.data_table(canvas, self.items)
        canvas.restoreState()

    def on_next_pages(self, canvas, doc):
        canvas.saveState()
        self.header(canvas, subheader=False)
        canvas.restoreState()

    def build_pdf(self, thing=None, items=None, user=None):
        self.thing = thing
        self.items = items

        self.doc = BaseDocTemplate('%s.pdf' % (thing.name),
                                pagesize=A4,
                                pageTemplates=[self.first_page, self.next_pages,],
                                showBoundary=1,
                                rightMargin=MARGIN,
                                leftMargin=MARGIN,
                                bottomMargin=MARGIN,
                                topMargin=MARGIN,
                                allowSplitting=1,
                                title='%s' % 'title')

        self.story.append(Spacer(0*mm, 2*mm))
        self.doc.build(self.story)

        response = HttpResponse(mimetype='application/pdf')
        response['Content-Disposition'] = 'attachment; filename=%s.pdf' % (reference)

        return response

    def __init__(self):
        self.thing = None
        self.items = None
        self.story = []

        #==========  FRAMES  ==========
        self.head = Frame(x1=MARGIN, y1=A4[1] - (2*MARGIN), width=A4[0] - (2*MARGIN), height=10*mm,
                       leftPadding=0, bottomPadding=0, rightPadding=0, topPadding=0, id='header',
                       showBoundary=1)#, overlapAttachedSpace=None, _debug=None)
        self.dataframe0 = Frame(x1=MARGIN, y1=10*mm, width=A4[0] - (2*MARGIN), height=187*mm,
                                leftPadding=0, bottomPadding=0, rightPadding=0, topPadding=0,
                                id='body', showBoundary=1)
        self.dataframeX = Frame(x1=MARGIN, y1=MARGIN, width=A4[0] - (2*MARGIN), height=257*mm,
                                leftPadding=0, bottomPadding=0, rightPadding=0, topPadding=0,
                                id='body', showBoundary=1)

        #==========  PAGES  ==========
        self.first_page = PageTemplate(id='firstpage', frames=[self.head, self.dataframe0], onPage=self.on_first_page)
        self.next_pages = PageTemplate(id='nextpages', frames=[self.head, self.dataframeX], onPage=self.on_next_pages)

谢谢你们的帮助!!

1 个回答

1

你发的代码缺少一些数据,单独看是不能运行的,所以我不能确定我的回答是否正确。如果这样不行的话,请补充你的代码!

首先,你根本不需要使用 wrapsplit 这两个方法!当你用 doc.build 来处理故事的时候,表格会自动分开。而且,split 并不是在原地分割表格,而是返回一个表格的列表。所以在你的情况下,splitter 是一个表格的列表,你在这个列表上循环,然后把一个 列表添加到框架里。我建议你跳过这部分。你是把不同的元素添加到各自的框架中。这样一来,你会把分割后的表格添加到 dataframeX,但 dataframeX 可能根本不会被用到,因为你从来没有使用过 next_pagesPageTemplate。为此,你需要在完成第一页后,给故事添加一个 NextPageTemplate()。我建议让 platypus 来处理这些事情:只需让你的方法返回生成元素的列表,然后在传递给 doc.build() 之前把它们连接起来。

撰写回答