Module ctsimu.report

PDF report generation.

Based on: https://medium.com/@parveengoyal198/mastering-pdf-report-generation-with-reportlab-a-comprehensive-tutorial-part-2-c970ccd15fb6

Functions

def main()

Classes

class NumberedCanvas (*args, **kwargs)

This class is the programmer's interface to the PDF file format. Methods are (or will be) provided here to do just about everything PDF can do.

The underlying model to the canvas concept is that of a graphics state machine that at any given point in time has a current font, fill color (for figure interiors), stroke color (for figure borders), line width and geometric transform, among many other characteristics.

Canvas methods generally either draw something (like canvas.line) using the current state of the canvas or change some component of the canvas state (like canvas.setFont). The current state can be saved and restored using the saveState/restoreState methods.

Objects are "painted" in the order they are drawn so if, for example two rectangles overlap the last draw will appear "on top". PDF form objects (supported here) are used to draw complex drawings only once, for possible repeated use.

There are other features of canvas which are not visible when printed, such as outlines and bookmarks which are used for navigating a document in a viewer.

Here is a very silly example usage which generates a Hello World pdf document.

Example::

from reportlab.pdfgen import canvas c = canvas.Canvas("hello.pdf") from reportlab.lib.units import inch # move the origin up and to the left c.translate(inch,inch) # define a large font c.setFont("Helvetica", 80) # choose some colors c.setStrokeColorRGB(0.2,0.5,0.3) c.setFillColorRGB(1,0,1) # draw a rectangle c.rect(inch,inch,6inch,9inch, fill=1) # make text go straight up c.rotate(90) # change color c.setFillColorRGB(0,0,0.77) # say hello (note after rotate the y coord needs to be negative!) c.drawString(3inch, -3inch, "Hello World") c.showPage() c.save()

Create a canvas of a given size. etc.

You may pass a file-like object to filename as an alternative to a string. For more information about the encrypt parameter refer to the setEncrypt method.

Most of the attributes are private - we will use set/get methods as the preferred interface. Default page size is A4. cropMarks may be True/False or an object with parameters borderWidth, markColor, markWidth and markLength

if enforceColorSpace is in ('cmyk', 'rgb', 'sep','sep_black','sep_cmyk') then one of the standard _PDFColorSetter callables will be used to enforce appropriate color settings. If it is a callable then that will be used.

Expand source code
class NumberedCanvas(Canvas):
    def __init__(self, *args, **kwargs):
        Canvas.__init__(self, *args, **kwargs)

        self._saved_page_states = []
        #print(self.getAvailableFonts())

        self.page_number_xpos = 190*mm
        self.page_number_ypos = 14*mm
        self.page_number_font = 'Helvetica'
        self.page_number_fsize = 9
        self.page_number_text = 'Page %d of %d'

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

    def save(self):
        """add page info to each page (page x of y)"""
        num_pages = len(self._saved_page_states)
        for state in self._saved_page_states:
            self.__dict__.update(state)
            self.draw_page_number(num_pages)
            Canvas.showPage(self)
        Canvas.save(self)

    def draw_page_number(self, page_count):
        self.setFont(self.page_number_font, self.page_number_fsize)
        self.drawRightString(self.page_number_xpos, self.page_number_ypos,
            self.page_number_text % (self._pageNumber, page_count))

Ancestors

  • reportlab.pdfgen.canvas.Canvas
  • reportlab.pdfgen.textobject._PDFColorSetter

Methods

def draw_page_number(self, page_count)
def save(self)

add page info to each page (page x of y)

def showPage(self)

Close the current page and possibly start on a new page.

class Report (filename, **kwargs)

Template class for PDF report

create a document template bound to a filename (see class documentation for keyword arguments)

