[reportlab-users] TOC and page x of y canvas

1,642 views
Skip to first unread message

brian

unread,
Nov 9, 2015, 3:35:11 PM11/9/15
to reportl...@lists2.reportlab.com
Does anyone know the answer to this question:
http://stackoverflow.com/questions/16041397/python-reportlab-clickable-toc-with-x-of-y-page-numbering

I'm doing a similar thing and when I create the TOC with a numbered
canvas the links don't work. If I remove the numbered canvas, the links
work. I'd like to not have to roll my own TOC solution.

Brian

_______________________________________________
reportlab-users mailing list
reportl...@lists2.reportlab.com
https://pairlist2.pair.net/mailman/listinfo/reportlab-users

Robin Becker

unread,
Nov 11, 2015, 7:01:55 AM11/11/15
to reportlab-users
On 09/11/2015 20:35, brian wrote:
> Does anyone know the answer to this question:
> http://stackoverflow.com/questions/16041397/python-reportlab-clickable-toc-with-x-of-y-page-numbering
>
>
> I'm doing a similar thing and when I create the TOC with a numbered canvas the
> links don't work. If I remove the numbered canvas, the links work. I'd like to
> not have to roll my own TOC solution.
>
> Brian

Perhaps you could post some code, and please not like the link above which
doesn't work without considerable editing and guessing. The attempt is being
made there to defer page building until the end which is a bit daft.

Do not use the numbered canvas it is wholly problematic/idiotic. It might be
possible to defer all page writing until the very end, but Canvas and everything
else wasn't written with that intention so it's almost certainly not a good idea.

Anyhoo if I do make the code work as intended the links are clickable, but I
still don't like the idea of using the NumberedCanvas.


So far as I can understand it there is/are two possible reasons to use the
NumberedCanvas. We need to find the total number of pages and possibly we need
to avoid having multiple passes.

Well if we don't know the number of Toc entries up front we cannot avoid
multiple passes because of a variable number of TOC pages. If we know the TOC
will fit on n pages then it is possible to do both TOC & page x of y with one
pass. So 1) we assume a for "y" the total number of pages and 2) we assume
form(s) for the whole TOC.

If that solution won't do then you are probably back to doing multi-build anyway
and that easily allows for variable length TOC and for collection of the number
of pages at the end of the story. Three passes will suffice.
--
Robin Becker

brian

unread,
Nov 11, 2015, 9:33:39 AM11/11/15
to reportlab-users
Below is the code. I also attached it in case the formatting in the
email gets lost.

I only want <pages x of y>. I don't care about multiple passes.

I'm new to ReportLab and PDF gen. It seems the bookmarks are tied to a
position in the document. I don't see the need to know the page numbers
when creating the TOC. You guys may be abstracting that detail away
behind the scenes.

I can tell you don't like the numbered canvas but I don't understand
what the solution is to get <page x of y>. Can you give me more details
of how to do this?

Thanks for your help!!!!

Brian

"""
reportgen with numbered canvas
"""

import sys

from reportlab.pdfgen import canvas
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.units import inch
from reportlab.lib.units import mm

from reportlab.platypus import SimpleDocTemplate
from reportlab.platypus.tableofcontents import TableOfContents
from reportlab.platypus import PageBreak
from reportlab.platypus import Paragraph

styles = getSampleStyleSheet()
styleN = styles['Normal']
styleH = styles['Heading1']
styleH2 = styles['Heading2']
styleTitle = styles['Title']

elements = []

class NumberedCanvas(canvas.Canvas):
"""
for adding page x of y to each page

based on: http://code.activestate.com/recipes/576832/
modified:
skip title page
renamed draw_page_number to draw_footer since footer is more
than page number

"""

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)

#modify so will skip the first page for adding footer
for (i, state) in enumerate(self._saved_page_states):
self.__dict__.update(state)

if i!=0:
self.draw_footer(num_pages)

canvas.Canvas.showPage(self)

canvas.Canvas.save(self)

def draw_footer(self, page_count):
"""
draws the footer on the page

originally called draw_page_number but renamed since adding
complete footer
"""

self.setFont("Helvetica", 7)
self.drawRightString(200*mm, 20*mm,
"Page %d of %d" % (self._pageNumber, page_count))

class ModifiedSimpleDocTemplate(SimpleDocTemplate):
"""
modified version of SimpleDocTemplate
"""

def afterFlowable(self, flowable):
"""
called after a flowable has been rendered

based on user manual to add TOC
Detect Level 1 and 2 headings, build outline, and track chapter title.
"""

if isinstance(flowable, Paragraph):
txt = flowable.getPlainText()

#had to add style
style = flowable.style.name

if style=='Heading1':
#make h1 clickable
key = 'h1-%s'%self.seq.nextf('heading1')
#logger.debug( 'heading 1: txt=%s and key = %s and
page=%s'%(txt, key, self.page))
self.canv.bookmarkPage(key)
self.notify('TOCEntry', (0, txt, self.page, key))

elif style=='Heading2':
key = 'h2-%s'%self.seq.nextf('heading2')
self.canv.bookmarkPage(key)
self.notify('TOCEntry', (1, txt, self.page, key))

doc = ModifiedSimpleDocTemplate('testing.pdf')

#TOC
if True:
elements.append( Paragraph('Table of Contents',styleH) )

toc = TableOfContents()
PS = ParagraphStyle
toc.levelStyles = [
PS(fontName='Times-Bold', fontSize=14,
name='TOCHeading1',
leftIndent=20, firstLineIndent=-20,
spaceBefore=5, leading=16),
PS(fontSize=12, name='TOCHeading2',
leftIndent=40, firstLineIndent=-20,
spaceBefore=0, leading=12),
PS(fontSize=10, name='TOCHeading3',
leftIndent=60, firstLineIndent=-20,
spaceBefore=0, leading=12),
PS(fontSize=10, name='TOCHeading4',
leftIndent=100, firstLineIndent=-20,
spaceBefore=0, leading=12),
]
elements.append(toc)
elements.append( PageBreak() )

#generate filler elements
if True:
for i in range(100):
#p = Paragraph("This is a Heading %s"%i,styleH)
p = Paragraph("This is a Heading %s"%i,styleH2)
elements.append( p )

doc.multiBuild( elements, canvasmaker=NumberedCanvas)
#canvasmaker=NumberedCanvas canvasmaker=canvas.Canvas
reportGen_numberedCanvas.py

Deeksha :)

unread,
Dec 20, 2017, 11:15:24 AM12/20/17
to reportlab-users
Reply all
Reply to author
Forward
0 new messages