Articol tehnic

Salvări la versiune PDF exactă în Delphi: conformitate PDFiumPas

PDFiumPas, wrapper-ul Delphi și C++Builder în jurul motorului PDFium al Google, salvează un document la o versiune PDF exactă de la 1.3 la 1.7 prin parametrul PdfVersion al metodei TPdf.SaveAs. Propriul apel FPDF_SaveWithVersion al PDFium rescrie doar antetul %PDF-M.m, fără a verifica dacă conținutul efectiv al documentului este legal la acea versiune. PDFiumPas închide acel gol cu o trecere de conformitate post-salvare care parcurge lanțul de revizii de referință încrucișată (cross-reference) activ și verifică declarațiile Adobe Extension Level înainte ca fișierul să părăsească metoda

Acea distincție contează cel mai mult în producția de tipar, unde un profil PDF/X numește o versiune PDF exactă, iar un instrument de preflight sau un RIP respinge orice contrazice silențios propriul antet, un scenariu acoperit de pe partea de ieșire în validarea documentelor PDF/X gata pentru tipar cu PDFiumPas. SaveAs expune ținta ca enumerarea TPdfVersion, pv13 până la pv17 alături de valorile mai vechi pv10 până la pv12, plus un TSaveOption independent pentru rescrieri incrementale sau complete. Transmiteți PdfVersion, iar PDFiumPas face două sarcini într-un singur apel: cere PDFium să marcheze antetul solicitat, apoi recitește octeții proaspăt scriși și refuză să returneze un fișier al cărui conținut activ nu poate exista legal la acea versiune

var
  Pdf: TPdf;
begin
  Pdf:= TPdf.Create(nil);
  try
    Pdf.FileName:= 'source.pdf';
    Pdf.Active:= True;
    try
      Pdf.SaveAs('press-ready.pdf', saNoIncremental, pv17);
    except
      on E: Exception do
        // E.Message names the offending feature and the version or
        // extension level it actually needs, for example:
        // "RichMedia annotations and RichMediaExecute actions require
        // /Extensions /ADBE with /BaseVersion /1.7 and /ExtensionLevel 3
        // or newer."
        raise;
    end;
  finally
    Pdf.Free;
  end;
end;

De ce este ultima definiție de obiect din fișier lucrul greșit de a avea încredere?

Ultimul obiect fizic cu un anumit număr într-un fișier PDF nu este neapărat obiectul pe care un cititor conform l-ar rezolva astăzi pentru acel număr. Un PDF care a trecut prin mai multe actualizări incrementale nu are un singur graf de obiecte, are un istoric al acestora stratificate în interiorul unui singur fișier, iar fiecare ciclu de adăugare poate elibera un obiect, redefinindu-l sub un nou număr de generație, sau poate lăsa corpul său fizic vechi așezat între doi marcatori endobj fără nicio intrare de referință încrucișată care să mai indice spre el

PDFiumPas a lovit exact acel mod de eșec înainte de a urmări explicit reviziile xref: o adnotare Redact orfelinizată de o rescriere ulterioară de obiect de pagină, sau un dicționar /MarkInfo rămas fizic prezent fără nicio intrare xref care să indice spre el, tot puteau apărea într-o scanare de octeți și tot puteau declanșa o verificare de caracteristică de versiune care nu se mai aplica documentului pe care un cititor l-ar deschide efectiv. Direcția eșecului era respingere falsă, nu acceptare falsă: un fișier care depășise cu adevărat o caracteristică în revizia sa curentă tot putea fi blocat de la salvare la o versiune mai mică din cauza conținutului pe care nimeni nu îl mai putea accesa

Cum determină PDFiumPas care definiții de obiecte sunt efectiv active?

