PDFium Component проверяет ограничения реализации ISO 19005-1 Annex C — name-токены до 127 байт, 8191 элемент массива, 4095 записей словаря и 28 уровней вложенности контейнеров — и сообщает о символическом TrueType-шрифте, несущем запись /Encoding. Обе проверки идут на пути побайтового сканирования, поэтому приложение Delphi или Lazarus получает вердикт без загрузки DLL PDFium вовсе
Это те провалы, что озадачивают более всего, потому что документ выглядит нормальным. Он рендерится, печатается, все шрифты встроены, выходной интент присутствует. Затем валидатор отвергает его из-за словаря с 4096 записями, и ничто в видимом документе не объясняет почему
Что в действительности защищают лимиты Annex C?
Совместимость с реализациями, что предшествуют вашему генератору. Annex C переносит ограничения реализации из PDF Reference в каждую часть PDF/A, и эти числа не произвольны — они описывают то, что соответствующий читатель исторически обязан был обрабатывать. Файл, превышающий их, может идеально открываться в современном просмотрщике и падать в архивном читателе, на котором система документооборота стандартизировалась пятнадцать лет назад, — именно тот сценарий, для предотвращения которого PDF/A и существует
Все четыре лимита включительны. Name-токен ровно в 127 байт проходит; 128 — нет. Массив ровно с 8191 элементом проходит; 8192 — нет. PDFium Component фиксирует обе стороны каждой границы в своём тестовом наборе именно поэтому: ошибка на единицу в проверке лимита порождает худший сорт валидатора — отвергающий корректные файлы и всё равно принимаемый на веру
uses FPdfPdfa;
var
Src: TFileStream;
Res: TPdfAValidationResult;
begin
Src := TFileStream.Create('archive.pdf', fmOpenRead or fmShareDenyWrite);
try
Res := ValidatePdfACompliance(Src);
if pvaiArrayOverLimit in Res.Issues then
Memo1.Lines.Add('An array carries more than 8191 elements');
if pvaiDictOverLimit in Res.Issues then
Memo1.Lines.Add('A dictionary carries more than 4095 entries');
if pvaiNestingOverLimit in Res.Issues then
Memo1.Lines.Add('Containers nest deeper than 28 levels');
if pvaiNameOverLimit in Res.Issues then
Memo1.Lines.Add('A name token is longer than 127 bytes');
finally
Src.Free;
end;
end;
Какие генераторы на деле упираются в эти лимиты?
Те, что строят структуру программно, — а это большинство бизнес-вывода. Форма с несколькими тысячами полей порождает массив /Annots или массив /Fields AcroForm, выходящий за 8191. Страница, чей словарь ресурсов накапливает по записи на каждое сгенерированное изображение или экземпляр шрифта, пробивает 4095. Глубоко сгенерированные деревья структуры — размеченный документ, построенный рекурсией по вложенной модели данных, — проходят за 28 уровней, никто не заметив, потому что никто не смотрит на глубину вложенности
Длинные имена приходят из другой привычки: кодирование данных в name-токены. Имя краски, построенное из идентификатора клиента, группа опционального содержимого, названная по полному пути файла, поле формы, чьё полностью квалифицированное имя конкатенирует шесть уровней иерархии. Имена дёшевы в генерации и легко делаются длинными, а 127 байт исчезают быстрее, чем ожидается, как только в деле участвует UTF-8-кодированная метка
Исправление в каждом случае структурно. Разделите массив, разделите словарь, выровняйте вложенность, сократите имя — рекомендация предпечатной подготовки для каждой проблемы называет конкретный лимит, а не сообщает, что файл невалиден. Внедрение маркеров здесь помочь не может: это не утверждения метаданных, а форма графа объектов
Почему символический TrueType не должен нести /Encoding
Потому что ISO 19005-1 §6.3.7 для символических TrueType-шрифтов допускает лишь встроенный cmap шрифта, а запись /Encoding противоречила бы ему. Символический шрифт отображает коды в глифы на собственных условиях — именно это «символический» и значит. Добавьте таблицу кодировки, и теперь есть два ответа на вопрос «какой глиф выбирает байт 0x41», без правила в файле, решающего, кто побеждает. Разные читалки разрешают это по-разному, и документ, рендерящийся как текст в одном просмотрщике, рендерится как dingbats в другом
PDFium Component читает символический флаг из /FontDescriptor, независимо от того, записан ли дескриптор inline в шрифтовом словаре или указан непрямым образом. Несимволический TrueType-шрифт сохраняет требуемое /WinAnsiEncoding или /MacRomanEncoding, не помечаясь, потому что для несимволических шрифтов кодировка — именно то, о чём просит стандарт. Проверка срабатывает на противоречии, а не на присутствии кодировки
if pvaiSymbolicTrueTypeEncoding in Res.Issues then
Memo1.Lines.Add(
'A symbolic TrueType font carries /Encoding; PDF/A admits only its ' +
'built-in cmap (ISO 19005-1 6.3.7)');
Практический источник этого дефекта — подмножество шрифтов, выполненное производителем, обращающимся со всяким TrueType одинаково. Symbol, Wingdings, штрих-кодовые шрифты и иконочные шрифты — обычные носители, и именно те шрифты, что деловой документ использует для чек-боксов, логотипов и штрих-кодов, и именно те, которые никто не перепроверяет, когда документ падает на валидации из-за «шрифтов»
Как проблемы попадают в предпечатной отчёт
Четыре контейнерных лимита классифицированы под структурой; проблема кодировки символического TrueType — под содержимым. Это разделение значимо, когда отчёт уходит двум разным людям: находки структуры обычно принадлежат тому, кто писал генератор, а находки содержимого — тому, кто поставил ассеты
Каждая проблема несёт рекомендацию, называющую средство конкретно: сократите name-токены до 127 байт или менее, разделите массивы так, чтобы ни один не нёс более 8191 элемента, удалите /Encoding из символических TrueType-шрифтов. Отчёт «не соответствует PDF/A» начинает расследование. Отчёт, называющий, какой лимит превышен и чем, заканчивает его
Валидация без DLL и почему это здесь важно
Все проверки выше идут против байтов файла, поэтому они работают в службе без развёрнутого бинарника PDFium, в шаге сборки или на машине, где загрузка нативной DLL — проблема политики. Это намеренная проектная линия в PDFium Component: проверки, ответимые из структуры, отвечаются из структуры, а DLL зарезервирована для тех, что подлинно требуют движка рендеринга
Для сопутствующего рабочего процесса — валидация по папке, формирование отчётов и решение, что делать с находками — см. разборы предпечатной валидации PDF/A в Delphi и CLI пакетного предпечатного отчёта. Для выбора архивного профиля, стоящего над всеми этими проверками, заметки о архивном соответствии PDF/A описывают, на какую часть и уровень целить, прежде чем вы начнёте исправлять находки
PDFium Component оборачивает движок PDFium для Delphi, C++Builder и Lazarus с высокоуровневым VCL API и набором валидаторов соответствия, работающих с DLL или без неё — см. страницу продукта PDFium Component для поддерживаемых стандартов и платформ