Reportlab:将自定义参数传递给 Canvas

Reportlab: Pass custom argument to Canvas

我正在使用 Python 开发报告生成器。到目前为止一切顺利,除了一个细节:我需要向报告添加自定义页脚。由于我还添加了更多元素,我需要使用自定义 Canvas 对象来构建报告,我认为应该可以将此自定义页脚添加到 canvas class ...但到目前为止我还没有成功。

这是我一直在写的代码:

进口:

from reportlab.pdfgen import canvas
from reportlab.platypus import SimpleDocTemplate, Paragraph
from reportlab.lib.units import cm
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.pagesizes import letter
from reportlab.lib.enums import TA_JUSTIFY

自定义 Canvas class:

class CustomCanvas(canvas.Canvas):
    """
    Adapted from http://code.activestate.com/recipes/576832/
    """
    def __init__(self, *args, **kwargs):
        canvas.Canvas.__init__(self, *args, **kwargs)
        self._saved_page_states = []
        # I'd like to pass the custom footer text to the constructor
        # to draw it at the footer for each page. This is a fragment of
        # text that will be read from a database
        if 'customFooterText' in kwargs:
            self.customFooter = kwargs['customFooterText']
        else:
            self.customFooter = 'Missing custom footer text :('

    def showPage(self):
        self._saved_page_states.append(dict(self.__dict__))
        self._startPage()

    def drawPageNumber(self, page_count):
        self.setFont('Helvetica', 8)
        self.drawRightString(21 * cm, 1 * cm, 
                             '%s / %s' % (self._pageNumber, page_count))

    def drawCustomFooter(self):
        """
        Here is where I'd like to draw the custom footer.
        """
        self.setFont('Helvetica', 8)
        self.drawString(1 * cm, 1 * cm, self.customFooter)

    def save(self):
        num_pages = len(self._saved_page_states)
        for state in self._saved_page_states:
            self.__dict__.update(state)
            self.drawPageNumber(num_pages)
            self.drawCustomFooter()
            canvas.Canvas.showPage(self)
        canvas.Canvas.save(self)

我的报表生成器:

class MyReport:
    def __init__(self):
        self.filename = 'test.pdf'
        self.doc = SimpleDocTemplate(self.filename, pagesize=letter,
                                     topMargin = 1 * cm, bottomMargin = 2 * cm,
                                     leftMargin = 1 * cm, rightMargin = 1 * cm)
        self.Story = []

    def generateReport(self):
        self.reportContent()
        self.doc.build(self.Story, canvasmaker=CustomCanvas)
        # Here is the issue: How to pass the kwarg to the CustomCanvas 
        # class so it can be drawn in each page?

    def reportContent(self):
        # In the "real life", this content will be generated with data from
        # a database.
        styles = getSampleStyleSheet()
        styles.add(ParagraphStyle(name='Justify', alignment=TA_JUSTIFY))
        justify = styles['Justify']
        txt = """
        Lorem ipsum dolor sit amet, consectetur adipiscing elit. 
        Maecenas tristique cursus enim at luctus. Proin tincidunt, 
        arcu vitae mattis pretium, lorem eros semper lectus, 
        venenatis luctus orci odio rhoncus nunc. Nam nulla arcu, 
        hendrerit at lacinia eget, gravida aliquam quam. Mauris 
        finibus ipsum at leo ullamcorper, ut faucibus est eleifend. 
        Maecenas vehicula malesuada tempor. Nulla et augue a purus 
        luctus tincidunt. Nam consectetur ut diam sit amet efficitur. 
        Morbi a volutpat orci. Donec id ipsum ut quam hendrerit gravida. 
        Nulla gravida, ante non euismod fermentum, metus nulla ullamcorper 
        sem, ut feugiat ipsum felis sit amet lectus.
        """
        for i in range(15):
            self.Story.append(Paragraph(txt, justify))

测试

report = MyReport()
report.generateReport()

创建报告时,第一页如下所示:

所以,问题是:如何使用 CustomCanvas class 中的 kwarg 将该字符串添加到每个页面?

我找到了一种方法来做我需要的...我单独留下自定义 canvas 并使用 onFirstPageonLaterPages 参数来包含我的自定义页脚(我不不在乎哪里指定了自定义页脚字符串,只要我能在运行时指定即可)。

