Конвертация HTML/XML в PDF без потери стилей и символов

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

Чтобы качественно конвертировать HTML или XML в PDF с сохранением сложной верстки и корректным отображением кириллицы, необходимо использовать движки рендеринга на базе Chromium (например, Puppeteer) для HTML или процессоры XSL-FO (например, Apache FOP) для XML. Ключевые условия успеха: принудительное использование кодировки UTF-8, встраивание (embedding) шрифтов в итоговый файл и адаптация CSS под печатные носители через медиа-запрос @media print.

Ниже рассмотрены проверенные методы, инструменты и настройки, которые гарантируют предсказуемый результат.

Краткий ответ: Для HTML лучше всего подходит headless-браузер (Chrome/Puppeteer), так как он поддерживает современные стандарты CSS. Для строго структурированного XML используйте связку XSLT + XSL-FO + Apache FOP. Всегда встраивайте шрифты в PDF.

Основные проблемы при конвертации

Прежде чем выбирать инструмент, важно понять, где чаще всего ломается процесс:

  1. Кодировка символов. Если исходный файл не в UTF-8 или шрифт не поддерживает нужные глифы (например, кириллицу), в PDF появятся «кракозябры» или пустые квадраты.
  2. Потеря стилей. Многие легкие конвертеры игнорируют Flexbox, Grid или сложные селекторы CSS3.
  3. Разрывы страниц. Текст может обрезаться посередине строки или таблицы, если не заданы правила break-inside.
  4. Отсутствующие ресурсы. Изображения и шрифты, подключенные относительными путями, могут не загрузиться при серверной генерации.

Метод 1: Конвертация HTML в PDF (Headless Chrome / Puppeteer)

Это самый популярный способ для веб-разработчиков. Он использует тот же движок, что и браузер пользователя, поэтому визуальная точность максимальна.

Почему это работает

Headless Chrome полностью рендерит страницу, выполняя JavaScript и применяя CSS, а затем «печатает» её в PDF.

Настройка окружения

Убедитесь, что ваш HTML-документ соответствует стандартам:

  • Кодировка указана явно: <meta charset="UTF-8">.
  • Шрифты подключены корректно (лучше использовать локальные файлы или надежные CDN).

Пример конфигурации (Node.js + Puppeteer)

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  
  // Переходим на страницу или загружаем HTML из строки
  await page.goto('https://example.com/invoice.html', { waitUntil: 'networkidle0' });

  await page.pdf({
    path: 'document.pdf',
    format: 'A4',
    printBackground: true, // Важно: печатать фоновые цвета и изображения
    margin: {
      top: '20mm',
      right: '20mm',
      bottom: '20mm',
      left: '20mm'
    },
    preferCSSPageSize: true // Использовать размеры из CSS @page
  });

  await browser.close();
})();

Совет по CSS: Используйте медиа-запрос @media print для скрытия ненужных элементов (меню, футеров сайта) и настройки разрывов страниц.

@media print {
  .no-print { display: none; }
  tr { break-inside: avoid; } /* Запрет разрыва строк таблицы */
}

Метод 2: Конвертация XML в PDF (XSL-FO + Apache FOP)

Если у вас есть чистые данные в XML (например, выгрузки из 1С, банковские отчеты), прямая конвертация в HTML может быть избыточной. Стандарт индустрии здесь — XSL-FO (Formatting Objects).

Алгоритм действий

  1. XML: Исходные данные.
  2. XSLT: Трансформация XML в промежуточный формат XSL-FO (описание разметки страницы, колонок, шрифтов).
  3. FOP (Apache FOP): Процессор, который превращает FO-файл в PDF.

Преимущества

  • Полный контроль над позиционированием элементов (абсолютная точность).
  • Идеально для документов со строгой структурой (счета, акты, накладные).
  • Независимость от браузерных движков.

Настройка шрифтов в FOP

Чтобы кириллица отображалась корректно, необходимо зарегистрировать шрифты в конфигурационном файле fop.xconf:

<fonts>
  <font embed-url="fonts/arial.ttf">
    <font-triplet name="Arial" style="normal" weight="normal"/>
  </font>
  <font embed-url="fonts/arialbd.ttf">
    <font-triplet name="Arial" style="normal" weight="bold"/>
  </font>
</fonts>

Сравнение инструментов

ИнструментТип источникаКачество CSSСложность настройкиЛицензия
Puppeteer / PlaywrightHTMLОтличное (CSS3)НизкаяOpen Source
wkhtmltopdfHTMLСреднее (устарел)НизкаяOpen Source
PrinceXMLHTML/XMLИдеальное (Paged Media)СредняяКоммерческая
Apache FOPXML (FO)Специфичное (FO)ВысокаяOpen Source
WeasyPrintHTMLХорошее (CSS Paged)СредняяOpen Source

Внимание: wkhtmltopdf больше не поддерживается активно и использует старый движок WebKit. Для современных проектов рекомендуется использовать Puppeteer или WeasyPrint.

Работа с кодировками и шрифтами

Самая частая ошибка — отсутствие символов в итоговом файле. Вот чеклист для избежания проблем:

  1. Всегда используйте UTF-8. Это должно быть указано и в мета-теге HTML, и в заголовках HTTP, и в настройках процессора XML.
  2. Встраивание шрифтов (Embedding). Не надейтесь на то, что на компьютере пользователя или сервера установлен нужный шрифт.
    • В CSS: @font-face с указанием пути к файлу .ttf или .woff2.
    • В настройках конвертера: включите флаг embedFonts (или аналогичный).
  3. Проверка поддержки глифов. Убедитесь, что выбранный шрифт содержит кириллические символы. Стандартные шрифты вроде Times New Roman или Arial обычно подходят, но кастомные веб-шрифты нужно проверять.

Частые ошибки и их решения

1. Обрезанные таблицы

Проблема: Строка таблицы разрывается между страницами, часть текста исчезает. Решение: Добавьте в CSS:

table, tr, td, th {
  break-inside: avoid;
}

2. Пустые квадраты вместо текста

Проблема: Шрифт не встроился или не поддерживает язык. Решение: Проверьте логи конвертера на ошибки загрузки шрифтов. Убедитесь, что путь к шрифту абсолютный или корректно указан относительно рабочей директории.

3. Неверные отступы и поля

Проблема: Принтер добавляет свои поля поверх ваших. Решение: В настройках PDF явно задавайте margin: 0 в CSS для body и управляйте полями только через параметры инструмента (например, margin в Puppeteer) или через @page { margin: 2cm; }.

FAQ

Вопрос: Можно ли конвертировать сложный HTML с JavaScript-графиками? Да, если использовать Headless Chrome (Puppeteer/Playwright). Перед генерацией PDF нужно дождаться полной отрисовки графиков, используя waitUntil: 'networkidle0' или ожидание конкретного селектора.

Вопрос: Какой инструмент выбрать для массовой генерации счетов? Если счета формируются из XML-данных — Apache FOP или коммерческий PrinceXML (он умеет принимать и HTML, и XML). Если верстка делается на HTML/CSS — Puppeteer. PrinceXML часто предпочтительнее для типографского качества, но он платный.

Вопрос: Как уменьшить размер итогового PDF? Отключите встраивание неиспользуемых символов шрифта (subsetting). Большинство современных инструментов делают это по умолчанию, но стоит проверить настройки. Также оптимизируйте изображения перед вставкой в HTML/XML.

Вопрос: Поддерживает ли конвертация гиперссылки? Да, стандартные теги <a href="..."> сохраняются как активные ссылки в PDF при использовании любых современных конвертеров.