在使用目录创建多个文档时,Reportlab不会重置序列

时间:2016-08-10 13:06:12

标签: python python-3.x reportlab

我正在使用模板功能,使用reportlab在一个程序执行中创建多个PDF文档。

这些文件的结构相同,标题相同。它们仅在标题下方的内容上有所不同。所有这些文档都包含目录元素。

我正在使用序列标签(<seq/>等)来创建编号标题e。 G。

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

这适用于单个文档,但只要我在第一个文档之后创建第二个文档,不重置序列并且第二个文档的TOC看起来像

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

创建第三个文档Top1将以3开头。

但是因为我正在创建一个新文档,创建一个新的BaseDocTemplate类,所以我期待重置序列。我怎样才能做到这一点?

我尝试使用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')])
         self.addPageTemplates(template)

# 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(toc)

    story.append(Paragraph('<b>Table of contents</b>', centered))
    story.append(PageBreak())
    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(PageBreak())
    story.append(Paragraph(heading[2].format('Second sub heading'), h2))
    story.append(Paragraph('Text in second sub heading', PS('body')))
    story.append(PageBreak())
    story.append(Paragraph(heading[2].format('Last heading'), h1))
    doc = MyDocTemplate(filename)
    doc.multiBuild(story)

if __name__ == "__main__":
    build_document("1.pdf")
    build_document("2.pdf")

2 个答案:

答案 0 :(得分:1)

我找到了一个快速的解决方案,解决了我的问题,但我不喜欢作为最终的解决方案。

问题是我使用的是同名的全局序列。 h1h2h3出现在每个文档中。并且在启动新文档时,reportlab似乎不会重置它们。所以我在填写故事列表之前手动重置。

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

答案 1 :(得分:0)

似乎正在使用的reportlab.lib.sequencer.Sequencer对象是全局对象。

您可以通过提供新的音序器来重置所有计数器

    from reportlab.lib.sequencer import setSequencer, Sequencer

    
    setSequencer(Sequencer())

您可以通过执行以下操作重置单个计数器:

    from reportlab.lib.sequencer import getSequencer

    s = getSequencer()
    s.reset('h1')

您也可以尝试直接使用Sequencer而不是注入XML。

Class Sequencer:简化段落编号的方法, 部分,图片和其他内容。功能包括注册 序列的新字符串格式,以及一些计数器的“链” 父母重设时。它跟踪许多 “计数器”,是根据要求创建的。

Usage::
 >>> seq = Sequencer()
 >>> seq.next('Bullets')
 1
 >>> seq.next('Bullets')
 2
 >>> seq.next('Bullets')
 3
 >>> seq.reset('Bullets')
 >>> seq.next('Bullets')
 1
 >>> seq.next('Figures')
 1
 >>>