HTMLPDF API превращает HTML-код, веб-страницы и архивы с CSS, изображениями, шрифтами и сценариями в PDF, позволяя задать формат листа, поля, ориентацию, колонтитулы, оглавление, ссылки и параметры рендеринга. Через живую демонстрацию можно править разметку, сразу проверять результат в области Preview и экспортировать документ, а через REST-запросы — автоматизировать выпуск счетов, отчётов, договоров и других печатных форм.
Рабочий процесс строится вокруг трёх источников: адреса доступной страницы, строки с готовой разметкой или файла с проектом. В запросе выбирают только один источник, дополняют его параметрами печати и получают бинарный PDF в ответе. Когда документ формируется дольше обычного, результат можно принять через callback; часто используемые логотипы, таблицы стилей и шрифты разрешено заранее разместить в хранилище Assets и обращаться к ним из шаблона через специальный путь.
Для первого теста удобнее открыть Live Demo: слева доступен код примера, рядом — предварительный просмотр, а команда Export to PDF запускает преобразование. Демонстрация полезна для проверки полей формы, таблиц, фоновых изображений, подключённых шрифтов и JavaScript до переноса шаблона в серверный код. После визуальной проверки те же входные данные отправляют на конечную точку PDF, передавая токен в заголовке Authentication.
Открыть HTMLPDF API
- Редактирование PDF
- Русский интерфейс
- Просто новичкам
- CSS3 поддерживается частично
- Загрузка ресурсов до 30 с
- Расход кредитов по объёму
Как устроена живая демонстрация

Страница Live Demo служит испытательным стендом для шаблонов. Над рабочей областью расположен список Load example: в нём можно выбрать простую заготовку, короткий Hello World, информационную страницу, сложный счёт или пример с веб-шрифтами. Переключатель Code показывает исходную разметку, Preview — то, как её интерпретирует встроенный браузер, а Export to PDF передаёт содержимое конвертеру. Такой порядок помогает отделить ошибку HTML от ошибки печатных параметров: сначала добиваются правильного вида в Preview, затем оценивают пагинацию уже в PDF.
Редактирование выполняется прямо в поле кода. Для интерактивных примеров изменение значения в форме отражается в предварительном просмотре, поэтому можно проверить вычисления итогов, подстановку реквизитов и поведение сценариев. Следует помнить, что Preview показывает экранное представление, тогда как при включённом use_print_media_type конвертер выбирает правила media=print. Если дизайн меняется только после экспорта, сравнивают оба набора CSS и убирают конфликтующие селекторы.
Экспорт из демонстрации подходит для единичной проверки, но не заменяет интеграцию. Он не формирует код приложения, не управляет очередью и не хранит шаблоны как бизнес-объекты. Зато стенд быстро выявляет несовместимую CSS3-конструкцию, не загруженный шрифт, неверную ширину таблицы или слишком ранний запуск JavaScript. Проверенный код затем переносят в запрос html либо помещают вместе с ресурсами в архив.
Три способа передать исходный документ
Преобразование страницы по адресу
Параметр url применяют, когда исходная страница уже доступна конвертеру по HTTP или HTTPS. Сервис сам загружает HTML, связанные таблицы стилей, изображения, сценарии и шрифты, после чего печатает отрендеренную страницу. Одновременно с url нельзя отправлять html или file: наличие двух источников считается ошибкой запроса. Для закрытой страницы предусмотрены username и password, которые используются для базовой HTTP-аутентификации источника, а не для авторизации в самой API.
Перед преобразованием адреса полезно проверить его из внешней сети без браузерных cookie. Страница, которая открывается только после интерактивного входа, редиректа через форму или выполнения сложного клиентского обмена токенами, может вернуть экран авторизации вместо отчёта. В таких случаях надёжнее сформировать полный HTML на своей стороне, встроить нужные данные и передать его строкой либо архивом. Для ресурсов, защищённых отдельными заголовками, проще заменить ссылки на заранее загруженные Assets.
Передача HTML-строки
Параметр html удобен для документов, которые собираются шаблонизатором приложения: счетов, актов, уведомлений, билетов и договоров. Максимальный размер HTML-строки ограничен 3 МБ. Внутри можно использовать встроенный CSS, Base64-изображения, SVG и ссылки на доступные ресурсы. Чтобы кириллица не превратилась в нечитаемые символы, документ должен содержать корректную кодировку, а параметр encoding обычно оставляют равным utf-8.
Строковый режим минимизирует количество файлов в запросе, но требует аккуратной экранировки. При отправке application/x-www-form-urlencoded знаки амперсанда, плюса и процента могут быть интерпретированы как служебные. Для крупной разметки безопаснее multipart/form-data или библиотека HTTP-клиента, которая сама кодирует поле. Перед запросом стоит сохранить сгенерированную строку в тестовый HTML-файл и открыть её локально: так обнаруживаются незакрытые теги и неверные пути ещё до расходования кредитов.
HTML-файл и архив с ресурсами
Через file принимаются html, htm, zip, tar.gz, tgz и tar.bz2. Архив нужен, когда шаблон использует отдельные CSS-файлы, изображения, JavaScript и шрифты. Общий предел составляет 100 МБ. После распаковки конвертер выбирает первый файл с расширением html или htm в алфавитном порядке, поэтому главную страницу лучше называть явно, например 00-index.html, и не оставлять рядом тестовые копии вроде draft.html.
Все относительные ссылки внутри проекта должны соответствовать структуре архива. Если index.html обращается к css/print.css и images/logo.png, эти пути обязаны существовать после распаковки без выхода на уровень выше. Регистр символов также важен: ссылка Logo.PNG не найдёт файл logo.png в среде с чувствительной файловой системой. Перед отправкой архив распаковывают в пустую папку и открывают выбранный HTML; если браузер показывает пропавшие ресурсы, API воспроизведёт ту же проблему.
Авторизация и структура запроса
Каждый запрос к API должен содержать заголовок Authentication со значением Token, пробелом и персональной строкой токена. Это не стандартный заголовок Authorization, поэтому автоматическая схема Bearer здесь не подходит. Ошибка в имени заголовка, отсутствие пробела или лишние кавычки приводят к ответу 401. Токен нельзя размещать в клиентском JavaScript публичной страницы: любой посетитель сможет извлечь его и расходовать кредиты. Запросы выполняют с сервера приложения, из защищённой функции или из закрытой системы автоматизации.
curl -H 'Authentication: Token $HTMLPDF_TOKEN' \
-d 'html=<html><body><h2>Счёт</h2></body></html>' \
-d 'filename=invoice.pdf' \
-d 'page_size=A4' \
-d 'margin_top=12mm' \
"$API_BASE/pdf" -o invoice.pdf
Основная конечная точка принимает POST и возвращает PDF как бинарное тело. Параметр filename задаёт имя, указанное в Content-Disposition, а disposition переключает attachment и inline. В серверном коде нужно проверять не только HTTP-статус, но и Content-Type: если вместо application/pdf пришёл JSON или текст ошибки, нельзя сохранять его с расширением PDF. Полезно также проверять первые байты результата на сигнатуру %PDF и не считать пустой ответ успешным.
Для учёта подразделений и клиентов предусмотрен параметр group. Название группы заранее создают в административной части, затем передают при каждой конвертации. Это не меняет содержимое документа, но позволяет разнести расход по проектам. Если указана неизвестная группа, запрос отклоняется, поэтому имя лучше хранить как конфигурационную константу и проверять при развёртывании приложения.
Настройка листа, полей и масштаба
Для стандартной печати достаточно page_size, orientation и четырёх полей margin_top, margin_right, margin_bottom, margin_left. По умолчанию используется A4 и книжная ориентация. Альбомный режим полезен для широких таблиц, но не исправляет переполнение автоматически: если контент шире печатной области, потребуется уменьшить CSS-ширины, включить перенос текста или подобрать zoom. Пользовательские page_width и page_height применяют вместо стандартного формата, когда нужен ярлык, чек или нестандартный бланк.
Размеры полей передаются с единицами, обычно в миллиметрах. Поля также резервируют место для колонтитулов. Если header или footer налезает на основной текст, увеличивают margin_top или margin_bottom и отдельно задают header_spacing либо footer_spacing. Уменьшение отступа колонтитула не расширяет полезную область листа автоматически; печатную геометрию проверяют на первой, средней и последней странице, где высота содержимого различается.
Параметр zoom масштабирует всю страницу, а smart_shrinking разрешает движку уменьшать содержимое, чтобы оно поместилось по ширине. Автоматическое сжатие удобно для неизвестных страниц, но может дать разный кегль в документах одной серии. Для корпоративных шаблонов предсказуемее зафиксировать ширину макета в CSS, подобрать viewport_size и оставить zoom постоянным. DPI влияет на расчёт и качество, но не заменяет исходное разрешение изображений.
Параметры dpi, image_dpi и image_quality отвечают за разные этапы. dpi задаёт базовое разрешение рендеринга в допустимом диапазоне, image_dpi ограничивает разрешение встроенных изображений, а image_quality управляет их JPEG-сжатием. Режим lowquality уменьшает размер файла ценой качества. Для счетов с текстом и небольшим логотипом лучше начать со стандартных значений; для каталога с фотографиями измеряют размер PDF и визуальные артефакты на нескольких страницах, а не повышают все показатели одновременно.

