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

79 Views Asked by At

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!

2

There are 2 best solutions below

1
andrewbyteforge On BEST ANSWER

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

2
andrewbyteforge On

Morning Rishikumar hope you are well buddy.

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.

Could you adjust the table size or use text wrapping.

I was recently using ReportLab to style a PDF for one of my projects and seem to face endless problems. I was only looking at one page and styling. The project is over on my github and is still underdevelopment but the PDF styles nicely.