ap0 ap0 - 1 year ago 96
Python Question

Reportlab does not reset sequences when creating multiple documents with table of contents

I am using a template function that creates multiple PDF documents in one program execution using reportlab.

These documents are identical by structure and have identical headings. They only differ in content below the headings. All of these documents contain a table of contents element.

I am using sequence tags (

etc.) to create numbered headings e. g.

1. Top1
1.1 Sub1
2. Top2
2.1 Sub1
2.2 Sub2

This works well for one single document but as soon as I create a second one right after the first the sequences are not reset and the second document's TOC looks like

2. Top1
2.1 Sub1
3. Top2
3.1 Sub1
3.2 Sub2

Creating a third document
will start with

But since I am starting a new document, creating a new
class, I was expecting the sequences to be reset. How can I achieve that?

I've tried to create a small as possible example using one of the tutorials for reportlab.

from reportlab.lib.styles import ParagraphStyle as PS
from reportlab.platypus import PageBreak
from reportlab.platypus.paragraph import Paragraph
from reportlab.platypus.doctemplate import PageTemplate, BaseDocTemplate
from reportlab.platypus.tableofcontents import TableOfContents
from reportlab.platypus.frames import Frame
from reportlab.lib.units import cm

class MyDocTemplate(BaseDocTemplate):
def __init__(self, filename, **kw):
self.allowSplitting = 0
super().__init__(filename, **kw)
template = PageTemplate('normal', [Frame(2.5*cm, 2.5*cm, 15*cm, 25*cm, id='F1')])

# Entries to the table of contents can be done either manually by
# calling the addEntry method on the TableOfContents object or automatically
# by sending a 'TOCEntry' notification in the afterFlowable method of
# the DocTemplate you are using. The data to be passed to notify is a list
# of three or four items countaining a level number, the entry text, the page
# number and an optional destination key which the entry should point to.
# This list will usually be created in a document template's method like
# afterFlowable(), making notification calls using the notify() method
# with appropriate data.

def afterFlowable(self, flowable):
"Registers TOC entries."
if flowable.__class__.__name__ == 'Paragraph':
text = flowable.getPlainText()
style = flowable.style.name
if style == 'Heading1':
self.notify('TOCEntry', (0, text, self.page))
if style == 'Heading2':
self.notify('TOCEntry', (1, text, self.page))

centered = PS(name = 'centered',
fontSize = 30,
leading = 16,
alignment = 1,
spaceAfter = 20)

h1 = PS(
name = 'Heading1',
fontSize = 14,
leading = 16)

h2 = PS(name = 'Heading2',
fontSize = 12,
leading = 14)

# Heading definition with sequence numbers
heading = {
1 : "<seq id='h1'/>.<seqreset id='h2'/><seqreset id='h3'/> {}",
2 : "<seq id='h1' inc='no'/>.<seq id='h2'/><seqreset id='h3'/> {}",
3 : "<seq id='h1' inc='no'/>.<seq id='h2' inc='no'/>.<seq id='h3'/> {}",

def build_document(filename):
# Build story.
story = []

# Create an instance of TableOfContents. Override the level styles (optional)
# and add the object to the story

toc = TableOfContents()
toc.levelStyles = [
PS(fontName='Times-Bold', fontSize=20, name='TOCHeading1', leftIndent=20, firstLineIndent=-20, spaceBefore=10, leading=16),
PS(fontSize=18, name='TOCHeading2', leftIndent=40, firstLineIndent=-20, spaceBefore=5, leading=12),

story.append(Paragraph('<b>Table of contents</b>', centered))
story.append(Paragraph(heading[1].format('First heading'), h1))
story.append(Paragraph('Text in first heading', PS('body')))
story.append(Paragraph(heading[2].format('First sub heading'), h2))
story.append(Paragraph('Text in first sub heading', PS('body')))
story.append(Paragraph(heading[2].format('Second sub heading'), h2))
story.append(Paragraph('Text in second sub heading', PS('body')))
story.append(Paragraph(heading[2].format('Last heading'), h1))
doc = MyDocTemplate(filename)

if __name__ == "__main__":

ap0 ap0
Answer Source

I have found a quick solution which solves my problem but which I don't like as a final solution.

The problem is that I am using global sequences with the same name. h1, h2 and h3 appear in every document. And reportlab doesn't seem to reset them when a new document is started. So instead I reset the manually before filling the story list.

def build_document(filename):
    # Build story. story = []

    # Reset the sequences
    story.append(Paragraph("<seqreset id='h1'/>", PS('body')))
    story.append(Paragraph("<seqreset id='h2'/>", PS('body')))
    story.append(Paragraph("<seqreset id='h3'/>", PS('body')))

    # ...  rest of the code from the question
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download