CSS для печати и управление разрывами страниц
Движок полностью ориентирован на CSS2 и частично поддерживает CSS3. Это означает, что традиционные блочные и табличные макеты обычно воспроизводятся стабильнее, чем современные сетки с Grid, сложными вариантами Flexbox, фильтрами и новыми функциями вычисления размеров. Шаблон, предназначенный для веб-приложения, не всегда стоит печатать без адаптации. Практичнее создать отдельный слой печатных правил: фиксировать ширины, отключать анимацию, убирать липкие панели и заменять интерактивные компоненты статическими представлениями.
При use_print_media_type=true выбираются таблицы стилей для печати; без этого сервис использует экранный режим. Разницу важно учитывать при локальном тесте: браузерная команда печати должна быть открыта с тем же media-режимом. Для принудительного переноса применяют page-break-before и page-break-after со значением always, а для сохранения блока целиком — page-break-inside: avoid. Последнее не является абсолютной гарантией, если элемент физически выше страницы.
Табличные строки движок старается не разрывать, однако сложная таблица с вложенными блоками, изображениями и rowspan может выйти за границу листа. Надёжный приём — делить большой отчёт на несколько таблиц, повторять заголовок через thead и ставить контролируемый разрыв между логическими группами. Изображение внутри ячейки полезно оборачивать в блочный контейнер с запретом внутреннего разрыва. Для длинного описания задают перенос слов и не используют фиксированную высоту строки.
Абсолютное позиционирование годится для небольших элементов бланка, но опасно для переменной длины текста. Если адрес клиента занимает три строки вместо одной, все следующие элементы могут наложиться друг на друга. Для динамических документов основную структуру строят потоком, а абсолютные координаты оставляют для декоративного фона, штампа или номера формы. Перед выпуском проверяют максимальные реальные значения полей, а не только короткий демонстрационный набор.

Шрифты, SVG, Base64 и удалённые ресурсы
Поддерживаются шрифты OTF, TTF и WOFF, а также кернинг. Шрифт можно подключить внешним адресом, вложить в архив, закодировать в Base64 или разместить в Assets. Последние два варианта уменьшают зависимость от стороннего сервера. Для кириллицы необходимо убедиться, что выбранный файл действительно содержит нужные глифы; название семейства в font-family должно совпадать с объявлением @font-face. Если шрифт не загрузился, движок подставит системный аналог, из-за чего изменятся переносы и пагинация.
SVG подходит для логотипов, диаграмм и векторных пиктограмм. Встроенный SVG обычно надёжнее внешнего файла, потому что не требует дополнительного сетевого запроса. Сложные SVG с фильтрами, масками и современными CSS-свойствами следует проверять отдельно: частичная поддержка CSS3 распространяется и на оформление векторной графики. Для критичного графика можно заранее преобразовать его в упрощённый SVG или качественный PNG, сохранив подписи как текст в HTML.
Base64 полезен для небольшого логотипа или значка, но увеличивает объём HTML примерно на треть и приближает строку к лимиту 3 МБ. Большие фотографии лучше держать в архиве либо Assets. Внешние изображения должны отвечать достаточно быстро: ожидание удалённых ресурсов ограничено примерно 30 секундами. Если один медленный домен задерживает весь документ, ресурс переносят ближе к шаблону, уменьшают файл или заменяют его локальной копией.
Хранилище Assets
Assets предназначено для постоянно используемых файлов. Через API можно получить список, создать ресурс, обновить его по имени, скачать по идентификатору и удалить. Принимаются JS, CSS, PNG, JPG, JPEG, GIF, TTF, OTF и WOFF. После загрузки ресурс ссылается из шаблона через {{assets_path}}, что удобно для единого логотипа, фирменного шрифта и общей таблицы стилей. Обновление файла с прежним именем позволяет менять брендирование без пересборки каждого архива.
Хранилище не следует превращать в базу индивидуальных вложений. Оно лучше подходит для стабильных ресурсов, которые используются во многих документах. Имена делают уникальными и предсказуемыми, например company-logo-v2.png, поскольку дубликат может вызвать конфликт. После замены критичного шрифта выполняют контрольную конвертацию: одинаковое имя не гарантирует одинаковые метрики символов, а значит, строки и страницы могут перераспределиться.
JavaScript, задержка и размер окна
JavaScript разрешён параметром javascript. Он нужен для вычисления итогов, построения диаграмм и заполнения DOM после загрузки. javascript_delay задаёт паузу перед снимком страницы; допустимый диапазон составляет от 1 до 800 мс, а базовое значение — 200 мс. Это небольшое окно, поэтому шаблон не должен ждать длительный сетевой запрос. Данные лучше вставлять в HTML заранее, а сценарий использовать только для быстрой локальной отрисовки.
Если график появляется в браузере, но отсутствует в PDF, сначала проверяют, завершает ли библиотека построение в пределах задержки. Затем исключают внешние запросы, анимацию появления и ленивую загрузку. Для Chart.js или другой библиотеки полезно отключить animation, задать фиксированные размеры canvas и разместить данные непосредственно в скрипте. Увеличение задержки до максимума оправдано только после оптимизации: оно замедляет каждую конвертацию и не исправит запрос, который занимает несколько секунд.
viewport_size задаёт виртуальное окно в формате ширинаxвысота, по умолчанию 800x600. От него зависят медиазапросы и адаптивный макет. Если страница переключается в мобильную колонку или скрывает таблицу, указывают размер, соответствующий настольному представлению, например 1280x800. Высота окна не равна высоте PDF: документ всё равно разбивается на страницы. Важнее ширина, потому что она выбирает ветку responsive CSS.
Для стабильного результата отключают sticky и fixed-элементы, которые рассчитаны на прокрутку. Фиксированная панель может повторяться на каждой странице и перекрывать текст. Если повторение действительно требуется, лучше использовать header и footer API. Модальные окна, cookie-баннеры и загрузочные скелетоны удаляют из печатной версии через CSS. Скрипт должен закончить изменения до рендеринга и не оставлять бесконечный индикатор.

