Apache FOP преобразует XSL-FO и связку XML с XSLT в готовые PDF-документы, а также выводит страницы в PostScript, PCL, AFP, TIFF, PNG, SVG и служебные XML-форматы. Пользователь управляет разметкой страниц, шрифтами, таблицами, колонтитулами, изображениями, закладками, метаданными, профилями PDF/A и PDF/X, шифрованием и пакетной генерацией через параметры командной строки, файл fop.xconf или Java API.
Работа обычно начинается с файла FO, в котором описаны размеры листа, поля, области колонтитулов и поток содержимого. Команда fop -fo document.fo -pdf document.pdf запускает разбор дерева форматирования, рассчитывает переносы строк и страниц, размещает графику, после чего передаёт сформированное дерево PDF-рендереру. Когда данные хранятся в обычном XML, к команде добавляют XSLT-шаблон: преобразование создаёт XSL-FO, а тот сразу поступает на верстку без промежуточного ручного этапа.
У программы нет визуального конструктора макета: результат контролируют по исходному FO, сообщениям журнала и открытому итоговому файлу. Поэтому надёжный процесс строят итерациями: сначала выводят короткий тест с одной страницей, затем подключают реальные шрифты и изображения, проверяют переполнение блоков и только после этого запускают весь набор данных. Общие параметры выносят в fop.xconf, а значения конкретного заказа передают в XSLT через -param, чтобы один шаблон выпускал счета, отчёты, каталоги или инструкции с разным содержимым.
Скачать Apache FOP
- Редактирование PDF
- Русский интерфейс
- Просто новичкам
- Нет визуального редактора
- Нужна настройка шрифтов
- XSL-FO поддержан не весь
Как устроен рабочий процесс Apache FOP
Входной документ проходит три различимые стадии. Сначала XML-парсер строит дерево форматирующих объектов и проверяет допустимость их вложения. Затем движок компоновки превращает логические блоки в области страниц: рассчитывает ширину строк, выбирает точки переноса, делит таблицы, применяет правила keep и создаёт статическое содержимое. На последней стадии выбранный рендерер переводит области в команды конкретного формата. Такое разделение полезно при диагностике: ошибка XML возникает до компоновки, предупреждение о переполнении относится к области, а потеря возможности в PCL или AFP обычно связана уже с ограничением рендерера.
Для разовой генерации достаточно FO-файла и имени результата. В повторяемом процессе рядом держат XML с данными, XSLT-шаблон, конфигурацию, каталог шрифтов и папку изображений. Относительные пути разрешаются от базового URI, поэтому запуск из другой рабочей директории способен внезапно сломать графику или подключаемые файлы. Практичнее передавать стабильную базу через конфигурацию или Java API, а в скрипте сначала переходить в каталог проекта. Тогда одинаковая команда даёт одинаковый результат на рабочей машине, сервере сборки и в контейнере.
Параметр -q уменьшает шум журнала, -d помогает увидеть подробности запуска, а -x печатает разобранные настройки. Для производственного задания полезно сохранять стандартный поток ошибок отдельно от выходного PDF: сообщения о подстановке шрифта, отсутствующем глифе или переполнении не портят бинарный поток и остаются доступными для анализа. Когда PDF направляется в stdout, вход можно принимать из stdin, но изображения и XSLT всё равно должны разрешаться по понятной базе, иначе перенос конвейера на другой узел станет нестабильным.
Структура XSL-FO: страницы, области и потоки
Макет задаётся не координатами каждого символа, а иерархией объектов. В layout-master-set описывают простые шаблоны страниц, после чего page-sequence выбирает нужный master и связывает основной поток с областью xsl-region-body. Области xsl-region-before, xsl-region-after, xsl-region-start и xsl-region-end предназначены для колонтитулов и боковых зон. Если высота верхней области больше верхнего поля тела, содержимое может наложиться; поэтому поля body и extent статической области рассчитывают совместно, а не независимо.
Для первой, чётной, нечётной и последней страницы используют условный page-sequence-master. Он выбирает simple-page-master по признакам page-position, odd-or-even и blank-or-not-blank. Такой подход позволяет сделать титульный лист без номера, зеркальные внутренние поля книжного разворота и отдельный завершающий лист. Когда условие не покрывает реальную последовательность, форматтер сообщает, что подходящий master не найден; исправление состоит не в увеличении памяти, а в добавлении fallback-правила, обычно с page-position="any".
Поток fo:flow должен соответствовать имени области тела, а fo:static-content — имени статической области. Несовпадение имён оставляет колонтитул пустым либо вызывает ошибку привязки. Номер текущей страницы выводит fo:page-number, общее число страниц получают через ссылку fo:page-number-citation-last на идентификатор завершающего блока. Идентификатор обязан быть уникальным и реально появиться в дереве, иначе вместо числа останется нерешённая ссылка или предупреждение.

Подготовка FO без лишних ошибок
FO остаётся XML, поэтому регистр имён, кавычки, закрывающие теги и экранирование амперсанда обязательны. На раннем этапе стоит проверять исходник обычным XML-валидатором: это отделяет синтаксическую ошибку от проблем верстки. Строгая проверка FOP обнаруживает недопустимое содержимое форматирующего объекта, например блок в месте, где требуется table-row. Параметр -r ослабляет некоторые проверки и способен пропустить проблемную конструкцию дальше, но он не превращает неверную структуру в переносимую; для стабильного шаблона лучше устранить причину.
Размеры можно задавать в pt, mm, cm, in, pc и относительных единицах, однако смешение единиц усложняет расчёт таблиц и полей. Для печатных шаблонов удобно держать размеры страницы и поля в миллиметрах, а толщины линий и типографику — в пунктах. Процентная ширина вычисляется относительно доступной области, а не всегда относительно физического листа. Если блок помещён внутрь table-cell или block-container, его сто процентов относятся к контейнеру, поэтому неожиданный узкий столбец часто объясняется родительской областью.
Свойства наследуются не одинаково: семейство и размер шрифта обычно переходят к потомкам, а границы и поля приходится задавать на нужном объекте. Вычисленные значения можно централизовать в XSLT-атрибутных наборах, чтобы одинаковые заголовки, ячейки и примечания не расходились после правок. При этом итоговый FO полезно сохранять режимом -foout: файл показывает, какие атрибуты реально создал XSLT, и позволяет понять, возникла ошибка в данных, преобразовании или верстке.
Преобразование XML и XSLT в PDF
Команда fop -xml data.xml -xsl layout.xsl -pdf result.pdf выполняет XSLT и верстку за один запуск. Это удобно для счетов, где XML содержит реквизиты и строки товаров, а XSLT создаёт заголовок, таблицу и итоговые суммы. Значения, не являющиеся частью данных, передают через повторяемый параметр -param name value: так задают язык, путь к фирменной графике, отметку черновика или режим двусторонней печати. Параметры следует объявить в XSLT и нормализовать, потому что командная строка передаёт текст, а не готовые числовые или логические типы.
При отладке связки сначала создают только FO: fop -xml data.xml -xsl layout.xsl -foout debug.fo. Если debug.fo неверен, PDF-рендерер ни при чём. Если FO корректен, его запускают отдельной командой и сравнивают журнал. Разделение особенно полезно при ошибках кодировки: XML может быть прочитан правильно, но XSLT создаст символ, которого нет в подключённом шрифте. Просмотр промежуточного FO показывает сам символ и выбранное font-family, а журнал FOP сообщает о недостающем глифе.
Для повторяемости важно контролировать XSLT-процессор и подключаемые документы. Встроенный механизм поддерживает стандартные преобразования Java, но расширения конкретного процессора могут отсутствовать. Каталоги XML включают параметром -catalog, чтобы публичные идентификаторы и схемы разрешались локально. Это снижает зависимость от сети и ускоряет пакетный запуск, но каталог должен входить в поставку проекта; ссылка на ресурс, существующий только на машине автора, неизбежно проявится как ошибка на сервере.
Командная строка и выбор формата вывода
Короткая форма fop input.fo output.pdf подходит только для очевидного PDF. В автоматизации лучше писать явные ключи -fo и -pdf, потому что журнал и скрипт сразу показывают назначение файлов. Выходной формат можно выбрать специальным ключом или универсальной парой -out MIME файл. Команда -out list выводит зарегистрированные MIME-типы, что помогает проверить фактический набор рендереров в используемой сборке перед запуском редкого формата.
Опция -dpi задаёт целевое разрешение там, где оно влияет на растровый вывод и пересчёт пиксельных размеров. Она не делает векторный PDF “более чётким”: текст, линии и SVG сохраняют геометрию, а разрешение важно главным образом для растровизации и изображений с размером в пикселях. Для PNG или TIFF выбирают DPI до расчёта макета, иначе тот же исходник может дать другой размер картинки относительно страницы. При печати дополнительно проверяют физический размер листа и поля устройства.
Режим -awt открывает предварительный просмотр через Java2D, а -print отправляет страницы в систему печати. Предпросмотр полезен для быстрой оценки, но не заменяет проверку целевого PDF: Java2D и PDF могут использовать отличающиеся источники шрифтов и поддерживать разные графические возможности. Если критична пиксельная близость, промежуточный формат создают с имитацией целевого рендерера, а затем выводят через Java2D с тем же набором метрик.