Expand source code
class Report(BaseDocTemplate):
    """Template class for PDF report"""

    def __init__(self, filename, **kwargs):
        super().__init__(filename, **kwargs)

        self.bodyWidth = self.pagesize[0] - self.leftMargin - self.rightMargin
        self.bodyXCenter = self.leftMargin + .5 * self.bodyWidth
        self.headerPad = 6*mm
        self.header_x1 = self.leftMargin
        self.header_y1 = self.pagesize[1] - self.topMargin + self.headerPad
        self.footer_x1 = self.leftMargin
        self.footer_y2 = self.bottomMargin - self.headerPad

        self.header_font = 'Courier'
        self.header_fsize = 9
        h = pdfmetrics.TypeFace(self.header_font)
        self.header_fheight = 2 + ((h.ascent - h.descent) * self.header_fsize) / 1000
        self.headerLeft = ''
        self.headerCenter = ''
        self.headerRight = ''
        self.footerLeft = ''
        self.footerLeft2 = ''
        self.footerCenter = ''
        self.footerRight = ''
    
        # Define the page frames
        self.frame = Frame(
            self.leftMargin, self.bottomMargin, 
            self.bodyWidth, self.pagesize[1] - self.topMargin - self.bottomMargin,
            leftPadding=0, bottomPadding=0,
            rightPadding=0, topPadding=0,
            id='normal'
        )

        # Define the PageTemplate
        self.addPageTemplates([
            PageTemplate(
                id='normal',
                frames=[self.frame],
                onPage=self._header,
                onPageEnd=self._footer
            )
        ])

    def _header(self, canvas, doc):
        # Draw the header
        canvas.saveState() 
        canvas.setFont(self.header_font, self.header_fsize)
        yLine = self.header_y1
        canvas.line(self.header_x1, yLine, self.header_x1 + self.bodyWidth, yLine)
        yText = yLine + 2 * mm
        canvas.drawString(self.leftMargin, yText, self.headerLeft)
        if canvas._pageNumber > 1:
            canvas.drawCentredString(self.bodyXCenter, yText, self.headerCenter)
        canvas.drawRightString(self.leftMargin + self.bodyWidth, yText, self.headerRight)
        canvas.restoreState()

    def _footer(self, canvas, doc):
        # Draw the footer
        canvas.saveState() 
        canvas.setFont(self.header_font, self.header_fsize)
        yLine = self.footer_y2
        canvas.line(self.footer_x1, yLine, self.footer_x1 + self.bodyWidth, yLine)
        yText = yLine - 2 * mm - self.header_fheight
        canvas.drawString(self.footer_x1, yText, self.footerLeft)
        canvas.drawCentredString(self.footer_x1 + 0.5 * self.bodyWidth, yText, self.footerCenter)
        canvas.drawRightString(self.footer_x1 + self.bodyWidth, yText, self.footerRight)
        if canvas._pageNumber == 1:
            canvas.drawString(self.footer_x1, yText - self.header_fheight, self.footerLeft2)
            canvas.drawString(self.footer_x1, yText - 2 * self.header_fheight, self.footerLeft3)
        canvas.restoreState()
        # Use the right-string position for the page number in NumberedCanvas
        try:
            canvas.page_number_xpos = self.footer_x1 + self.bodyWidth
            canvas.page_number_ypos = yText
            canvas.page_number_font = self.header_font
            canvas.page_number_fsize = self.header_fsize
            canvas.page_number_text = 'Page %d of %d'
        except:
            pass

    def setHeader(self, left = '', center = '', right = ''):
        self.headerLeft = left
        self.headerCenter = center
        self.headerRight = right

    def setFooter(self, left = '', center = '', right = '', left2 = '', left3 = ''):
        self.footerLeft = left
        self.footerLeft2 = left2
        self.footerLeft3 = left3
        self.footerCenter = center
        self.footerRight = right

    def df2table(self, df):
        data = [df.columns.values.tolist()] + df.values.tolist()
        return Table(data, [70, 48],
            repeatRows=1,
            style=[
                ('BACKGROUND', (0, 0), (-1, 0), colors.grey),
                ('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
                ('ALIGN', (0, 0), (-1, 0), 'CENTER'),
                ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
                ('FONTSIZE', (0, 0), (-1, 0), 7),
                ('BOTTOMPADDING', (0, 0), (-1, 0), 4),
                ('BACKGROUND', (0, 1), (-1, -1), colors.beige),
                ('TEXTCOLOR', (0, 1), (-1, -1), colors.black),
                ('ALIGN', (0, 1), (-1, -1), 'CENTER'),
                ('FONTNAME', (0, 1), (-1, -1), 'Helvetica'),
                ('FONTSIZE', (0, 1), (-1, -1), 9),
                ('BOTTOMPADDING', (0, 1), (-1, -1), 6),
                ('GRID', (0, 0), (-1, -1), 1, colors.black)],
            # hAlign = 'LEFT',
            spaceBefore=10, spaceAfter=10)

Ancestors

  • reportlab.platypus.doctemplate.BaseDocTemplate

Methods

def df2table(self, df)
def setFooter(self, left='', center='', right='', left2='', left3='')
def setHeader(self, left='', center='', right='')