Brána pro příjem archivů odmítla dávku souborů "PDF/A-2b", které se na každém prohlížeči na stole otevíraly bez potíží. Dodavatel přísahal, že jsou v pořádku. Nebyly: každý z nich nesl akci JavaScriptu ukrytou v katalogu, přesně ten typ věci, který běžné oko snadno přehlédne a plný validátor PDF/A jako veraPDF okamžitě zachytí. Zádrhel byl v tom, že nikdo nechtěl kvůli jediné odpovědi ano/ne na soubor přidávat do dávkové služby v Delphi nástrojový řetězec v Javě. To je mezera ValidatePdfACompliance kterou vyplňuje PDFium Component, a stojí za to pochopit, jak dospívá k verdiktu, aniž by kdy plně parsoval obsahový stream
Proč na to samotný PDFium nestačí
První věc, kterou je fér říct: přibalený pdfium.dll vůbec neumí PDF/A. Není tu žádný ConvertToPDFA, žádný zapisovač OutputIntent a žádné veřejné XMP API. Všechny části PDF/A v této knihovně, jak zapisování, tak kontrola, žijí v čistém Pascalu v FPdfPdfa.pas a fungují na parsování po bajtech plus postupné aktualizaci. Když tedy zavoláte validátor, neptáte se na nic renderovacího enginu Chromium. Procházíte pascalovým tokenovým skenerem strukturální bajty souboru
Veřejné API je záměrně malé. Jedna funkce načte stream od pozice 0 a vrátí záznam:
function ValidatePdfACompliance(Source: TStream): TPdfAValidationResult;
type
TPdfAValidationResult = record
Conformance: TPdfAConformance; // pacUnknown, pacNone, pac1b, pac2u, ...
Issues: TPdfAValidationIssues; // a set of TPdfAValidationIssue
function IsCompliant: Boolean; // True only when level <> unknown/none
end; // AND Issues is empty
IsCompliant vyjadřuje pravidlo, které v bráně rozhoduje: soubor projde jen tehdy, když byla zjištěna skutečná úroveň shody a sada problémů je prázdná. Parse, který uspěje, ale nenajde žádný marker pdfaid, se vyhodnotí jako pacNone, , . Pokud skenujete syrové bajty souboru, vložená binární těla streamů, komprimované obrázky, ICC profily i fontové programy budou náhodně obsahovat byteové sekvence, které těmto tokenům připomínají. Budete hlásit
nebo
„nalezeno“, protože tři bajty uvnitř JPEG náhodou složily to slovo. To je továrna na falešně pozitivní výsledky./JavaScriptŘešením je /LZWDecode: prochází soubor a vyplňuje mezery mezi každým /BM a /AA klíčovým slovem mezerami, přičemž zachová strukturu slovníku beze změny. Teprve potom se spustí sken. Každá kontrola jmenného tokenu ve validátoru pracuje s touto očištěnou kopií. Pokud si z tohoto článku odnesete jednu věc, pak právě tuhle. Stejný postup používá i validátor PDF/UA, který si udržuje vlastní kopii rutiny, protože oba standardy se vyvíjejí nezávisle./3D29 problémů a co který znamená
je zdokumentovaný kontrakt. Číselné hodnoty jsou zamrzlé, protože na nich závisí testy DUnitX, ukázky i vrstva reportů, takže nová zjištění se vždy jen přidávají na konec. K verzi v1.63.0 je tu 29 členů. Dělí se do několika rodin:PdfStructureBytesMetadata a identitastream: endstream,
,
TPdfAValidationIssue (ISO 19005-1 6.1.3),
- .Barva a výstup
pvaiMissingXmpMetadata:pvaiMissingPdfAIdentifier,pvaiMissingTrailerId, apvaiMissingXmpDates - Color and output:
pvaiMissingOutputIntent,pvaiMissingIccProfile, andpvaiMixedDeviceColorSpaceskdyž se současně objeví DeviceRGB i DeviceCMYK (6.2.3.3) - Přísné zákazy pro každou část:
pvaiEncryptionPresent(slovník/Encryptje zcela zakázán),pvaiJavaScriptPresent,pvaiForbiddenAction,pvaiAdditionalActions,pvaiLzwUsed,pvaiXfaPresent,pvaiNeedAppearancesTrue,pvaiForbiddenAnnotation - Písma:
pvaiFontNotEmbeddeda přísnějšípvaiUnembeddedFont, pluspvaiUnicodeMappingMissingpro tvrzení Level U bez/ToUnicode - Značkování:
pvaiLevelAStructureMissingkdyž tvrzení o shodě=A nemá žádnou značkovanou strukturu
Šest nejnovějších členů, přidaných na pozice 24 až 29, pokrývá jemné případy, na které recenzenti skutečně narážejí: pvaiTrappedTrue (hodnota /Trapped /True v Info slovníku, „falešný přítel“, protože hodnota musí být False nebo Unknown), pvaiForbiddenActionSubtype (Sound nebo Movie použité jako akce, ne jen jako anotace), pvaiTransparentColorSpace (režim míchání jiný než Normal nebo /CA//ca není rovno 1.0), pvaiAnnotationDictViolation, pvaiUnembeddedFont a pvaiMixedDeviceColorSpaces
Podmíněné kontroly podle části: A-1 je přísná, A-2 a A-3 jsou mírnější
PDF/A není jedna sada pravidel. Tři věci, které PDF/A-1 zakazuje, jsou od PDF/A-2 dál výslovně povoleny: transparentnost (skupina /Transparency nebo aktivní /SMask, 6.4), volitelný obsah (/OCProperties, 6.1.13) a vložené soubory (/EmbeddedFiles nebo /EF, 6.1.11). Naivní validátor, který všechny tři označí u každého souboru, hromadně odmítne zcela platné dokumenty PDF/A-2
Validátor tedy čte číslo části z příznaku pdfaid prostřednictvím PdfAPartOf a všechny tyto kontroly povolí jen při PartNo = 1. Kontroly re?imu prolnut? a alfa kan?lu anotac? pro nov? probl?my s pr?hlednost? jsou podobn? jen pro ??st 1:
if PartNo = 1 then
begin
if PdfHasName(Struct, '/BM') then
if not PdfHasBMNormal(Struct) then // only /Normal or /Compatible allowed
Include(Result.Issues, pvaiTransparentColorSpace);
if PdfHasCaNotOne(Struct, '/CA') or PdfHasCaNotOne(Struct, '/ca') then
Include(Result.Issues, pvaiTransparentColorSpace);
end;
Stoj? za zm?nku jedno konzervativn? v?choz? pravidlo: kdy? chyb? ?pln? zna?ka pdfaid, ??st se pova?uje za 1, tedy za nejp??sn?j??. D?vod je ten, ?e neidentifikovan? soubor by m?l podl?hat nejp??sn?j??m pravidl?m, ne b?t jen tak pu?t?n d?l. JavaScript, zak?zan? akce, LZW, XFA, NeedAppearances, zak?zan? anotace a nevlo?en? p?sma z?st?vaj? zak?zan? pro ka?dou ??st, tak?e tyto kontroly nikdy nestoj? za branou
Rozbalov?n? objektov?ch stream?, aby se nic neschovalo
PDF 1.5 zavedlo stream k???ov?ch odkaz? a objektov? stream (/Type /ObjStm), a pro naivn? skener bajt? vytv??ej? slep? m?sto. Katalog, OutputIntent, slovn?k akce, cokoli, co samo nen? stream, m??e b?t uvnit? ObjStm komprimov?no pomoc? Flate. Projd?te syrovou strukturu a neuvid?te z toho nic, pak nahl?s?te ?ist? soubor, kter? je v?echno, jen ne ?ist?
PdfExpandObjectStreams tuto mezeru uzav?r?. Ne? se spust? jak?koli kontrola, valid?tor provede Data := PdfExpandObjectStreams(Data). Rutina najde ka?d? ObjStm, p?e?te jeho /N a /First hlavi?ku, aby z?skala ??sla obsa?en?ch objekt? a jejich offsety, a rozbal? t?lo pomoc? PdfInflate (zlib z RTL, System.ZLib v Delphi a zstream ve FPC), a p?ipoj? ka?d? obsa?en? objekt jako b??n? N 0 obj ... endobj na konec kopie bajt?. St?vaj?c? kontroly token? pak tyto objekty najdou beze zm?ny sv? logiky
Dv? omezen? z toho d?laj? n?co ?ist?ho, ne k?ehk?ho. Streamov? objekty, Metadata, ICC profil a programy p?sem nemohou b?t v objektov?m streamu, mohou tam b?t jen slovn?ky, kter? samy nejsou streamy, tak?e rozbalov?n? se v?dy t?k? jen slovn?k? a p?ipojen? objekty nenesou ??dn? stream kl??ov? slovo, kter? by naru?ilo f?zi odstra?ov?n? t?la. A proto?e p?idan? obsah dopad? a? za %%EOF, zp?tn? hled?n? od startxref st?le najde p?vodn? trailer. Samotn? trailer cross-reference streamu u? byl vy?e?en d??ve, ve verzi v1.49.3, t?m, ?e se Root, Size a ID ?etly p??mo z prost?ho slovn?ku xref-streamu, t?ma rozveden? v doprovodn?m ?l?nku o ov??ov?n? object and cross-reference streams; pr?ce na object streamu musela p?idat jen krok rozbalen?, bez pot?eby dek?dovat xref polo?ky typu 2 nebo rozmot?vat PNG prediktor
Poctiv? omezen? kontroly na ?rovni bajt?
Tohle je p?edb??n? n?stroj, ne certifikovan? valid?tor, a hranice jsou skute?n?. Vkl?d?n? p?sem je heuristika po??t?n? a dostat ji spr?vn? si vy??dalo opravu, kter? stoj? za zm?nku. P?vodn? kontrola pou??vala PdfCountName('/FontDescriptor'), ale ka?d? p?smo p?isp?v? dv?ma /FontDescriptor tokeny, jednou referenc? ze slovn?ku p?sma a jedn?m /Type v samotn?m objektu deskriptoru, tak?e po?et byl 2N proti N vlo?en?m program?m a test byl v?dy pravdiv?. Oprava je PdfCountDescriptorRefs, kter? po??t? jen /FontDescriptor N G R referen?n? formu, jednu na p?smo, a vyvol? pvaiUnembeddedFont jen tehdy, kdy? je vlo?en?ch program? skute?n? m?n?:
K := PdfCountDescriptorRefs(Struct); // one per font dict
Emb := PdfCountName(Struct, '/FontFile')
+ PdfCountName(Struct, '/FontFile2')
+ PdfCountName(Struct, '/FontFile3');
if (K > 0) and (Emb < K) then
Include(Result.Issues, pvaiUnembeddedFont);
I po oprav? je to hrub?: sm??en? dokument, kde m? ka?d? deskriptor n?hodou n?jak? FontFile, m??e i tak pustit jednotliv? nevyhovuj?c? p?smo. Rozbalov?n? objektov?ch stream? m? tak? zn?m? vedlej?? ??inek, odhal? v?choz? zdroje standardu 14, kter? nese AcroForm /DR, nap??klad /Helv, a heuristika je poctiv? ozna?? jako nevlo?en?, i kdy? je veraPDF pust? d?l, proto?e se ve skute?nosti nikdy nepou??vaj? p?i vykreslov?n?. Kontroly na ?rovni oper?tor? content streamu (6.2.10) jsou zcela mimo rozsah, proto?e by vy?adovaly pln? rozbor obsahu, ne jen kontrolu bajt?. Berte valid?tor jako rychlou prvn? br?nu bez z?vislost?, kter? zachyt? poru?en?, je? injekce markeru neum? opravit, a pro fin?ln? certifikaci si nechte plnohodnotn? valid?tor
Takhle vypad? kontroln? polovina p??b?hu. Dopl?uj?c? z?pisov? ??st, kde SaveAsPdfA vkl?d? XMP, OutputIntent a sRGB ICC profil a poctiv? sn??? po?adavek Level A, kter? nem? ??dnou tagovanou strukturu, stoj? na stejn? byteov? mechanice. Ob? poloviny jsou sou??st? PDFium Component for Delphi, jedin?ho bal??ku VCL nad ?ist? Pascalovou implementac? PDF/A bez extern?ho runtime k instalaci