PDFiumPas, Delphi- og C++Builder-wrapperen omkring Googles PDFium-motor, gemmer et dokument ved en præcis PDF-version fra 1.3 til 1.7 gennem TPdf.SaveAs-metodens PdfVersion-parameter. PDFiums eget FPDF_SaveWithVersion-kald omskriver kun %PDF-M.m-headeren, uden at tjekke om dokumentets faktiske indhold er lovligt ved den version. PDFiumPas lukker det hul med et efter-gemning-konformitetsgennemløb, der gennemgår den aktive kryds-reference-revisionskæde og tjekker Adobe Extension Level-erklæringer, før filen forlader metoden
Det skel betyder mest i trykproduktion, hvor en PDF/X-profil navngiver en præcis PDF-version, og et preflight-værktøj eller en RIP afviser alt, der i stilhed er uenig med sin egen header, et scenarie dækket fra output-siden i validering af trykklare PDF/X-dokumenter med PDFiumPas. SaveAs eksponerer målet som TPdfVersion-enum'en, pv13 til pv17 sammen med de ældre pv10-til-pv12-værdier, plus en uafhængig TSaveOption til inkrementelle eller fulde omskrivninger. Send PdfVersion, og PDFiumPas udfører to opgaver i ét kald: den beder PDFium om at stemple den anmodede header, og genlæser derefter de friskskrevne bytes og nægter at give en fil tilbage, hvis aktive indhold ikke lovligt kan eksistere ved den version
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;
Hvorfor er den sidste objektdefinition i filen det forkerte at stole på?
Det sidste fysiske objekt med et givet nummer i en PDF-fil er ikke nødvendigvis det objekt, en konform læser ville løse for det nummer i dag. En PDF, der har gennemgået flere inkrementelle opdateringer, har ikke én objektgraf, den har en historie af dem lagdelt inde i en enkelt fil, og hver append-cyklus kan frigive et objekt, omdefinere det under et nyt generationsnummer, eller lade dets gamle fysiske krop sidde mellem to endobj-markører uden nogen kryds-reference-post, der peger på det længere
PDFiumPas ramte netop den fejltilstand, før den sporede xref-revisioner eksplicit: en Redact-annotation forældreløs efter en senere side-objekt-omskrivning, eller en /MarkInfo-ordbog efterladt fysisk til stede uden nogen xref-post, der peger på den, kunne stadig dukke op i en byte-skanning og stadig udløse et versions-funktions-tjek, der ikke længere gjaldt for det dokument, en læser rent faktisk ville åbne. Fejlretningen var falsk afvisning, ikke falsk accept: en fil, der genuint var kommet forbi en funktion i sin aktuelle revision, kunne stadig blive blokeret fra at gemme ved en lavere version på grund af indhold, ingen kunne nå længere
Hvordan afgør PDFiumPas, hvilke objektdefinitioner der rent faktisk er aktive?
PDFiumPas løser det aktive objektsæt på samme måde en konform læser gør, ved at gennemgå kryds-reference-kæden i stedet for at skanne bytes for objekt-headere. Løseren starter ved den sidste startxref-forskydning i filen og følger hvert /Prev-link baglæns gennem ældre revisioner, parser klassiske kryds-reference-tabeller, hybride /XRefStm-linkede streams, og rene kryds-reference-streams undervejs. Gennemgangen kører nyest-til-ældst og afgør hvert objektnummer første gang, det ses, så en fri post i en senere revision korrekt overskygger en objektkrop skrevet i en tidligere, og en omdefinering under en ny forskydning eller generation vinder altid over det, den erstattede
Objekt-stream-medlemmer får et ekstra tjek et rent forskydningsopslag ikke kan levere alene, en mekanisme dækket dybere i validering af objekt- og kryds-reference-streams med PDFiumPas. Et komprimeret objekt genoprettet fra en /ObjStm skal have sin forældre-stream bekræftet aktiv i den samme gennemgang, og dets indeks skal stemme overens med medlemmets egen position inde i den streams header, før PDFiumPas behandler det som levende indhold. ISO 32000-1 afsnit 7.5.8.4 beskriver endda et hybrid-reference-tilfælde, hvor en klassisk kompatibilitetstabel markerer et objekt frit, mens trailer'ens /XRefStm-post samtidig definerer det samme objekt som et komprimeret medlem et andet sted; PDFiumPas fletter den supplerende xref-stream ind i den samme revision, før de klassiske poster anvendes, så den komprimerede definition vinder på den måde, specifikationen tilsigter
Adobe Extension Levels: gaten over versionsnummeret
En %PDF-1.7-header lover kun det funktionssæt, ISO 32000-1 standardiserede i 2008, mens flere egenskaber PDF-producenter er afhængige af i dag, blev sendt bagefter som Adobe-eksklusive tillæg lagdelt oven på det samme versionsnummer. Adobe registrerede hvert tillæg som et BaseVersion- og ExtensionLevel-par registreret i dokumentkatalogens /Extensions-ordbog under et udvikler-præfiks, ADBE for Adobes egne tillæg, så en læser kan skelne en almindelig PDF 1.7-fil fra en, der også implementerer et nummereret extension-niveau. At gemme ved pv17 uden den erklæring er ikke en fejl i sig selv; det bliver kun en fejl, i det øjeblik det aktive indhold rent faktisk afhænger af en funktion, erklæringen skal dække
Hvilke høj-version-funktioner udløser den eksplicit-version-gate?
PDFiumPas tjekker en specifik, spec-drevet liste frem for at gætte ud fra versionsnummeret alene. Billed-ordbøger der bærer en eksplicit /SMaskInData-post eller en /BitsPerComponent-værdi på 16 kræver begge PDF 1.5, med seksten-bit-tilfældet der følger PDF Reference 1.5 afsnit 4.8's billed-komponent-regler direkte. RichMedia-annotationer og RichMediaExecute-handlinger kræver /BaseVersion /1.7 med /ExtensionLevel 3 eller højere. PRC 3D-streams, identificeret ved en ordbog der bærer både /Type /3D og /Subtype /PRC, kræver den samme basisversion, men kun /ExtensionLevel 1. Geospatiale Measure-ordbøger og Projection-annotationer kræver /BaseVersion /1.7 med /ExtensionLevel 3, det samme Adobe-tillæg RichMedia afhænger af
Det geospatiale tjek bærer en spec-læsnings-detalje værd at kende, hvis man nogensinde bygger sin egen versions-gatede logik oven på PDFiumPas. ISO 32000-1 Tabel 254 markerer Measure-ordbogens /Type-post som valgfri og bemærker kun, at "hvis til stede, skal den være Measure", mens Tabel 311 gør /Type obligatorisk for 3D-stream-ordbogen, PRC-indhold bor i. Rigtig GeoPDF-output fra kortlægningsværktøjer udelader rutinemæssigt /Type på Measure-ordbogen og skriver kun /Subtype /GEO, så PDFiumPas' geospatiale detektor matcher på /Subtype alene frem for at kræve begge nøgler den måde, dens PRC-3D-detektor sikkert kan. At kræve /Type på begge ordbøger ville have ladet konformt GeoPDF-indhold glide forbi gaten uopdaget og lande i en almindelig PDF 1.7-fil uden nogen extension-niveau-erklæring til at bakke det op
Nedgraderer PDFiumPas automatisk ikke-understøttede funktioner?
Ikke som en generel egenskab, og at antage andet er den fejl, man skal undgå her. SaveAs render målversionen gennem en intern rutine, ValidatePdfVersionCompliance, og når den rutine finder en funktion, målversionen eller dens extension-niveau-erklæring ikke kan understøtte, kaster SaveAs en undtagelse, der bærer rutinens fejltekst, i stedet for at skrive filen; kalderen får en præcis, funktions-navngivet grund tilbage, aldrig et i-stilhed-omskrevet dokument. Det ene sted PDFiumPas rent faktisk omskriver indhold automatisk, er et PDF 1.3-mål, hvor den strimler de semantisk neutrale /BM /Normal-, /CA 1- og /ca 1-gennemsigtighedsstandarder, PDFium altid skriver ind i ExtGState-ordbøger uanset målversion, fordi de specifikke værdier ikke bærer nogen visuel betydning, og PDF 1.3 går forud for nøglerne helt
// 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
Ægte ikke-standard gennemsigtighed og billed-soft-masks fejler stadig direkte ved et PDF 1.3-mål, fordi at fjerne dem ville ændre, hvordan siden rent faktisk ser ud, og PDFiumPas træffer ikke den beslutning på dine vegne. To beslægtede grænser er værd at planlægge omkring, før en præcis version går ind i en batch-pipeline. Eksplicit-version-output bærer aldrig en /Encrypt-ordbog; gemningen fejler straks, hvis kilden er beskyttet, hvilket tilfældigvis stemmer overens med PDF/X- og PDF/A-profiler, der alligevel forbyder kryptering, men det betyder, at dekryptering er et separat trin i ens workflow frem for noget SaveAs gør for en. PDFiumPas har heller ingen offentlig metode til at skrive en /Extensions /ADBE-erklæring på et katalog, så en kildefil, der indeholder RichMedia-, PRC-3D- eller geospatialt indhold, men mangler den erklæring, vil ikke bestå gaten, uanset hvilken PdfVersion man anmoder om; erklæringen skal allerede eksistere i kilden, typisk fordi forfatterværktøjet skrev den, eller funktionen skal fjernes før gemningen. Den skrivebeskyttede TPdf.PdfVersion-egenskab er værd at tjekke, før en præcis-version-gemning overhovedet forsøges, da den løser den samme katalog-bevidste effektive version, header eller /Version-override, hvad end der er aktuelt, som selve gemme-tids-validatoren er afhængig af
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);
Behandl en SaveAs-undtagelse på et præcist-versions-mål som en preflight-rapport frem for en bug: beskeden navngiver den præcise klausul, kildedokumentet overtræder, hvilket er præcis den information et trykkeri eller en arkiv-pipeline har brug for, før en fil går længere. Den eksplicit-version-gemme-vej, den aktive xref-revisions-løser, og Adobe Extension Level-tjekkene beskrevet her leveres som en del af standard-PDFiumPas-komponenten til Delphi og C++Builder; produktsiden bærer den fulde TPdf.SaveAs-reference sammen med resten af konformitets- og formular-API'et