Колонтитулы, номера страниц и оглавление
Параметры header и footer принимают самостоятельный HTML. В них можно использовать переменные {{page}}, {{pages}}, {{webpage}}, {{title}}, {{section}}, {{subsection}} и {{subsubsection}}. Колонтитул должен быть полноценным HTML-документом с doctype, а стили лучше размещать внутри него: оформление основной страницы не всегда доступно в отдельном контексте. Для номера страницы достаточно вывести текущую и общую величину, но место под двузначные и трёхзначные номера резервируют заранее.
Переменные section и вложенные варианты получают значения из заголовков документа. Они полезны в инструкции или каталоге, где в верхней строке нужно показывать текущую главу. Если структура построена не заголовками, значения могут оставаться пустыми. В этом случае раздел передают прямо в HTML колонтитула при формировании запроса. Логотип в header делают небольшим и подключают из Assets или Base64, чтобы его загрузка не зависела от внешнего узла.
outline=true создаёт навигационное дерево PDF по HTML-заголовкам, а outline_depth ограничивает глубину, по умолчанию до четвёртого уровня. Чтобы оглавление было осмысленным, уровни не пропускают: после h2 используют h3, а не h5. Служебные заголовки, которые не должны попадать в дерево, лучше оформить другим элементом. Параметр title задаёт метаданные PDF и может отличаться от видимого заголовка страницы.
internal_links сохраняет переходы по якорям внутри документа, external_links — кликабельность внешних адресов. Для публичного отчёта обе возможности полезны, но в архивной копии внешние ссылки иногда отключают. page_offset изменяет начальный номер, что удобно, если PDF является частью более крупного комплекта. Визуальный номер в footer и логическая нумерация должны использовать одну схему, иначе пользователь увидит на странице одно значение, а в навигации другое.
Создание изображения вместо PDF
Конечная точка image использует те же три источника — url, html или file — и возвращает растровое изображение. Выходной формат выбирается расширением filename: PNG, JPG или GIF. Для JPEG доступен quality от 0 до 100. Параметры width и height задают размер, а поля crop позволяют вырезать область. Этот режим применяют для миниатюры отчёта, превью счёта, изображения для письма или визуального контроля шаблона.
Растровый результат не заменяет PDF, когда нужен поиск по тексту, масштабирование без потери качества, оглавление и многостраничная структура. Зато изображение удобно сравнивать в автоматическом тесте: эталонный кадр и новый результат анализируют попиксельно. При таком тестировании фиксируют viewport, шрифты, данные и задержку JavaScript, иначе небольшие различия среды будут выглядеть как ошибка шаблона.
Обрезка полезна только при заранее известной геометрии. Для динамического содержимого фиксированный crop может отсечь сумму, подпись или последнюю строку. Сначала формируют полный кадр, измеряют фактическую область и лишь затем вводят координаты. Если требуется несколько фрагментов одной страницы, выгоднее получить единый PNG и выполнить локальную нарезку, чем повторно расходовать запросы на каждый участок.
Callback и длительные операции
Параметр callback переводит получение результата в асинхронный сценарий. Начальный ответ сообщает, что документ обрабатывается, а готовый файл позже отправляется POST-запросом на указанный адрес в multipart-поле file. Обработчик должен быть доступен извне, принимать бинарный файл и быстро возвращать успешный статус. Нельзя предполагать, что callback придёт в той же сессии пользователя или на тот же сервер приложения.
Для сопоставления результата с заказом в callback-адрес добавляют непредсказуемый идентификатор операции или сохраняют соответствие адреса в базе. Сам адрес защищают длинным секретом и проверкой ожидаемого Content-Type. Обработчик сначала записывает файл во временное место, проверяет сигнатуру PDF, размер и бизнес-идентификатор, затем атомарно переносит его в постоянное хранилище. Повторная доставка не должна создавать дубликаты.
Асинхронный режим особенно полезен для отчётов с множеством изображений и для пакетной генерации. Пользовательский запрос не держат открытым: интерфейс показывает состояние готовится, а отдельный процесс отмечает документ готовым после callback. Для потерянного уведомления предусматривают тайм-аут и повторную постановку задачи с идемпотентным ключом. Слепой бесконечный повтор опасен, потому что каждая успешная повторная конвертация снова расходует кредиты.
Кредиты, группы и контроль расхода
Количество оставшихся кредитов можно получить отдельным GET-запросом credits. Один кредит соответствует каждой начатой половине мегабайта готового PDF, поэтому файл размером чуть больше 0,5 МБ требует уже два кредита. На расход влияют встроенные фотографии, фоновые изображения, дублированные шрифты и слишком высокое качество JPEG. HTML малого размера не гарантирует дешёвый результат: учитывается сформированный PDF.
Новые учётные записи получают тестовый запас кредитов без обязательной банковской карты. Купленные кредиты имеют срок действия, а следующая покупка продлевает срок всего остатка. В производственной системе полезно ежедневно сохранять баланс, рассчитывать среднее потребление и предупреждать до достижения критического уровня. Проверку выполняют до запуска большой очереди, но не перед каждым документом, чтобы не создавать лишнюю нагрузку.
Группы позволяют видеть расход по клиенту, продукту или окружению. Например, production-invoices отделяют от staging-tests, а внутри агентства каждому заказчику назначают своё имя. Это помогает найти шаблон, который внезапно начал создавать слишком тяжёлые файлы. Группа не является механизмом доступа: один токен по-прежнему управляет всей учётной записью, поэтому для независимых клиентов лучше использовать отдельные политики и не передавать им общий секрет.
Ограничение частоты составляет 12 запросов в секунду на IP. Очередь должна соблюдать этот предел с запасом, учитывать повторные попытки и не выпускать резкий пакет после перезапуска. Для равномерного потока применяют token bucket или фиксированное число рабочих процессов. При ответе о превышении лимита задачу откладывают с увеличивающейся паузой и случайной добавкой, а не отправляют немедленно снова.
Коды ошибок и что они означают
| Код | Ситуация | Действие |
|---|---|---|
| 400 | Нет источника, передано несколько источников, неверный архив, группа или ресурс | Исправить структуру и параметры запроса |
| 401 | Токен отсутствует или записан неверно | Проверить заголовок Authentication |
| 402 | Недостаточно кредитов | Пополнить баланс или сократить размер результата |
| 403 | Учётная запись не активирована | Завершить активацию доступа |
| 409 | Конфликт имени Asset | Обновить существующий файл либо выбрать другое имя |
| 410 | Запрошенный Asset не найден | Обновить идентификатор или повторно загрузить ресурс |
| 413 | HTML или архив превышает лимит | Уменьшить исходные данные |
| 500 | Внутренняя ошибка обработки | Повторить с задержкой и сохранить диагностический запрос |
| 502 | Страница или ресурс недоступны | Проверить адрес, статус и внешнюю доступность |
| 504 | Ресурсы не загрузились за отведённое время | Ускорить или локализовать зависимости |
| 511 | Источник требует сетевой авторизации | Передать корректные данные доступа или HTML |
Ответ 400 требует анализа тела, потому что одним кодом обозначаются разные ошибки клиента. При автоматической обработке сохраняют безопасную копию параметров без токена и персональных данных. Ошибки 401, 402 и 403 не следует повторять автоматически: без изменения учётных данных или баланса результат будет тем же. Ошибки 500, 502 и 504 можно повторить ограниченное число раз, но только после паузы.
Если сервер вернул 200, это ещё не повод считать документ корректным. Проверяют Content-Type, сигнатуру, ненулевой размер и по возможности количество страниц. В системах с финансовыми документами дополнительно извлекают контрольные строки — номер счёта, итог и дату — из готового PDF. Такой тест выявляет ситуацию, когда успешно напечаталась страница входа, сообщение об ошибке источника или пустой шаблон.

