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

时间:2018-03-18 03:00:31

标签: python-3.x reportlab

我正在使用Python编写报表生成器。到目前为止,除了一个细节之外,一切进展顺利:我需要在报告中添加自定义页脚。由于我还添加了更多元素,我需要使用自定义Canvas对象来构建报表,我认为应该可以将此自定义页脚添加到画布类...但到目前为止我还没有成功

这是我一直在写的代码:

进口:

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

自定义画布类:

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()

创建报告时,第一页看起来像这样:

enter image description here

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

1 个答案:

答案 0 :(得分:2)

我找到了一种方法来做我需要的...我单独留下自定义画布并使用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

自定义画布类:

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

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()

我觉得有点愚蠢,因为我发现它很容易 但我把它留给子孙后代,万一有人需要做类似的事情。

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