Модуль fpdf2
поддерживает автоматическое создание содержания PDF-документа, основываясь на секциях/заголовках документа, а так же может дополнительно отображать структуру документа на экране, позволяя пользователю интерактивно перемещаться от одной части документа к другой. Схема состоит из древовидной иерархии элементов схемы (иногда называемых закладками), которые служат в качестве визуального оглавления, отображающего структуру документа для пользователя.
Начиная с версии FPDF2.3.3, обе функциональности поддерживаются за счет использования метода FPDF.start_section()
, который добавляет запись во внутреннюю "схему" таблицы, которая затем используется для визуализации содержания и схемы PDF-документа.
Обратите внимание, что по умолчанию вызов FPDF.start_section()
записывает только текущую позицию в PDF и ничего не отображает. Однако можно настроить глобальные стили заголовков, вызвав метод FPDF.set_section_title_styles()
, после чего вызов FPDF.start_section()
будет визуализировать заголовки секции с использованием определенных стилей.
Чтобы отобразить схему документа для создаваемого PDF-документа, нужно просто вызвать метод FPDF.start_section()
для каждого иерархического раздела, который необходимо определить.
Если также нужно куда-то вставить оглавление, то вызовите метод FPDF.insert_toc_placeholder()
в любом месте, где необходимо его разместить. Обратите внимание, что разрыв страницы всегда будет запускаться после вставки оглавления.
from fpdf import FPDF, TitleStyle def print_text(pdf, text, **kwargs): """функция печати абзацев""" # атрибут pdf.epw возвращает ширину документа pdf.multi_cell(w=pdf.epw, h=pdf.font_size, txt=text, ln=1, **kwargs) def render_toc(pdf, outline): """Составление содержания PDF-документа""" pdf.y = 20 pdf.set_font("Sans", size=16) pdf.cell(w=pdf.epw, h=pdf.font_size, txt="Содержание:", ln=1, align="C") pdf.y += 20 pdf.set_font("Serif", size=12) for section in outline: link = pdf.add_link() pdf.set_link(link, page=section.page_number) text = f'{" " * section.level * 2} {section.name}' text += ( f' {"." * (60 - section.level*2 - len(section.name))} {section.page_number}' ) pdf.multi_cell(w=pdf.epw, h=pdf.font_size, txt=text, ln=1, link=link) pdf = FPDF() # директория где лежат системные шрифты OS Linux font_dir = '/usr/share/fonts/truetype/freefont' # добавляем TTF-шрифты, поддерживающие кириллицу. # обратите внимание курсив и жирный курсив # не добавляем, т.к. ими пользоваться не будем # шрифт FreeSerif, pdf.add_font("Serif", style="", fname=f"{font_dir}/FreeSerif.ttf", uni=True) pdf.add_font("Serif", style="B", fname=f"{font_dir}/FreeSerifBold.ttf", uni=True) # шрифт FreeSans pdf.add_font("Sans", style="", fname=f"{font_dir}/FreeSans.ttf", uni=True) pdf.add_font("Sans", style="B", fname=f"{font_dir}/FreeSansBold.ttf", uni=True) # устанавливаем по умолчанию шрифт pdf.set_font("Serif", size=13) # определение стилей заголовков pdf.set_section_title_styles( # Стиль названия секции: TitleStyle(font_family="Sans", font_style="B", font_size_pt=24, color=128, underline=True, t_margin=10, l_margin=10, b_margin=0), # Стиль названия заголовка: TitleStyle(font_family="Sans", font_style="B", font_size_pt=20, color=128, underline=True, t_margin=10, l_margin=20, b_margin=5) ) # добавляем страницу pdf.add_page() # установка курсора pdf.set_y(pdf.eph/2 - 30) # установка шрифта pdf.set_font(size=40) # печатаем Название документа print_text(pdf, "Название документа", align="C") pdf.set_font(size=12) # выводим содержание/схему # документа на новой странице pdf.add_page() pdf.insert_toc_placeholder(render_toc) ################### Текст документа ################### # это текст, который вставляется в функцию `print_text()` (Для уменьшения количества кода) text = """Также как новая модель организационной деятельности требует определения и уточнения первоочередных требований. Курс на социально-ориентированный национальный проект способствует подготовке внутренних резервов и ресурсов. Реализация намеченных плановых заданий представляет собой интересный эксперимент проверки вывода текущих активов.""" pdf.start_section("Секция 1") pdf.start_section("Заголовок 1.1", level=1) print_text(pdf, text) pdf.add_page() pdf.start_section("Заголовок 1.2", level=1) print_text(pdf, text) pdf.add_page() pdf.start_section("Секция 2") pdf.start_section("Заголовок 2.1", level=1) print_text(pdf, text) pdf.add_page() pdf.start_section("Заголовок 2.2", level=1) print_text(pdf, text) pdf.output("simple_outline.pdf")
При получении PDF-документа из HTML-разметки, используемый класс HTMLMixin
автоматически создает структура документа, которую можно вставить в оглавление с помощью специального тега <toc>
.
Пользовательский стиль оглавления может быть достигнут путем переопределения метода render_toc()
в подклассе fpdf.html.HTML2FPDF
:
from fpdf import FPDF, HTMLMixin, HTML2FPDF class MyHTML2FPDF(HTML2FPDF): # переопределяем метод составления содержания def render_toc(self, pdf, outline): pdf.cell(txt='Содержание:', ln=1) for section in outline: pdf.cell(txt=f'* {section.name} (page {section.page_number})', ln=1) class MyPDF(FPDF, HTMLMixin): # устанавливаем класс, который будет # преобразовывать HTML в PDF HTML2FPDF_CLASS = MyHTML2FPDF pdf = MyPDF() # директория где лежат системные шрифты OS Linux font_dir = '/usr/share/fonts/truetype/freefont' # добавляем TTF-шрифты, поддерживающие кириллицу. # курсив и жирный курсив не добавляем, т.к. ими пользоваться не будем pdf.add_font("Serif", style="", fname=f"{font_dir}/FreeSerif.ttf", uni=True) pdf.add_font("Serif", style="B", fname=f"{font_dir}/FreeSerifBold.ttf", uni=True) pdf.add_page() # устанавливаем добавленный шрифт по умолчанию pdf.set_font("Serif", size=12) pdf.write_html("""<toc></toc> <h1>Заголовок 1</h1> <h2>Заголовок 2</h2> <h3>Заголовок 3</h3> <h4>Заголовок 4</h4> <h5>Заголовок 5</h5> <h6>Заголовок 6</h6> <p>Параграф<p><p>Параграф<p><p>Параграф<p><p>Параграф<p>""") pdf.output("html_toc.pdf")
Обратите внимание на HTML-тег <toc pages="1"></toc>
. Атрибут pages="1"
говорит о том сколько страниц нужно пропустить вначале документа перед его контентом, чтобы уместить содержание.
from fpdf import FPDF, HTMLMixin, HTML2FPDF class MyFPDF(FPDF, HTMLMixin): pass pdf = MyFPDF() # директория где лежат системные шрифты OS Linux font_dir = '/usr/share/fonts/truetype/freefont' # добавляем TTF-шрифты, поддерживающие кириллицу. pdf.add_font("Sans", style="", fname=f"{font_dir}/FreeSans.ttf", uni=True) pdf.add_font("Sans", style="B", fname=f"{font_dir}/FreeSansBold.ttf", uni=True) pdf.add_font("Sans", style="I", fname=f"{font_dir}/FreeSansOblique.ttf", uni=True) pdf.add_font("Sans", style="BI", fname=f"{font_dir}/FreeSansBoldOblique.ttf", uni=True) # устанавливаем добавленный шрифт по умолчанию pdf.set_font("Sans", size=13) # Добавляем страницу pdf.add_page() # это абзацы, которые вставляются в html (Для уменьшения количества кода) p_tags = """ <p>Новая модель организационной деятельности требует определения и уточнения первоочередных требований.</p> <p>Курс на социально-ориентированный проект способствует подготовке резервов и ресурсов.</p> <p>Реализация намеченных плановых заданий представляет собой интересный эксперимент.</p>""" # преобразуем HTML в PDF pdf.write_html( f"""<h1>Название Документа</h1> <br><br><br> <u>Содержание:</u> <br> <toc pages="1"></toc> <h2>Заголовок 0</h2>{p_tags}<h2>Заголовок 1</h2>{p_tags}<h2>Заголовок 2</h2>{p_tags} <h2>Заголовок 3</h2>{p_tags}<h2>Заголовок 4</h2>{p_tags}<h2>Заголовок 5</h2>{p_tags} <h2>Заголовок 6</h2>{p_tags}<h2>Заголовок 7</h2>{p_tags}<h2>Заголовок 8</h2>{p_tags} <h2>Заголовок 9</h2>{p_tags}<h2>Заголовок 10</h2>{p_tags}<h2>Заголовок 11</h2>{p_tags} <h2>Заголовок 12</h2>{p_tags}<h2>Заголовок 13</h2>{p_tags}<h2>Заголовок 14</h2>{p_tags} <h2>Заголовок 15</h2>{p_tags}<h2>Заголовок 16</h2>{p_tags}<h2>Заголовок 17</h2>{p_tags} <h2>Заголовок 18</h2>{p_tags}<h2>Заголовок 19</h2>{p_tags}<h2>Заголовок 20</h2>{p_tags} <h2>Заголовок 21</h2>{p_tags}<h2>Заголовок 22</h2>{p_tags}<h2>Заголовок 23</h2>{p_tags} <h2>Заголовок 24</h2>{p_tags}<h2>Заголовок 25</h2>{p_tags}<h2>Заголовок 26</h2>{p_tags} """ ) pdf.output("html_toc_2_pages.pdf")
FPDF2
, используемым в материале.FPDF.start_section(name, level=0)
:Метод FPDF.start_section()
начинает раздел в структуре документа. Если метод FPDF.section_title_styles()
был предварительно определен и настроен, то название раздела будет выведено в виде заголовка.
Аргументы:
name
: строка, название раздела;level=0
: целое число, уровень раздела в структуре документа. По умолчанию 0 - означает верхний уровень.FPDF.insert_toc_placeholder(render_toc_function, pages=1)
:Метод FPDF.insert_toc_placeholder()
настраивает визуализацию содержания/схемы PDF-документа в конце его создания и заранее резервирует немного вертикального пространства, чтобы вставить его.
Аргументы:
render_toc_function
: функция, которая будет вызываться для визуализации содержания/схемы PDF-документа. Эта функция получит два аргумента: pdf
- экземпляр FPDF, и схему outline
, список OutlineSection
.pages=1
: целое число - количество страниц, которые будет охватывать оглавление, включая текущую страницу. После вызова этого метода произойдет столько разрывов страниц, сколько содержит аргумент pages
.FPDF.set_section_title_styles(level0, level1, level2, level3, level4, level5, level6)
:Метод FPDF.set_section_title_styles()
устанавливает стиль заголовков разделов. После вызова этого метода вызовы FPDF.start_section()
будут визуально отображать имена разделов.
Аргументы:
level0
: принимает тип TitleStyle
, определяет стиль заголовка раздела верхнего уровня;level1=None
: тип TitleStyle
, необязательный стиль для заголовков разделов уровня 1;level2=None
: тип TitleStyle
, необязательный стиль для заголовков разделов уровня 2;level3=None
: тип TitleStyle
, необязательный стиль для заголовков разделов уровня 3;level4=None
: тип TitleStyle
, необязательный стиль для заголовков разделов уровня 4;level5=None
: тип TitleStyle
, необязательный стиль для заголовков разделов уровня 5;level6=None
: тип TitleStyle
, необязательный стиль для заголовков разделов уровня 6;FPDF.TitleStyle(font_family, font_style, font_size_pt, color, underline, t_margin, l_margin, b_margin)
:Класс FPDF.TitleStyle()
встроенный тип, который задает стиль заголовков разделов.
Аргументы:
font_family=None
: необязательный str
, название шрифта;font_style=None
: необязательный str
, стиль шрифта (жирный 'B'
, курсив 'I'
, и их сочетание 'BI'
)font_size_pt=None
: необязательный, int
, размер шрифта в пунктах;color=None
: необязательный, может принимать int
или tuple
, цвет загоровка;underline=None
: необязательный bool
, подчеркивать или нет.t_margin=None
: необязательный int
, отступ сверху; l_margin=None
: необязательный int
, отступ слева;b_margin=None
: необязательный int
, отступ снизу.