我的解决方案:

进口:

from reportlab.pdfgen import canvas
from reportlab.platypus import SimpleDocTemplate, Paragraph
from reportlab.lib.units import cm
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.pagesizes import letter
from reportlab.lib.enums import TA_JUSTIFY

自定义 Canvas class:

我单独留下了 Custom Canvas class。它做了它需要做的事情:

class CustomCanvas(canvas.Canvas):
    """
    Adapted from http://code.activestate.com/recipes/576832/
    """
    def __init__(self, *args, **kwargs):
        canvas.Canvas.__init__(self, *args, **kwargs)
        self._saved_page_states = []

    def showPage(self):
        self._saved_page_states.append(dict(self.__dict__))
        self._startPage()

    def drawPageNumber(self, page_count):
        self.setFont('Helvetica', 8)
        self.drawRightString(21 * cm, 1 * cm, 
                             '%s / %s' % (self._pageNumber, page_count))
    def save(self):
        num_pages = len(self._saved_page_states)
        for state in self._saved_page_states:
            self.__dict__.update(state)
            self.drawPageNumber(num_pages)
            canvas.Canvas.showPage(self)
        canvas.Canvas.save(self)

我的报表生成器:

我在此处添加了 **kwargs 参数,因此我可以传递带有页脚文本的自定义参数:

class MyReport:
    def __init__(self, *args, **kwargs):
        self.filename = 'test.pdf'
        self.doc = SimpleDocTemplate(self.filename, pagesize=letter,
                                     topMargin = 1 * cm, bottomMargin = 2 * cm,
                                     leftMargin = 1 * cm, rightMargin = 1 * cm)
        if 'left_footer' in kwargs:
            self.left_footer = kwargs['left_footer']
        else:
            self.left_footer = None
        self.Story = []

    def onMyFirstPage(self, canvas, doc):
        # If the left_footer attribute is not None, then add it to the page
        canvas.saveState()
        if self.left_footer is not None:
            canvas.setFont('Helvetica', 8)
            canvas.drawString(1 * cm, 1 * cm, self.left_footer)
        canvas.restoreState()

    def onMyLaterPages(self, canvas, doc):
        # If the left_footer attribute is not None, then add it to the page
        canvas.saveState()
        if self.left_footer is not None:
            canvas.setFont('Helvetica', 8)
            canvas.drawString(1 * cm, 1 * cm, self.left_footer)
        canvas.restoreState()

    def generateReport(self):
        self.reportContent()
        self.doc.build(self.Story, canvasmaker=CustomCanvas, 
                       onFirstPage=self.onMyFirstPage,
                       onLaterPages=self.onMyLaterPages)

    def reportContent(self):
        styles = getSampleStyleSheet()
        styles.add(ParagraphStyle(name='Justify', alignment=TA_JUSTIFY))
        justify = styles['Justify']
        txt = """
        Lorem ipsum dolor sit amet, consectetur adipiscing elit. 
        Maecenas tristique cursus enim at luctus. Proin tincidunt, 
        arcu vitae mattis pretium, lorem eros semper lectus, 
        venenatis luctus orci odio rhoncus nunc. Nam nulla arcu, 
        hendrerit at lacinia eget, gravida aliquam quam. Mauris 
        finibus ipsum at leo ullamcorper, ut faucibus est eleifend. 
        Maecenas vehicula malesuada tempor. Nulla et augue a purus 
        luctus tincidunt. Nam consectetur ut diam sit amet efficitur. 
        Morbi a volutpat orci. Donec id ipsum ut quam hendrerit gravida. 
        Nulla gravida, ante non euismod fermentum, metus nulla ullamcorper 
        sem, ut feugiat ipsum felis sit amet lectus.
        """
        for i in range(15):
            self.Story.append(Paragraph(txt, justify))

测试

report = MyReport(left_footer='A custom test footer for my pages')
# I can now specify my custom footer in runtime!
report.generateReport()

现在我发现它是如此简单,我觉得有点傻。
但我把这个留给后代,以防有人需要做类似的事情。

无论如何,如果其他人有更好的解决方案,请添加您的答案。