Файл fop.xconf и базовые параметры
Конфигурацию удобно начинать с образца conf/fop.xconf: в нём большинство секций отключено комментариями и снабжено диапазонами значений. Копию помещают рядом с проектом и передают через -c, не изменяя файл внутри каталога программы. Тогда обновление не затрёт настройки, а система контроля версий покажет изменение шрифта, профиля PDF или разрешения. Корневой base URI задаёт точку разрешения относительных ресурсов; без него одинаковый путь может означать разные файлы при запуске из IDE, Ant и shell-скрипта.
В общей секции настраивают строгость валидации, доступность, разрешение, кэш шрифтов и политику экономии памяти. Параметры конкретного рендерера помещают в элемент с его MIME-типом: у PDF есть свои настройки шифрования, линейризации, потоков объектов и цветовых профилей; у PostScript — автоповорот альбомных страниц; у PCL и AFP — собственные ограничения и оптимизации. Ошибка размещения параметра в чужой секции обычно не даёт желаемого эффекта, поэтому после правки полезно включить вывод разобранной конфигурации.
Для PDF можно включить object streams, уменьшающие размер за счёт упаковки объектов, и linearization, позволяющую начинать просмотр файла до полной загрузки. Эти функции решают разные задачи и не должны восприниматься как взаимозаменяемое “сжатие”. Потоки объектов полезны для хранения и передачи, а линейризация добавляет структуру быстрого веб-просмотра и может увеличить служебные накладные расходы. После изменения сравнивают размер, скорость открытия и совместимость с теми средствами, которые дальше подписывают, проверяют или объединяют PDF.
Шрифты: регистрация, встраивание и подстановка
Шрифты — наиболее частая причина различий между тестовым и производственным PDF. Для PDF и PostScript нельзя полагаться только на то, что гарнитура установлена в системе: нужный файл регистрируют в конфигурации и связывают с font-triplet, содержащим family, style и weight. Входной FO обращается именно к triplet. Если запрошено “SemiBold”, а зарегистрированы только normal и bold, движок выберет ближайший вариант либо подставит базовый шрифт, что изменит ширину строк и число страниц.
Поддерживаются TrueType, коллекции TrueType, Type 1 и OpenType в зависимости от рендерера. Для TTC указывают sub-font, иначе невозможно понять, какую гарнитуру брать из контейнера. При PDF-выводе TrueType обычно встраивается подмножеством с использованными глифами, что уменьшает файл. Полное встраивание может понадобиться для дальнейшего редактирования или специфического производственного процесса, но оно увеличивает объём и требует учитывать лицензионные биты шрифта.
Секция auto-detect сканирует стандартные каталоги и доступные ресурсы classpath. Это ускоряет прототип, но делает результат зависимым от окружения: на одном сервере будет найден корпоративный шрифт, на другом — подстановка. Для контролируемой генерации задают конкретные каталоги и включают файлы гарнитур в развёртывание. Автообнаружение использует кэш fop-fonts.cache; если исправленный файл по-прежнему игнорируется, кэш удаляют или запускают -flush, затем проверяют журнал новой регистрации.
Предупреждение о missing glyph означает, что выбранная гарнитура не содержит конкретный символ. FOP пытается найти замену в ограниченных группах, а затем может вывести знак решётки. Исправление состоит в выборе шрифта с нужным диапазоном Unicode или в явной подстановке семейства, а не в смене кодировки готового PDF. Для кириллицы проверяют букву “ё”, неразрывный дефис, знаки валют и математические символы: обычный тестовый текст часто не охватывает их, поэтому отдельная карта символов должна входить в набор регрессионных документов.

