Articol tehnic

Preflight PDF/A și PDF/UA în Delphi cu PDF Library for Delphi

PDF/A și PDF/UA răspund la două întrebări care nu au nicio legătură una cu cealaltă, iar tratarea lor ca o singură bifă de accesibilitate-și-arhivare este exact modul în care fișiere defecte ajung într-o arhivă purtând o etichetă de conformitate. PDF/A întreabă dacă un fișier se va reda fidel și peste douăzeci de ani. PDF/UA întreabă dacă tehnologia asistivă îl poate citi astăzi. Un document poate trece perfect unul dintre teste și poate eșua la celălalt, așa că singurul verdict onest vine din rularea ambelor și din rularea lor înainte ca fișierul să fie scris pe disc, nu după ce un sistem din aval are încredere în identificatorul de conformitate încorporat în metadatele sale. Acel identificator este o autodeclarație. Nimic din format nu impune să fie adevărat, iar o aplicație care scrie „PDF/A-1b” în XMP fără să valideze față de standard produce un fișier care pare conform pentru orice consumator care citește doar eticheta. losLab PDF Library (PDF Library for Delphi) închide acel decalaj pentru Delphi și C++Builder integrând ambii validatori în bibliotecă, astfel încât verificarea rulează în proces, fără niciun serviciu extern de pornit

Două standarde care resping fișiere din motive opuse

ISO 19005 (PDF/A) este un contract de reproducere. Un fișier conform trebuie să se redea identic peste decenii, pe un software care nu a văzut niciodată sistemul care l-a produs, așa că regulile atacă dependențele externe: fiecare font încorporat, culoarea ancorată la un OutputIntent ICC încorporat sau exprimată într-un spațiu independent de dispozitiv, fără criptare în PDF/A-1, fără JavaScript, metadate XMP care concordă cu dicționarul de informații al documentului. ISO 14289 (PDF/UA) este, în schimb, un contract de semantică. Tehnologia asistivă trebuie să parcurgă documentul și să extragă un sens, care trăiește într-un strat complet diferit: un arbore de structură complet, text alternativ pentru figuri, un titlu de document setat pentru afișare, niveluri de titluri care nu sar peste trepte, relații între antete de tabel care supraviețuiesc odată ce pagina nu mai este pe ecran

Pentru că cele două standarde supraveghează straturi diferite, fișierele care vă mușcă sunt cele aflate la granița dintre ele. Un document perfect din punct de vedere al arhivării poate fi tăcut pentru un cititor de ecran. Unul etichetat impecabil poate face referire la un font desktop care nu va mai exista peste zece ani. Publicațiile din sectorul public sunt locul obișnuit unde ambele cerințe apar simultan, iar un pipeline de acolo nu le poate colapsa într-o singură poartă de control. Constatările merg către persoane diferite. Fonturile neîncorporate sunt un defect în codul care generează PDF-ul, în timp ce textul alternativ lipsă aparține celui care deține șabloanele de conținut, iar un raport care le amestecă pe amândouă ajunge pur și simplu retransmis de două ori

Care parte din PDF/A vizați contează la fel de mult ca faptul că o atingeți. PDF/A-1 este înghețat la PDF 1.4 și respinge transparența și JPEG2000, la ambele apelând ieșirile moderne de raportare fără să stea pe gânduri. PDF/A-2 (ISO 19005-2, construit pe ISO 32000-1) le acceptă pe amândouă și este alegerea implicită rezonabilă pentru o arhivă nouă. PDF/A-3 merge mai departe și permite fișiere încorporate de orice tip, pe care se bazează formatele reglementate de facturare electronică. O echipă care încă se standardizează pe PDF/A-1b în 2026 poartă de obicei o cerință pe care cineva a scris-o acum cincisprezece ani, iar renegocierea părții vizate este adesea mai ieftină decât eliminarea transparenței din fiecare grafic pe care sistemul îl produce

Diagramă PDF Library for Delphi care compară contractul de reproducere PDF/A cu contractul de semantică PDF/UA, plus o matrice trecere-eșec a documentelor care satisfac unul și eșuează pe celălalt
PDF/A asigură randarea fidelă peste decenii, în timp ce PDF/UA asigură citirea asistată astăzi, iar niciun verdict nu implică pe celălalt

Constatări structurate la momentul ingerării

Punctul de intrare din API-ul flat este CheckFileCompliance, cu selectorul de test 1 pentru PDF/A și 2 pentru PDF/UA. Acesta returnează un handle către o listă de șiruri ale cărei elemente sunt constatări individuale, una pe linie, exact forma pe care o poartă de control automatizată dorește să o parcurgă:

function GateArchiveUpload(Pdf: TPDFlib; const FileName: string): Boolean;
var
  ListId, I: Integer;
