Генерация PDF в проектах на Python: от простого к сложному

Раздел: Работа с документами -> Работа с PDF

Основные подходы к созданию PDF на Python

Какое решение позволяет быстро и гибко создавать PDF с текстом, изображениями и таблицами без внешних зависимостей?

Наиболее эффективным решением для создания PDF с нуля является библиотека FPDF2 (форк оригинального FPDF). Она не требует внешних программ, поддерживает Unicode через TrueType шрифты и предоставляет простой API для добавления текста, изображений, таблиц и линий.

Установка

pip install fpdf2

Python создание pdf (создание pdf на python)

Пример создания простого PDF

from fpdf import FPDF

pdf = FPDF()
pdf.add_page()
pdf.set_font('Arial', 'B', 16)
pdf.cell(40, 10, 'Привет, мир!')
pdf.output('hello.pdf')

Пояснение шагов

  1. Создаётся экземпляр класса FPDF.
  2. Метод add_page() добавляет новую страницу.
  3. set_font() задаёт шрифт (имя, стиль, размер). Для кириллицы используется шрифт Arial (его необходимо добавить отдельно, но встроенный Helvetica не поддерживает русские буквы).
  4. cell() выводит текст в ячейке. Параметры: ширина, высота, текст.
  5. output() сохраняет документ.

Работа с кириллицей

Для корректного отображения русского текста нужно добавить TrueType шрифт с поддержкой кириллицы, например DejaVuSans:

pdf.add_font('DejaVu', '', 'DejaVuSans.ttf', uni=True)
pdf.set_font('DejaVu', '', 14)

Добавление изображения и таблицы

pdf.image('logo.png', x=10, y=8, w=30)
pdf.set_font('DejaVu', '', 10)
for row in [['Имя', 'Возраст'], ['Анна', '25'], ['Иван', '30']]:
    for col in row:
        pdf.cell(40, 10, col, border=1)
    pdf.ln()

Типичные проблемы и ошибки:

  • Ошибка: FPDF error: Unsupported font type: /Type1 при использовании встроенного шрифта. Решение: добавить TTF шрифт с помощью add_font().
  • Проблема: текст не помещается в ячейку. Решение: использовать multi_cell() для автоматического переноса.
  • Ошибка: UnicodeEncodeError при записи. Решение: убедиться, что установлен шрифт с Unicode и параметр uni=True.

Вариант 1: ReportLab - сложная графика и точное позиционирование

Как создать PDF с точным позиционированием элементов, векторной графикой и диаграммами?

Библиотека ReportLab предоставляет низкоуровневый API для рисования линий, прямоугольников, текста и изображений с абсолютными координатами. Она идеальна для профессиональных отчётов, накладных и графиков.

from reportlab.pdfgen import canvas
c = canvas.Canvas('report.pdf')
c.drawString(100, 750, 'Заголовок отчёта')
c.line(50, 740, 550, 740)
c.drawImage('logo.png', 50, 700, width=100, height=50)
c.save()

Проблемы:

  • Необходимость ручного расчёта координат Y (от нижнего края).
  • Ошибка AttributeError: module 'reportlab' has no attribute 'graphics' - нужно импортировать from reportlab.graphics.shapes import Drawing.

Вариант 2: pdfkit - конвертация HTML в PDF

Как превратить веб-страницу или HTML-шаблон в PDF с сохранением стилей?

pdfkit является обёрткой для утилиты wkhtmltopdf. Он принимает HTML строку или URL и возвращает PDF. Удобен для генерации документов на основе шаблонов.

import pdfkit
pdfkit.from_string('<h1>Привет</h1><p>Текст</p>', 'output.pdf')
pdfkit.from_url('http://example.com', 'page.pdf')

Типичные ошибки:

  • Не установлен wkhtmltopdf - скачать с официального сайта и добавить в PATH.
  • Русские символы отображаются кракозябрами - указать опцию --encoding UTF-8.

Вариант 3: WeasyPrint - современный HTML+CSS в PDF

Как конвертировать HTML с CSS3 (flexbox, grid) в PDF без внешних зависимостей?

WeasyPrint использует собственный движок рендеринга, поддерживает продвинутые CSS-свойства и не требует wkhtmltopdf. Подходит для сложных дизайнов.

from weasyprint import HTML
HTML(string='<html><body><h1>Документ</h1></body></html>').write_pdf('output.pdf')
HTML(filename='template.html').write_pdf('result.pdf')

Проблемы:

  • Медленная работа при большом количестве страниц.
  • Некоторые CSS-свойства (например, @page с полями) могут игнорироваться.

Расширенные примеры создания PDF

Ниже приведены практические примеры, демонстрирующие возможности различных библиотек.

Пример 1: Многостраничный документ с колонтитулами (FPDF2)

Создание документа с нумерацией страниц и повторяющимся верхним и нижним колонтитулом.

