Мне нужно создать PDF-файл с оглавлением в Python (v3.10) с помощью reportlab (v3.6.10) (SimpleDocTemplate). Номера страниц должны задаваться динамически, но примеры, которые мне удалось найти, не позволяют этого. Вот что у меня есть на данный момент.
(Оглавлению нужны гиперссылки для перехода внутри документа - это работает и опубликовано здесь, так как я не нашел онлайн-примера)
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.platypus import SimpleDocTemplate, Paragraph, Flowable, Paragraph, PageBreak, Spacer, Table
from reportlab.platypus import PageBreak
from reportlab.platypus.tableofcontents import TableOfContents
from reportlab.lib.units import inch
class DelayedRef(Flowable):
_ZEROSIZE = True
def __init__(self, toc, *args):
self.args = args
self.toc = toc
def wrap(self,w,h):
return 0,0
def draw(self,*args,**kwd):
self.toc.addEntry(*self.args)
def addPageNumber(canvas, doc):
"""
Add the page number
"""
page_num = canvas.getPageNumber()
text = "- %s -" % page_num
canvas.drawRightString(4.5*inch,0.5*inch, text)
def simple_toc():
docFile = 'test.pdf'
print(docFile)
ipsum = '''Lorem ipsum dolor sit amet, consectetur adipiscing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
culpa qui officia deserunt mollit anim id est laborum.'''
doc = SimpleDocTemplate(docFile)
story = []
styles = getSampleStyleSheet()
story.append(Paragraph("The Title", style=styles['Heading1']))
story.append(Paragraph("Notes:", style=styles['Normal']))
story.append(Paragraph(ipsum, style=styles['Normal']))
story.append(PageBreak())
story.append(Paragraph('↑', style=styles['Heading1']))
story.append(DelayedRef(toc, 0, '1. Part 1 Header', 1))
for x in range(10):
story.append(Paragraph(ipsum, style=styles['Normal']))
story.append(PageBreak())
story.append(Paragraph('↑', style=styles['Heading1']))
story.append(DelayedRef(toc, 0, '2. Part 2 Header', 1))
story.append(Paragraph(ipsum, style=styles['Normal']))
story.append(Paragraph('↑', style=styles['Heading2']))
story.append(DelayedRef(toc, 1, '2.1 Part 2.1 Header', 1))
for x in range(10):
story.append(Paragraph(ipsum, style=styles['Normal']))
story.append(PageBreak())
story.append(Paragraph('↑', style=styles['Heading1']))
story.append(DelayedRef(toc, 0, '3. Part 3 Header',1))
doc.multiBuild(story, onFirstPage=addPageNumber, onLaterPages=addPageNumber)
if __name__ == '__main__':
simple_toc()
Подробнее здесь: https://stackoverflow.com/questions/776 ... ng-problem
Оглавление Python ReportLab Проблема с нумерацией страниц ⇐ Python
-
111
Re: Оглавление Python ReportLab Проблема с нумерацией страниц
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.platypus import SimpleDocTemplate, Paragraph, Flowable, Paragraph, PageBreak, Spacer, Table
from reportlab.platypus import PageBreak
from reportlab.platypus.tableofcontents import TableOfContents
from reportlab.lib.units import inch
from reportlab.lib.utils import simpleSplit
class DelayedRef(Flowable):
_ZEROSIZE = True
def __init__(self, toc, *args):
self.args = args
self.toc = toc
def wrap(self,w,h):
return 0,0
def draw(self,*args,**kwd):
self.toc.addEntry(*self.args)
def addPageNumber(canvas, doc):
"""
Add the page number
"""
page_num = canvas.getPageNumber()
text = "- %s -" % page_num
canvas.drawRightString(4.5*inch,0.5*inch, text)
def simple_toc():
docFile = 'test.pdf'
print(docFile)
ipsum = '''Lorem ipsum dolor sit amet, consectetur adipiscing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
culpa qui officia deserunt mollit anim id est laborum.'''
doc = SimpleDocTemplate(docFile)
# на момент создания оглавления на второй странице номера страниц динамических разделов неизвестны, поэтому надо сначала прогнать "вхолостую" весь документ, чтобы их вычитслить
# прогон для расчета номеров страниц оглавления
pagesforTOC = []
pagenumber = 3 #первая страница для оглавления
story0 = []
styles = getSampleStyleSheet()
story0.append(Paragraph("The Title", style=styles['Heading1']))
story0.append(Paragraph("Notes:", style=styles['Normal']))
story0.append(Paragraph(ipsum, style=styles['Normal']))
story0.append(PageBreak())
story0.append(Paragraph('<a name="TOC"/>Table of Contents', style=styles['Heading1']))
toc = TableOfContents()
toc.levelStyles = [
ParagraphStyle(fontName='Helvetica', fontSize=14, name='Heading1', leftIndent=20, firstLineIndent=-20,
spaceBefore=5, leading=16),
ParagraphStyle(fontName='Times-Roman', fontSize=14, name='Heading2', leftIndent=20, firstLineIndent=-20,
spaceBefore=5, leading=16)]
story0.append(toc)
story0.append(PageBreak())
pagesforTOC.append(pagenumber)
pagenumber += 1
story0.append(Paragraph('<a name="Part1"/>1. Part 1 Header <a href="#TOC" color="blue">↑</a>',
style=styles['Heading1']))
story0.append(DelayedRef(toc, 0, '<a href="#Part1" color="blue">1. Part 1 Header</a>', 1))
lines = 0
parwidth = 595.2 - 20 - 20 # ширина текста
linesnumber = 70 # ограничение на число строк на странице
currentparnum = 0
for x in range(10):
currentparnum += 1
story0.append(Paragraph(ipsum, style=styles['Normal']))
lines = lines + len(simpleSplit(ipsum, 'Times-Roman', round(10), parwidth)) # считаем число строк, 'Times-Roman', round(10) - шрифт и его размер для используемого стиля
if lines>linesnumber:
# переход на новую страницу надо задать явно
story0.append(PageBreak())
pagenumber += 1
lines = 0
currentparnum = 0
if currentparnum == 10:
story0.append(PageBreak())
pagesforTOC.append(pagenumber)
pagenumber += 1
story0.append(Paragraph('<a name="Part2"/>2. Part 2 Header <a href="#TOC" color="blue">↑</a>',
style=styles['Heading1']))
story0.append(DelayedRef(toc, 0, '<a href="#Part2" color="blue">2. Part 2 Header</a>', 1))
story0.append(Paragraph(ipsum, style=styles['Normal']))
story0.append(Paragraph('<a name="Part21"/>2.1 Part 2.1 Header <a href="#TOC" color="blue">↑</a>',
style=styles['Heading2']))
story0.append(DelayedRef(toc, 1, '<a href="#Part21" color="blue">2.1 Part 2.1 Header</a>', 1))
lines = 0
parwidth = 595.2 - 20 - 20 # ширина текста
linesnumber = 70 # ограничение на число строк на странице
currentparnum = 0
for x in range(10):
currentparnum += 1
story0.append(Paragraph(ipsum, style=styles['Normal']))
lines = lines + len(simpleSplit(ipsum, 'Times-Roman', round(10), parwidth))
if lines>linesnumber:
story0.append(PageBreak())
pagenumber += 1
lines = 0
currentparnum = 0
if currentparnum == 10:
story0.append(PageBreak())
story0.append(PageBreak())
pagenumber += 1
pagesforTOC.append(pagenumber)
story0.append(Paragraph('<a name="Part3"/>3. Part 3 Header <a href="#TOC" color="blue">↑</a>',
style=styles['Heading1']))
story0.append(DelayedRef(toc, 0, '<a href="#Part3" color="blue">3. Part 3 Header</a>', 1))
story0 = []
print(pagesforTOC)
# конец расчета номеров страниц
xxx = 0
story = []
styles = getSampleStyleSheet()
story.append(Paragraph("The Title", style=styles['Heading1']))
story.append(Paragraph("Notes:", style=styles['Normal']))
story.append(Paragraph(ipsum, style=styles['Normal']))
story.append(PageBreak())
story.append(Paragraph('<a name="TOC"/>Table of Contents', style=styles['Heading1']))
toc = TableOfContents()
toc.levelStyles = [ParagraphStyle(fontName='Helvetica', fontSize=14, name='Heading1',leftIndent=20, firstLineIndent=-20, spaceBefore=5,leading=16),
ParagraphStyle(fontName='Times-Roman', fontSize=14, name='Heading2',leftIndent=20, firstLineIndent=-20, spaceBefore=5,leading=16)]
story.append(toc)
story.append(PageBreak())
par = Paragraph('<a name="Part1"/>1. Part 1 Header <a href="#TOC" color="blue">↑</a>', style=styles['Heading1'])
story.append(par)
story.append(DelayedRef(toc, 0, '<a href="#Part1" color="blue">1. Part 1 Header</a>', pagesforTOC[xxx]))
xxx += 1
lines = 0
parwidth = 595.2 - 20 - 20 # ширина текста
linesnumber = 70 # ограничение на число строк на странице
for x in range(10):
story.append(Paragraph(ipsum, style=styles['Normal']))
lines = lines + len(simpleSplit(ipsum, 'Times-Roman', round(10), parwidth))
if lines > linesnumber:
story.append(PageBreak())
pagenumber += 1
lines = 0
story.append(PageBreak())
story.append(Paragraph('<a name="Part2"/>2. Part 2 Header <a href="#TOC" color="blue">↑</a>', style=styles['Heading1']))
story.append(DelayedRef(toc, 0, '<a href="#Part2" color="blue">2. Part 2 Header</a>', pagesforTOC[xxx]))
story.append(Paragraph(ipsum, style=styles['Normal']))
story.append(Paragraph('<a name="Part21"/>2.1 Part 2.1 Header <a href="#TOC" color="blue">↑</a>', style=styles['Heading2']))
story.append(DelayedRef(toc,0, '<a href="#Part21" color="blue">2.1 Part 2.1 Header</a>', pagesforTOC[xxx]))
xxx += 1
lines = 0
parwidth = 595.2 - 20 - 20 # ширина текста
linesnumber = 70 # ограничение на число строк на странице
for x in range(10):
story.append(Paragraph(ipsum, style=styles['Normal']))
lines = lines + len(simpleSplit(ipsum, 'Times-Roman', round(10), parwidth))
if lines > linesnumber:
story.append(PageBreak())
pagenumber += 1
lines = 0
story.append(PageBreak())
story.append(Paragraph('<a name="Part3"/>3. Part 3 Header <a href="#TOC" color="blue">↑</a>', style=styles['Heading1']))
story.append(DelayedRef(toc, 0, '<a href="#Part3" color="blue">3. Part 3 Header</a>', pagesforTOC[xxx]))
doc.multiBuild(story, onFirstPage=addPageNumber, onLaterPages=addPageNumber)
if __name__ == '__main__':
simple_toc()
from reportlab.platypus import SimpleDocTemplate, Paragraph, Flowable, Paragraph, PageBreak, Spacer, Table
from reportlab.platypus import PageBreak
from reportlab.platypus.tableofcontents import TableOfContents
from reportlab.lib.units import inch
from reportlab.lib.utils import simpleSplit
class DelayedRef(Flowable):
_ZEROSIZE = True
def __init__(self, toc, *args):
self.args = args
self.toc = toc
def wrap(self,w,h):
return 0,0
def draw(self,*args,**kwd):
self.toc.addEntry(*self.args)
def addPageNumber(canvas, doc):
"""
Add the page number
"""
page_num = canvas.getPageNumber()
text = "- %s -" % page_num
canvas.drawRightString(4.5*inch,0.5*inch, text)
def simple_toc():
docFile = 'test.pdf'
print(docFile)
ipsum = '''Lorem ipsum dolor sit amet, consectetur adipiscing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
culpa qui officia deserunt mollit anim id est laborum.'''
doc = SimpleDocTemplate(docFile)
# на момент создания оглавления на второй странице номера страниц динамических разделов неизвестны, поэтому надо сначала прогнать "вхолостую" весь документ, чтобы их вычитслить
# прогон для расчета номеров страниц оглавления
pagesforTOC = []
pagenumber = 3 #первая страница для оглавления
story0 = []
styles = getSampleStyleSheet()
story0.append(Paragraph("The Title", style=styles['Heading1']))
story0.append(Paragraph("Notes:", style=styles['Normal']))
story0.append(Paragraph(ipsum, style=styles['Normal']))
story0.append(PageBreak())
story0.append(Paragraph('<a name="TOC"/>Table of Contents', style=styles['Heading1']))
toc = TableOfContents()
toc.levelStyles = [
ParagraphStyle(fontName='Helvetica', fontSize=14, name='Heading1', leftIndent=20, firstLineIndent=-20,
spaceBefore=5, leading=16),
ParagraphStyle(fontName='Times-Roman', fontSize=14, name='Heading2', leftIndent=20, firstLineIndent=-20,
spaceBefore=5, leading=16)]
story0.append(toc)
story0.append(PageBreak())
pagesforTOC.append(pagenumber)
pagenumber += 1
story0.append(Paragraph('<a name="Part1"/>1. Part 1 Header <a href="#TOC" color="blue">↑</a>',
style=styles['Heading1']))
story0.append(DelayedRef(toc, 0, '<a href="#Part1" color="blue">1. Part 1 Header</a>', 1))
lines = 0
parwidth = 595.2 - 20 - 20 # ширина текста
linesnumber = 70 # ограничение на число строк на странице
currentparnum = 0
for x in range(10):
currentparnum += 1
story0.append(Paragraph(ipsum, style=styles['Normal']))
lines = lines + len(simpleSplit(ipsum, 'Times-Roman', round(10), parwidth)) # считаем число строк, 'Times-Roman', round(10) - шрифт и его размер для используемого стиля
if lines>linesnumber:
# переход на новую страницу надо задать явно
story0.append(PageBreak())
pagenumber += 1
lines = 0
currentparnum = 0
if currentparnum == 10:
story0.append(PageBreak())
pagesforTOC.append(pagenumber)
pagenumber += 1
story0.append(Paragraph('<a name="Part2"/>2. Part 2 Header <a href="#TOC" color="blue">↑</a>',
style=styles['Heading1']))
story0.append(DelayedRef(toc, 0, '<a href="#Part2" color="blue">2. Part 2 Header</a>', 1))
story0.append(Paragraph(ipsum, style=styles['Normal']))
story0.append(Paragraph('<a name="Part21"/>2.1 Part 2.1 Header <a href="#TOC" color="blue">↑</a>',
style=styles['Heading2']))
story0.append(DelayedRef(toc, 1, '<a href="#Part21" color="blue">2.1 Part 2.1 Header</a>', 1))
lines = 0
parwidth = 595.2 - 20 - 20 # ширина текста
linesnumber = 70 # ограничение на число строк на странице
currentparnum = 0
for x in range(10):
currentparnum += 1
story0.append(Paragraph(ipsum, style=styles['Normal']))
lines = lines + len(simpleSplit(ipsum, 'Times-Roman', round(10), parwidth))
if lines>linesnumber:
story0.append(PageBreak())
pagenumber += 1
lines = 0
currentparnum = 0
if currentparnum == 10:
story0.append(PageBreak())
story0.append(PageBreak())
pagenumber += 1
pagesforTOC.append(pagenumber)
story0.append(Paragraph('<a name="Part3"/>3. Part 3 Header <a href="#TOC" color="blue">↑</a>',
style=styles['Heading1']))
story0.append(DelayedRef(toc, 0, '<a href="#Part3" color="blue">3. Part 3 Header</a>', 1))
story0 = []
print(pagesforTOC)
# конец расчета номеров страниц
xxx = 0
story = []
styles = getSampleStyleSheet()
story.append(Paragraph("The Title", style=styles['Heading1']))
story.append(Paragraph("Notes:", style=styles['Normal']))
story.append(Paragraph(ipsum, style=styles['Normal']))
story.append(PageBreak())
story.append(Paragraph('<a name="TOC"/>Table of Contents', style=styles['Heading1']))
toc = TableOfContents()
toc.levelStyles = [ParagraphStyle(fontName='Helvetica', fontSize=14, name='Heading1',leftIndent=20, firstLineIndent=-20, spaceBefore=5,leading=16),
ParagraphStyle(fontName='Times-Roman', fontSize=14, name='Heading2',leftIndent=20, firstLineIndent=-20, spaceBefore=5,leading=16)]
story.append(toc)
story.append(PageBreak())
par = Paragraph('<a name="Part1"/>1. Part 1 Header <a href="#TOC" color="blue">↑</a>', style=styles['Heading1'])
story.append(par)
story.append(DelayedRef(toc, 0, '<a href="#Part1" color="blue">1. Part 1 Header</a>', pagesforTOC[xxx]))
xxx += 1
lines = 0
parwidth = 595.2 - 20 - 20 # ширина текста
linesnumber = 70 # ограничение на число строк на странице
for x in range(10):
story.append(Paragraph(ipsum, style=styles['Normal']))
lines = lines + len(simpleSplit(ipsum, 'Times-Roman', round(10), parwidth))
if lines > linesnumber:
story.append(PageBreak())
pagenumber += 1
lines = 0
story.append(PageBreak())
story.append(Paragraph('<a name="Part2"/>2. Part 2 Header <a href="#TOC" color="blue">↑</a>', style=styles['Heading1']))
story.append(DelayedRef(toc, 0, '<a href="#Part2" color="blue">2. Part 2 Header</a>', pagesforTOC[xxx]))
story.append(Paragraph(ipsum, style=styles['Normal']))
story.append(Paragraph('<a name="Part21"/>2.1 Part 2.1 Header <a href="#TOC" color="blue">↑</a>', style=styles['Heading2']))
story.append(DelayedRef(toc,0, '<a href="#Part21" color="blue">2.1 Part 2.1 Header</a>', pagesforTOC[xxx]))
xxx += 1
lines = 0
parwidth = 595.2 - 20 - 20 # ширина текста
linesnumber = 70 # ограничение на число строк на странице
for x in range(10):
story.append(Paragraph(ipsum, style=styles['Normal']))
lines = lines + len(simpleSplit(ipsum, 'Times-Roman', round(10), parwidth))
if lines > linesnumber:
story.append(PageBreak())
pagenumber += 1
lines = 0
story.append(PageBreak())
story.append(Paragraph('<a name="Part3"/>3. Part 3 Header <a href="#TOC" color="blue">↑</a>', style=styles['Heading1']))
story.append(DelayedRef(toc, 0, '<a href="#Part3" color="blue">3. Part 3 Header</a>', pagesforTOC[xxx]))
doc.multiBuild(story, onFirstPage=addPageNumber, onLaterPages=addPageNumber)
if __name__ == '__main__':
simple_toc()
Мобильная версия