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

brian a001 at yakkadesign.com
Wed Nov 11 09:33:35 EST 2015


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




On 11/11/2015 07:01 AM, Robin Becker wrote:
> 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.


-------------- next part --------------
A non-text attachment was scrubbed...
Name: reportGen_numberedCanvas.py
Type: text/x-python-script
Size: 3897 bytes
Desc: not available
URL: <https://pairlist2.pair.net/pipermail/reportlab-users/attachments/20151111/00fe039e/attachment.bin>


More information about the reportlab-users mailing list