Search code examples
pythondjangopdf-generationreportlab

How to get Page no of pdf and assign to a table in ReportLab, Python


I'm working on a PDF generation script using ReportLab in Python. The script generates a PDF with a client table and some additional information. I have implemented a custom PageNumCanvas class to handle page numbering.(PageNumCanvas is from https://www.blog.pythonlibrary.org/2013/08/12/reportlab-how-to-add-page-numbers/)

this is the PageNumCanvas. I override(in save method) the first page to add the page no on a fixed location.

But i need to load the page_count dynamically, in the client table.

class PageNumCanvas(canvas.Canvas):
    """
    http://code.activestate.com/recipes/546511-page-x-of-y-with-reportlab/
    http://code.activestate.com/recipes/576832/

    This below code is taken from the 
    https://www.blog.pythonlibrary.org/2013/08/12/reportlab-how-to-add-page-numbers/
    """
    #----------------------------------------------------------------------
    def __init__(self, *args, **kwargs):
        """Constructor"""
        canvas.Canvas.__init__(self, *args, **kwargs)
        self.pages = []
        
    #----------------------------------------------------------------------
    def showPage(self):
        """
        On a page break, add information to the list
        """
        self.pages.append(dict(self.__dict__))
        self._startPage()
        
    #----------------------------------------------------------------------
    def save(self):
        """
        Add the page number to each page (page x of y)
        """
        page_count = len(self.pages)
        self.__dict__.update(self.pages[0])
        self.setFont("Helvetica", 9)
        self.drawRightString(270, 679, str(page_count))
        for page in self.pages:
            self.__dict__.update(page)
            self.draw_page_number(page_count)
            canvas.Canvas.showPage(self)
            
        canvas.Canvas.save(self)
        
    #----------------------------------------------------------------------
    def draw_page_number(self, page_count):
        """
        Add the page number
        """
        page = "Page %s of %s" % (self._pageNumber, page_count)
        self.setFont("Helvetica", 9)
        self.drawRightString(200*mm, 20*mm, page)

class YourPdfClass:
    def __init__(self):
        pass
    def header(self, canvas, doc):
           pass
    def write_pdf_lines(self, columns_fields, file_path):

        pdf_filename = file_path
        pdf_document = SimpleDocTemplate(pdf_filename, pagesize=letter
                                         )

        # Calculate the available width within the margins
        available_width = pdf_document.width
        styless = getSampleStyleSheet()
        client_data=client_data = [
                [
                    Paragraph(f"Client: <b>{x}</b>", styless['Normal']),
                    Paragraph(f"Downloaded By: <b>{y}</b>", styless['Normal']),
                    Paragraph(f"Date and Time: <b>{05-Jan-2024, 03:20 pm}</b>", styless['Normal'])
                ],
                [
                    Paragraph(f"Records: <b>{z}</b>", styless['Normal']),
                    Paragraph("Pages:{}", styless['Normal']),
                    ""
                ]
]

        client_table=Table(client_data,
                        #    colWidths=[available_width / 3] * 3,
                           spaceBefore=10)
        # Build the PDF document
        print("we started making pdf")
        pdf_document.build([client_table],canvasmaker=PageNumCanvas)

    def save(self):
        """
        Add the page number to each page (page x of y)
        """
        page_count = len(self.pages)
        self.__dict__.update(self.pages[0])
        self.setFont("Helvetica", 9)
        self.drawRightString(270, 679, str(page_count))
        for page in self.pages:
            self.__dict__.update(page)
            self.draw_page_number(page_count)
            canvas.Canvas.showPage(self)
            
        canvas.Canvas.save(self)

This load the data in fixed location. but if the dynamic value of other fields are large, it will get bigger, so it will look like misplaced.

Any insights into why this might be happening and suggestions for fixing it would be greatly appreciated. Thank you!


Solution

  • Ok sorry lets try again.

    How's about we render the document but don't save it and use PageNumCanvas to count the pages. At this point we know the number of pages and we can update the cell that has the page count re-rendering the document.

    using {{TOTAL_PAGES}}

    So change the client_data:

    client_data = [
    # ... (other rows)
    [
        Paragraph(f"Records: <b>{z}</b>", styless['Normal']),
        Paragraph("Pages: <b>{{TOTAL_PAGES}}</b>", styless['Normal']),  
        ""
    ]
    

    ]

    and:

    def write_pdf_lines(self, columns_fields, file_path):
         (your existing code to set up the document and table)
    
    # First Pass: Render the document to count pages
    pdf_document.build([client_table], canvasmaker=PageNumCanvas)
    
    # Get the total pages from your PageNumCanvas instance
    page_count = len(self.pages)
    
    # Update the placeholder in the client_data
    for row in client_data:
        for i, cell in enumerate(row):
            if isinstance(cell, Paragraph):
                text = cell.text
                # Replace placeholder with actual page count
                if "{{TOTAL_PAGES}}" in text:
                    row[i] = Paragraph(text.replace("{{TOTAL_PAGES}}", 
                      str(page_count)), styless['Normal'])
    
    # Second Pass: Re-render the document with the updated page count
    pdf_document.build([client_table], canvasmaker=PageNumCanvas)
    

    Let me know how you get on. Sorry for the late reply I am quite often away working. Good luck :)