Переносы, языки и сложные письменности
Автоматические переносы зависят от значения language, country и доступного шаблона переноса. Без корректного языка движок не знает допустимых точек и оставляет слово целиком, из-за чего узкая колонка переполняется. Шаблоны поставляются отдельно для ряда языков и подключаются как ресурсы; их наличие проверяют на реальном длинном слове, а не по отсутствию ошибки запуска. Свойства hyphenate, hyphenation-remain-character-count и hyphenation-push-character-count позволяют запретить некрасивые короткие фрагменты по краям строки.
Для арабского, иврита и индийских письменностей важны complex-script features: формирование глифов, двунаправленный порядок и позиционирование знаков. Ключ -nocs отключает эту обработку и нужен главным образом для диагностики или совместимости, но в многоязычном документе способен разрушить текст. Даже при включённой обработке выбранный OpenType должен содержать нужные таблицы и глифы. Контрольный PDF проверяют визуально и извлечением текста, потому что красивое изображение строки ещё не гарантирует правильный порядок символов для поиска.
При смешении кириллицы, латиницы, CJK и символов полезно задать упорядоченный список семейств. Первый шрифт обслуживает основной текст, последующие работают как резерв. Нельзя рассчитывать, что fallback сохранит одинаковые метрики: замена одного знака может заметно отличаться по высоте и толщине. Для финансовых форм особенно проверяют цифры, разделители и знаки валют, а для технических инструкций — стрелки, индексы, греческие буквы и моноширинные фрагменты.
Блоки, интервалы и управление разрывами
Основной текст помещают в fo:block. Отступы start-indent и end-indent влияют на доступную ширину, а space-before и space-after участвуют в условном разрешении соседних интервалов. Если между двумя абзацами получается не сумма, а неожиданно меньшее значение, это следствие правил conditionality и precedence, а не потерянного атрибута. Для точного результата задают минимум, оптимум и максимум интервала либо используют единое правило на уровне стиля заголовка и следующего блока.
Свойства keep-with-next, keep-with-previous и keep-together управляют разрывами, но “always” создаёт жёсткое ограничение. Несколько крупных блоков с взаимными keep могут образовать группу больше страницы, после чего движок вынужден нарушать правило или сообщает о невозможной компоновке. Надёжнее применять целочисленную силу к обычным абзацам, а “always” оставлять для короткого заголовка с первой строкой и для компактных карточек. Widows и orphans помогают избежать одиночной строки вверху или внизу страницы.
Принудительный разрыв задают break-before или break-after. Внутри повторяемого XSLT важно не ставить break-before у каждого элемента без условия, иначе первая страница окажется пустой или каждый ряд уйдёт на новый лист. Для секций проверяют позицию элемента в наборе, а для чётной или нечётной страницы используют соответствующее значение разрыва и шаблон пустого листа. Пустая страница может быть логически необходима для двусторонней печати, поэтому её статические области и нумерацию проектируют отдельно.
Таблицы: ширина, повтор заголовков и разбиение
Таблица состоит из columns, header, footer и body; строки содержат cells, а ячейка обязана иметь block. При table-layout="fixed" ширины колонок предсказуемы и не требуют измерять всё содержимое, что ускоряет большие отчёты. Пропорциональные колонки задают функцией proportional-column-width, но она работает в контексте фиксированной таблицы. Если сумма явных ширин и отступов превышает доступную область, край таблицы выйдет за страницу, поэтому расчёт включает границы, padding и start-indent родителя.
Заголовок в fo:table-header повторяется после разрыва таблицы. Это не просто визуальная копия: для доступного PDF нужно правильно обозначить структуру и область заголовочных ячеек. Длинную строку FOP может разделить, если содержимое допускает разрыв; запрет keep-together на каждой строке повышает риск переполнения страницы. Для строки с несколькими абзацами обычно оставляют разрыв разрешённым, а критичные суммы или подписи защищают отдельным keep.
Объединение ячеек выполняют number-columns-spanned и number-rows-spanned. Вертикальное объединение требует, чтобы сетка следующих строк оставалась согласованной; ошибка в количестве ячеек проявляется как нарушение структуры или смещённые данные. Границы при модели collapsing и separate рассчитываются по-разному, поэтому макет с тонкой сеткой проверяют на стыках объединений и разрывов страницы. В производственных формах часто надёжнее отдельная модель границ с явными сторонами, чем ставка на автоматическое разрешение конфликтов.
Для очень больших таблиц критичны память и поток данных. FOP должен рассчитать области, а сложные keeps и процентные размеры увеличивают объём состояния. Упрощение до fixed layout, отказ от вложенных таблиц ради обычных блоков и уменьшение количества уникальных свойств дают заметный эффект. Если документ всё равно огромен, его делят на логические части, создают промежуточные форматы и объединяют страницы на стадии IF, сохраняя единый итоговый PDF.
Списки, лидеры и оглавления
Маркированные и нумерованные списки строят через list-block, list-item-label и list-item-body. Расстояние между маркером и текстом определяют provisional-distance-between-starts и provisional-label-separation, а start-indent дочерних блоков связывают с вычисленными позициями. Если длинный номер “10.12.” наезжает на текст, увеличивают расстояние начала, а не добавляют пробелы в содержимое. Пробелы зависят от шрифта и могут исчезать при нормализации, тогда как геометрические свойства сохраняют сетку.
Объект leader заполняет промежуток точками, линией или пробелом и особенно полезен в оглавлении. Слева помещают название раздела, затем leader с leader-pattern="dots", справа — page-number-citation. Номер становится известен после компоновки, поэтому ссылки разрешаются в несколько проходов внутренней обработки. Если идентификатор отсутствует или повторяется, оглавление не сможет показать корректную страницу. Для длинного заголовка задают возможность переноса так, чтобы лидер оставался перед номером, а не образовывал отдельную пустую строку.
Закладки PDF создают расширениями bookmark-tree, bookmark и bookmark-title. Внутренняя ссылка basic-link с internal-destination должна указывать на тот же id, что и закладка. Видимый текст, закладка и логическая структура — отдельные сущности: изменение заголовка не обновляет цель автоматически. В XSLT лучше формировать идентификатор одной функцией и использовать его во всех местах, предварительно удаляя пробелы и символы, неудобные для диагностики.
Изображения и правила разрешения ресурсов
Растровую или векторную графику подключают через external-graphic. Путь src разрешается относительно базового URI FO или конфигурации; в Java-встраивании его контролирует resolver. Если файл существует, но журнал сообщает “Image not found”, сначала проверяют базу, регистр имени и права процесса. Сетевые адреса в шаблоне делают генерацию зависимой от доступности сервера, поэтому логотипы и служебные изображения лучше поставлять рядом с шаблоном и обращаться к ним по контролируемому относительному пути.
Размер изображения определяется intrinsic size, content-width, content-height, width и height. Сочетание scale-to-fit и scaling="uniform" сохраняет пропорции, а раздельное указание ширины и высоты способно растянуть объект. Значение content-width="scale-to-fit" работает внутри заданного viewport; если сам viewport не ограничен, ожидаемого уменьшения может не произойти. Для фотографий проверяют эффективное DPI после масштабирования, а для штрихкодов запрещают интерполяцию, способную размыть границы.
Если графика не помещается, overflow определяет обрезку или видимость за пределами viewport. Это особенно заметно при align и display-align: положение содержимого меняется внутри области, но размер области остаётся прежним. Сначала полезно временно добавить рамку контейнеру, чтобы увидеть геометрию, затем убрать диагностический стиль. Официальные тестовые страницы с несколькими вариантами выравнивания помогают понять, какая комбинация свойств отвечает за положение, а какая — за масштаб.



SVG и векторная графика
SVG можно подключать внешним файлом или помещать внутрь fo:instream-foreign-object. Векторный путь, текст и простые заливки способны сохраниться в PDF без растровизации, что важно для схем, диаграмм и логотипов. Реальную поддержку определяет связка FOP и Batik: сложные фильтры, маски, шрифтовые особенности или внешние ресурсы могут вести себя иначе, чем в браузере. Перед массовым выпуском создают тестовый лист со всеми используемыми типами SVG, включая прозрачность и обрезку.
Viewport SVG и viewport FO взаимодействуют: атрибуты viewBox и preserveAspectRatio управляют внутренним масштабом, а свойства content-width и content-height — размещением внешнего объекта. Двойное масштабирование приводит к неожиданным полям или обрезке. Если требуется точный размер диаграммы, задают физическую ширину на одном уровне и оставляют второму уровню согласованный viewBox. Для одинаковых графиков в отчёте XSLT должен подставлять только данные, не меняя геометрию контейнера.
Текст внутри SVG может использовать иной механизм шрифтов, чем обычный FO-текст. Если гарнитура не разрешается, диаграмма визуально отличается от подписей страницы или превращает текст в замену. Один способ повысить переносимость — преобразовать критичные надписи в контуры до передачи, но тогда теряются поиск и доступность. Предпочтительнее зарегистрировать шрифт и проверить, что его видят обе части графического стека. Для доступного документа краткое описание задают на внешнем объекте, поскольку визуальная сложность SVG не создаёт осмысленный альтернативный текст автоматически.


Обрезка, позиционирование и контейнеры
Block-container создаёт отдельную область с размерами, положением и, при необходимости, поворотом. Absolute-position полезен для бланков и этикеток, но выводит элемент из обычного потока: следующий блок не учитывает занятую высоту. Поэтому координатный макет требует заранее рассчитанной сетки и тестов на максимальном тексте. Для обычного отчёта потоковые блоки устойчивее к изменению данных, а контейнеры стоит оставить для штампов, адресных окон и фиксированных зон.
Reference-orientation поворачивает систему координат на кратный прямому углу. Поворот меняет интерпретацию ширины и высоты, поэтому контейнер, который помещался до поворота, может выйти за область. Перед использованием в PCL или AFP проверяют ограничения промежуточного и конечного рендерера: не все форматы поддерживают произвольные преобразования. Для n-up и спуска полос удобнее модифицировать IF, где страницы уже рассчитаны, чем пытаться кодировать всю схему вложенными FO-контейнерами.
Clip и overflow решают, что делать с выходящим содержимым, но не исправляют первопричину переполнения. Обрезка допустима для фотографии в рамке, зато опасна для текста и штрихкода. Диагностический тест должен включать максимальную длину поля, отрицательное значение, пустое значение и символы широкого шрифта. Если контейнер фиксирован по высоте, XSLT может выбирать уменьшенный стиль или переносить продолжение в отдельный блок, но такое правило должно быть явным и проверяемым.


