Tehnični članak

Natančno shranjevanje različice PDF v Delphiju: skladnost s PDFiumPas

PDFiumPas, ovoj Delphi in C++Builder okoli Googlovega mehanizma PDFium, shrani dokument v natančni različici PDF od 1.3 do 1.7 prek parametra PdfVersion metode TPdf.SaveAs. Klic FPDF_SaveWithVersion v samem PDFiumu samo prepiše glavo %PDF-M.m, ne da bi preveril, ali je dejanska vsebina dokumenta pri tej različici dovoljena. PDFiumPas to vrzel zapolni s preverjanjem skladnosti po shranjevanju, ki prehodi aktivno verigo revizij navzkrižnih sklicev in preveri deklaracije Adobe Extension Level, preden datoteka zapusti metodo

Ta razlika je najpomembnejša pri tiskarski produkciji, kjer profil PDF/X določa natančno različico PDF, orodje za preflight ali RIP pa zavrne vse, kar se tiho ne ujema z njegovo glavo, kar je z izhodne strani obravnavano v članku preverjanje dokumentov PDF/X, pripravljenih za tisk, s PDFiumPas. SaveAs cilj izpostavi kot enum TPdfVersion, z vrednostmi pv13 do pv17 poleg starejših vrednosti pv10 do pv12, ter z neodvisnim TSaveOption za inkrementalni ali celotni prepis. Posredujte PdfVersion in PDFiumPas v enem klicu opravi dve nalogi: PDFiumu naroči, naj zapiše zahtevano glavo, nato pa znova prebere pravkar zapisane bajte in zavrne vrnitev datoteke, katere aktivna vsebina pri tej različici pravno ne more obstajati

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;

Zakaj je zadnja definicija objekta v datoteki napačna stvar za zaupanje?

Zadnji fizični objekt z določeno številko v datoteki PDF ni nujno objekt, ki bi ga skladen bralnik danes razrešil za to številko. PDF, ki je šel skozi več inkrementalnih posodobitev, nima enega samega grafa objektov, temveč zgodovino slojev v eni datoteki, vsak cikel dodajanja pa lahko sprosti objekt, ga na novo definira pod novo generacijsko številko ali pusti njegovo staro fizično telo med dvema oznakama endobj, ne da bi ga katerikoli vnos navzkrižnega sklica še kazal

PDFiumPas je pred izrecnim sledenjem revizijam xref naletel prav na ta način odpovedi: opomba Redact, osirotela zaradi poznejšega prepisa objekta strani, ali slovar /MarkInfo, ki je fizično ostal prisoten brez vnosa xref, se je lahko še vedno pojavil pri pregledu bajtov in sprožil preverjanje funkcije različice, ki za dokument, kot bi ga dejansko odprl bralnik, ni več veljala. Smer odpovedi je bila lažna zavrnitev, ne lažno sprejetje: datoteka, ki je v trenutni reviziji res prenehala uporabljati določeno funkcijo, je bila še vedno lahko blokirana pri shranjevanju v nižji različici zaradi vsebine, do katere ni bilo več mogoče dostopati

Kako PDFiumPas ugotovi, katere definicije objektov so dejansko aktivne?

PDFiumPas razreši aktivni nabor objektov tako kot skladen bralnik, tako da prehodi verigo navzkrižnih sklicev, namesto da bi v bajtih iskal glave objektov. Razreševalnik začne pri zadnjem odmiku startxref v datoteki in sledi vsaki povezavi /Prev nazaj skozi starejše revizije, pri tem pa razčleni klasične tabele navzkrižnih sklicev, hibridne tokove, povezane prek /XRefStm, in čiste tokove navzkrižnih sklicev. Prehod poteka od najnovejše proti najstarejši reviziji in vsako številko objekta določi ob prvem pojavu, zato prost vnos v poznejši reviziji pravilno zasenči telo objekta, zapisano v starejši, redefinicija z novim odmikom ali generacijo pa vedno zmaga nad tem, kar je nadomestila

Člani tokov objektov potrebujejo dodatno preverjanje, ki ga navadno iskanje odmika samo ne more zagotoviti, mehanizem pa je podrobneje obravnavan v članku preverjanje objektnih tokov in tokov navzkrižnih sklicev s PDFiumPas. Stisnjeni objekt, pridobljen iz /ObjStm, mora imeti potrjeno, da je njegov nadrejeni tok aktiven v istem prehodu, njegov indeks pa se mora ujemati z lastnim položajem člana v glavi tega toka, preden ga PDFiumPas obravnava kot živo vsebino. Razdelek 7.5.8.4 standarda ISO 32000-1 celo opisuje hibridni primer sklicev, v katerem klasična združljivostna tabela označi objekt kot prost, medtem ko vnos /XRefStm v trailerju istočasno določi isti objekt kot stisnjenega člana drugje; PDFiumPas dopolnilni tok xref združi v isto revizijo, preden uporabi klasične vnose, zato stisnjena definicija zmaga tako, kot predvideva specifikacija

Adobe Extension Level: vrata nad številko različice

Glava %PDF-1.7 obljublja le nabor funkcij, ki jih je standardiziral ISO 32000-1 leta 2008, medtem ko so bile številne zmožnosti, na katere se izdelovalci PDF danes zanašajo, izdane pozneje kot dodatki samo za Adobe, naloženi na isto številko različice. Adobe je vsak dodatek registriral kot par BaseVersion in ExtensionLevel, zapisan v slovarju /Extensions kataloga dokumenta pod predpono razvijalca, za Adobejeve lastne razširitve ADBE, zato lahko bralnik razlikuje navadno datoteko PDF 1.7 od datoteke, ki izvaja tudi oštevilčeno raven razširitve. Shranjevanje v pv17 brez te deklaracije samo po sebi ni napaka; napaka postane šele v trenutku, ko je aktivna vsebina dejansko odvisna od funkcije, ki naj bi jo deklaracija pokrivala

