PDF/E-1 e profilul de arhivare pentru documentele de inginerie, iar PDFlibPas îl implementează ca un mod de autor pe care îl activați cu SetPDFEMode plus un preflight cu limite care citește content stream-urile operator cu operator. Profilul nu e un PDF/A cu altă etichetă: are propriul namespace de identificare, propria cerință de metadate de ciclu de viață și o regulă care face validarea conținutului mai strictă decât în orice profil de arhivare întâlnit până acum
Livrabilele de inginerie sunt motivul pentru care există profilul. Un set de planșe care trebuie să fie lizibil și demonstrabil neschimbat peste douăzeci de ani, cu un istoric de revizii care supraviețuiește și cu o culoare care înseamnă același lucru pe plotter-ul din clădirea alăturată. Aceste cerințe produc o specificație ale cărei exigențe stau mai ales în afara conținutului paginii, în metadate și managementul culorii, exact locul în care un scriitor PDF generic greșește
Identificare proprie, nu o variațiune pe PDF/A
Primul lucru de înțeles corect e că identificarea PDF/E-1 nu poate fi produsă adaptând tiparul PDF/A sau PDF/X. Folosește un namespace XMP distinct, http://www.aim.org/pdfe/ns/id/, iar valoarea versiunii trebuie să apară în două locuri: ca intrare în informațiile documentului și ca proprietate XMP calificată cu namespace. Emisia doar a proprietății XMP, sau doar a intrării de informații, produce un fișier care poartă intenția și pică la validare
Output intent-ul are o formă la fel de specifică. PDF/E-1 cere un profil ICC încorporat cu identificatorul de subtip ISO_PDFE1, iar profilul trebuie să aibă un număr de componente care să se potrivească cu familia de culori de dispozitiv folosită efectiv de document. Clauza din urmă e locul în care implementările greșesc pe furiș, pentru că înseamnă că intent-ul nu poate fi ales dinainte și apoi ignorat
De ce are nevoie culoarea de dispozitiv de o străbatere a întregului document?
Pentru că spațiile de culoare se ascund în dicționare de resurse pe care o scanare la nivel de pagină nu le ajunge niciodată. PDF/E-1 tratează DeviceRGB și DeviceCMYK ca familii reciproc exclusive pentru un document, deci validarea profilului înseamnă să știi fiecare spațiu de culoare de dispozitiv folosit de orice din fișier. Un form XObject are resursele lui. La fel un pattern, la fel o imagine. Un tiling pattern într-un form XObject într-o pagină stă trei niveluri în adâncime, iar un validator care verifică doar resursele de vârf ale paginii va da drept bun un document care folosește ambele familii
Străbaterea înregistrează deci spațiile de culoare în timp ce parcurge pagini, forme, imagini și pattern-uri ca o singură traversare, și abia apoi decide dacă documentul e coerent și dacă output intent-ul se potrivește. Același raționament antrenează în general arhitectura preflight-ului: traversarea parțială produce treceri false, iar o trecere falsă la o verificare de conformitate e mai rea decât lipsa oricărei verificări, pentru că e înregistrată drept dovadă
var
Lib: TPDFlib;
Diag: WideString;
begin
Lib := TPDFlib.Create(nil);
try
Lib.LoadFromFile('assembly-drawings.pdf');
if Lib.SetPDFEMode(1) = 0 then
raise Exception.Create('PDF/E author mode was refused');
// Modul de autor ține metadatele de ciclu de viață la pas la fiecare salvare.
// Întrebați înainte de salvare dacă documentul ar trece propria sa poartă
if not Lib.PDFEReadyForSave then
begin
Diag := Lib.GetPDFEDiagnostics;
Writeln('PDF/E blockers: ', Diag);
Exit;
end;
Lib.SaveToFile('assembly-drawings-pdfe.pdf');
finally
Lib.Free;
end;
end;
Metadatele de ciclu de viață sunt o obligație la fiecare salvare
PDF/E-1 cere mai mult decât un identificator de document. Setul minim include identificatorul de document de media management, un identificator de versiune, o clasă de redare, ora creării, ora modificării, ora metadatelor și un titlu. E un vocabular de urmărire a reviziilor, iar el există pentru că se așteaptă ca un livrabil de inginerie să fie reemis, nu scris o singură dată
Consecința pentru o implementare e că aceste câmpuri nu pot fi stabilite la crearea documentului. Dacă ora modificării se scrie când activați modul și documentul e editat după aceea, snapshot-ul XMP și starea reală a documentului au derivat, iar un validator care le compară raportează o inconsecvență pe care nimeni n-a intenționat-o. Modul de autor sincronizează deci câmpurile imediat înainte de fiecare salvare, astfel încât metadatele descriu octeții pe care urmează să-i scrie, nu octeții care existau când a fost activat modul
E un principiu general pentru metadatele de conformitate și merită enunțat separat de PDF/E: metadatele derivate aparțin pe calea de salvare, nu pe cea de editare. Orice câmp calculat din starea documentului trebuie recalculat în momentul în care starea îngheață, altfel e un cache fără invalidare
Regula care face validarea conținutului strictă
PDF/E-1 nu permite operatorilor din secțiunea de compatibilitate să absoarbă conținut necunoscut. În PDF-ul obișnuit, BX și EX incadrează o regiune în care un consumator trebuie să ignore operatorii pe care nu-i recunoaște, adică ieșirea de salvare care lasă un producător să emită construcții mai noi fără să strice cititoarele mai vechi. Sub PDF/E-1 ieșirea aceea e închisă, deci orice operator pe care preflight-ul nu îl recunoaște e raportat necondiționat, indiferent dacă stă sau nu într-o secțiune de compatibilitate
Efectul asupra unui validator e semnificativ. Nu poate sări regiuni pe care nu le înțelege, ceea ce înseamnă că parser-ul de operanzi trebuie să parseze cu adevărat fiecare operator din fiecare content stream. Aici intră în scenă limitele. Traversarea e plafonată la 128 de niveluri de imbricare, un milion de obiecte și 64 MiB de conținut, iar aceste limite nu sunt reglaje de performanță. Un fișier ostil sau pur și simplu stricat poate prezenta un graf de obiecte cu cicluri sau o adâncime de imbricare care transformă un validator recursiv într-un stack overflow, iar limitele sunt cele care împiedică o trecere de validare să devină un vector de denial-of-service. Aceeași postură defensivă e descrisă în parsarea în siguranță a PDF-urilor neverificabile
// Validare de sine stătătoare a unui fișier pe care nu l-ați produs,
// fără a-l încărca într-o instanță de document
var
Issues: TStringList;
Stream: TFileStream;
I: Integer;
begin
Issues := TStringList.Create;
Stream := TFileStream.Create('incoming.pdf', fmOpenRead or fmShareDenyWrite);
try
if CheckCompliancePDFE(Stream, '', 0, Issues) = 0 then
for I := 0 to Issues.Count - 1 do
Writeln('PDF/E: ', Issues[I]);
finally
Stream.Free;
Issues.Free;
end;
end;
Ce repară poarta de salvare și ce refuză
Poarta își împarte munca în două etape, iar împărțirea e o idee de design folositoare în sine. Mai întâi normalizează lucrurile reparabile în siguranță: fanioanele de tipărire ale adnotațiilor, fanioanele fără zoom și fără rotire ale adnotațiilor de text și fanionul de generare a aspectului din dicționarul de formular. Sunt setări cu o singură valoare corectă sub profil și fără conținut de informație, deci repararea lor tăcută e corectă, iar refuzul din cauza lor ar fi pedanterie
Apoi verifică constrângerile care nu pot fi reparate fără a schimba ce înseamnă documentul: versiunea, identificarea, criptarea, output intent-ul, coerența culorii de dispozitiv și prezența conținutului de formular dinamic. Un document care pică vreuna dintre ele e refuzat, pentru că a inventa un output intent sau a alege o familie de culori în locul autorului ar produce un fișier care trece validarea și înșală asupra conținutului
Citirea diagnosticelor prin GetPDFEDiagnostics înainte de salvare transformă acel refuz într-o listă acționabilă, nu într-o operație eșuată. Într-un pipeline de lot, apelați-o pe fiecare document, înregistrați blocajele per fișier și dirijați eșecurile spre o coadă pe care o privește un om. E cu mult mai util decât o salvare care ridică excepție, pentru că blocajele tind să se adune: patruzeci de documente care pică din același output intent lipsă e o singură corecție, nu patruzeci
Alegerea dintre profilurile de arhivare
PDF/E-1 e ținta potrivită când livrabilul e documentație de inginerie cu un ciclu de viață al reviziilor și, mai ales, când coerența culorii de dispozitiv contează, pentru că ieșirea merge spre plottere și imprimante de format mare. PDF/A e ținta potrivită când scopul e lizibilitatea pe termen lung a documentelor în general și e profilul cu cel mai larg suport în validatoare. Cele două nu sunt interschimbabile, iar un document poate satisface unul și pică pe celălalt
Dacă alegeți, plecați de la cine validează fișierul la capătul lanțului. Uneltele de validare PDF/A sunt peste tot, iar preflight-ul corespunzător din PDFlibPas e descris în preflight PDF/A și PDF/UA. Validarea PDF/E e mai specializată și e de regulă o cerință contractuală, nu un implicit. Când o arhivă existentă trebuie adusă la un profil pentru care nu a fost scrisă niciodată, calea de reparare a metadatelor din conversia în PDF/A cu reparare de metadate e tiparul de urmat, iar aceeași formă se aplică și aici: identificați, reparați ce e sigur, refuzați restul cu o listă
Modul de autor, preflight-ul de conținut cu limite și verificarea de conformitate de sine stătătoare vin toate cu PDFlibPas, biblioteca PDF pentru Delphi, deci un document poate fi produs sub profil și verificat independent după aceea printr-o cale de cod separată, ceea ce e singurul aranjament în care merită să aveți încredere pentru o pretenție de conformitate