Формирование PDF: метаданные, ссылки и вложения
PDF-рендерер сохраняет текст, векторную графику, изображения, внутренние и внешние ссылки, закладки и метаданные, если соответствующие конструкции присутствуют во входе. Заголовок, автор, тема и ключевые слова задаются декларациями документа или расширениями, а расширенные сведения можно передать в XMP. Метаданные следует формировать из тех же данных, что и титульный блок: расхождение между видимым названием и Document Title затрудняет поиск и архивный контроль.
Внутренние назначения работают по идентификаторам FO. Ссылка на ещё не сверстанный раздел допустима, но id обязан быть уникален во всём документе. Внешние ссылки сохраняются как действия PDF; если адрес собирается из данных, XSLT должен экранировать его и запрещать неожиданные схемы. FOP не оценивает деловую безопасность ссылки, он только кодирует полученное значение. Для публичных документов полезен отдельный этап проверки аннотаций, который перечисляет все внешние назначения перед публикацией.
Встраивание готового PDF как изображения зависит от дополнительных возможностей графического стека и ограничений конкретного режима. Для присоединения сканов или многостраничного TIFF существует расширение external-document: каждая страница исходного изображения становится полной страницей результата. Такой объект не ведёт себя как обычный абзац и не смешивается с потоком текста. Размер, ориентацию и порядок вставки проверяют заранее, особенно если скан имеет нестандартный DPI или внутренний поворот.
PDF/A для долговременного хранения
Профиль включают ключом -pdfprofile либо специализированным параметром PDF/A-1b. После включения требования становятся частью генерации, а нарушение приводит к PDFConformanceException. Все шрифты должны быть встроены, поэтому базовое family задают уже на корневом объекте и связывают с реально зарегистрированной гарнитурой. Один забытый фрагмент с несуществующим шрифтом способен сорвать весь выпуск, даже если визуально он был бы заменён обычным Times.
PDF/A запрещает шифрование, поэтому нельзя одновременно требовать архивный профиль и парольные ограничения. Нужен также подходящий цветовой output intent и согласованные метаданные. Изображения с неподходящим цветовым пространством, прозрачностью или профилем проверяют отдельным валидатором, потому что успешная запись файла не доказывает соответствие всем требованиям стандарта. Рабочая схема включает генерацию, независимую валидацию и сохранение отчёта проверки рядом с документом.
Выбор уровня PDF/A зависит от потребителя и структуры исходника. Базовый профиль решает задачу визуального воспроизведения, а требования к тегам и логической структуре требуют дополнительной подготовки FO. Не стоит включать профиль только названием в конфигурации и считать задачу завершённой: тестовый набор должен содержать все шрифты, прозрачные изображения, ссылки, метаданные и типичные таблицы. Ошибка на редкой странице большого отчёта иначе обнаружится слишком поздно.
PDF/X и печатное производство
PDF/X ориентирован на контролируемый обмен печатными материалами. При его включении особенно важны output intent, цветовые пространства и отсутствие запрещённых возможностей. RGB-изображение может потребовать преобразования в CMYK по профилю производственного процесса, но автоматическая конверсия не заменяет цветопробу. Тонкие линии, overprint и прозрачности проверяют в том RIP или префлайт-средстве, которое использует типография.
Размер страницы и trim/bleed должны следовать техническому заданию. XSL-FO хорошо задаёт физическую геометрию, однако расширения PDF для дополнительных boxes требуют отдельной конфигурации или деклараций. Если макет печатается с вылетом, фон и изображение выводят за линию обреза, а важный текст оставляют внутри безопасной зоны. Простое увеличение page-width без изменения области содержимого создаст белую рамку, а не полноценный bleed.
Для смешанного электронного и печатного выпуска разумно создавать два результата из одного XML, но с разными профилями и ресурсами. Электронный PDF может содержать RGB, ссылки и оптимизированные изображения, а печатный — CMYK, шрифты и параметры типографии. Общую структуру сохраняет XSLT, а различия передают параметрами и конфигурацией рендерера. Сравнение страниц должно подтверждать, что изменение профиля не нарушило переносы и нумерацию.
Доступный PDF и логическая структура
Режим доступности включается ключом -a, методом user agent или параметром fop.xconf. Он создаёт tagged PDF, но не способен вывести смысл из неструктурированного шаблона. Табличная ячейка должна находиться внутри строки, изображение — иметь краткий alt-текст, а порядок block-container должен соответствовать порядку чтения. Если визуальные блоки расположены координатно в одном порядке, а в FO записаны в другом, экранный диктор прочитает структуру FO, а не положение на листе.
Свойство role позволяет сопоставить блоки с H1–H6, списками, параграфами и другими типами структуры. Декоративное содержимое помечают artifact, чтобы повторяющийся номер страницы или фон не мешал чтению. Статические области можно дублировать в структуре на каждой странице, но для колонтитула чаще правильнее роль artifact. Решение зависит от смысла: название раздела в шапке может помогать навигации, тогда как декоративная линия не несёт информации.
Для таблиц обычный table-header задаёт заголовки колонок. Заголовки строк требуют расширения на table-column, чтобы в структуре появился правильный Scope. Один жирный шрифт не превращает ячейку в заголовочную для вспомогательных технологий. После генерации проверяют дерево тегов, порядок чтения, альтернативный текст и навигацию с клавиатуры. Автоматическая проверка обнаруживает часть ошибок, но осмысленность описания изображения и заголовков оценивает человек.
Шифрование и ограничения действий
PDF можно защитить паролем владельца и пользователя, а также запретить печать, копирование, изменение, аннотации, заполнение форм, сборку документа, извлечение для доступности или высококачественную печать. Параметры командной строки удобны для разовой задачи, но секрет нельзя оставлять в истории shell или открытом журнале. В производственной системе пароль передают через защищённый механизм, а конфигурационный файл ограничивают правами доступа.
Разрешения PDF исполняются программой просмотра и не являются криптографической защитой содержимого от владельца корректного ключа. Поэтому конфиденциальный процесс должен контролировать выдачу файла и ключа, а не полагаться только на флаг nocopy. Для PDF/A шифрование запрещено; при одновременной настройке профильной и защищённой генерации следует разделить продукты или выбрать другое требование. Проверка итогового файла должна подтвердить алгоритм и фактические разрешения, а не только успешный код возврата FOP.
Пароли в командной строке могут быть необязательными аргументами, что создаёт риск неверного разбора следующего ключа. Скрипт должен явно отделять значения, правильно экранировать специальные символы и не печатать полную команду в лог. При пакетной обработке каждое задание получает собственный набор параметров; повторное использование глобальной конфигурации с секретом для разных клиентов повышает вероятность утечки.
Цифровая подпись PDF
Для подписи используется хранилище PKCS#12, содержащее закрытый ключ и сертификат. В секции PDF-рендерера указывают путь к keystore, имя подписанта, причину, местоположение и пароль. Реализация требует криптографических библиотек Bouncy Castle в classpath; если зависимость отсутствует, обычный PDF может формироваться, а подпись завершится ошибкой. Поэтому наличие подписи проверяют отдельным тестом при развёртывании, а не предполагают по существованию конфигурации.
Закрытый ключ нельзя помещать в общую папку шаблонов или репозиторий. Сервисный процесс получает доступ к keystore через секретное хранилище и минимальные права, а пароль не записывается в диагностический вывод. Срок действия сертификата и цепочку доверия контролируют вне FOP. Подписанный PDF проверяют валидатором, который показывает целостность, сертификат и время; размер файла или визуальная подпись на странице ничего не говорят о криптографическом состоянии.
Подписание выполняют после окончательного формирования структуры. Любое последующее изменение, объединение или оптимизация способно сделать подпись недействительной. Если в процессе нужен спуск полос, добавление обложки или объединение частей, операции проводят до стадии подписи. Для нескольких подписей и сложного рабочего процесса может потребоваться специализированный PDF-инструмент, тогда FOP отвечает только за исходный документ, а подпись накладывается заключительным шагом.
PostScript, PCL и печать
PostScript-рендерер подходит для систем, где последующая обработка и печать построены вокруг PS. Он использует собственную конфигурацию шрифтов и умеет автоматически поворачивать альбомную страницу для подачи в портретной ориентации. PDF и PS близки по модели, но не идентичны: прозрачность, цвет и некоторые графические конструкции могут преобразовываться иначе. Контрольный набор отправляют через реальный RIP, потому что просмотр PS на экране не воспроизводит все настройки устройства.
PCL ориентирован на принтерный поток и имеет более жёсткие ограничения по графике, шрифтам и преобразованиям. Часть элементов может быть растеризована через Java2D, что увеличивает размер и меняет качество. Целевое DPI задают с учётом принтера, а шрифты проверяют на каждой модели, если поток должен использовать ресурсы устройства. Для сложной полиграфики PDF обычно точнее; PCL выбирают, когда инфраструктура требует прямого задания и преимущества устройства важнее универсальности.
Ключ -print передаёт страницы системному принтеру, а дополнительные параметры выбирают устройство и диапазон. Такой путь удобен в рабочем месте, но на сервере часто отсутствует графическая среда или очередь печати. Надёжнее сначала создать контролируемый PDF либо PCL, сохранить журнал и только затем передать файл подсистеме печати. Это разделяет ошибку верстки и ошибку устройства, упрощает повтор задания и предотвращает повторный расчёт документа при временной недоступности принтера.
AFP для высокопроизводительной печати
AFP используется в транзакционной печати и требует понимания ресурсов принтера, шрифтов, изображений и групп страниц. Рендерер может встраивать или ссылаться на ресурсы, а неправильная стратегия резко увеличивает поток. Повторяющийся логотип не должен записываться как новый объект на каждой странице, если среда поддерживает общий ресурс. Настройки проверяют по размеру выходного файла, скорости RIP и фактической доступности ресурса на устройстве.
Цвет, сжатие и нативное встраивание изображений зависят от возможностей AFP и конфигурации. Изображение, которое хорошо выглядит в PDF, может потребовать отдельной подготовки, например преобразования цветового пространства или ограничения разрешения. Для массовых счетов тестируют не одну страницу, а выборку с фотографиями, штрихкодами, пустыми полями и максимальным количеством строк. Ошибка ресурса на редкой странице иначе проявится уже в длинной печатной очереди.
Группы страниц помогают организовать задания и атрибуты документа. При объединении нескольких частей следует убедиться, что границы групп соответствуют требованиям постобработки и конвертования. Если промежуточные IF-файлы конкатенируются, метаданные и ресурсы должны оставаться согласованными. Производственный контроль включает подсчёт страниц, контрольные суммы задания и сверку ключевых идентификаторов, а не только успешное завершение форматтера.
TIFF, PNG и другие растровые результаты
Растровый рендерер превращает страницу в пиксели, поэтому качество определяется физическим размером и DPI. A4 при 300 DPI создаёт существенно больше данных, чем экранный PNG при 96 DPI. Для предпросмотра достаточно умеренного разрешения, а для OCR, печати или архивного TIFF выбирают значение по требованиям потребителя. Увеличение DPI после растеризации не возвращает потерянную детализацию; его задают до вывода.
Многостраничный TIFF удобен для факсимильных и документных систем. Сжатие подбирают по типу содержимого: чёрно-белый текст выигрывает от специализированного двоичного сжатия, фотография — от другого метода. Цветовой режим и прозрачность проверяют заранее, потому что TIFF-потребитель может ожидать конкретную глубину. Если страницы получаются пустыми или обрезанными, сравнивают геометрию FO и растрового viewport, а не только открывают первый кадр.
PNG обычно создаётся по страницам и удобен для миниатюр, визуальной регрессии и публикации фрагментов. Большой документ порождает множество файлов, поэтому схему имён и каталог вывода задают так, чтобы параллельные задания не перезаписывали друг друга. Для регрессионного теста изображения сравнивают с допуском: сглаживание текста может немного отличаться после обновления Java или шрифта, тогда как смещение блока на несколько пикселей должно считаться реальным изменением.