PDFiumPas rezolvă setul de obiecte active la fel cum face un cititor conform, parcurgând lanțul de referință încrucișată, în loc să scaneze octeții pentru antete de obiecte. Rezolvatorul pornește de la ultimul offset startxref din fișier și urmărește fiecare legătură /Prev înapoi prin revizii mai vechi, analizând tabele clasice de referință încrucișată, fluxuri hibride legate de /XRefStm și fluxuri pure de referință încrucișată pe parcurs. Parcurgerea rulează de la cel mai nou la cel mai vechi și stabilește fiecare număr de obiect prima dată când este văzut, astfel încât o intrare liberă într-o revizie ulterioară eclipsează corect un corp de obiect scris într-una anterioară, iar o redefinire sub un offset sau o generație nouă câștigă întotdeauna față de ce a înlocuit

Membrii de flux de obiecte primesc o verificare suplimentară pe care o simplă căutare de offset nu o poate oferi de una singură, un mecanism acoperit mai în profunzime în validarea fluxurilor de obiecte și xref cu PDFiumPas. Un obiect comprimat recuperat dintr-un /ObjStm trebuie să aibă fluxul său părinte confirmat activ în aceeași parcurgere, iar indexul său trebuie să fie de acord cu propria poziție a membrului în interiorul antetului acelui flux, înainte ca PDFiumPas să îl trateze ca conținut viu. ISO 32000-1 secțiunea 7.5.8.4 descrie chiar un caz de referință hibridă unde un tabel de compatibilitate clasic marchează un obiect liber, în timp ce intrarea /XRefStm a trailer-ului definește simultan același obiect ca un membru comprimat în altă parte; PDFiumPas îmbină fluxul xref suplimentar în aceeași revizie înainte ca intrările clasice să fie aplicate, astfel încât definiția comprimată câștigă așa cum intenționează specificația

Nivelurile de extensie Adobe: poarta de deasupra numărului de versiune

Un antet %PDF-1.7 promite doar setul de caracteristici pe care ISO 32000-1 l-a standardizat în 2008, în timp ce mai multe capabilități pe care producătorii PDF se bazează astăzi au fost livrate ulterior ca suplimente doar-Adobe stratificate peste același număr de versiune. Adobe a înregistrat fiecare suplement ca o pereche BaseVersion și ExtensionLevel înregistrată în dicționarul /Extensions al catalogului documentului sub un prefix de dezvoltator, ADBE pentru propriile extensii Adobe, astfel încât un cititor poate distinge un simplu fișier PDF 1.7 de unul care implementează de asemenea un nivel de extensie numerotat. Salvarea la pv17 fără acea declarație nu este o eroare de una singură; devine una doar în momentul în care conținutul activ chiar depinde de o caracteristică pe care declarația ar trebui să o acopere

Care caracteristici de versiune înaltă declanșează poarta de versiune explicită?

PDFiumPas verifică o listă specifică, condusă de specificație, în loc să ghicească doar din numărul de versiune. Dicționarele de imagine care poartă o intrare explicită /SMaskInData sau o valoare /BitsPerComponent de 16 necesită amândouă PDF 1.5, cazul de șaisprezece biți urmând direct regulile de componente de imagine ale PDF Reference 1.5 secțiunea 4.8. Adnotările RichMedia și acțiunile RichMediaExecute necesită /BaseVersion /1.7 cu /ExtensionLevel 3 sau mai mare. Fluxurile 3D PRC, identificate printr-un dicționar care poartă atât /Type /3D, cât și /Subtype /PRC, necesită aceeași versiune de bază dar doar /ExtensionLevel 1. Dicționarele Measure geospațiale și adnotările Projection necesită /BaseVersion /1.7 cu /ExtensionLevel 3, același suplement Adobe de care depinde RichMedia

Verificarea geospațială poartă un detaliu de citire a specificației care merită cunoscut dacă vreodată construiți propria logică cu poartă de versiune peste PDFiumPas. ISO 32000-1 Tabelul 254 marchează intrarea /Type a dicționarului Measure ca opțională, notând doar că "dacă este prezentă, trebuie să fie Measure," în timp ce Tabelul 311 face /Type obligatoriu pentru dicționarul de flux 3D în care trăiește conținutul PRC. Ieșirea GeoPDF reală din instrumente de mapare de obicei omite /Type pe dicționarul Measure și scrie doar /Subtype /GEO, așa că detectorul geospațial al PDFiumPas se potrivește pe /Subtype singur, în loc să ceară ambele chei așa cum poate face în siguranță detectorul său PRC 3D. A cere /Type pe ambele dicționare ar fi lăsat conținutul GeoPDF conform să treacă neobservat de poartă, ajungând într-un simplu fișier PDF 1.7 fără nicio declarație de nivel de extensie care să îl susțină

