PDFium Component styrer PDF optional content-lag (OCG-er) i Delphi gjennom to TPdf-metoder: InspectOptionalContent lister opp hvert lag sammen med synligheten PDFium faktisk vil rendre, og SaveAsOptionalContentConfigured skriver en verifisert kopi der lagene du velger, er slått på eller av. Den andre metoden nøytraliserer også Usage- og /AS-reglene som ellers stille ville omgjort redigeringen din. Begge jobber på dokumentet som allerede er åpent i TPdf, så det finnes ingen andre parser å holde i sync med det viseren viser
Forespørselen kommer vanligvis fra et CAD- eller GIS-hus: tegningsettet leveres med mål, annotasjoner og en tittelblokk på separate lag, og kunden vil ha en kopi med målene skjult før den går til en leverandør. PDFium renderer optional content riktig, men det offentlige ABI-et har ingen funksjon til å enumerate OCG-er, velge en konfigurasjon eller vippe en lagtilstand. Så du går ned på objektnivå, redigerer /OCProperties, lagrer, laster på nytt, og laget er der fortsatt. Årsaken er PDFiums synlighetslogikk, og den er verdt å forstå før du rører noen byte
Hvorfor endrer redigering av /ON og /OFF ikke det PDFium renderer?
Å redigere /ON- og /OFF-matrisene i konfigurasjonsordboken er ikke nok, fordi PDFium lar en eksplisitt tilstand inne i OCG-ens egen /Usage-ordbok vinne over de matrisene, og en /AS-auto-tilstandsregel kan deretter overstyre begge. ISO 32000-1 §8.11.4 beskriver konfigurasjoner og usage-ordbøker som adskilte mekanismer; PDFiums renderer folder dem sammen til én enkelt beslutning, og InspectOptionalContent gjenskaper den i denne rekkefølgen:
- Start fra konfigurasjonens
/BaseState, der/ONog/Unchangedbegge teller som synlige og bare/OFFskjuler - Anvend konfigurasjonens
/ON-matrise, deretter/OFF-matrisen, så en gruppe oppført i begge ender skjult - Anvend gruppens eksplisitte Usage-tilstand for den etterspurte bruken, som
/Usage << /View << /ViewState /OFF >> >>, som overstyrer alt over - Behandle en gruppe hvis
/Intentinneholder verken/Vieweller/Allsom synlig, siden den ikke deltar i view-intent-synlighet - Til slutt kjøre
/AS-matrisen til den valgte konfigurasjonen, hvis oppføringer for den matchende hendelsen setter tilstanden til gruppene de lister
Det tredje steget er det som brenner folk. En fil lagret av et layoutverktøy bærer ofte /ViewState /ON på hver OCG, og PDFium ignorerer da din omhyggelig redigerte /OFF-matrise: lagringen lykkes, filen åpnes rent igjen, og laget tegnes fortsatt. For Print og Export leser OcExplicitUsageState PrintState eller ExportState først og faller tilbake til ViewState når den spesifikke oppføringen mangler, så en enslig ViewState /ON fester laget for utskrift også. Marked content som refererer til en OCMD (§8.11.2.2) løses deretter mot disse per-gruppe-resultatene, gjennom /P-policyen eller, når den finnes, /VE-synlighetsuttrykket
Hvordan lister du opp lagene PDFium faktisk viser?
TPdf.InspectOptionalContent returnerer en TPdfOptionalContentInventory hvis Groups-matrise bærer hver OCGs objektnummer, navn, intents, de tre Usage-tilstandene, språk, zoom-område, Locked-flagg, radiogruppe-indeks og den beregnede EffectiveVisible. Metoden får først PDFium til å lagre det gjeldende in-memory-dokumentet, ekspanderer objektstrømmer og skanner resultatet, så redigeringer gjort tidligere i økten gjenspeiles. Konfigurasjonsindeks 0 er alltid standard /D-ordboken, og oppføringene i /Configs følger fra indeks 1; standardargumentet -1 velger indeks 0. Et dokument uten /OCProperties får metoden til å returnere False med årsaken i ErrorMessage i stedet for å kaste unntak
procedure TFormMain.ListLayers;
var
Inv: TPdfOptionalContentInventory;
G: TPdfOptionalContentGroup;
begin
// Usage står som standard til ocuView; -1 velger konfigurasjon 0, /D-ordboken
if not Pdf.InspectOptionalContent(Inv) then
begin
Memo1.Lines.Add('No usable layers: ' + Inv.ErrorMessage);
Exit;
end;
Memo1.Lines.Add(Format('Configuration %d: %s',
[Inv.SelectedConfigurationIndex,
string(Inv.Configurations[Inv.SelectedConfigurationIndex].Name)]));
for G in Inv.Groups do
Memo1.Lines.Add(Format('obj %d %s visible=%s locked=%s radio=%d',
[G.ObjectNumber, string(G.Name),
BoolToStr(G.EffectiveVisible, True),
BoolToStr(G.Locked, True), G.RadioGroupIndex]));
end;
Memberships-matrisen rapporterer hver OCMD med Policy sin (ocmpAnyOn, ocmpAllOn, ocmpAnyOff, ocmpAllOff), rå VisibilityExpression-tekst og sin egen EffectiveVisible. Noen kantregler er bevisste. /P står som standard til /AnyOn, og en OCMD uten grupper teller som synlig. En referanse til et objektnummer som ikke er en kjent OCG, behandles som synlig i stedet for å feile hele uttrykket. /VE-evaluering stopper på en hekkedybde på 32 og behandler alt dypere som skjult, noe som hindrer et fiendtlig eller selvrefererende uttrykk i å gjøre inspeksjonen om til en stack overflow
Skrive en ny lagtilstand med SaveAsOptionalContentConfigured
TPdf.SaveAsOptionalContentConfigured tar imot en matrise av TPdfOptionalContentStateChange-records (gruppeobjektnummer pluss Visible) og skriver et dokument der den valgte konfigurasjonen produserer nøyaktig den tilstanden. Den valgte konfigurasjonen får /BaseState /ON pluss komplette /ON- og /OFF-matriser som dekker hver gruppe, og hver OCG som allerede har en Usage-ordbok, får en eksplisitt ViewState (eller PrintState / ExportState, etter Options.Usage) som matcher den nye tilstanden sin. Med TPdfOptionalContentConfigureOptions.Default fjernes /AS-nøkkelen til den valgte konfigurasjonen, slik at en open-, print- eller export-hendelse ikke kan vippe lagene tilbake
procedure TFormMain.SaveWithoutDimensions(DimensionsObj, NotesObj: Integer);
var
Changes: TPdfOptionalContentStateChanges;
Options: TPdfOptionalContentConfigureOptions;
Report: TPdfOptionalContentConfigureReport;
begin
SetLength(Changes, 2);
Changes[0].GroupObjectNumber := DimensionsObj;
Changes[0].Visible := False;
Changes[1].GroupObjectNumber := NotesObj;
Changes[1].Visible := True;
// Konfigurasjon 0, ocuView, DisableAutomaticState og EnforceRadioGroups True
Options := TPdfOptionalContentConfigureOptions.Default;
if not Pdf.SaveAsOptionalContentConfigured('C:\Out\Drawing-NoDims.pdf',
Changes, Options, Report) then
raise Exception.Create('Layer update rejected: ' + Report.ErrorMessage);
Log(Format('%d of %d groups changed, %d Usage states rewritten, /AS removed: %s',
[Report.ChangedGroupCount, Report.GroupCount,
Report.UpdatedUsageStateCount,
BoolToStr(Report.RemovedAutomaticState, True)]));
end;
Skrivestien beholder PDFiums egen lagrede output som en byte-for-byte-prefiks og legger bare til den omskrevne konfigurasjonseieren og OCG-objektene som bærer Usage-ordbøker, etterfulgt av en ny xref-seksjon og trailer. Før én eneste byte når målet ditt, åpnes resultatet på nytt i en separat TPdf under den strenge lastepolicyen, og metoden feiler hvis kryssreferansetabellen ikke validerer. Fil-overloaden går et steg lenger: den skriver til en midlertidig fil ved siden av målet og erstatter målet først etter at verifiseringen lykkes, så en avvist oppdatering etterlater aldri en halvskrevet tegning. Det er samme verifiserte inkrementelle revisjonstilnærming som brukes av PDF navnetre- og nummertre-redigereren i PDFium Component
Hva nekter den konfigurerte lagringen å gjøre?
Den konfigurerte lagringen nekter enhver endring dokumentet selv forbyr eller ikke kan representere trygt, og hvert avslag skjer før målet røres. Et objektnummer som ikke er i /OCGs, feiler kontant. Å endre en gruppe oppført i konfigurasjonens /Locked-matrise feiler, selv om å gjenopprette den gjeldende verdien er tillatt. Med EnforceRadioGroups på avvises ethvert /RBGroups-sett som ville ende med mer enn ett synlig medlem, i stedet for å slå de andre av stille. Krypterte dokumenter avvises fordi klartekst-inkrementobjekter ikke kan bære den aktive sikkerhetshåndtereren. Signerte dokumenter kaster EPdfError med mindre du sender AllowSignedDocument = True, siden å endre det en side viser, kan bryte signaturdekning eller en sertifiseringspolicy
function TFormMain.SavePrintPreset(Target: TStream;
const Changes: TPdfOptionalContentStateChanges): Boolean;
var
Options: TPdfOptionalContentConfigureOptions;
Report: TPdfOptionalContentConfigureReport;
begin
Options := TPdfOptionalContentConfigureOptions.Default;
Options.Usage := ocuPrint; // skriver /Print << /PrintState ... >>
Options.ConfigurationIndex := 1; // første oppføring i /Configs, ikke /D
try
Result := Pdf.SaveAsOptionalContentConfigured(Target, Changes, Options,
Report); // AllowSignedDocument forblir False
if not Result then
ShowMessage(Report.ErrorMessage);
except
on E: EPdfError do
begin
ShowMessage(E.Message); // signert fil: ingenting skrevet til Target
Result := False;
end;
end;
end;
Kjenn avveiningene før du kobler dette inn i en batchjobb. Den tillagte revisjonen sitter oppå PDFiums fulle reagre, ikke de opprinnelige filbytene dine, og det er nøyaktig derfor signert input trenger eksplisitt samtykke. Omskrivingen normaliserer også den valgte konfigurasjonen til /BaseState /ON, så en forfatters /Unchanged- eller /OFF-basislinje erstattes av eksplisitte matriser med samme resulterende synlighet. Å droppe /AS fjerner utskriftsorgan-triks som et vannmerkelag som bare vises på papir; sett DisableAutomaticState til False for å beholde de reglene, og godta at de kan overstyre den etterspurte tilstanden din for den hendelsen. På plussiden forbyr både PDF/A-2 (ISO 19005-2 klausul 6.9) og PDF/UA (ISO 14289-1 klausul 7.10) /AS i konfigurasjonsordbøker, så standard-outputen fjerner ett problem PDF/A-preflight-validering med PDFium Component ellers ville rapportert
Hvor lagstyring passer inn i en Delphi PDF-viser
I en viser er lagstyring en avkryssingsliste drevet av inventaret pluss en gjeninnlesing av det lagrede resultatet. Fyll avkryssingslisten fra Groups, deaktiver oppføringene som er Locked, behandle medlemmer som deler en RadioGroupIndex som gjensidig utelukkende, og ved bruk skriv til en TMemoryStream og last den strømmen tilbake inn i TPdf, så viseren tegner den nye tilstanden. Kablingen mellom TPdf og TPdfView er dekket i å bygge en funksjonsrik PDF-viser med PDFium VCL i Delphi. Lisensiering, prøvenedlastinger og resten av funksjonssettet finnes på produktsiden for PDFium Component for Delphi