SVG, текстовый и служебный вывод
Экспериментальный SVG-вывод создаёт набор слайдов и может требовать дополнительного компонента. Его не следует считать заменой обычного встраивания SVG в PDF: это другой рендерер с собственными ограничениями. Перед использованием проверяют зарегистрированный MIME-тип и конкретные конструкции шаблона. Если задача состоит в публикации векторной страницы в браузере, сравнивают результат с PDF по шрифтам, клипам, прозрачности и ссылкам.
Текстовый рендерер -txt выдаёт приближённое представление страницы в plain text. Он полезен для диагностики порядка и наличия содержимого, но не сохраняет полноценную геометрию, изображения и типографику. Такой файл можно применять как контроль извлечённого текста или вход для поиска, однако он не доказывает доступность PDF и не заменяет просмотр структуры тегов. Столбцы и таблицы могут потерять визуальные отношения.
Area Tree XML и Intermediate Format XML предназначены для служебного процесса. Они фиксируют результат компоновки до окончательного рендеринга и позволяют отложить выбор формата, объединить части или изменить страницы. Эти XML не стандартизованы как переносимый издательский формат между независимыми движками. Их сохраняют вместе с информацией о конфигурации и шрифтах, иначе повторный рендерер может использовать другие метрики и получить отличия.
Area Tree XML и Intermediate Format
Area Tree XML представляет дерево областей почти один к одному и содержит больше структурной информации. Он полезен для тестов движка и тонкой модификации, но чувствителен к пробелам и внутренней структуре. IF компактнее, оптимизирован для скорости и снабжён схемой, поэтому чаще подходит для постобработки, конкатенации и отложенного вывода. Выбор делают по операции: для анализа внутренней компоновки берут area tree, для производственной сборки страниц — IF.
Промежуточный файл нужно создавать с имитацией конечного рендерера, потому что разные выходы используют разные источники и метрики шрифтов. IF, рассчитанный под Java2D, может выглядеть иначе при выводе в PDF. Для близких пар, таких как PDF и PostScript, ограничение меньше, но всё равно полезно фиксировать MIME цели. В командной строке MIME указывают рядом с -at или -if, затем подают результат через -atin или -ifin.
IF можно обработать XSLT, SAX или DOM: добавить наложение, повернуть страницу, собрать n-up, вставить готовые страницы или объединить задания. После модификации файл проверяют по схеме и на целевом рендерере. PCL и AFP допускают не все преобразования viewport: обычно безопасны перенос и поворот на 90 градусов, а произвольная матрица может быть недоступна. Поэтому постобработка должна учитывать конечный формат, а не только валидность XML.
При объединении нескольких IF каждый документ уже имеет рассчитанные страницы. Это экономит повторную верстку и позволяет параллельно форматировать разделы, затем собрать единый поток. Однако сквозная нумерация, оглавление и ссылки между частями должны быть решены до разделения либо добавлены отдельным этапом. Нельзя ожидать, что простая конкатенация пересчитает page-number-citation из первого файла на объект второго: компоновка уже завершена.
Встраивание через Java API
Java API выбирают, когда форматирование является частью сервиса, очереди заданий или приложения. Обычно создают FopFactory из базового URI и конфигурации, для каждого документа — новый FOUserAgent и экземпляр Fop с MIME результата, а затем направляют SAX-события XSLT в default handler FOP. Выходной поток закрывают в finally или try-with-resources. Factory можно переиспользовать после настройки, но FOUserAgent содержит параметры конкретного задания и не должен бездумно делиться между потоками.
Base URI определяет разрешение изображений и шрифтов. В сервере его задают явно, а пользовательские пути пропускают через ResourceResolver, который ограничивает доступ. Без фильтра шаблон может попытаться прочитать произвольный файл или сетевой ресурс. Для недоверенного XML также защищают парсер от внешних сущностей и контролируют XSLT. FOP отвечает за форматирование, но безопасность всего конвейера зависит от настроек XML-процессора, resolver и хранилища ресурсов.
FOUserAgent задаёт метаданные, доступность, целевое разрешение, обработчики событий и параметры рендерера. Эти значения удобнее устанавливать программно, когда они различаются по заданию, а общие шрифты и профили оставлять в конфигурации. После обработки доступна информация о результатах и событиях; сервис преобразует предупреждения в структурированный журнал, привязанный к идентификатору документа. Так оператор видит не только исключение, но и страницу, объект и ресурс, вызвавшие проблему.
Потоковый SAX-конвейер избегает хранения полного промежуточного FO в памяти, но движок компоновки всё равно удерживает необходимые структуры страницы и ссылки. Большие таблицы, плавающие объекты и ссылки вперёд увеличивают требования. Ограничение памяти тестируют на максимальном документе и включают политику conservation, которая обменивает часть памяти на дисковый ввод-вывод. Временный каталог должен иметь достаточно места и быстрый диск, иначе “экономия” превратится в задержку.
Ant, Maven и пакетная сборка
Ant task позволяет описать набор FO или XML/XSLT-файлов в build.xml и генерировать документы вместе с остальной сборкой. Это удобно для руководств и отчётов, где исходники уже проходят через Ant. В задаче задают basedir, outdir, format, конфигурацию и файлы; для параллельных целей разделяют каталоги вывода. Ошибка одного документа должна останавливать сборку или фиксироваться по принятой политике, иначе пакет может считаться успешным с отсутствующим PDF.
В Maven-проекте зависимости FOP объявляют явно и фиксируют вместе с XML Graphics Commons, Batik и дополнительными библиотеками. Случайная замена транзитивной зависимости способна изменить SVG, PDF-парсер или криптографию. Dependency tree сохраняют в диагностике, а обновление запускают только после регрессионного набора. Бинарная поставка удобна для командной строки, а библиотечная зависимость — для API; смешение JAR из разных комплектов создаёт труднообъяснимые ошибки классов.
Пакетный shell-скрипт проверяет код возврата, существование и ненулевой размер результата, затем запускает префлайт или валидатор. Одного факта создания файла недостаточно: FOP может завершить документ с предупреждениями о переполнении или подстановке. Для каждого задания сохраняют входной идентификатор, длительность, число страниц, размер и краткий итог журнала. Повтор должен быть идемпотентным: временный файл пишут под уникальным именем и атомарно перемещают только после всех проверок.
События, журнал и контроль качества
Система событий сообщает о недостающих ресурсах, подстановках шрифта, неразрешённых ссылках, переполнении и особенностях рендерера. В Java можно подключить EventListener и преобразовать события в собственные уровни. Для счета отсутствие декоративной картинки допустимо, а пропущенный глиф в сумме — критично; одинаковый уровень журнала не всегда отражает деловой риск. Поэтому фильтр учитывает event id, локатор и тип документа, а не просто запрещает все warning.
Строка переполнения часто указывает объект и величину выхода за область. Исправление начинают с проверки доступной ширины: поля страницы, indent, padding, border и ширина таблицы суммируются. Затем смотрят неразрывные фрагменты, длинные адреса, URL-подобный текст и keep. Уменьшение шрифта глобально скрывает проблему и меняет все страницы; устойчивее разрешить перенос, вставить точки разрыва или скорректировать конкретную колонку.
Визуальная регрессия строится на наборе эталонных документов, охватывающих таблицы, изображения, SVG, кириллицу, сложные письменности, ссылки, профили и ошибки. PDF рендерят в PNG одинаковым инструментом и сравнивают страницы. Порог нужен для небольших различий сглаживания, но геометрическое смещение, исчезновение символа или новая страница должно останавливать выпуск. Параллельно сравнивают число страниц, размеры MediaBox и перечень встроенных шрифтов.
Текстовая регрессия извлекает текст из PDF и сопоставляет ключевые значения с XML. Она обнаруживает пропавшую сумму или неправильный порядок, который может быть незаметен на миниатюре. Для тегированного PDF дополнительно проверяют структуру и alt-тексты. Ни один метод не достаточен в одиночку: визуальное сравнение не видит семантическую ошибку ссылки, а извлечение текста не замечает наезд двух блоков.
Производительность и память
Скорость определяется не только количеством страниц, но и сложностью каждой страницы. Большая фотография, SVG с тысячами путей, вложенная таблица и множество уникальных шрифтов создают разную нагрузку. Профилирование проводят на реальном наборе, отдельно измеряя XSLT, компоновку и запись. Если XSLT занимает большую часть времени, изменение рендерера не поможет; если задержка растёт на выводе TIFF, важны DPI и растровый буфер.
FopFactory и кэш шрифтов выгодно переиспользовать в сервисе, но задания изолируют потоками и выходными файлами. Конкурентный запуск увеличивает пропускную способность до тех пор, пока не упирается в память, диск или загрузку шрифтов. Число воркеров подбирают экспериментально; запуск по одному потоку на каждое ядро может вызвать сборку мусора и замедлить все задания. Ограничитель очереди защищает сервер от всплеска больших документов.
Опция conservation сериализует часть area tree и снижает пиковое потребление памяти ценой дискового ввода-вывода. Она полезна для длинных документов, но не исправляет бесконечный шаблон или изображение гигантского размера. Временный путь размещают на локальном диске, следят за свободным местом и удаляют остатки после сбоя. Для контейнера лимит памяти и размер tmp задают явно, иначе процесс может быть уничтожен системой без понятного Java-исключения.
Изображения предварительно приводят к разумному разрешению и цветовой модели. Вставка фотографии на 20 мегапикселей в рамку 30 мм заставляет декодировать лишние данные и увеличивает память, даже если PDF потом показывает маленькую картинку. SVG упрощают, удаляя невидимые элементы и ненужные фильтры. Шрифтовые подмножества снижают размер PDF, но слишком много гарнитур и начертаний создают отдельные встроенные ресурсы; стиль документа должен использовать ограниченный набор.
Типовые ошибки запуска и их устранение
Команда не находит Java или основной класс
Сначала проверяют java -version в том же окружении, где запускается скрипт. Служба, cron и интерактивный shell могут иметь разный PATH. Затем используют штатный fop или fop.bat из бинарной поставки, не собирая classpath вручную. Если API подключён через Maven, ошибка NoClassDefFoundError означает несовместимую или отсутствующую зависимость; dependency tree и фактический каталог JAR дают больше информации, чем повторная установка всего набора.
Файл конфигурации не применяется
Путь после -c должен вести к читаемому XML, а корневой элемент и секции — быть корректными. Включают -x и ищут ожидаемый параметр в разобранных настройках. Частая причина — запуск другой копии скрипта fop, которая использует иной каталог библиотек, либо относительный путь от неожиданной рабочей директории. В сервисе печатают абсолютный путь конфигурации и её контрольную сумму без секретов.
Шрифт найден, но текст заменён
Проверяют точное совпадение family, style и weight между FO и font-triplet. Затем смотрят наличие нужного глифа и удаляют кэш, если файл изменялся. Для TTC указывают sub-font. Если используется fallback, журнал покажет подстановку; визуально похожая гарнитура всё равно меняет переносы. Тестовый FO должен выводить название семейства во всех начертаниях и набор символов, после чего PDF проверяют утилитой списка встроенных шрифтов.
Изображение не открывается
Сначала вычисляют абсолютный путь из base URI и src, затем проверяют права пользователя процесса. Для SVG отдельно проверяют подключаемые шрифты, изображения и стили внутри файла. Если ресурс приходит по сети, фиксируют таймаут и код ответа; лучше скачать его до форматирования. Повреждённый файл открывают независимым декодером. Сообщение о неизвестном формате может означать правильное расширение при неверной сигнатуре.
Таблица выходит за правое поле
Суммируют ширины columns, border, padding и indent, затем сравнивают с шириной region-body. Процент таблицы относится к родителю, а пропорциональные колонки требуют fixed layout. Длинную неразрывную строку разрешают переносить или сокращают по деловому правилу. Масштабирование всей страницы не следует использовать как первое средство: оно уменьшает шрифт и нарушает физические размеры формы.
Появляется пустая страница
Проверяют break-before, требования odd/even, page-sequence-master и блок с большой жёсткой связкой keep. Пустой лист может быть вставлен намеренно для перехода на нечётную страницу. Если он не нужен, условие разрыва корректируют в XSLT, а не удаляют страницу после PDF: постобработка изменит сквозные номера и ссылки. Для диагностики временно выводят имя используемого master в колонтитуле.
PDF/A завершается исключением
Сначала устраняют невстроенный шрифт и отключают шифрование. Затем проверяют output intent, изображения и метаданные. Исключение содержит конкретное нарушение, поэтому его сохраняют целиком; повторный запуск с расслабленной валидацией FO не отменяет требований PDF/A. После успешной генерации независимый валидатор остаётся обязательным, поскольку профиль включает больше правил, чем может проверить одна стадия.
Номера страниц в оглавлении не разрешаются
Цель page-number-citation должна иметь уникальный id и действительно присутствовать в том же процессе компоновки. Если главы форматируются отдельно и затем объединяются IF или PDF, ссылка между частями уже не пересчитается. Оглавление формируют в общем FO либо выполняют двухэтапную схему: сначала собирают фактические страницы, затем создают окончательный документ с известными номерами.
Практический сценарий: счёт или акт
Данные счёта хранят в XML: продавец, покупатель, строки, ставки, суммы и подписи. XSLT вычисляет не бухгалтерские значения, а только отображение уже проверенных данных; округление и налоги лучше рассчитывать в бизнес-системе. FO задаёт шапку, реквизиты, таблицу с фиксированными колонками и итоговый блок. Для длинного списка table-header повторяется, а итог удерживается с подписью, если группа помещается на странице.
Числа выравнивают по правому краю и форматируют единым правилом, не вставляя пробелы вручную. Неразрывный пробел между суммой и валютой должен существовать в выбранном шрифте. Идентификатор документа помещают в видимый текст и метаданные, а штрихкод или QR создают заранее как SVG либо изображение. Тесты включают нулевую скидку, отрицательную корректировку, очень длинное название товара и максимальное число знаков после запятой.
Для электронной отправки создают PDF со ссылками и подходящим профилем; для архива — отдельный PDF/A без шифрования. Если нужен защищённый экземпляр, его выпускают как другой артефакт с паролем, а не пытаются совместить несовместимые требования. После генерации сверяют номер, итоговую сумму, число строк и страниц с XML, затем только перемещают файл в систему хранения.
Практический сценарий: многостраничный отчёт
Отчёт обычно содержит титул, оглавление, главы, таблицы и диаграммы. Page-sequence-master различает титульную, левую и правую страницу; static-content показывает название главы и номер. Заголовки получают стабильные id, а оглавление — ссылки и page-number-citation. Чтобы не держать всю бизнес-логику в одном XSLT, данные предварительно нормализуют, а шаблон отвечает за структуру и вид.
Диаграммы готовят в SVG с одинаковым viewBox и набором шрифтов. Таблицы используют fixed layout и повтор заголовка. Для разделов с альбомными таблицами выбирают отдельный simple-page-master и переключают master, а не поворачивают всю готовую страницу. Закладка PDF дублирует иерархию глав, а role заголовков обеспечивает структуру доступного документа.
Регрессионный набор отчёта включает короткую и длинную главу, таблицу на границе страницы, пустую выборку, диаграмму без данных и текст на нескольких языках. При обновлении сравнивают число страниц и ключевые развороты. Если одна строка изменила перенос и сдвинула всё оглавление, это может быть корректным следствием новых метрик; решение принимают по требованиям, а не автоматически принимают новый эталон.
Практический сценарий: каталог и техническая документация
Каталог сочетает карточки, изображения, таблицы характеристик и перекрёстные ссылки. Для повторяемой сетки используют таблицу или блоки с фиксированной шириной, но избегают absolute-position для текста переменной длины. Изображения заранее нормализуют по размеру и цветовому профилю, а missing image заменяют явной технической карточкой, чтобы пустое место не прошло незамеченным. Артикул служит идентификатором ссылки и ключом контроля данных.
Техническая инструкция требует нумерованных разделов, предупреждений, кода и схем. Моноширинный шрифт регистрируют отдельно и проверяют символы псевдографики. Предупреждение оформляют блоком с границей и role, а не только цветом. SVG-схемы получают alt-текст; большие чертежи могут выводиться на альбомном master. Для печати добавляют bleed и профиль по требованиям производства, для экрана сохраняют активные ссылки и закладки.
Общий XML позволяет выпускать несколько языков, но длина перевода меняет страницы. Нельзя фиксировать номер раздела по языку без повторной компоновки. Язык указывают на соответствующих блоках для переносов и доступности, а fallback-шрифты проверяют на каждом алфавите. Скриншотные изображения текста лучше заменить вектором или локализованным рисунком, иначе перевод останется внутри картинки и не попадёт в поиск.
Сравнение Apache FOP с аналогами
| Программа | Лучше подходит для | Главное ограничение |
|---|---|---|
| Apache FOP | Автоматической генерации PDF и печатных потоков из XML/XSL-FO в Java-процессах | Неполная реализация XSL-FO и отсутствие визуального конструктора |
| Antenna House Formatter | Сложной коммерческой верстки XSL-FO или HTML/CSS, многоязычной типографики и допечатной подготовки | Для постоянного производственного использования требуется коммерческая лицензия |
| RenderX XEP | Корпоративной XSL-FO-верстки в PDF, PostScript, AFP и других печатных форматах | Промышленная поставка ориентирована на лицензируемое внедрение |
| Prince | Печатных PDF из HTML и CSS, когда шаблоны уже близки веб-разработчикам | Не принимает XSL-FO как основной язык макета |
| WeasyPrint | Отчётов, счетов и билетов из HTML/CSS в Python-проектах | Не поддерживает XSL-FO и часть сложных возможностей CSS |
Apache FOP выбирают, когда данные и шаблоны уже построены вокруг XML/XSLT, важны Java API, открытая лицензия и несколько технических форматов вывода. Antenna House Formatter подходит для максимально сложной типографики, полного коммерческого сопровождения и выбора между XSL-FO и CSS. RenderX XEP уместен в корпоративной XSL-FO-инфраструктуре с требованиями к промышленным печатным потокам. Prince и WeasyPrint рациональнее, если команда уверенно работает с HTML/CSS и не хочет вводить FO: Prince ориентирован на развитую печатную верстку, а WeasyPrint — на открытый Python-конвейер и типовые документы.
PDF Commander не включён в таблицу прямых форматтеров: он предназначен для ручной работы с уже созданным PDF, а не для расчёта страниц по XSL-FO. Его используют после генерации, когда нужно визуально исправить отдельный готовый файл, объединить страницы или выполнить пользовательскую операцию без изменения XML-шаблона. Для массового повторяемого выпуска правку лучше возвращать в XSLT или конфигурацию FOP, иначе следующий документ снова потребует ручного вмешательства.
Ограничения, которые важно учитывать до внедрения
Поддерживается существенная, но не полная часть XSL-FO 1.1. Перед переносом сложного шаблона проверяют таблицу соответствия каждого редкого объекта и свойства. Неизвестная возможность может быть проигнорирована, заменена расширением или привести к ошибке. Особенно внимательно тестируют floats, сложные keeps, расширенную типографику, bidi, цвет и свойства, которые раньше использовались в другом форматтере. Совместимость языка не гарантирует идентичную пагинацию.
Визуального редактора макета нет, поэтому специалист работает с XML, XSLT, конфигурацией и журналом. Для команды без опыта FO стоимость поддержки может оказаться выше стоимости самого запуска. Эту проблему уменьшают библиотека компонентов, тестовые данные и автоматическая регрессия. Универсальный шаблон, состоящий из сотен условных веток, труднее сопровождать, чем несколько общих модулей и небольшие профили документов.
Рендереры отличаются по точности и набору возможностей. PDF считается наиболее полно поддерживаемым, тогда как PCL, AFP, текстовый и растровые выходы имеют собственные ограничения. Один FO нельзя объявить одинаково выглядящим везде без проверки. Для каждого целевого формата нужен эталон и отдельные настройки шрифтов, графики и DPI. Если бизнесу достаточно PDF, лишние форматы не следует включать “на будущее”: они увеличивают тестовую матрицу.
FOP создаёт документ, но не заменяет редактор PDF, систему электронной подписи с полным жизненным циклом, префлайт типографии или валидатор архивного стандарта. Эти этапы строят вокруг форматтера. Чёткая граница ответственности упрощает диагностику: XML/XSLT формирует содержание, FOP рассчитывает и рендерит, независимые средства проверяют результат, а система доставки управляет доступом и хранением.
Как построить надёжный производственный конвейер
- Зафиксируйте набор Java-зависимостей, конфигурацию и файлы шрифтов; не полагайтесь на случайные ресурсы системы.
- Создавайте FO из нормализованного XML и сохраняйте его для проблемных заданий, чтобы разделять ошибки данных, XSLT и верстки.
- Перед массовым выпуском запускайте короткий smoke-тест со шрифтами, SVG, таблицей, ссылками и выбранным PDF-профилем.
- Сохраняйте журнал событий и переводите критичные предупреждения в отказ задания по деловым правилам.
- Проверяйте результат независимыми средствами: число страниц, встроенные шрифты, профиль, подпись, структуру тегов и ключевые значения.
- Пишите во временный файл и публикуйте его атомарно только после всех проверок, чтобы потребитель не увидел неполный PDF.
- Используйте визуальные и текстовые регрессионные тесты при изменении шаблона, шрифта, Java или библиотек графики.
- Ограничивайте доступ resolver к файлам и сети, особенно когда XML, XSLT или изображения поступают от внешнего пользователя.
- Измеряйте пиковую память и время на максимальном документе, а число параллельных воркеров выбирайте по фактическому профилю.
- Храните идентификатор задания, контрольные суммы входа и результата, чтобы точно воспроизвести выпущенный документ.
Smoke-тест должен завершаться быстро и давать диагностически насыщенную страницу. На ней размещают кириллицу и редкие символы, небольшой SVG, фотографию, таблицу с повторяемым заголовком, внутреннюю ссылку, метаданные и номер страницы. Для профиля PDF/A добавляют проверку встраивания шрифта, для доступности — структуру и alt-текст, для подписи — отдельный keystore тестовой среды. Такой файл обнаруживает неверный classpath или конфигурацию до запуска тысяч документов.
Версии шаблона и конфигурации записывают во внутренние метаданные или журнал задания, но не обязательно показывают на странице. Когда пользователь сообщает об ошибке, оператор находит точный XML, XSLT, fop.xconf, шрифты и зависимости. Без этого воспроизведение превращается в догадки: тот же XML с другим шрифтом способен дать другое число страниц. Контрольные суммы ресурсов удобнее простого номера релиза внутреннего проекта.
Результат считается принятым только после содержательной проверки. Для счёта это совпадение суммы и реквизитов, для отчёта — наличие всех разделов и страниц, для печати — профиль и геометрия, для доступного документа — структура и порядок чтения. Код возврата форматтера показывает техническое завершение, но не подтверждает бизнес-полноту. Поэтому финальный валидатор получает исходные данные и сопоставляет их с PDF.
Чек-лист перед выпуском документа
- FO является корректным XML, а все обязательные идентификаторы уникальны.
- Выбранный page master покрывает первую, обычную, последнюю и возможную пустую страницу.
- Поля region-body не пересекаются с extent верхнего и нижнего колонтитула.
- Все семейства, начертания и нужные Unicode-глифы зарегистрированы и встроены по требованиям.
- Изображения разрешаются от контролируемой базы, имеют разумный размер и ожидаемое цветовое пространство.
- Таблицы помещаются по ширине, повторяют заголовок и корректно разрывают максимальную строку.
- Закладки и внутренние ссылки указывают на существующие id, а общее число страниц разрешено.
- Для PDF/A отключено шифрование и пройдена независимая проверка профиля.
- Для доступности включены теги, alt-тексты, роли заголовков и правильный порядок чтения.
- Журнал не содержит пропавших глифов, неразрешённых ресурсов, переполнений и неожиданных подстановок.
- Число страниц, ключевые значения и встроенные шрифты сопоставлены с ожидаемым результатом.
- Временные и секретные файлы удалены, а готовый документ опубликован атомарно.
Итоговый подход к работе
Apache FOP наиболее эффективен там, где документ должен воспроизводимо собираться из структурированных данных без ручного размещения каждого элемента. Качество результата определяется не количеством параметров команды, а дисциплиной шаблона: продуманными page masters, явными шрифтами, ограниченными ресурсами, проверяемыми таблицами и осмысленной структурой. Один хорошо спроектированный XSLT способен выпускать тысячи персонализированных PDF, сохраняя одинаковые поля, стили и правила разрыва.
Начинать следует с минимального FO и PDF, затем последовательно добавлять конфигурацию шрифтов, реальную таблицу, графику, ссылки и требуемый профиль. Каждое усложнение закрепляют тестом. Когда возникает ошибка, стадию определяют по артефактам: исходный XML, сохранённый FO, журнал компоновки, промежуточный IF и конечный файл. Такой порядок быстрее случайной правки свойств и не скрывает проблему глобальным уменьшением шрифта.
Для стабильного выпуска командную строку или Java API окружают проверками: контролируют ресурсы, преобразуют события в понятные статусы, сравнивают страницы и извлечённые значения, валидируют профиль и только потом передают документ пользователю или печатной системе. Тогда FOP выполняет свою основную роль — рассчитывает сложную страничную верстку по формальным правилам — а весь процесс остаётся воспроизводимым, диагностируемым и безопасным.