This is my first time using ReportLab, and I've been stuck on this problem for a couple days. There are two main components to this issue: 1) Some of my pages are portrait and some are landscape. I'm using two different PageTemplates for this, but since I just learned how to use PageTemplates today, I may not be doing it correctly. 2) I'm including a "Page X of Y" on each page, but that's done with code on the canvas instead of the PageTemplates, since the people who wrote the code I'm using said that PageTemplates can't know how many pages are in the entire document, while the canvas can.
Imports:
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import letter, landscape
from reportlab.lib.units import inch
from reportlab.platypus import SimpleDocTemplate, Frame, Table, TableStyle, Image, Paragraph, PageBreak, PageTemplate, NextPageTemplate
Here's the code that numbers the pages in the canvas (as well as a two-part header):
class NumberedCanvas(canvas.Canvas):
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 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.Canvas.showPage(self)
canvas.Canvas.save(self)
def draw_page_number(self, page_count):
self.setFont('Helvetica',8)
self.drawString(x=0.25*inch, y=10.5*inch, text='This is the upper left header')
self.drawRightString(x=8.25*inch, y=10.5*inch, text='This is the upper right header')
self.drawRightString(x=8.25*inch, y=0.5*inch, text='Page %d of %d' % (self._pageNumber, page_count))
Here's the code for the document template and the 'portrait' and 'landscape' PageTemplates:
doc = SimpleDocTemplate('output.pdf', pagesize=letter, topMargin=0.5*inch, bottomMargin=0.6*inch)
portrait_frame = Frame(doc.leftMargin, doc.bottomMargin, doc.width, doc.height, id='portrait_frame ')
landscape_frame = Frame(doc.leftMargin, doc.bottomMargin,
width=letter[1]-doc.leftMargin-doc.rightMargin,
height=letter[0]-doc.topMargin-doc.bottomMargin, id='landscape_frame ')
doc.addPageTemplates([PageTemplate(id='portrait',frames=portrait_frame),
PageTemplate(id='landscape',frames=landscape_frame, pagesize=landscape(letter))
])
Here's how I create the story and build the pdf:
elements = []
# Page 1 is portrait orientation (side question: page 1 and only page 1 defaults to the first AddTemplate created- why is that? I had to create the portrait PageTemplate just because of that.)
elements.append(--generate logo--)
elements.append(--generate paragraph--)
elements.append(PageBreak())
# Page 2 is portrait orientation
elements.append(--generate table--)
elements.append(NextPageTemplate('landscape'))
elements.append(PageBreak())
# Page 3 is landscape orientation
elements.append(--generate table--)
doc.build(elements, canvasmaker=NumberedCanvas)
The problem is that the two header parts and page number are in the same position on the landscape page as they would be on a portrait page, and the reason is obvious- that's how draw_page_number is coded in the NumberedCanvas class (the result is that the page number is an additional 1.25 inches from the right side of the page, and the headers aren't even visible because they're above the top of the page). If I could access the id of the PageTemplate during the for loop in the NumberedCanvas's save function, I could set up a simple if statement, calculate the appropriate x and y locations for the headers and page number, and send those to the draw_page_number function. However, I looked at the contents of the state variable during the loop and couldn't find anything to indicate what kind of page I was on.
Is there a solution to this? Please know that I'm very shaky when it comes to understanding how classes work, so if I have to rewrite some things in there, I'd really appreciate any details and background as you'd care to give me.