PDFiumPas, omotač za Delphi i C++Builder oko Google-ovog PDFium mehanizma, čuva dokument u tačnoj PDF verziji od 1.3 do 1.7 preko parametra PdfVersion metode TPdf.SaveAs. Sopstveni PDFium poziv FPDF_SaveWithVersion samo prepisuje zaglavlje %PDF-M.m, bez provere da li je stvarni sadržaj dokumenta dozvoljen u toj verziji. PDFiumPas popunjava tu prazninu proverom usklađenosti posle čuvanja, koja prolazi kroz lanac aktivnih unakrsnih referenci i proverava deklaracije Adobe Extension Level pre nego što datoteka napusti metodu
Ta razlika je najvažnija u štampi, gde PDF/X profil zahteva tačnu PDF verziju, a preflight alat ili RIP odbija sve što se neprimetno ne slaže sa sopstvenim zaglavljem, što je sa izlazne strane obrađeno u članku provera PDF/X dokumenata spremnih za štampu pomoću PDFiumPas-a. SaveAs izlaže cilj kao TPdfVersion enumeraciju, od pv13 do pv17 uz starije vrednosti pv10 do pv12, kao i nezavisni TSaveOption za inkrementalno ili potpuno ponovno upisivanje. Prosledite PdfVersion i PDFiumPas u jednom pozivu obavlja dva posla: traži od PDFium-a da upiše zahtevano zaglavlje, zatim ponovo čita upravo upisane bajtove i odbija da vrati datoteku čiji aktivni sadržaj ne može zakonito da postoji u toj verziji
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;
Zašto je poslednja definicija objekta u datoteci pogrešna stvar kojoj treba verovati?
Poslednji fizički objekat sa određenim brojem u PDF datoteci nije nužno objekat koji bi usklađen čitač danas razrešio za taj broj. PDF koji je prošao kroz nekoliko inkrementalnih izmena nema jedan graf objekata, već istoriju slojeva u jednoj datoteci, a svaki ciklus dopisivanja može osloboditi objekat, ponovo ga definisati pod novim generacionim brojem ili ostaviti njegovo staro fizičko telo između dva endobj markera bez unakrsne reference koja na njega više pokazuje
PDFiumPas je upravo na taj način grešio pre nego što je počeo eksplicitno da prati revizije unakrsnih referenci: Redact anotacija koju je kasnija izmena objekata stranice ostavila bez vlasnika, ili rečnik /MarkInfo koji je fizički ostao prisutan bez unakrsne reference koja na njega pokazuje, i dalje su mogli da se pojave u skeniranju bajtova i aktiviraju proveru funkcije verzije koja se više nije odnosila na dokument koji bi čitač zaista otvorio. Posledica je bilo lažno odbijanje, a ne lažno prihvatanje: datoteka koja je u aktuelnoj reviziji zaista prestala da koristi neku funkciju i dalje je mogla biti blokirana pri čuvanju u nižoj verziji zbog sadržaja do kojeg više niko nije mogao da dođe
Kako PDFiumPas određuje koje su definicije objekata zaista aktivne?
PDFiumPas razrešava skup aktivnih objekata na isti način kao usklađen čitač, prolazeći kroz lanac unakrsnih referenci umesto da pretražuje bajtove u potrazi za zaglavljima objekata. Razrešivač počinje od poslednjeg pomaka startxref u datoteci i prati svaku vezu /Prev unazad kroz starije revizije, usput obrađujući klasične tabele unakrsnih referenci, hibridne tokove povezane preko /XRefStm i čiste tokove unakrsnih referenci. Obilazak ide od najnovije ka najstarijoj reviziji i prihvata svaki broj objekta prvi put kada ga vidi, pa slobodan unos u kasnijoj reviziji ispravno zasenjuje telo objekta upisano u ranijoj, dok redefinicija sa novim pomakom ili generacionim brojem uvek ima prednost nad onim što je zamenila
Članovi toka objekata zahtevaju dodatnu proveru koju obično traženje po pomaku ne može samo da pruži, a taj mehanizam je detaljnije obrađen u članku provera tokova objekata i unakrsnih referenci pomoću PDFiumPas-a. Komprimovani objekat pronađen u /ObjStm mora imati roditeljski tok potvrđen kao aktivan u istom obilasku, a njegov indeks mora odgovarati sopstvenom položaju člana u zaglavlju tog toka pre nego što ga PDFiumPas prihvati kao živi sadržaj. ISO 32000-1, odeljak 7.5.8.4, čak opisuje hibridni slučaj u kome klasična tabela kompatibilnosti označava objekat kao slobodan, dok unos /XRefStm u traileru istovremeno definiše taj isti objekat kao komprimovani član na drugom mestu; PDFiumPas spaja dopunski tok unakrsnih referenci sa istom revizijom pre primene klasičnih unosa, pa komprimovana definicija dobija prednost onako kako specifikacija nalaže
Adobe nivoi proširenja: kapija iznad broja verzije
Zaglavlje %PDF-1.7 samo obećava skup funkcija koji je ISO 32000-1 standardizovao 2008. godine, dok su neke mogućnosti na koje se proizvođači PDF-a danas oslanjaju stigle kasnije kao Adobe-ove dopune nadograđene na isti broj verzije. Adobe je svaku dopunu registrovao kao par BaseVersion i ExtensionLevel, upisan u rečnik /Extensions kataloga dokumenta pod prefiksom proizvođača, pri čemu je ADBE prefiks za Adobe-ova proširenja, tako da čitač može razlikovati običnu PDF 1.7 datoteku od one koja podržava i numerisani nivo proširenja. Čuvanje u pv17 bez te deklaracije samo po sebi nije greška; greška nastaje tek kada aktivni sadržaj zaista zavisi od funkcije koju ta deklaracija treba da pokrije
Koje funkcije viših verzija aktiviraju kapiju eksplicitne verzije?
PDFiumPas proverava konkretnu listu zasnovanu na specifikaciji, umesto da nagađa samo na osnovu broja verzije. Rečnici slika sa izričitim unosom /SMaskInData ili vrednošću /BitsPerComponent 16 zahtevaju PDF 1.5, pri čemu slučaj sa šesnaest bitova direktno prati pravila za komponente slike iz PDF Reference 1.5, odeljak 4.8. RichMedia anotacije i akcije RichMediaExecute zahtevaju /BaseVersion /1.7 sa /ExtensionLevel 3 ili višim nivoom. PRC 3D tokovi, prepoznati po rečniku koji sadrži i /Type /3D i /Subtype /PRC, zahtevaju istu osnovnu verziju, ali samo /ExtensionLevel 1. Geoprostorni Measure rečnici i Projection anotacije zahtevaju /BaseVersion /1.7 sa /ExtensionLevel 3, istu Adobe-ovu dopunu od koje zavise RichMedia funkcije
Geoprostorna provera sadrži detalj iz specifikacije koji vredi znati ako ikada napravite sopstvenu logiku za ograničavanje verzije na vrhu PDFiumPas-a. Tabela 254 standarda ISO 32000-1 označava unos /Type u Measure rečniku kao opcionalan i navodi samo da "if present, shall be Measure,", dok Tabela 311 čini /Type obaveznim za rečnik 3D toka u kojem se nalazi PRC sadržaj. Stvarni GeoPDF izlaz iz kartografskih alata redovno izostavlja /Type iz Measure rečnika i upisuje samo /Subtype /GEO, pa PDFiumPas-ov geoprostorni detektor proverava samo /Subtype, umesto da zahteva oba ključa kao što njegov PRC 3D detektor bezbedno može. Zahtev za /Type u oba rečnika omogućio bi usklađenom GeoPDF sadržaju da neprimećeno prođe kapiju i završi u običnoj PDF 1.7 datoteci bez deklaracije nivoa proširenja koja bi ga podržala
Da li PDFiumPas automatski snižava verziju nepodržanih funkcija?
Ne kao opštu mogućnost, i upravo je suprotna pretpostavka greška koju ovde treba izbeći. SaveAs prosleđuje ciljnu verziju internoj rutini ValidatePdfVersionCompliance, a kada ta rutina pronađe funkciju koju ciljna verzija ili deklarisani nivo proširenja ne podržava, SaveAs podiže izuzetak sa tekstom greške te rutine umesto da upiše datoteku; pozivalac dobija precizan razlog sa imenom funkcije, a nikada ne dobija neprimetno prepisan dokument. Jedino mesto na kojem PDFiumPas automatski prepisuje sadržaj jeste cilj PDF 1.3, kada uklanja semantički neutralne podrazumevane vrednosti transparentnosti /BM /Normal, /CA 1 i /ca 1 koje PDFium uvek upisuje u rečnike ExtGState bez obzira na ciljnu verziju, jer te konkretne vrednosti nemaju vizuelno značenje, a PDF 1.3 je stariji od tih ključeva
// 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
Stvarna transparentnost koja nije podrazumevana i meke maske slika i dalje odmah izazivaju grešku pri cilju PDF 1.3, jer bi njihovo uklanjanje promenilo stvarni izgled stranice, a PDFiumPas tu odluku neće doneti umesto vas. Pre nego što tačna verzija uđe u grupni tok obrade, treba planirati još dva povezana ograničenja. Izlaz sa eksplicitnom verzijom nikada ne sadrži rečnik /Encrypt; čuvanje odmah ne uspeva ako je izvor zaštićen, što se poklapa sa PDF/X i PDF/A profilima koji ionako zabranjuju šifrovanje, ali znači da je dešifrovanje poseban korak u vašem toku rada, a ne nešto što SaveAs obavlja umesto vas. PDFiumPas takođe nema javnu metodu za upisivanje deklaracije /Extensions /ADBE u katalog, pa izvorna datoteka koja sadrži RichMedia, PRC 3D ili geoprostorni sadržaj, ali nema tu deklaraciju, neće proći proveru bez obzira na vrednost koju zatražite kroz PdfVersion; deklaracija već mora postojati u izvoru, najčešće zato što ju je upisao alat za izradu, ili funkcija mora biti uklonjena pre čuvanja. Svojstvo samo za čitanje TPdf.PdfVersion vredi proveriti pre pokušaja čuvanja u tačnoj verziji, jer razrešava isti efektivni katalogom određen nivo — zaglavlje ili nadjačavanje preko /Version, šta god je trenutno važeće — na koji se oslanja i validator tokom čuvanja
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);
Izuzetak koji SaveAs podigne pri ciljanju tačne verzije tretirajte kao izveštaj pre provere, a ne kao programsku grešku: poruka imenuje tačnu klauzulu koju izvorni dokument krši, upravo ono što je štampariji ili arhivskoj obradi potrebno pre nego što datoteka ode dalje. Putanja za čuvanje u eksplicitnoj verziji, razrešivač aktivne xref revizije i provere Adobe Extension Level opisane ovde deo su standardnog proizvoda PDFiumPas Component za Delphi i C++Builder; stranica proizvoda sadrži potpunu referencu za TPdf.SaveAs uz ostale API-je za usklađenost i obrasce