PDFiumPas, obal pro Delphi a C++Builder kolem enginu PDFium od Googlu, ukládá dokument na přesnou verzi PDF od 1.3 do 1.7 přes parametr PdfVersion metody TPdf.SaveAs. Vlastní volání FPDF_SaveWithVersion v PDFium jen přepíše hlavičku %PDF-M.m, aniž by kontrolovalo, zda je skutečný obsah dokumentu na této verzi legální. PDFiumPas tuto mezeru uzavírá průchodem kontroly shody po uložení, který prochází aktivní řetěz revizí křížového odkazu a kontroluje deklarace Adobe Extension Level dřív, než soubor opustí metodu
Toto rozlišení je nejdůležitější v tiskové produkci, kde profil PDF/X pojmenovává přesnou verzi PDF a preflight nástroj nebo RIP odmítne cokoli, co se tiše rozchází s vlastní hlavičkou, scénář popsaný z výstupní strany v ověřování tiskově připravených dokumentů PDF/X pomocí PDFiumPas. SaveAs vystavuje cíl jako výčet TPdfVersion, pv13 až pv17 vedle starších hodnot pv10 až pv12, plus nezávislý TSaveOption pro přírůstkové nebo plné přepisy. Předejte PdfVersion a PDFiumPas udělá dvě práce v jednom volání: požádá PDFium, aby otiskl požadovanou hlavičku, pak znovu přečte čerstvě zapsané bajty a odmítne vrátit soubor, jehož aktivní obsah nemůže na této verzi legálně existovat
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;
Proč je poslední definice objektu v souboru špatná věc, které se dá věřit?
Poslední fyzický objekt s daným číslem v souboru PDF není nutně objekt, který by konformní čtenář pro toto číslo rozřešil dnes. PDF, které prošlo několika přírůstkovými aktualizacemi, nemá jeden graf objektů, má jejich historii vrstvenou uvnitř jednoho souboru, a každý cyklus připojení může uvolnit objekt, znovu jej definovat pod novým generačním číslem, nebo nechat jeho staré fyzické tělo sedět mezi dvěma značkami endobj bez jakéhokoli záznamu křížového odkazu, který by na něj ještě ukazoval
PDFiumPas na přesně tento režim selhání narazil dřív, než explicitně sledoval revize xref: anotace Redact osiřelá pozdějším přepisem objektu stránky, nebo slovník /MarkInfo fyzicky ponechaný přítomný bez záznamu xref, který by na něj ukazoval, se stále mohl objevit v bajtovém skenu a stále spustit kontrolu funkce vázané na verzi, která se už na dokument, jaký by čtenář skutečně otevřel, nevztahovala. Směr selhání bylo falešné odmítnutí, ne falešné přijetí: soubor, který se ve své aktuální revizi skutečně posunul za nějakou funkci, mohl přesto uváznout na uložení na nižší verzi kvůli obsahu, ke kterému se už nikdo nedostal
Jak PDFiumPas určuje, které definice objektů jsou skutečně aktivní?
PDFiumPas rozřešuje aktivní množinu objektů stejně jako konformní čtenář, procházením řetězu křížových odkazů místo skenování bajtů na hlavičky objektů. Resolver začíná na posledním posunu startxref v souboru a sleduje každý odkaz /Prev zpětně přes starší revize, přitom parsuje klasické tabulky křížového odkazu, hybridní proudy propojené přes /XRefStm i čisté proudy křížového odkazu. Průchod běží od nejnovějšího k nejstaršímu a usadí každé číslo objektu při prvním spatření, takže volný záznam v pozdější revizi správně zastíní tělo objektu zapsané v dřívější, a redefinice pod novým posunem nebo generací vždy vyhraje nad tím, co nahradila
Členové proudu objektů dostávají dodatečnou kontrolu, kterou obyčejné vyhledání posunu samo o sobě nemůže poskytnout, mechanismus popsaný podrobněji v ověřování proudů objektů a křížových odkazů pomocí PDFiumPas. Komprimovaný objekt obnovený z /ObjStm musí mít svůj rodičovský proud potvrzený jako aktivní ve stejném průchodu a jeho index se musí shodovat s vlastní pozicí člena uvnitř hlavičky tohoto proudu, dřív než jej PDFiumPas bere jako živý obsah. ISO 32000-1 oddíl 7.5.8.4 dokonce popisuje případ hybridního odkazu, kdy klasická kompatibilní tabulka označí objekt jako volný, zatímco záznam /XRefStm trailer současně definuje tentýž objekt jako komprimovaný člen jinde; PDFiumPas sloučí doplňkový proud xref do stejné revize dřív, než se aplikují klasické záznamy, takže komprimovaná definice vyhraje tak, jak specifikace zamýšlí
Úrovně rozšíření Adobe: brána nad číslem verze
Hlavička %PDF-1.7 slibuje jen sadu funkcí, kterou ISO 32000-1 standardizovala v roce 2008, zatímco několik schopností, na které se producenti PDF dnes spoléhají, se objevilo později jako doplňky výhradně od Adobe vrstvené navrch stejného čísla verze. Adobe zaregistrovala každý doplněk jako dvojici BaseVersion a ExtensionLevel zaznamenanou ve slovníku /Extensions katalogu dokumentu pod prefixem vývojáře, ADBE pro vlastní rozšíření Adobe, takže čtenář dokáže odlišit obyčejný soubor PDF 1.7 od toho, který navíc implementuje očíslovanou úroveň rozšíření. Uložení na pv17 bez této deklarace není samo o sobě chyba; stane se jí teprve v okamžiku, kdy aktivní obsah skutečně závisí na funkci, kterou má tato deklarace pokrývat
Které funkce vysokých verzí spouštějí bránu explicitní verze?
PDFiumPas kontroluje konkrétní seznam řízený specifikací místo hádání jen z čísla verze. Slovníky obrázků nesoucí explicitní záznam /SMaskInData nebo hodnotu /BitsPerComponent rovnou 16 obě vyžadují PDF 1.5, přičemž šestnáctibitový případ přímo následuje pravidla obrazových složek z PDF Reference 1.5 oddílu 4.8. Anotace RichMedia a akce RichMediaExecute vyžadují /BaseVersion /1.7 s /ExtensionLevel 3 nebo vyšší. Proudy 3D PRC, identifikované slovníkem nesoucím jak /Type /3D, tak /Subtype /PRC, vyžadují stejnou základní verzi, ale jen /ExtensionLevel 1. Geoprostorové slovníky Measure a anotace Projection vyžadují /BaseVersion /1.7 s /ExtensionLevel 3, stejný doplněk Adobe, na kterém závisí RichMedia
Geoprostorová kontrola nese detail čtení specifikace, který se vyplatí znát, pokud kdy budete stavět vlastní logiku bránovanou verzí navrch PDFiumPas. ISO 32000-1 tabulka 254 označuje záznam /Type slovníku Measure jako volitelný, poznamenávajíc jen, že „pokud je přítomen, musí být Measure", zatímco tabulka 311 dělá /Type povinným pro slovník proudu 3D, ve kterém žije obsah PRC. Skutečný výstup GeoPDF z mapovacích nástrojů rutinně vynechává /Type na slovníku Measure a zapisuje jen /Subtype /GEO, takže detektor geoprostorových dat v PDFiumPas se shoduje jen na /Subtype místo toho, aby vyžadoval oba klíče tak, jak to bezpečně dělá jeho detektor PRC 3D. Vyžadovat /Type na obou slovnících by nechalo konformní obsah GeoPDF proklouznout branou nezjištěný a přistát v obyčejném souboru PDF 1.7 bez deklarace úrovně rozšíření, která by jej podepřela
Degraduje PDFiumPas automaticky nepodporované funkce?
Ne jako obecná schopnost, a předpokládat opak je tady chyba, které se vyhnout. SaveAs vede cílovou verzi přes interní rutinu ValidatePdfVersionCompliance, a když tato rutina najde funkci, kterou cílová verze nebo její deklarace úrovně rozšíření nedokáže podpořit, SaveAs vyvolá výjimku nesoucí text chyby této rutiny místo zápisu souboru; volající dostane zpátky přesný, funkcí pojmenovaný důvod, nikdy tiše přepsaný dokument. Jediné místo, kde PDFiumPas obsah skutečně přepisuje automaticky, je cíl PDF 1.3, kde odstraní sémanticky neutrální výchozí hodnoty průhlednosti /BM /Normal, /CA 1 a /ca 1, které PDFium vždy zapisuje do slovníků ExtGState bez ohledu na cílovou verzi, protože tyto konkrétní hodnoty nenesou žádný vizuální význam a PDF 1.3 tyto klíče vůbec předchází
// 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
Skutečná nevýchozí průhlednost a obrazové soft masky pořád na cíli PDF 1.3 rovnou selžou, protože jejich odstranění by změnilo, jak stránka skutečně vypadá, a PDFiumPas toto rozhodnutí za vás neudělá. Dva příbuzné limity se vyplatí naplánovat dřív, než přesná verze vstoupí do dávkové pipeline. Výstup s explicitní verzí nikdy nenese slovník /Encrypt; uložení okamžitě selže, pokud je zdroj chráněný, což se náhodou shoduje s profily PDF/X a PDF/A, které šifrování stejně zakazují, ale znamená to, že dešifrování je samostatný krok ve vašem pracovním postupu, ne něco, co za vás udělá SaveAs. PDFiumPas také nemá žádnou veřejnou metodu pro zápis deklarace /Extensions /ADBE do katalogu, takže zdrojový soubor, který obsahuje RichMedia, PRC 3D, nebo geoprostorový obsah, ale postrádá tuto deklaraci, branou neprojde bez ohledu na to, jaké PdfVersion požádáte; deklarace musí už existovat ve zdroji, typicky protože ji zapsal autorský nástroj, nebo funkce musí ven ještě před uložením. Vlastnost TPdf.PdfVersion jen pro čtení se vyplatí zkontrolovat dřív, než se o uložení na přesnou verzi vůbec pokusíte, protože rozřeší tutéž efektivní verzi vědomou si katalogu, hlavičku nebo přepis /Version, ať je aktuální cokoli, na kterou se spoléhá i sám validátor v čase uložení
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);
Berte výjimku SaveAs na cíli přesné verze jako preflight report, ne jako chybu: zpráva pojmenovává přesnou klauzuli, kterou zdrojový dokument porušuje, což je přesně informace, kterou tiskárna nebo archivní pipeline potřebuje dřív, než soubor půjde dál. Cesta ukládání s explicitní verzí, resolver aktivní revize xref a kontroly Adobe Extension Level popsané zde se dodávají jako součást standardní komponenty PDFiumPas pro Delphi a C++Builder; stránka produktu nese úplnou referenci TPdf.SaveAs spolu se zbytkem API pro shodu a formuláře