Retrogradează automat PDFiumPas caracteristicile neacceptate?

Nu ca o capabilitate generală, iar presupunerea contrarie este greșeala de evitat aici. SaveAs canalizează versiunea țintă printr-o rutină internă, ValidatePdfVersionCompliance, iar atunci când acea rutină găsește o caracteristică pe care versiunea țintă sau declarația sa de nivel de extensie nu o poate susține, SaveAs ridică o excepție care poartă textul de eroare al rutinei, în loc să scrie fișierul; apelantul primește înapoi un motiv precis, numit după caracteristică, niciodată un document rescris silențios. Singurul loc unde PDFiumPas chiar rescrie automat conținut este o țintă PDF 1.3, unde elimină implicitele de transparență semantic neutre /BM /Normal, /CA 1 și /ca 1 pe care PDFium le scrie întotdeauna în dicționarele ExtGState indiferent de versiunea țintă, pentru că acele valori specifice nu poartă nicio semnificație vizuală, iar PDF 1.3 precedă complet acele chei

// PDF 1.3 targets rewrite the saved bytes to strip transparency
// defaults PDFium always emits, so incremental mode cannot apply
Pdf.SaveAs('legacy-archive.pdf', saIncremental, pv13);
// raises: PDF 1.3 normalization is incompatible with incremental
// save mode

Transparența autentic non-implicită și măștile soft de imagine încă eșuează direct la o țintă PDF 1.3, pentru că eliminarea lor ar schimba modul în care arată efectiv pagina, iar PDFiumPas nu va lua acea decizie în numele dvs. Două limite înrudite merită planificate înainte ca o versiune exactă să intre într-o conductă de lot. Ieșirea la versiune explicită nu poartă niciodată un dicționar /Encrypt; salvarea eșuează imediat dacă sursa este protejată, ceea ce se întâmplă să se alinieze cu profilurile PDF/X și PDF/A care interzic oricum criptarea, dar înseamnă că decriptarea este un pas separat în fluxul dvs. de lucru, nu ceva ce face SaveAs pentru dvs. PDFiumPas nu are de asemenea nicio metodă publică pentru a scrie o declarație /Extensions /ADBE pe un catalog, așa că un fișier sursă care conține conținut RichMedia, PRC 3D, sau geospațial dar căruia îi lipsește acea declarație nu va trece de poartă indiferent de ce PdfVersion cereți; declarația trebuie să existe deja în sursă, de obicei pentru că instrumentul de generare a scris-o, sau caracteristica trebuie să iasă înainte de salvare. Proprietatea doar-citire TPdf.PdfVersion merită verificată înainte ca o salvare la versiune exactă să fie măcar încercată, întrucât rezolvă aceeași versiune efectivă conștientă de catalog, antet sau suprascriere /Version, oricare este curentă, pe care validatorul de la momentul salvării însuși se bazează

Pdf.FileName:= 'incoming.pdf';
Pdf.Active:= True;
// PdfVersion resolves the same catalog-aware effective version the
// save-time validator uses, so a mismatch here is worth investigating
// before spending a full SaveAs attempt on it
LogSourceVersion('incoming.pdf', Pdf.PdfVersion);

Tratați o excepție SaveAs pe o țintă de versiune exactă ca un raport de preflight, nu ca un bug: mesajul numește clauza exactă pe care documentul sursă o încalcă, ceea ce este exact informația de care are nevoie o tipografie sau o conductă de arhivare înainte ca un fișier să meargă mai departe. Calea de salvare la versiune explicită, rezolvatorul de revizii xref active, și verificările de nivel de extensie Adobe descrise aici sunt livrate ca parte a componentei PDFiumPas standard pentru Delphi și C++Builder; pagina de produs conține referința completă a TPdf.SaveAs, alături de restul API-ului de conformitate și formulare