begin
  ListId := Pdf.CheckFileCompliance(FileName, '', 1, 0);  // 1 = PDF/A
  if ListId = 0 then
  begin
    // 0 înseamnă „fără constatări” SAU „fișier ilizibil” -- dezambiguizați înainte de a folosi valoarea
    Result := Pdf.LastErrorCode = 0;
    Exit;
  end;
  for I := 0 to Pdf.GetStringListCount(ListId) - 1 do
    LogFinding(FileName, Pdf.GetStringListItem(ListId, I));
  Pdf.ReleaseStringList(ListId);
  Result := False;
end;

Două detalii decid dacă acest lucru rulează nesupravegheat. Primul este o valoare de retur care înseamnă două lucruri opuse. CheckFileCompliance returnează 0 când fișierul este complet conform și, de asemenea, când fișierul nu a putut fi deschis deloc, pentru că intern o listă de rezultate goală se colapsează la 0 în ambele cazuri. O poartă de control care citește 0 ca reușită va lăsa încărcări corupte să treacă direct în arhivă, așa că dezambiguizați cu LastErrorCode înainte de a avea încredere în zero, așa cum face poarta de mai sus. Al doilea detaliu privește etapa din ciclul de viață în care se află fișierul. Verificatorul rulează pe cititorul de tip streaming al bibliotecii în loc de modelul complet al documentului, deschizând fișierul direct cu partajare pentru citire și fără a apela niciodată LoadFromFile, motiv pentru care poate parcurge intrări de mai mulți gigaocteți fără să construiască un arbore de obiecte. Aceeași deschidere de tip streaming eșuează în timp ce un alt proces încă deține fișierul pentru scriere, iar o încărcare în desfășurare este exact acea stare. Verificați abia după ce transferul se încheie

Proiectarea de tip streaming dă roade din nou sub sarcină. Fiecare verificare își deschide intrarea doar pentru citire și o partajează pentru citire, astfel încât un audit de corpus se extinde pe fire de execuție worker sau procese, cu o singură instanță TPDFlib per worker și fără contenție între ele. Resursa care necesită disciplină este chiar handle-ul. Fiecare rezultat nenul de la CheckFileCompliance rămâne alocat până când apelați ReleaseStringList, iar o poartă de control care rulează îndelung și uită să le elibereze nu se blochează, ci pur și simplu pierde memorie treptat până când cineva începe să caute motivul

Rapoarte pentru oameni, diff-uri pentru porțile de build

O listă de constatări este forma potrivită pentru o poartă de control și forma nepotrivită pentru un e-mail către echipa de șabloane. CreatePreflightReport redă aceeași analiză ca proză lizibilă, CreatePreflightReportEx adaugă un selector de format al raportului, iar SavePreflightReport îl scrie pe disc, astfel încât raportul să poată călători în interiorul pachetului de documente livrat. Numeroase contracte de arhivare fac din acel raport un livrabil de sine stătător, nu doar un artefact intern

Membrul acestei familii care își câștigă locul pe tăcute este ComparePreflightReports. Conformitatea este o suprafață de regresie ca orice altă bucată de comportament. O modificare de șablon, un font corporativ nou licențiat sau o actualizare a bibliotecii pot introduce fiecare o constatare care nu exista la lansarea anterioară, iar niciuna dintre ele nu se anunță singură. Păstrați rapoarte de referință pentru un set de documente reprezentative sub control de versiuni, regenerați-le după fiecare modificare și rulați ComparePreflightReports pentru a calcula diferența. Un diff gol este un artefact de lansare care merită păstrat. O constatare surpriză face build-ul să eșueze, ceea ce este un loc mult mai ieftin pentru a o descoperi decât auditul

Generarea unei ieșiri care trece de la prima încercare

Verificarea preliminară își dovedește utilitatea pentru fișierele care sosesc din altă parte. Pentru documentele produse de propriul dumneavoastră cod, descoperirea încălcărilor după generare și corectarea lor ulterioară este calea lentă. PDF Library for Delphi oferă un mod pe partea de generare pentru fiecare standard, iar le puteți activa pe amândouă pentru același document:

Diagramă PDF Library for Delphi a ingerării de preflight scalate, cu o instanță de bibliotecă în flux per muncitor, deschideri filtrate după terminarea încărcărilor și liste de constatări eliberate
Porțile se deschid doar după ce transferurile se completează, fiecare worker transmite input-ul read-only printr-o instanță privată a bibliotecii, iar fiecare handle returnat datorează o eliberare
var
  Pdf: TPDFlib;
  Diag: WideString;
