Articol tehnic

Limite de implementare PDF/A și verificări de codare a fonturilor

PDFium Component validează limitele de implementare ISO 19005-1 Annex C — token-uri de nume de 127 octeți, 8191 elemente de matrice, 4095 intrări de dicționar și 28 de niveluri de imbricare a containerelor — și raportează un font TrueType simbolic care poartă o intrare /Encoding. Ambele verificări rulează pe calea de scanare a octeților, astfel încât o aplicație Delphi sau Lazarus primește verdictul fără să încarce deloc DLL-ul PDFium

Acestea sunt eșecurile care îi puzzlează cel mai mult pe oameni, deoarece documentul arată bine. Randeează, se tipărește, fiecare font este încorporat, intenția de ieșire este prezentă. Apoi un validator îl respinge peste un dicționar care are 4096 intrări, iar nimic în documentul vizibil nu explică de ce

Ce protejează de fapt limitele Annex C?

Interoperabilitatea cu implementări care precedă generatorul dumneavoastră. Annex C duce mai departe limitele de implementare din PDF Reference în fiecare parte PDF/A, iar numerele nu sunt arbitrare — ele descriu ce a fost istoric obligatoriu să gestioneze un cititor conform. Un fișier care le depășește se poate deschide perfect într-un vizualizator modern și să eșueze în cititorul de arhivă pe care un sistem de înregistrări l-a standardizat acum cincisprezece ani, ceea ce este exact scenariul pe care PDF/A există pentru a-l preveni

Cele patru limite sunt incluzive. Un token de nume de exact 127 octeți validează; 128 nu. O matrice cu exact 8191 de elemente validează; 8192 nu. PDFium Component fixează ambele părți ale fiecărei granițe în suita sa de testare din acel motiv, deoarece o eroare off-by-one într-o verificare de limită produce cel mai rău tip de validator: unul care respinge fișiere conforme și este crezut oricum

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;

Care generatoare lovesc de fapt aceste limite?

Cele care construiesc structură programatic, ceea ce este majoritatea ieșirii line-of-business. Un formular cu câteva mii de câmpuri produce o matrice /Annots sau o matrice AcroForm /Fields care crește peste 8191. O pagină al cărei dicționar de resurse acumulează o intrare per imagine generată sau instanță de font traversează 4095. Arborii de structură generați profund — un document etichetat construit prin recursiune peste un model de date imbricat — trec de 28 de niveluri fără ca nimeni să observe, deoarece nimeni nu se uită la adâncimea de imbricare

Numele lungi vin dintr-un obicei diferit: încodarea de date în token-uri de nume. Un nume de colorant construit dintr-un identificator de client, un grup de conținut opțional numit după o cale completă de fișier, un câmp de formular al cărui nume complet calificat concatenează șase niveluri de ierarhie. Numele sunt ieftine de generat și ușor de făcut lungi, iar 127 de octeți dispar mai repede decât v-ați aștepta odată ce este implicată o etichetă codată UTF-8

Remediul este structural în fiecare caz. Scindeți matricea, scindeți dicționarul, aplatizați imbricarea, scurtați numele — recomandarea de preflight pentru fiecare problemă numește limita concretă mai degrabă decât vă spune că fișierul este invalid. Injecția de markere nu poate ajuta aici: acestea nu sunt revendicări de metadate, sunt forma grafului de obiecte

De ce un font TrueType simbolic nu trebuie să poarte /Encoding

Pentru că ISO 19005-1 §6.3.7 admite doar cmap-ul încorporat al fontului pentru fonturile TrueType simbolice, iar o intrare /Encoding l-ar contrazice. Un font simbolic mapează coduri la glife în proprii termeni — asta înseamnă simbolic. Adăugați o tabelă de codare și există acum două răspunsuri la întrebarea „ce glifă selectează octetul 0x41”, fără nicio regulă în fișier care să spună care câștigă. Cititoare diferite o rezolvă diferit, iar un document care randeează ca text într-un vizualizator randeează ca dingbats în altul

PDFium Component citește fanionul simbolic din /FontDescriptor, indiferent dacă descriptorul este scris inline în dicționarul fontului sau referențiat indirect. Un font TrueType non-simbolic își păstrează /WinAnsiEncoding sau /MacRomanEncoding obligatoriu fără să fie marcat, deoarece pentru fonturile non-simbolice codarea este exact ce cere standardul. Verificarea se declanșează pe contradicție, nu pe prezența unei codări

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)');

Sursa practică a acestui defect este submulțimea de font făcută de un producător care tratează fiecare font TrueType la fel. Symbol, Wingdings, fonturi de coduri de bare și fonturi de pictograme sunt purtătorii uzuale — exact fonturile pe care un document de afaceri le folosește pentru căsuțe de validare, logo-uri și coduri de bare, și exact cele pe care nimeni nu le reexaminează când un document eșuează validarea pe „fonturi”

Cum sosesc problemele într-un raport de preflight

Cele patru limite de container sunt clasificate sub structură; problema codării TrueType simbolice este clasificată sub conținut. Această scindare contează când un raport merge la două persoane diferite: constatările de structură aparțin de obicei celui care a scris generatorul, iar constatările de conținut aparțin de obicei celui care a furnizat activele

Fiecare problemă poartă o recomandare care numește remediul în termeni concreți — scurtați token-urile de nume la 127 octeți sau mai puțin, scindeți matricele astfel încât niciuna să nu poarte mai mult de 8191 elemente, eliminați /Encoding din fonturile TrueType simbolice. Un raport care spune „neconform PDF/A” pornește o investigație. Un raport care spune ce limită a fost depășită și cu ce încheie una

Validarea fără DLL, și de ce contează asta aici

Toate verificările de mai sus rulează pe octeții fișierului, astfel încât funcționează într-un serviciu care nu are niciun binar PDFium implementat, într-un pas de build, sau pe o mașină unde încărcarea unui DLL nativ este o problemă de politică. Aceasta este o linie de design intenționată în PDFium Component: verificările care pot fi răspunse din structură sunt răspunse din structură, iar DLL-ul este rezervat celor care au nevoie cu adevărat de un motor de randare

Pentru fluxul înconjurător — rularea validării peste un dosar, producerea de rapoarte, și deciderea ce să faceți cu constatările — vedeți prezentările detaliate despre validarea de preflight PDF/A în Delphi și CLI-ul de raport de preflight în lot. Pentru alegerea profilului de arhivare care stă deasupra tuturor acestor verificări, notele despre conformanța de arhivare PDF/A acoperă ce parte și nivel să vizați înainte de a începe să reparați constatări

PDFium Component împachetează motorul PDFium pentru Delphi, C++Builder și Lazarus cu un API VCL de nivel înalt și un set de validatori de conformanță care rulează cu sau fără DLL — vedeți pagina produsului PDFium Component pentru standardele și platformele suportate