bdoubleu
bdoubleu

Reputation: 6107

ReportLab: call same PageTemplate multiple times with different kwargs?

Is it possible to call the same PageTemplate multiple times within one doc.build?

For example if you have a PageTemplate for orders that uses order instance attributes in the header and footer, how would you go about creating a PDF of all orders for a given date?

class OrderTemplate(BaseDocTemplate):
    def __init__(self, filename, **kwargs):
        self.order = kwargs['order']
        BaseDocTemplate.__init__(self, filename **kwargs)
        main_frame = Frame(0, 0, self.pagesize[0], self.pagesize[1], id='main_frame')
        template = PageTemplate(id='header_footer_frame', frames=[main_frame], 
                                onPage=self.header_footer)
        self.addPageTemplates([template])

        # title
        canvas.setFont('Helvetica', 12)
        company_name = self.order.name.upper()
        first_row = canvas.beginText(0, doc.pagesize[1] - 10)
        first_row.textLine(company_name)
        canvas.drawText(first_row)

class Report:
    @staticmethod
    def build_pdf(**kwargs):
        buffer = BytesIO()
        doc = OrderTemplate(buffer, pagesize=kwargs['pagesize'], 
                            title=kwargs['page_title'],
                            order=kwargs['order'])

        doc.build(kwargs['story'], canvasmaker=NumberedCanvas)

        # Get the value of the BytesIO buffer and write it to the response.
        pdf = buffer.getvalue()
        buffer.close()
        return pdf

    @staticmethod
    def build_order(**kwargs):
        orders = kwargs['orders']
        pagesize = letter

        # Flow-able content
        story = []

        for order in orders:
            lines = OrderLine.objects.filter(order=order).order_by('pk')
            story.append(table)
            story.append(PageBreakIfNotEmpty())

        return Report.build_pdf(pagesize=pagesize, page_title=page_title, 
                                story=story, template=template, order=order)

The build_order() method generates a page for each order with the correct associated lines but the header/footer is the same across each page. I need to somehow call the PageTemplate for each loop and pass it the new order kwargs.

Upvotes: 1

Views: 618

Answers (1)

bdoubleu
bdoubleu

Reputation: 6107

Here's the solution I arrived at based on the use of chapters in ReportLab's user guide rltemplate.py

class OrderTemplate(PageTemplate):
    def __init__(self, id, pagesize=letter):
        frame = Frame(0, 0, pagesize[0], pagesize[1], id='main_frame')
        PageTemplate.__init__(self, id, [frame])

    def afterDrawPage(self, canvas, doc):
        canvas.saveState()
        canvas.setPageSize(doc.pagesize)
        canvas.setTitle(doc.title)

        if doc.order:
            # title
            canvas.setFont('Helvetica', 12)
            company_name = doc.order.name.upper()
            first_row = canvas.beginText(0, doc.pagesize[1] - 10)
            first_row.textLine(company_name)
            canvas.drawText(first_row)

        canvas.restoreState()

class BatchPrintTemplate(BaseDocTemplate):
    def __init__(self, filename, **kwargs):
        self.orders = kwargs['orders']
        self.order_iter = iter(self.orders)
        BaseDocTemplate.__init__(self, filename, **kwargs)

    def beforeDocument(self):
        self.order = next(self.order_iter)

    def afterInit(self):
        self.addPageTemplates(OrderTemplate('Order', pagesize=self.pagesize))

    def afterFlowable(self, flowable):
        if hasattr(flowable, 'new_order'):
            try:
                self.order = next(self.order_iter)
            except StopIteration:
                pass

When building your flowables you have to mark each page break with a new_order attribute. Setting the next order to use in the header is handled by afterFlowable.

class BatchPrint:
    def build_pdf(**kwargs):
        buffer = BytesIO()
        doc = BatchPrintTemplate(buffer, pagesize=kwargs['pagesize'], title=kwargs['page_title'],
                                 orders=kwargs['orders'])
        doc.build(kwargs['story'])
        # Get the value of the BytesIO buffer and write it to the response.
        pdf = buffer.getvalue()
        buffer.close()
        return pdf

    def build_orders(**kwargs):
        story = []
        for order in orders:
            ## build your flowables here ##
            story.append(table)

            story.append(PageBreak())
            story[-1].new_order = True

        return BatchPrint.build_pdf(pagesize=pagesize, page_title=page_title, story=story, orders=orders)

Upvotes: 1

Related Questions