Конвертация HTML в PDF: от простого принта до автоматизации

Иван Корнев·27.05.2026·4 мин

Чтобы качественно конвертировать 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)ВысокоеНизкаяБесплатно
PuppeteerNode.jsВысокоеСредняяБесплатно
PlaywrightNode/Py/.NETВысокоеСредняяБесплатно
WeasyPrintPythonХорошееНизкаяБесплатно (AGPL)
PrinceXMLCLI/APIОтличноеНизкаяПлатная лицензия
wkhtmltopdfCLIУстаревшееНизкаяБесплатно

Избегайте использования 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.

Безопасно ли использовать онлайн-конвертеры? Для публичных данных — да. Для персональных данных, счетов или внутренней документации — нет. Вы отправляете данные на чужой сервер. Всегда используйте локальные библиотеки или собственные серверные решения для конфиденциальной информации.