Пример
from fpdf import FPDF

class CustomPDF(FPDF):
    def header(self):
        self.set_font('DejaVu', 'B', 12)
        self.cell(0, 10, 'Отчёт компании', 0, 1, 'C')
    def footer(self):
        self.set_y(-15)
        self.set_font('DejaVu', 'I', 8)
        self.cell(0, 10, f'Страница {self.page_no()}/{{nb}}', 0, 0, 'C')

pdf = CustomPDF()
pdf.alias_nb_pages()
pdf.add_font('DejaVu', '', 'DejaVuSans.ttf', uni=True)
pdf.add_font('DejaVu', 'B', 'DejaVuSans-Bold.ttf', uni=True)
pdf.add_font('DejaVu', 'I', 'DejaVuSans-Oblique.ttf', uni=True)

for i in range(1, 4):
    pdf.add_page()
    pdf.set_font('DejaVu', '', 12)
    pdf.cell(0, 10, f'Содержимое страницы {i}', 0, 1)

pdf.output('document_with_header_footer.pdf')
Результат: PDF-файл из 3 страниц, на каждой странице вверху выводится текст "Отчёт компании", внизу - номер страницы и общее количество.

Пример 2: Создание PDF с таблицей, автоматическим переносом текста (FPDF2)

Пример
from fpdf import FPDF

pdf = FPDF()
pdf.add_page()
pdf.add_font('DejaVu', '', 'DejaVuSans.ttf', uni=True)
pdf.set_font('DejaVu', '', 10)

# Заголовки
headers = ['Имя', 'Описание', 'Цена']
col_widths = [40, 100, 30]
for i, header in enumerate(headers):
    pdf.cell(col_widths[i], 10, header, border=1, align='C')
pdf.ln()

# Данные
data = [
    ['Товар A', 'Длинное описание, которое не помещается в одну строку', '100'],
    ['Товар B', 'Краткое описание', '250'],
]
for row in data:
    for i, cell_text in enumerate(row):
        pdf.cell(col_widths[i], 10, cell_text, border=1)
    pdf.ln()

pdf.output('table.pdf')
Результат: Таблица с тремя колонками, текст в ячейках обрезается по ширине - для переноса нужна multi_cell.

Для переноса текста внутри ячейки используется multi_cell() с последующим выравниванием высоты строк.

Пример 3: График на PDF с помощью ReportLab

Пример
from reportlab.pdfgen import canvas
from reportlab.graphics.shapes import Drawing, Line, Circle, String
from reportlab.graphics import renderPDF

d = Drawing(400, 200)
d.add(Line(50, 50, 350, 150))  # линия тренда
d.add(Circle(100, 70, 5, fillColor='red'))  # точка данных
d.add(String(150, 100, 'Продажи', fontName='Helvetica', fontSize=12))

c = canvas.Canvas('chart.pdf')
renderPDF.draw(d, c, 50, 400)
c.save()
Результат: PDF-страница с простым графиком: линия, точка и текст. Можно добавлять сложные диаграммы через reportlab.graphics.charts.

Пример 4: Объединение нескольких PDF в один (PyPDF2)

Пример
from PyPDF2 import PdfMerger

merger = PdfMerger()
merger.append('file1.pdf')
merger.append('file2.pdf')
merger.write('merged.pdf')
merger.close()
Результат: Файл merged.pdf содержит страницы из file1.pdf и file2.pdf в порядке добавления.

Пример 5: Добавление водяного знака (ReportLab + PyPDF2)

Пример
from reportlab.pdfgen import canvas
from PyPDF2 import PdfReader, PdfWriter

# Создание водяного знака
c = canvas.Canvas('watermark.pdf')
c.setFont('Helvetica', 60)
c.setFillColorRGB(0.5, 0.5, 0.5, 0.3)
c.rotate(45)
c.drawString(100, 100, 'Черновик')
c.save()

# Наложение на существующий PDF
reader = PdfReader('original.pdf')
watermark = PdfReader('watermark.pdf').pages[0]
writer = PdfWriter()
for page in reader.pages:
    page.merge_page(watermark)
    writer.add_page(page)
with open('watermarked.pdf', 'wb') as f:
    writer.write(f)
Результат: На каждой странице исходного документа появляется полупрозрачная надпись "Черновик" под углом 45 градусов.

Пример 6: Защита PDF паролем (pypdf)

Пример
from pypdf import PdfReader, PdfWriter

reader = PdfReader('document.pdf')
writer = PdfWriter()
for page in reader.pages:
    writer.add_page(page)

writer.encrypt('user_password', 'owner_password')
with open('protected.pdf', 'wb') as f:
    writer.write(f)
Результат: PDF-файл, открытие которого требует пароль пользователя, а владелец может изменять права.

Создание PDF на Python - comments

En
Python создание pdf (python)