PDFium Component validuje implementační limity podle ISO 19005-1 Annex C — name tokeny 127 bytů, 8191 prvků pole, 4095 položek slovníku a 28 úrovní zanoření kontejnerů — a hlásí symbolické TrueType písmo, které nese položku /Encoding. Obě kontroly běží na cestě byte-scan, takže aplikace v Delphi nebo Lazarus dostane verdikt bez nutnosti načítat PDFium DLL vůbec
To jsou selhání, která lidi zarážejí nejvíc, protože dokument vypadá v pořádku. Renderuje se, tiskne, každé písmo je vložené, output intent je přítomen. Validátor ho pak odmítne kvůli slovníku, který má 4096 položek, a nic ve viditelném dokumentu nevysvětluje proč
Co vlastně limity Annex C chrání?
Interoperabilitu s implementacemi, které předcházejí vašemu generátoru. Annex C přenáší implementační limity z PDF Reference do každé části PDF/A a čísla nejsou libovolná — popisují to, co byl konformní čtenář historicky povinen zvládnout. Soubor, který je překračuje, se může v moderním prohlížeči otevřít perfektně a selhat v archivačním čtenáři, na kterém se před patnácti lety standardizoval records systém — přesně ten scénář, jemuž má PDF/A zabránit
Čtyři limity jsou inkluzivní. Name token přesně 127 bytů projde; 128 nikoliv. Pole přesně 8191 prvků projde; 8192 nikoliv. PDFium Component z toho důvodu ve své testovací sadě připíná obě strany každé hranice, protože off-by-one v kontrole limitu produkuje nejhorší druh validátoru: takový, který odmítá konformní soubory a přesto se mu věří
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;
Kteří generátoři tyto limity skutečně překročí?
Takoví, kteří staví strukturu programově, což je většina line-of-business výstupu. Formulář s několika tisíci poli produkuje pole /Annots nebo pole AcroForm /Fields, které přerůstá 8191. Stránka, jejíž resources dictionary nahromadí jednu položku na vygenerovaný obrázek nebo instanci písma, překročí 4095. Hluboce generované stromy struktury — značkovaný dokument stavěný rekurzí přes vnořený datový model — překročí 28 úrovní, aniž si toho kdo všimne, protože nikdo nedívá se na hloubku zanoření
Dlouhé názvy přicházejí z jiného zvyku: kódování dat do name tokenů. Název kolorantu složený z identifikátoru zákazníka, optional-content group pojmenovaná po plné cestě k souboru, formulářové pole, jehož plně kvalifikovaný název concatenuje šest úrovní hierarchie. Názvy se generují snadno a dlouhé udělají se snadno a 127 bytů zmizí rychleji, než čekáte, jakmile se do toho namíchá UTF-8 kódovaný label
Oprava je v každém případě strukturální. Rozdělte pole, rozdělte slovník, zploštěte zanoření, zkraťte název — preflight doporučení pro každý problém pojmenuje konkrétní limit spíše než aby říkalo, že soubor je neplatný. Injekce markerů zde nepomůže: to nejsou tvrzení metadat, to je tvar grafu objektů
Proč symbolické TrueType písmo nesmí nést /Encoding
Protože ISO 19005-1 §6.3.7 u symbolických TrueType písem připouští jen vestavěný cmap písma a položka /Encoding by tomu odporovala. Symbolické písmo mapuje kódy na glyphy po svém — to je význam slova symbolické. Přidejte encoding tabulku a najednou existují dvě odpovědi na otázku „který glyph vybere bajt 0x41", bez pravidla v souboru, které by rozhodlo, která vyhrává. Různé čtenáře to řeší různě a dokument, který se v jednom prohlížeči renderuje jako text, se v jiném renderuje jako dingbats
PDFium Component čte příznak symbolic ze /FontDescriptor, ať už je descriptor zapsán inline ve slovníku písma nebo odkázán nepřímo. Nesymbolické TrueType písmo si ponechá svůj požadovaný /WinAnsiEncoding nebo /MacRomanEncoding bez příznaku, protože pro nesymbolická písma je encoding přesně to, co standard vyžaduje. Kontrola spouští na rozporu, nikoli na přítomnosti encoding
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)');
Praktickým zdrojem této vady je subsetting písem producentem, který zachází se všemi TrueType písmy stejně. Symbol, Wingdings, čárové kódy a icon fonts jsou obvyklými nosiči — přesně ta písma, která obchodní dokument používá pro zaškrtávací políčka, loga a čárové kódy, a přesně ta, která nikdo nepřekontroluje, když dokument selhá v validaci kvůli „písmům"
Jak se problémy dostanou do preflight reportu
Čtyři limity kontejnerů se klasifikují pod structure; problém s encoding symbolického TrueType se klasifikuje pod content. Toto rozdělení má váhu, když report jde na dvě různé osoby: structure nálezy obvykle patří tomu, kdo napsal generátor, a content nálezy tomu, kdo dodal assety
Každý problém nese doporučení, které pojmenuje nápravu konkrétně — zkrátit name tokeny na 127 bytů nebo méně, rozdělit pole tak, aby žádné neneslo víc než 8191 prvků, odstranit /Encoding ze symbolických TrueType písem. Report, který říká „není konformní s PDF/A", startuje vyšetřování. Report, který říká, který limit byl překročen a o kolik, ho ukončuje
Validace bez DLL a proč to zde má váhu
Všechny výše uvedené kontroly běží proti bajtům souboru, takže fungují ve službě, která nemá nasazený PDFium binary, v build kroku nebo na stroji, kde je načítání nativní DLL politickým problémem. To je záměrná designová linie v PDFium Component: kontroly, které lze odpovědět ze struktury, se odpovídají ze struktury a DLL se rezervuje pro ty, které skutečně potřebují renderovací engine
Pro okolní workflow — spuštění validace nad složkou, produkci reportů a rozhodování, co s nálezy dělat — viz průvodci preflight validací PDF/A v Delphi a CLI pro dávkové preflight reporty. Pro volbu archivačního profilu, která stojí nad všemi těmito kontrolami, poznámky k archivační shodě PDF/A rozebírají, kterou část a úroveň cílit, než začnete opravovat nálezy
PDFium Component obaluje engine PDFium pro Delphi, C++Builder a Lazarus s vysokoúrovňovým VCL API a sadou validátorů shody, které běží s DLL i bez něj — viz stránka produktu PDFium Component pro podporované standardy a platformy