Конвертация HTML в PDF: от простого принта до автоматизации
Чтобы качественно конвертировать HTML в PDF, выберите метод в зависимости от задачи: для разовых документов используйте функцию «Печать» в браузере (Ctrl+P → Сохранить как PDF), а для автоматической генерации отчетов или счетов применяйте headless-браузеры (Puppeteer/Playwright) или специализированные библиотеки (WeasyPrint, PrinceXML). Ключ к успеху — правильная настройка CSS-правил @media print и управление шрифтами.
Ниже мы разберем основные способы конвертации, технические нюансы верстки под печать и способы устранения типичных ошибок.
Оглавление
Способы конвертации: какой инструмент выбрать
Выбор инструмента зависит от объема задач, требований к качеству верстки и необходимости автоматизации.
1. Браузерный вывод (Ручной режим)
Самый простой способ для сохранения одной страницы.
- Как использовать: Откройте страницу в Chrome, Edge или Firefox, нажмите
Ctrl+P(илиCmd+Pна Mac) и выберите «Сохранить как PDF». - Плюсы: Не требует установки ПО, поддерживает современный CSS и JavaScript.
- Минусы: Невозможно автоматизировать массовую конвертацию; результаты могут отличаться в разных браузерах.
2. Headless-браузеры (Puppeteer, Playwright)
Стандарт индустрии для разработчиков. Эти инструменты запускают браузер без графического интерфейса и программно сохраняют страницу.
- Для кого: Для веб-приложений, генерирующих счета, билеты или отчеты.
- Плюсы: Идеальный рендеринг (так как используется реальный движок Chrome/WebKit), полный контроль над JS и ожиданием загрузки контента.
- Минусы: Требует ресурсов сервера, сложнее в настройке окружения.
3. Специализированные библиотеки (WeasyPrint, wkhtmltopdf, PrinceXML)
Утилиты, созданные специально для преобразования HTML/CSS в PDF.
- WeasyPrint (Python): Отлично работает с современным CSS, легковесный.
- PrinceXML: Коммерческое решение с лучшей поддержкой сложной типографики и стандартов Paged Media.
- wkhtmltopdf: Устаревшее решение на базе старого Qt WebKit. Не рекомендуется для новых проектов из-за плохой поддержки современного CSS (Flexbox/Grid).
Подготовка HTML и CSS для печати
PDF — это статический формат, поэтому динамические элементы веб-страницы нужно адаптировать.
Использование @media print
Все стили, специфичные для PDF, следует оборачивать в медиа-запрос. Это позволит скрыть ненужные элементы (меню, футеры, кнопки) и оптимизировать текст.
@media print {
/* Скрываем навигацию и рекламу */
nav, .ads, .no-print {
display: none !important;
}
/* Настраиваем тело документа */
body {
font-size: 12pt;
color: #000;
background: #fff;
}
/* Гарантируем разрывы страниц там, где нужно */
h1, h2, h3 {
page-break-after: avoid;
}
table {
page-break-inside: auto;
}
tr {
page-break-inside: avoid;
page-break-after: auto;
}
}
Настройка размеров страницы через @page
Чтобы контролировать поля и размер листа, используйте правило @page:
@page {
size: A4;
margin: 2cm;
}
Если вы используете Puppeteer, настройки полей можно передавать программно в методе page.pdf(), что имеет приоритет над CSS-стилями.
Частые ошибки и их решения
Даже при использовании мощных инструментов конвертация часто ломается из-за нюансов верстки.
1. Обрезанный контент на разрывах страниц
Проблема: Текст или изображение разрезается посередине при переходе на новую страницу.
Решение: Используйте свойства break-inside, page-break-inside и orphans/widows.
.card, .table-row {
break-inside: avoid; /* Запрещает разрыв элемента */
}
p {
orphans: 3; /* Минимум строк внизу страницы */
widows: 3; /* Минимум строк вверху новой страницы */
}
2. Отсутствие шрифтов или кракозябры
Проблема: В PDF используются стандартные шрифты вместо кастомных, или символы отображаются некорректно. Причина: Шрифты не успели загрузиться или путь к ним недоступен для рендерера. Решение:
- Используйте абсолютные пути к файлам шрифтов.
- В Puppeteer дожидайтесь события загрузки шрифтов:
await page.evaluateHandle('document.fonts.ready'). - Для WeasyPrint убедитесь, что шрифты установлены в системе или указаны корректные URL.
3. Пустой PDF или отсутствие JS-контента
Проблема: Данные, подгружаемые через AJAX/Fetch, не попали в файл. Причина: Конвертация началась раньше, чем завершился запрос к API. Решение:
- В headless-браузерах используйте явные ожидания (
waitForSelector,waitForNetworkIdle). - Не полагайтесь только на
DOMContentLoaded, так как он не ждет асинхронных данных.
4. Проблемы с фоновыми цветами и изображениями
Проблема: Фоны элементов исчезли в PDF. Причина: Браузеры по умолчанию экономят краску и не печатают фоны. Решение:
- В CSS добавьте:
-webkit-print-color-adjust: exact; print-color-adjust: exact;. - В настройках Puppeteer включите флаг
printBackground: true.
Сравнение популярных инструментов
| Инструмент | Язык/Среда | Качество CSS | Сложность настройки | Стоимость |
|---|---|---|---|---|
| Chrome Print | Любой (UI) | Высокое | Низкая | Бесплатно |
| Puppeteer | Node.js | Высокое | Средняя | Бесплатно |
| Playwright | Node/Py/.NET | Высокое | Средняя | Бесплатно |
| WeasyPrint | Python | Хорошее | Низкая | Бесплатно (AGPL) |
| PrinceXML | CLI/API | Отличное | Низкая | Платная лицензия |
| wkhtmltopdf | CLI | Устаревшее | Низкая | Бесплатно |
Избегайте использования wkhtmltopdf для современных проектов. Он не поддерживает CSS Grid, Flexbox и многие современные свойства, что приводит к сломанной верстке.
FAQ
Как уменьшить размер итогового PDF-файла? Оптимизируйте изображения перед вставкой в HTML (используйте JPEG/WebP вместо PNG для фото). В Puppeteer можно понизить качество вывода, хотя это влияет на четкость текста. Удаляйте неиспользуемые шрифты и оставляйте только необходимые начертания.
Можно ли добавить колонтитулы (header/footer)?
Да. В Puppeteer это делается через параметры headerTemplate и footerTemplate, куда передается HTML-разметка. В CSS для принтеров можно использовать @top-center, @bottom-right внутри правила @page, но поддержка этого свойства варьируется в разных рендерерах.
Как конвертировать HTML-строку, а не URL?
В Puppeteer используйте page.setContent(htmlString). В WeasyPrint передавайте строку напрямую в конструктор HTML(string=html_content). При этом убедитесь, что относительные пути к ресурсам (картинкам, стилям) заменены на абсолютные или base64.
Безопасно ли использовать онлайн-конвертеры? Для публичных данных — да. Для персональных данных, счетов или внутренней документации — нет. Вы отправляете данные на чужой сервер. Всегда используйте локальные библиотеки или собственные серверные решения для конфиденциальной информации.