Практический сценарий: автоматический счёт
Шаблон счёта удобно хранить как HTML с маркерами данных. Сервер получает заказ, рассчитывает позиции и налоги, экранирует пользовательские значения и подставляет их в таблицу. Логотип и шрифт размещают в Assets, а CSS фиксирует ширины столбцов, выравнивание денежных сумм и запрет разрыва строки позиции. Перед отправкой проверяют, что итог в HTML совпадает с расчётом в базе; JavaScript не должен быть единственным местом, где вычисляется сумма.
Для каждого документа задают понятный filename, A4, поля, footer с номером страницы и group клиента. Если список позиций длинный, thead повторяется на новых страницах, а блок с итогами защищается от внутреннего разрыва. Примечание и банковские реквизиты не фиксируют у нижней границы абсолютными координатами: они должны перейти на следующую страницу, если таблица заняла больше места.
После ответа файл проверяют и сохраняют под внутренним идентификатором, а имя из Content-Disposition используют только для выдачи пользователю. Одновременно записывают хеш, размер, число кредитов по приблизительной формуле и параметры шаблона. Если счёт формируется повторно, система должна понимать, создаётся ли новая редакция или возвращается уже утверждённый файл. Идемпотентность защищает от двойного расхода при сетевом тайм-ауте.
Официальные примеры показывают несколько вариантов одной задачи: строгую монохромную сетку, цветные строки и компактную композицию с крупным итогом. Они полезны как проверка возможностей таблиц, шрифтов и фоновых элементов, но производственный шаблон надо испытывать на реальных длинных названиях товаров, отрицательных скидках, нулевых ставках и многострочных адресах.

Практический сценарий: отчёт с диаграммами
Для отчёта данные агрегируют до создания HTML. Таблицы строят сервером, а графики — встроенным SVG либо JavaScript-библиотекой с отключённой анимацией. Каждый график получает фиксированную ширину и высоту; легенда не должна зависеть от наведения курсора. Цвета дополняют подписями и узорами, чтобы смысл сохранялся при чёрно-белой печати. Внешний запрос к аналитическому API во время рендеринга исключают.
Сводная страница обычно содержит заголовок периода, ключевые показатели и краткие выводы, а подробные таблицы начинаются с контролируемого разрыва. Для альбомных таблиц можно сформировать отдельный PDF, потому что ориентация задаётся для всего запроса. Если в одном отчёте нужны разные форматы листов, части создают отдельно и объединяют уже после конвертации другим PDF-инструментом.
График проверяют не только визуально, но и по данным: подписи осей, масштаб и сумма категорий должны соответствовать исходному набору. При пустом периоде шаблон выводит ясное сообщение вместо пустого canvas. Для воспроизводимости сохраняют версию HTML-шаблона и набор параметров. Изменение viewport или шрифта способно сдвинуть подписи и перенести блок на другую страницу даже при тех же данных.