begin
  Pdf := TPDFlib.Create;
  try
    Pdf.NewDocument;
    Pdf.SetPDFAMode(1);
    Pdf.LoadOutputIntentProfile('sRGB-IEC61966-2.1.icc', 'RGB');
    Pdf.SetPDFUAMode('en-US');
    Pdf.SetInformation(1, 'Quarterly Statement');  // /Title: necesar pentru PDF/UA
    // ... desenați aici conținutul etichetat ...
    Diag := Pdf.GetPDFUADiagnostics;
    if Diag <> '' then
      Writeln('fix before shipping: ', Diag);
    Pdf.SaveToFile('statement.pdf');
    // verificarea preliminară care contează rulează pe fișierul salvat:
    Writeln(Pdf.CreatePreflightReport('statement.pdf', '', 1, 0));
  finally
    Pdf.Free;
  end;
end;

Capcana se ascunde la momentul salvării. Mai multe dintre reparațiile de conformitate au loc în timp ce documentul este serializat, nu atunci când activați modul: forțarea steagului de tipărire pe adnotări, scrierea valorii implicite AFRelationship pentru fișierele încorporate PDF/A-3, normalizarea ordinii de tabulare și a descrierilor câmpurilor de formular pentru PDF/UA. Documentul aflat în memorie nu este identic octet cu octet cu cel care ajunge pe disc, așa că singurul verdict de verificare preliminară care contează este cel calculat din fișierul salvat. Validați chiar fișierul statement.pdf. Nu deduceți conformitatea din obiectul aflat încă în memorie, pentru că octeții pe care i-ați judeca nu sunt octeții pe care i-ați livrat

Diagramă PDF Library for Delphi a remedierilor de conformitate aplicate la salvare, în timpul serializării, de aceea preflight aparține fișierului PDF salvat, nu modelului din memorie
Serializarea forțează indicatorii de imprimare a adnotărilor, stabilește AFRelationship implicit și normalizează ordinea tab, astfel încât inspecția memoriei judecă octeți pe care nimeni nu îi expediază vreodată

Scenariile de facturare care poartă XML lizibil de mașini alături de documentul vizual urmează tiparul ZUGFeRD și Factur-X, construit pe PDF/A-3. Acestea ar trebui să seteze explicit relația fișierului atașat cu SetPDFA3DefaultAFRelationship, deoarece ISO 19005-3 impune ca fiecare fișier încorporat să își declare rolul în raport cu documentul. Lăsați-o nesetată și XML-ul încorporat devine doar un bloc de date fără scop declarat, lucru pe care validatorul îl observă

Arbitri independenți: veraPDF și Acrobat

Un producător nu ar trebui să fie singurul judecător al propriei sale ieșiri. Verificatoarele PDF Library for Delphi vă oferă verdicte rapide și structurate în proces, ceea ce este exact ce vă doriți pe calea critică, dar poarta de lansare pentru un lot de arhivare tot ar trebui să treacă ieșirea printr-un validator pe care nimeni din echipa dumneavoastră nu l-a scris. veraPDF este implementarea de referință întreținută de comunitate pentru PDF/A și instrumentul pe care majoritatea arhivelor îl numesc în criteriile lor de acceptare, așa că acesta este cel de urmat. Profilurile de verificare preliminară ale Acrobat oferă un departajator util atunci când veraPDF și verificarea în proces nu sunt de acord. Notați numele validatorului și versiunea acestuia alături de fiecare raport stocat. Afirmația că un fișier a trecut de veraPDF spune foarte puțin fără numărul de build care l-a trecut, deoarece instrumentul își înăsprește regulile între lansări

Validatoarele chiar nu sunt de acord la marginile standardelor, iar atunci când se întâmplă acest lucru, răspunsul nu este să alegeți instrumentul care vă place. Reduceți fișierul la un eșantion minim care încă declanșează dezacordul și citiți-l în raport cu textul standardului. O oră din asta scoate de obicei la iveală unul din două lucruri: un bug autentic al instrumentului care merită raportat în amonte, sau o clauză pe care echipa dumneavoastră a citit-o greșit și ar trebui notată în notele de conformitate, astfel încât următoarea persoană să nu mai redeschidă disputa

Intrările criptate beneficiază de o scurtătură. Ambele verificatoare acceptă un argument de parolă, dar un fișier PDF/A-1 cu un dicționar de criptare este deja neconform, pentru că ISO 19005-1 interzice criptarea în mod categoric, astfel încât o trimitere criptată poate fi respinsă înainte de a rula orice analiză mai profundă. Determinarea a ceea ce acordă efectiv un dicționar de criptare este o sarcină separată, tratată în auditarea criptării și permisiunilor PDF

Constatările PDF/UA aproape întotdeauna se trag din felul în care a fost realizat arborele de structură de la bun început, iar tehnicile de etichetare din spatele acestuia se regăsesc în construirea arborilor de structură PDF etichetați în Delphi. Arhivele care necesită și semnături digitale ar trebui să combine această poartă de control cu fluxul din semnarea și validarea PAdES. Referința completă a API-ului de verificare preliminară se află pe pagina de produs losLab PDF Library for Delphi