Katere funkcije višjih različic sprožijo preverjanje izrecne različice?

PDFiumPas preverja določen seznam, utemeljen v specifikaciji, namesto da bi ugibal samo iz številke različice. Slovarji slik z izrecnim vnosom /SMaskInData ali z vrednostjo /BitsPerComponent 16 zahtevajo PDF 1.5, primer s šestnajstimi biti pa neposredno sledi pravilom o komponentah slik iz razdelka 4.8 PDF Reference 1.5. Opombe RichMedia in dejanja RichMediaExecute zahtevajo /BaseVersion /1.7 z /ExtensionLevel 3 ali višjim. Tokovi 3D PRC, prepoznani po slovarju z obema vnosoma /Type /3D in /Subtype /PRC, zahtevajo isto osnovno različico, vendar le /ExtensionLevel 1. Slovarji Geospatial Measure in opombe Projection zahtevajo /BaseVersion /1.7 z /ExtensionLevel 3, isto razširitev Adobe, od katere je odvisen RichMedia

Preverjanje geoprostorskih podatkov vsebuje podrobnost pri branju specifikacije, ki jo je vredno poznati, če boste kdaj na vrhu PDFiumPas gradili lastno logiko omejevanja različic. Tabela 254 standarda ISO 32000-1 označuje vnos /Type slovarja Measure kot neobvezen in navaja le, da mora biti, če je prisoten, Measure, medtem ko tabela 311 določa /Type kot obvezen za slovar 3D-toka, v katerem je vsebina PRC. Izhod GeoPDF iz kartografskih orodij v praksi pogosto izpusti /Type v slovarju Measure in zapiše samo /Subtype /GEO, zato geoprostorski detektor PDFiumPas preverja samo /Subtype, namesto da bi zahteval oba ključa, kot lahko varno stori njegov detektor 3D PRC. Če bi /Type zahtevali v obeh slovarjih, bi skladna vsebina GeoPDF lahko zdrsnila mimo preverjanja in pristala v navadni datoteki PDF 1.7 brez deklarirane ravni razširitve, ki bi jo podprla

Ali PDFiumPas samodejno zniža različico za nepodprte funkcije?

Ne kot splošno zmožnost, in prav nasprotna domneva je napaka, ki se ji je treba izogniti. SaveAs ciljno različico posreduje notranji rutini ValidatePdfVersionCompliance, in ko ta rutina najde funkcijo, ki je ciljna različica ali njena deklaracija ravni razširitve ne more podpreti, SaveAs sproži izjemo z besedilom napake rutine, namesto da bi zapisala datoteko; klicatelj dobi natančen razlog z imenom funkcije, nikoli tiho prepisanega dokumenta. Edino mesto, kjer PDFiumPas samodejno prepiše vsebino, je cilj PDF 1.3, pri katerem odstrani pomensko nevtralne privzete vrednosti prosojnosti /BM /Normal, /CA 1 in /ca 1, ki jih PDFium vedno zapiše v slovarje ExtGState ne glede na ciljno različico, saj te določene vrednosti nimajo vizualnega pomena, PDF 1.3 pa je starejši od teh ključev

// 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

Prava nestandardna prosojnost in mehke maske slik pri cilju PDF 1.3 še vedno neposredno povzročita napako, ker bi njuna odstranitev spremenila dejanski videz strani, PDFiumPas pa te odločitve ne bo sprejel namesto vas. Preden natančna različica vstopi v paketni cevovod, je vredno načrtovati še dve povezani omejitvi. Izhod z izrecno različico nikoli ne vsebuje slovarja /Encrypt; shranjevanje takoj odpove, če je vir zaščiten, kar se sicer ujema s profiloma PDF/X in PDF/A, ki prepovedujeta šifriranje, vendar pomeni, da je dešifriranje ločen korak v vašem postopku in ga SaveAs ne opravi namesto vas. PDFiumPas prav tako nima javne metode za zapis deklaracije /Extensions /ADBE v katalog, zato izvorna datoteka z vsebino RichMedia, PRC 3D ali geospatial brez te deklaracije ne bo prestala preverjanja, ne glede na zahtevani PdfVersion; deklaracija mora že obstajati v viru, običajno zato, ker jo je zapisalo avtorsko orodje, ali pa mora biti funkcija pred shranjevanjem odstranjena. Pred poskusom shranjevanja v natančni različici je vredno preveriti lastnost samo za branje TPdf.PdfVersion, saj razreši isto katalogu prilagojeno učinkovito različico, glavo ali preglasitev /Version, karkoli je trenutno veljavno, na katero se opira tudi preverjanje ob shranjevanju

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

Izjemo SaveAs pri cilju z natančno različico obravnavajte kot poročilo preflight in ne kot napako: sporočilo poimenuje natančno določilo, ki ga izvorni dokument krši, kar je prav tista informacija, ki jo tiskarna ali arhivski cevovod potrebuje, preden datoteka nadaljuje pot. Pot shranjevanja z izrecno različico, razreševalnik aktivnih revizij xref in preverjanja Adobe Extension Level so opisani kot del standardne komponente PDFiumPas za Delphi in C++Builder; stran izdelka vsebuje celoten sklic za TPdf.SaveAs skupaj z drugimi API-ji za skladnost in obrazce