使用 ReportLab Platypus 将多个页面与 BaseDocTemplate 结合使用

142 阅读3分钟

当我们需要使用 Platypus 库来生成 PDF 文档,并且每个部分具有不同的页面模板(以及不同的页眉和页脚)时,可能需要用到 BaseDocTemplate。在使用 BaseDocTemplate 生成 PDF 文档时,如果想要在每个页面上显示不同的内容,可以使用 Frame 来实现。但是,在使用 Frame 时可能会遇到问题,例如无法在 Frame 中显示内容,或者只能在第一页上显示内容。

2、解决方案

以下是如何使用 BaseDocTemplate 和 Frame 在不同的页面上显示不同内容的方法:

  1. 首先,我们需要创建一个 Report 类,并在其中定义一些属性和方法。Report 类包含一个用于存储最终 PDF 文档的流对象、一个用于存储不同部分的列表对象、一些用于设置页面大小和页边距的属性、一个用于存储页面的列表对象、一个 BaseDocTemplate 对象、一个用于存储画布的画布对象和一个用于存储样式表的样式表对象。

  2. 接下来,我们需要定义 Report 类的 generate 方法。generate 方法用于生成 PDF 文档。首先,它会创建一个 BaseDocTemplate 对象,并将其设置为使用给定的流对象和页面大小。然后,它会为封页创建一个页面模板,并将封页添加到页面列表中。接下来,它会为每个部分创建一个页面模板,并将每个部分添加到页面列表中。最后,它会调用 BaseDocTemplate 对象的 build 方法,将页面列表中的内容构建成 PDF 文档。

  3. 然后,我们需要定义 Report 类的 coverpage 方法,该方法用于生成封面页。coverpage 方法创建一个画布对象,并在其中设置一些属性,例如字体和字号。然后,它将标题居中显示在画布上。最后,它将画布保存为一个图像,并将其添加到页面列表中。

  4. 最后,我们需要定义 Report 类的 render_section 方法,该方法用于生成每个部分的内容。render_section 方法首先创建一个画布对象,并在其中设置一些属性。然后,它将标题居中显示在画布上。接下来,它在标题下方添加一个间隔符。最后,它将画布保存为一个图像,并将其添加到页面列表中。

  5. 在这个示例代码中,我们使用 Report 类来生成一个名为 report.pdf 的 PDF 文档。这个文档包含一个封页和三个部分,每个部分的内容都是不同的。

from reportlab.platypus import (BaseDocTemplate, Paragraph, Spacer,
                            PageBreak, Frame, PageTemplate, NextPageTemplate)
from reportlab.pdfgen import canvas
from reportlab.lib import pagesizes, units, styles, enums

class Report(object):
    def __init__(self, stream, sections):
        self.stream = stream
        self.sections = sections
        w, h = pagesizes.A4
        self._width = w
        self._height = h
        self._story = []
        self._doc = None
        self._canvas = canvas.Canvas(self.stream)
        self._stylesheet = styles.getSampleStyleSheet()

    def generate(self):
        self._doc = BaseDocTemplate(self.stream,
                                    pagesize=(self._width, self._height),
                                    showBoundary=True
                                    )
        self.coverpage()
        for i, p in enumerate(self.sections):
            self.render_section(i, p)
        self._doc.build(self._story)
        self._canvas.save()

    def coverpage(self):
        frame = Frame(0, 0, self._width, self._height)
        self._doc.addPageTemplates(PageTemplate(id='cover', frames=[frame]))
        self._story.append(PageBreak())
        self._canvas.saveState()
        self._canvas.setFont('Helvetica', 16)
        self._canvas.drawCentredString(self._width / 2.0, self._height - 108,
                                       "This is the first page")
        self._canvas.restoreState()

    def render_section(self, num, text):
        frame = Frame(0, 0, self._width, self._height, showBoundary=1)
        self._doc.addPageTemplates(PageTemplate(id='section-%d' % num,
                                                frames=[frame]))
        h1 = self._stylesheet['Heading1']
        h1.alignment = enums.TA_CENTER
        frames = [NextPageTemplate('section-%d' % num),
                  Paragraph(self.sections[num], h1),
                  Spacer(1, units.inch * 0.2),
                  PageBreak()]
        self._story.extend(frames)


if __name__ == '__main__':
    Report('report.pdf', "Why is this not showing?".split(" ")).generate()

以上是使用 ReportLab Platypus 将多个页面与 BaseDocTemplate 结合使用的方法。