Практический сценарий: договор или инструкция
Длинный текстовый документ строят семантическими заголовками и абзацами, чтобы outline сформировал навигационное дерево. Разделы получают устойчивые якоря, внутренние ссылки ведут к приложениям и определениям. Колонтитул показывает название текущей главы и номер страницы. Для подписного блока предусматривают минимальную высоту и запрет внутреннего разрыва, но не пытаются удержать вместе раздел, который может оказаться длиннее листа.
Юридически значимые значения подставляют как текст, а не как изображение. Пользовательские данные экранируют, чтобы фрагмент вроде не разрушил таблицу. Пустые необязательные пункты удаляют вместе с заголовком, иначе в документе останутся незаполненные секции. Перед выпуском система сравнивает контрольные поля с записью в базе и фиксирует SHA-256 готового PDF.
HTMLPDF API формирует файл, но не выполняет квалифицированную электронную подпись, редактирование страниц или распознавание сканов. Подписание, штампование, объединение приложений и защита паролем организуются отдельным этапом. Границу ответственности важно учитывать: ошибка в бизнес-данных должна блокироваться до конвертации, а ошибка визуального рендеринга — обнаруживаться тестом шаблона.
Интеграция с серверными языками
API не привязана к конкретному языку: нужен HTTP-клиент, умеющий отправить заголовок, поля формы и файл. В PHP используют cURL или PSR-совместимый клиент, в Python — requests либо httpx, в Node.js — fetch, axios или стандартный модуль, в Java и .NET — штатные HTTP-клиенты. Критично включить потоковую запись результата, чтобы крупный PDF не копировался несколько раз в памяти.
Клиентскую обёртку полезно разделить на четыре уровня: подготовка параметров, отправка, проверка ответа и сохранение результата. Метод генерации принимает только один тип источника и валидирует это до сети. Токен считывается из секретного хранилища, в журнал попадают статус, длительность, группа и размер, но не полный HTML с персональными данными. Ошибка преобразуется в типизированное исключение с исходным HTTP-кодом.
Тайм-аут соединения и общий тайм-аут задают отдельно. Короткий connect timeout быстро обнаруживает сетевую проблему, а read timeout должен учитывать сложность рендеринга. Повторы разрешают только для ошибок, которые могут быть временными, и сопровождают идемпотентной бизнес-логикой. При callback основной метод возвращает идентификатор задачи, а при синхронной работе — поток PDF и проверенные метаданные.
# Псевдокод серверного адаптера
assert exactly_one(url, html, file)
headers = {"Authentication": "Token " + secret_token}
response = post(pdf_endpoint, headers, fields, timeout)
assert response.status == 200
assert response.content_type == "application/pdf"
assert response.body.starts_with("%PDF")
save_atomically(response.body)
Тесты адаптера не должны каждый раз обращаться к платной конвертации. HTTP-ответы 400, 401, 402, 413, 502 и успешный PDF имитируют локально, а небольшой интеграционный тест запускают по расписанию с отдельной группой. Эталонный документ содержит кириллицу, SVG, таблицу, перенос страницы, колонтитул и внешний ресурс. Так одна проверка покрывает наиболее чувствительные участки.
Безопасность данных и секретов
Передаваемый HTML и данные для PDF автоматически удаляются после обработки в течение короткого периода, указанного сервисом. Тем не менее приложение должно отправлять только сведения, необходимые для документа. Секреты базы, служебные комментарии шаблона и скрытые поля формы удаляют до запроса. Если документ содержит особо чувствительную информацию, оценивают требования организации к внешней обработке и региону хранения.
Токен хранится вне исходного кода и не включается в URL. Переменная окружения лучше жёстко записанной строки, но для производственной системы предпочтительно централизованное хранилище секретов с ограничением доступа. При подозрении на утечку токен заменяют, а журналы проверяют по группам и расходу. Пример cURL в тикете или чате всегда должен содержать маску, а не рабочее значение.
Исходный HTML может содержать пользовательский текст, поэтому шаблонизатор обязан экранировать его. Иначе злоумышленник вставит тег, удалённое изображение или сценарий, который выполнится в среде рендера. Для полей, где действительно разрешена разметка, используют белый список элементов и атрибутов. Внешние адреса изображений и ссылок валидируют отдельно, чтобы не превращать конвертер в средство обращения к нежелательным сетевым ресурсам.
Готовый PDF также проверяют перед публикацией. Метаданные title не должны раскрывать внутренний номер проекта, внешние ссылки — вести на тестовые домены, а вложенные шрифты — нарушать лицензию. Если файл передаётся по публичной ссылке, контроль доступа реализуется в хранилище приложения. API генерирует документ, но не управляет правами конечного получателя.
Производительность и пакетная обработка
Время конвертации складывается из загрузки источника, получения ресурсов, выполнения JavaScript и печати. Самый предсказуемый вариант — HTML или архив без внешних зависимостей. Веб-шрифты, аналитические скрипты и изображения с разных доменов увеличивают разброс. Для пакетной очереди измеряют медиану и высокий процентиль, а не только среднее: несколько медленных документов способны удерживать рабочих процессов больше обычного.
Параллелизм ограничивают как лимитом запросов, так и доступными кредитами. Двенадцать запросов в секунду не означают, что нужно постоянно отправлять максимум: тяжёлые документы могут накапливаться. Очередь выпускает фиксированное количество задач, отслеживает продолжительность и снижает скорость при росте ошибок. Короткие счета и тяжёлые отчёты полезно разделить, чтобы один класс не блокировал другой.
Кэширование допустимо, когда одинаковый набор данных должен дать одинаковый документ. Ключ включает идентификатор шаблона, нормализованные данные и параметры печати. Нельзя кэшировать только по номеру заказа, если сумма или адрес могут измениться. После обновления логотипа или шрифта версия Assets тоже должна попасть в ключ, иначе приложение вернёт файл со старым оформлением.
Размер результата оптимизируют у источника. Фотографии уменьшают до реального печатного разрешения, повторяющиеся изображения выносят в общий ресурс, ненужные начертания шрифта не подключают, фоновые текстуры исключают. lowquality применяют только после визуальной проверки. Сжатие не должно разрушать штрихкоды, мелкие подписи и тонкие линии таблицы.
Сравнение HTMLPDF API с аналогами
| Программа | Лучше подходит для | Главное ограничение |
|---|---|---|
| HTMLPDF API | Автоматизации HTML, URL и архивов с Assets и группами | CSS3 поддерживается не полностью |
| PDF Commander | Ручного редактирования, сборки и оформления готовых PDF | Нет REST-конвейера HTML в PDF |
| PDFShift | Проектов, которым нужен рендеринг на Chromium | Облачная обработка и API-лимиты |
| DocRaptor | Сложной печатной вёрстки и CSS Paged Media | Требует тщательной подготовки print CSS |
| Gotenberg | Самостоятельного серверного контура в Docker | Нужно администрировать инфраструктуру |
| WeasyPrint | Python-проектов со статическим HTML и печатным CSS | Не выполняет JavaScript |
HTMLPDF API разумно выбирать, когда уже есть HTML-шаблоны, нужны URL, архивы, постоянные Assets и учёт расхода по группам. PDFShift подходит команде, которая зависит от современного Chromium-рендеринга. DocRaptor предпочтителен для издательской пагинации, сносок и сложных правил печати. Gotenberg даёт контроль над собственным контуром, но требует сопровождения контейнеров, а WeasyPrint удобен для Python и статической разметки без клиентских сценариев. PDF Commander нужен на другом этапе — когда человек вручную правит, объединяет или оформляет уже полученные PDF.
При выборе сначала составляют эталон из самой трудной страницы: кириллица, фирменный шрифт, широкая таблица, SVG, график, колонтитул и несколько разрывов. Один и тот же HTML прогоняют через кандидатов и сравнивают не только внешний вид, но и размер, скорость, кликабельность ссылок, доступность текста и поведение при ошибке ресурса. Название движка само по себе не гарантирует соответствия конкретному шаблону.
Диагностика типовых сбоев
Ниже собраны проверки, привязанные к входам, параметрам и ограничениям HTMLPDF API. Их удобно выполнять в указанном порядке: сначала подтвердить корректность источника и ресурсов, затем печатную геометрию, после этого — сетевую доставку и учёт результата.
PDF пустой, хотя HTML непустой
Причина. Чаще всего корневой контейнер скрыт экранными стилями, весь текст окрашен в цвет фона или JavaScript оставляет страницу в состоянии загрузки. Исправление. Откройте исходник с печатным media-режимом, временно отключите сценарии, добавьте видимую рамку body и проверьте, что контент находится в обычном потоке. Затем возвращайте правила по одному. В ответе проверьте сигнатуру PDF: пустой визуально документ всё равно может быть технически корректным файлом. Проверка. Контрольный шаблон с одним абзацем должен напечататься с теми же параметрами; если он работает, проблема находится в разметке, а не в токене или конечной точке.
Вместо отчёта напечаталась форма входа
Причина. Источник по url зависит от cookie, интерактивной авторизации или перенаправления, которого у сервиса нет. Исправление. Используйте username и password только для HTTP Basic, а для сложной авторизации сформируйте HTML на своём сервере. Все закрытые изображения перенесите в архив или Assets. Не передавайте пользовательскую сессию в URL и не пытайтесь вставить токен приложения в видимый код. Проверка. Сделайте внешний запрос к странице без cookie и убедитесь, что он сразу возвращает содержимое отчёта, а не HTML формы.
Кириллица отображается квадратами
Причина. Выбранный шрифт не содержит кириллических глифов, файл шрифта недоступен или кодировка документа определена неверно. Исправление. Укажите utf-8, добавьте meta charset, подключите проверенный TTF, OTF или WOFF через архив либо Assets и задайте одинаковое имя семейства в @font-face и font-family. Не полагайтесь на шрифт, установленный только на компьютере разработчика. Проверка. В тестовую строку включите русские буквы, цифры, знаки валют и длинное тире; сравните извлечённый текст готового PDF с исходной строкой.
Шрифт подменился и изменил пагинацию
Причина. Ресурс загружается после печати, адрес отвечает перенаправлением или формат не распознан. Исправление. Сделайте шрифт локальным для конвертации, исключите CSS, который переопределяет семейство, и проверьте сетевую доступность. Не подключайте набор из десятка начертаний, если используются два: это увеличивает размер и создаёт больше точек отказа. Проверка. Измерьте ширину контрольной фразы и число страниц. Совпадение только визуального названия семейства недостаточно.
Таблица выходит за правое поле
Причина. Сумма фиксированных ширин, padding и border больше печатной области, либо длинная строка не переносится. Исправление. Используйте table-layout: fixed, задайте ширину 100%, включите перенос длинных идентификаторов и уменьшите горизонтальные отступы. Если данные действительно требуют ширины, выберите landscape, но не маскируйте проблему чрезмерным zoom. Проверка. Проверьте строку с самым длинным артикулом и максимальным денежным значением; короткий демонстрационный набор не выявляет переполнение.
Заголовок таблицы не повторяется
Причина. Структура построена div-элементами или строки заголовка находятся в tbody. Исправление. Используйте семантические table, thead и tbody. Избегайте transform у таблицы и вложенных таблиц без необходимости. Если движок всё равно нестабилен на сложном макете, разбейте данные на отдельные таблицы с явными заголовками. Проверка. Сформируйте минимум три страницы и проверьте начало каждой, а не только разрыв между первой и второй.
Строка таблицы разрезана между страницами
Причина. Высота строки больше доступного остатка либо внутри ячейки находятся элементы с противоречивым позиционированием. Исправление. Добавьте page-break-inside: avoid к строке или внутреннему блоку, удалите фиксированную высоту и абсолютные элементы. Если строка выше целой страницы, разделите содержание на логические части — запрет разрыва физически выполнить невозможно. Проверка. Тестируйте строку с максимальным описанием и несколькими изображениями.
Колонтитул перекрывает текст
Причина. Под него не зарезервировано достаточное верхнее или нижнее поле, а spacing трактуется отдельно от основного отступа. Исправление. Увеличьте соответствующий margin, задайте header_spacing или footer_spacing и уменьшите высоту разметки колонтитула. Уберите внешние поля body внутри header HTML, если они неожиданно увеличивают блок. Проверка. Проверьте первую и последнюю страницы: разные значения переменных и переносы могут менять высоту строки.
Номер страницы не подставляется
Причина. Переменная написана с ошибкой или footer передан не как полноценный HTML-документ. Исправление. Используйте поддерживаемые маркеры {{page}} и {{pages}}, добавьте doctype, html и body, а стили поместите внутрь. Не экранируйте фигурные скобки шаблонизатором приложения; при необходимости выводите их как литералы до отправки. Проверка. Сформируйте двухстраничный документ и убедитесь, что обе переменные меняются ожидаемо.
Оглавление содержит лишние пункты
Причина. Декоративные подписи оформлены заголовочными тегами и попадают в outline. Исправление. Оставьте h2–h6 только для структурных разделов, а визуальные подписи оформите классом на div или p. Уменьшите outline_depth, если подробные уровни не нужны. Не нарушайте иерархию заголовков ради размера шрифта. Проверка. Просмотрите дерево закладок отдельно от визуального содержания и проверьте переход к каждому пункту.
Внутренняя ссылка ведёт не туда
Причина. Идентификатор повторяется, содержит нестабильные символы или элемент перемещён при сборке шаблона. Исправление. Генерируйте уникальные ASCII-id, проверяйте соответствие href и id и не дублируйте фрагменты с одинаковыми идентификаторами. Оставьте internal_links включённым. Для автоматического теста извлеките аннотации PDF и сопоставьте их с ожидаемыми разделами. Проверка. Проверьте ссылки после окончательной пагинации, потому что вставка раздела меняет номера страниц.
Фоновые цвета исчезли
Причина. Параметр background выключен или печатный CSS сбрасывает background. Исправление. Включите background и проверьте правила media=print. Для важных различий не полагайтесь только на фон: добавьте границы, подписи или пиктограммы. Большая полноформатная текстура резко увеличивает размер результата. Проверка. Сравните цветной и чёрно-белый просмотр, чтобы таблица оставалась понятной в обоих случаях.
SVG отображается частично
Причина. Вектор использует фильтры, маски или CSS3-функции, которые движок поддерживает не полностью. Исправление. Упростите SVG, перенесите основные атрибуты непосредственно на элементы, удалите анимацию и внешние зависимости. Для критичной иллюстрации создайте совместимую статическую версию. Не превращайте весь текст диаграммы в кривые без необходимости: это ухудшит доступность. Проверка. Проверьте линии, подписи, градиенты и прозрачность на реальном масштабе печати.
Canvas-график не успевает построиться
Причина. Сценарий выполняется после javascript_delay, ждёт сеть или использует анимацию. Исправление. Передайте данные сразу в HTML, отключите animation, зафиксируйте размеры canvas и увеличьте delay в пределах 1–800 мс. Событие window load не всегда означает завершение сторонней библиотеки, поэтому рендер должен быть синхронным и коротким. Проверка. Создайте контрольную метку в DOM после построения и убедитесь, что она присутствует в момент печати.
Страница стала мобильной
Причина. viewport_size попал под медиазапрос, скрывающий десктопную таблицу или меняющий навигацию. Исправление. Установите ширину окна, соответствующую макету, и создайте отдельные print-правила, не зависящие от устройства. Уберите элементы управления, которые нужны только на экране. Не увеличивайте page_width вместо исправления responsive CSS. Проверка. Проверьте Preview и PDF с одинаковой шириной окна, затем повторите тест на граничном значении медиазапроса.
Фиксированная панель повторяется на каждом листе
Причина. position: fixed воспринимается как элемент печатной страницы. Исправление. Скройте панель в print CSS либо замените её на штатный header/footer. То же относится к cookie-баннеру, плавающей кнопке и индикатору чата. Для элементов, которые должны появиться один раз, используйте обычный поток. Проверка. Просмотрите не менее трёх страниц, потому что повтор может быть незаметен на коротком документе.
Изображение размыто
Причина. Исходник слишком мал, image_quality снижен или браузер растягивает картинку выше естественного размера. Исправление. Подготовьте изображение под физический размер печати, не увеличивайте CSS-ширину сверх разрешения и проверьте image_dpi. Для логотипа предпочтителен SVG, для фотографии — качественный JPEG. Повышение общего dpi не восстановит отсутствующие детали. Проверка. Оцените файл при масштабе 100% и на пробной печати, а не только при сильном увеличении в просмотрщике.
PDF стал слишком тяжёлым
Причина. Встроены полноразмерные фотографии, несколько копий шрифтов или Base64-дубли. Исправление. Уменьшите изображения до нужного размера, подключайте каждый шрифт один раз, используйте Assets для повторяемых ресурсов и подберите image_quality. lowquality применяйте после сравнения мелкого текста и графики. Удалите скрытые изображения, которые остаются в DOM. Проверка. Сравните вклад каждого ресурса, последовательно исключая фотографии, шрифты и фон; так причина определяется точнее, чем случайным снижением качества.
Архив принят, но открылась не та страница
Причина. Конвертер выбрал первый HTML-файл по алфавиту. Исправление. Назовите точку входа 00-index.html или оставьте в архиве единственный HTML верхнего уровня. Удалите резервные копии, файлы документации и примеры. Зафиксируйте структуру архива автоматическим тестом перед отправкой. Проверка. После распаковки отсортируйте список HTML тем же способом и убедитесь, что первым является нужный файл.
Ресурсы в архиве не находятся
Причина. Относительный путь, регистр или глубина каталога не совпадает с разметкой. Исправление. Распакуйте проект в пустую папку, откройте точку входа и проверьте консоль браузера. Используйте прямые относительные пути без выхода через .., одинаковый регистр и безопасные имена. Не ссылайтесь на локальные пути компьютера. Проверка. Автоматическая проверка должна пройти по src и href и подтвердить наличие каждого локального файла.
Asset не обновляется
Причина. Загружен файл с другим именем либо шаблон продолжает ссылаться на прежний ресурс. Исправление. Проверьте список Assets, идентификатор и имя, затем выполните PUT для существующего имени или обновите ссылку. Добавьте версию к имени, если кэширование мешает мгновенной замене. После изменения очистите кэш готовых PDF. Проверка. Сделайте документ с заметной контрольной меткой в ресурсе и сравните его хеш с предыдущим.
При загрузке Asset получен 409
Причина. Файл с таким именем уже существует. Исправление. Решите, требуется обновление или отдельная версия. Для обновления используйте соответствующий метод, для параллельного ресурса — уникальное имя. Не удаляйте старый файл до проверки всех шаблонов, которые на него ссылаются. Проверка. Получите список ресурсов и подтвердите, что новый шаблон обращается к ожидаемому имени.
Источник отвечает 511
Причина. Удалённая страница требует сетевой аутентификации, которую запрос не прошёл. Исправление. Если используется Basic Auth, передайте username и password. Для других схем создайте HTML на своей стороне или откройте защищённый одноразовый адрес с коротким сроком жизни. Не помещайте постоянный секрет в строку запроса. Проверка. Проверьте адрес из чистого HTTP-клиента без сессии и убедитесь, что возвращается сам документ.
Получен 504 при внешних ресурсах
Причина. Один или несколько файлов не загрузились примерно за 30 секунд. Исправление. Найдите медленный домен, локализуйте ресурс в архиве или Assets, уменьшите файл и удалите необязательные счётчики. CDN должен отдавать прямой ответ без длинной цепочки перенаправлений. Не лечите сетевой тайм-аут увеличением javascript_delay. Проверка. Измерьте каждый внешний ресурс отдельно и повторите конвертацию без него, чтобы подтвердить причинную связь.
Ответ 200 сохранён как повреждённый PDF
Причина. Приложение записало текст, промежуточный ответ или обрезало поток. Исправление. Проверяйте Content-Type, сигнатуру %PDF, Content-Length и фактический размер. Записывайте бинарные байты, а не строку в кодировке. При потоковой передаче закрывайте файл только после полного чтения и используйте атомарное переименование. Проверка. Откройте файл независимым парсером и извлеките количество страниц до выдачи пользователю.
Callback приходит повторно
Причина. Сеть или отправитель повторяет уведомление после неясного подтверждения. Исправление. Сделайте обработчик идемпотентным: идентификатор задачи должен сопоставляться с единственной записью, а одинаковый хеш — не создавать вторую копию. Сначала сохраняйте во временный файл, затем отмечайте операцию завершённой в транзакции и быстро отвечайте успехом. Проверка. Повторите один и тот же тестовый POST дважды и убедитесь, что статус и ссылка на документ не дублируются.
Callback не удаётся сопоставить с заказом
Причина. В адресе нет идентификатора, а имя файла недостаточно уникально. Исправление. Добавьте в callback URL случайный идентификатор задачи и сохраните его до отправки. Не доверяйте пользовательскому filename как ключу. Обработчик должен проверять, что задача ожидает результат и не была отменена. Проверка. Создайте две параллельные операции с одинаковым именем файла и подтвердите правильное распределение.
Баланс кредитов падает быстрее ожиданий
Причина. PDF перешёл через границу очередных 0,5 МБ, запросы повторяются или шаблон содержит тяжёлые ресурсы. Исправление. Записывайте размер каждого результата, число попыток и группу. Найдите документы рядом с границей, оптимизируйте изображения и исключите автоматические повторы после фактического успеха. Проверяйте очередь на дубликаты по бизнес-ключу. Проверка. Сопоставьте изменение баланса с журналом успешных конвертаций за тот же период.
Пакет получает ошибки частоты
Причина. Рабочие процессы одновременно превышают 12 запросов в секунду с одного IP. Исправление. Вынесите ограничение в общий диспетчер, а не задавайте его отдельно каждому процессу. Используйте token bucket, небольшую случайную задержку и exponential backoff. После простоя не выпускайте всю накопленную очередь одним рывком. Проверка. Нагрузочный тест должен измерять число стартов за скользящую секунду, включая повторы.
Результат отличается между тестом и боевой средой
Причина. Различаются данные, viewport, print media, версии Assets или порядок загрузки ресурсов. Исправление. Сохраняйте полный набор параметров, идентификатор шаблона и хеш ресурсов. Сравните сформированный HTML до сети, а затем исключайте различия по одному. Не объясняйте расхождение только браузером разработчика: Live Demo и API должны получать одинаковый вход. Проверка. Воспроизведите боевую задачу в отдельной группе с обезличенными данными и теми же параметрами.
PDF открывается, но текст нельзя искать
Причина. Часть содержания превращена в canvas или изображение, либо шрифт встроен нестандартно. Исправление. Оставляйте текст HTML-текстом, а графику — SVG или изображением. Не делайте скриншот всей страницы через конечную точку image, если нужен документ. Проверьте шрифт и извлечение текста независимой библиотекой. Проверка. Автотест ищет в готовом PDF номер документа и фамилию; отсутствие строки считается дефектом.
Сумма на экране и в PDF различается
Причина. JavaScript пересчитывает данные асинхронно или использует локаль, отличающуюся от серверной. Исправление. Вычисляйте финансовые значения на сервере и передавайте готовые строки. Сценарий может форматировать представление, но не быть источником истины. Зафиксируйте валюту, правила округления и разделители. Уберите зависимость от часового пояса клиента. Проверка. Извлеките итог из PDF и сравните с записью заказа до отправки документа.
Разные документы получают одинаковый filename
Причина. Имя задано статически как out.pdf и перезаписывается в хранилище приложения. Исправление. Формируйте безопасное имя из типа, номера и даты, но сохраняйте файл по внутреннему уникальному ключу. Content-Disposition предназначен для пользователя, а не для обеспечения уникальности на сервере. Очищайте запрещённые символы. Проверка. Параллельный тест с десятью заданиями не должен потерять ни одного результата.
Длинный документ обрывается в конце
Причина. Приложение преждевременно закрывает поток, источник лениво подгружает разделы или CSS скрывает переполнение. Исправление. Уберите виртуализацию и lazy loading, проверьте overflow у контейнеров и дождитесь завершения записи ответа. Для URL-источника все разделы должны присутствовать в DOM без прокрутки. Сверьте число ожидаемых записей с текстом PDF. Проверка. Контрольная последняя строка и итоговое число элементов должны извлекаться из готового файла.
Печатается экранный баннер cookie
Причина. Страница по URL показывает его новому посетителю, а print CSS не скрывает блок. Исправление. Лучше передать очищенный HTML. Если используется URL, добавьте печатное правило для баннера и убедитесь, что согласие не требуется для загрузки критичных ресурсов. Не пытайтесь автоматизировать клик сложным скриптом в коротком окне рендера. Проверка. Откройте страницу в чистой сессии и проверьте, что баннер не перекрывает содержимое при печати.
Контрольный чек-лист перед запуском
- В запросе присутствует ровно один источник: url, html или file.
- Токен передаётся только в заголовке Authentication и не попадает в журнал.
- HTML использует UTF-8, а выбранные шрифты содержат кириллицу.
- Архив распаковывается без ошибок, и первым HTML по алфавиту является точка входа.
- Все относительные ресурсы существуют с точным регистром имени.
- Viewport и media-режим соответствуют печатному шаблону.
- Поля учитывают высоту header и footer, номера страниц проверены на длинном документе.
- JavaScript не зависит от медленной сети и завершается в пределах задержки.
- Таблицы проверены на самых длинных значениях и нескольких страницах.
- Ответ валидируется по статусу, Content-Type, сигнатуре, размеру и контрольному тексту.
- Повторы ограничены, а сохранение результата идемпотентно.
- Группа, баланс и размер PDF записываются для анализа расхода.
После прохождения чек-листа создают небольшой эталонный набор: короткий счёт, многостраничную таблицу, отчёт с SVG и документ с колонтитулами. Эти файлы генерируют после изменения CSS, шрифтов, Assets или кода HTTP-клиента. Визуальное сравнение дополняют проверкой извлечённого текста и ссылок, потому что одинаковый внешний вид не гарантирует сохранения структуры PDF.
Проверка печатного эталона
Эталон должен содержать не только красивую первую страницу, но и длинную таблицу, разрыв внутри раздела, кириллицу, SVG, внешний и внутренний переход, колонтитул, страницу без данных и страницу с максимальным объёмом. После изменения шаблона сравнивают геометрию ключевых блоков, число страниц, размер файла и извлечённые строки. Разница фиксируется как ожидаемая только после ручного подтверждения.
Наблюдаемость очереди
Для каждой задачи записывают время постановки, начало запроса, длительность ответа, HTTP-код, группу, размер PDF и число попыток. Отдельные метрики показывают долю 502 и 504, средний вес результата и остаток кредитов. Токен, полный HTML и персональные данные в техническую телеметрию не попадают. По этим показателям видно, замедлился ли источник, вырос ли документ или нарушился лимит частоты.
Разделение тестовых и боевых задач
Тесты направляют в отдельную группу и используют обезличенные данные. Это упрощает анализ расхода и исключает смешение контрольных документов с клиентскими. Интеграционный тест не запускают на каждый коммит: основную логику проверяют моками, а реальную конвертацию выполняют после изменений рендера и по расписанию. Результат хранится ограниченное время и сравнивается с эталоном.
Организация библиотеки шаблонов
Шаблон лучше хранить не как единственную строку в коде контроллера, а как набор версионируемых файлов: HTML-структуру, печатную таблицу стилей, локальные изображения и данные для теста. Сборщик подставляет значения, удаляет необязательные секции и выдаёт один из трёх допустимых источников для запроса. При изменении макета номер версии шаблона записывают рядом с готовым PDF; тогда можно восстановить, почему старый счёт выглядит иначе, и повторно создать документ с теми же правилами.
Общие элементы — реквизиты, строка итогов, колонтитул, таблица позиций — оформляют повторно используемыми частями, но перед отправкой получают цельный HTML. Вложенные сетевые шаблоны во время конвертации не подгружают: это добавляет тайм-ауты и делает результат зависимым от доступности внутреннего сервера. У каждой части есть тест с длинными значениями, пустыми полями и кириллицей. Изменение общего компонента запускает генерацию всех эталонных документов, а не только одного красивого примера.
Для разных типов документов создают отдельные профили параметров. Счёт может использовать A4, компактные поля и footer с номером; отчёт — альбомную ориентацию и больший viewport; договор — outline и внутренние ссылки. Профиль валидируется до запроса: неизвестное поле, два источника или недопустимое значение javascript_delay останавливают задачу. Такой слой не позволяет отдельному разработчику случайно отключить фон, выбрать неверный формат листа или передать токен в теле.
Проверка доступности и структуры готового PDF
Визуальное совпадение не является единственным критерием. Текст в готовом файле должен извлекаться в логическом порядке, ссылки — оставаться кликабельными, а заголовки — формировать понятное дерево закладок. Табличный макет иногда выглядит правильно, но при копировании перемешивает колонки; это важно для поиска, архивирования и последующей обработки. Контрольный тест извлекает номер документа, дату, имя получателя и итог, а также проверяет наличие ожидаемых ссылочных аннотаций.
Изображения получают содержательный альтернативный текст в исходном HTML, но степень переноса семантики зависит от рендера. Критичные сведения нельзя помещать только в цветную диаграмму или декоративный значок. Под графиком добавляют текстовый вывод, в таблице используют явные заголовки, а порядок блоков в DOM соответствует чтению сверху вниз. Если доступность является обязательным нормативным требованием, готовый PDF дополнительно проверяют специализированным валидатором и при необходимости обрабатывают после генерации.
При сравнении эталонов допускают небольшое отличие сглаживания, но не изменение количества страниц, обрезку текста, исчезновение глифа или перемещение итогового блока. Автоматический визуальный diff дополняют зонами интереса: номер, сумма, таблица и колонтитул проверяются строже фона. Новое отличие подтверждает человек, после чего эталон обновляется вместе с причиной изменения. Это защищает от незаметной подмены шрифта или ресурса Assets.
Переход от ручной печати к API-конвейеру
Страницу, которую раньше сохраняли командой браузера, сначала очищают от навигации, cookie-баннеров, кнопок и элементов, зависящих от прокрутки. Затем фиксируют print CSS и проверяют страницу на нескольких наборах данных. Только после этого автоматизируют запрос. Попытка сразу печатать произвольный интерфейс по url обычно приводит к мобильной компоновке, повторяющейся панели, неполным ленивым спискам и нестабильным диаграммам.
На первом этапе синхронный запрос проще отлаживать: приложение получает файл и сразу проверяет его. Когда шаблон стабилен и объём растёт, тяжёлые документы переводят на callback и очередь. Пользователь видит состояние задачи, а рабочий процесс хранит число попыток и причину последней ошибки. Важно не смешивать ошибку формирования данных с временной сетевой ошибкой: первая требует исправления заказа или шаблона, вторая допускает ограниченный повтор.
Ручной контроль сохраняют для исключений. Оператору показывают не исходный HTML, а готовый PDF и ключевые данные из заказа. Если требуется добавить страницу, скрыть конфиденциальный фрагмент или подписать файл, это выполняется отдельным инструментом после конвертации и фиксируется как новая операция. Исходный шаблон не меняют ради единичного документа, иначе следующий автоматический выпуск получит непредсказуемое отличие.
Параметры, которые следует фиксировать в журнале
Для воспроизводимости записывают тип источника, имя шаблона, его хеш, group, page_size, пользовательские размеры, orientation, поля, zoom, dpi, print media, viewport, состояние JavaScript, задержку, параметры колонтитулов и outline. Полный HTML с персональными данными в обычный журнал не помещают; вместо него сохраняют защищённый идентификатор входного набора и хеш. Токен исключается безусловно.
У результата фиксируют HTTP-код, Content-Type, длительность, размер, SHA-256, число страниц и контрольные извлечённые строки. Для callback добавляют время ожидания и число доставок. Эти записи позволяют отличить изменение шаблона от сбоя ресурса и увидеть, какой параметр вызвал рост файла. При расследовании 504 полезны адреса зависимостей, но они должны быть очищены от секретных query-параметров.
Журнал связывают с балансом кредитов и группой. Если средний размер счетов внезапно вырос, можно найти первый документ после изменения ресурса, сравнить хеш шаблона и проверить изображения. Если увеличилось число повторов, анализируют сеть и идемпотентность. Наблюдаемость превращает расход кредитов из приблизительной статьи затрат в измеряемый показатель каждого рабочего процесса.
Итоговый рабочий подход
Наиболее устойчивый конвейер начинается не с подбора случайных параметров, а с отдельного печатного шаблона. Данные рассчитываются сервером, ресурсы локализуются в архиве или Assets, ширина и разрывы задаются CSS2-совместимыми правилами, а JavaScript оставляется только там, где без него нельзя построить статическую графику. Live Demo используется как стенд, после чего тот же вход отправляется на PDF-конечную точку.
В производственной интеграции важны три независимых контроля: корректность бизнес-данных до запроса, техническая целостность ответа и визуальная регрессия шаблона. Токен хранится как секрет, поток запросов ограничивается, временные ошибки повторяются с паузой, а готовый файл проверяется на сигнатуру и контрольные строки. Такой процесс позволяет выпускать документы автоматически и одновременно замечать подмену шрифта, потерю ресурса, сдвиг таблицы или рост расхода до того, как дефект увидит получатель.
HTMLPDF API особенно полезен для систем, где HTML уже является источником оформления: интернет-магазинов, биллинга, CRM, аналитики и внутренних порталов. Его сильные стороны раскрываются при предсказуемой разметке, небольшом числе быстрых зависимостей и дисциплинированной обработке ошибок. Когда требуется ручное исправление страниц, объединение готовых файлов или подписание, результат передают следующему PDF-инструменту, не пытаясь превратить конвертер HTML в универсальный редактор.