PDFium Component валідує межі реалізації з ISO 19005-1 Annex C — імена по 127 байтів, 8191 елементів масиву, 4095 записів словника та 28 рівнів вкладеності контейнерів — і звітує про символічний TrueType-шрифт, що несе запис /Encoding. Обидві перевірки працюють на шляху байтового сканування, тож застосунок Delphi або Lazarus отримує вердикт взагалі без завантаження DLL PDFium
Це ті відмови, що дивують людей найбільше, бо документ виглядає нормально. Він рендериться, друкується, усі шрифти вбудовані, вихідна наміренція присутня. Тоді валідатор відхиляє його через словник, що має 4096 записів, і ніщо у видимому документі не пояснює чому
Що насправді захищають межі Annex C?
Сумісність із реалізаціями, що передують вашому генератору. Annex C переносить межі реалізації з PDF Reference у кожну частину PDF/A, і ці числа не довільні — вони описують те, що відповідний читач історично мав обробляти. Файл, що перевищує їх, може ідеально відкриватися в сучасному переглядачі й відмовляти в архівному читачі, на якому стандартизувалася система записів п'ятнадцять років тому, — а це саме той сценарій, для запобігання якому існує PDF/A
Усі чотири межі включні. Ім'я рівно 127 байтів проходить; 128 — ні. Масив рівно з 8191 елементом проходить; з 8192 — ні. PDFium Component фіксує обидві сторони кожного кордону у своєму тестовому комплекті з цієї причини, бо помилка off-by-one у перевірці межі породжує найгірший вид валідатора: той, що відхиляє відповідні файли й якому все одно довіряють
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 рівнів без чиєїсь уваги, бо ніхто не дивиться на глибину вкладеності
Довгі імена приходять з іншої звички: кодування даних у токени імен. Ім'я барвника, побудоване з ідентифікатора клієнта, група необов'язкового вмісту, названа на повний шлях до файла, поле форми, чиє повне ім'я зчеплює шість рівнів ієрархії. Імена дешеві для генерації та легкі для подовження, і 127 байтів зникають швидше, ніж ви б очікували, щойно залучено UTF-8-кодований напис
Виправлення в кожному випадку структурне. Розщепіть масив, розщепіть словник, сплощіть вкладеність, скоротіть ім'я — рекомендація префлайта для кожної проблеми називає конкретну межу, а не каже, що файл невідповідний. Вставлення маркерів тут не допоможе: це не заяви метаданих, а форма графу об'єктів
Чому символічний TrueType-шрифт не повинен нести /Encoding
Тому що ISO 19005-1 §6.3.7 для символічних шрифтів TrueType визнає лише вбудовану cmap шрифта, а запис /Encoding суперечив би їй. Символічний шрифт відображає коди на гліфи за власними правилами — це й означає «символічний». Додайте таблицю кодування, і тепер є дві відповіді на запит «який гліф вибирає байт 0x41», без правила у файлі, яке б вирішувало, який переміг. Різні читачі вирішують це по-різному, і документ, що рендериться як текст в одному переглядачі, рендериться як dingbats в іншому
PDFium Component читає символічний прапорець із /FontDescriptor, незалежно від того, чи дескриптор написано інлайново у словнику шрифта, чи посилається опосередковано. Несимволічний 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 — під вміст. Цей поділ важливий, коли звіт іде двом різним людям: знахідки структури зазвичай належать тому, хто писав генератор, а знахідки вмісту — тому, хто постачав активи
Кожна проблема несе рекомендацію, що називає лік конкретними термінами — скоротити токени імен до 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 щодо підтримуваних стандартів та платформ