PDFium Component styr PDF optional content-lager (OCG:er) i Delphi genom två TPdf-metoder: InspectOptionalContent listar varje lager tillsammans med den synlighet PDFium faktiskt renderar, och SaveAsOptionalContentConfigured skriver en verifierad kopia där de lager du väljer är slagna på eller av. Den andra metoden neutraliserar också Usage- och /AS-reglerna som annars tyst skulle dra tillbaka din ändring. Båda arbetar på dokumentet som redan är öppet i TPdf, så det finns ingen andra parser att hålla synkroniserad med vad visaren visar
Önskemålet kommer oftast från en CAD- eller GIS-verkstad: ritningssatsen levereras med mått, anteckningar och ett ritningshuvud på separata lager, och kunden vill ha en kopia med måtten dolda innan den går till en leverantör. PDFium renderar optional content korrekt, men dess publika ABI saknar funktioner för att räkna upp OCG:er, välja en konfiguration eller vända ett lagertillstånd. Så du går ner på objektnivå, redigerar /OCProperties, sparar, laddar om, och lagret finns fortfarande kvar. Orsaken är PDFiums synlighetslogik, och den är värd att förstå innan du rör en enda byte
Varför ändrar redigering av /ON och /OFF inte vad PDFium renderar?
Att redigera arrayerna /ON och /OFF i konfigurationsordboken räcker inte, för PDFium låter ett explicit tillstånd inuti OCG:ns egen /Usage-ordbok vinna över de arrayerna, och en /AS-autotillståndsregel kan sedan sänka båda. ISO 32000-1 §8.11.4 beskriver konfigurationer och usage-ordböcker som separata mekanismer; PDFiums renderare viker ihop dem till ett enda beslut, och InspectOptionalContent återskapar det i denna ordning:
- Starta från konfigurationens
/BaseState, där/ONoch/Unchangedbåda räknas som synliga och bara/OFFdöljer - Tillämpa konfigurationens
/ON-array, sedan dess/OFF-array, så att en grupp som finns i båda slutar dold - Tillämpa gruppens explicita Usage-tillstånd för den begärda användningen, som
/Usage << /View << /ViewState /OFF >> >>, vilket sänker allt ovan - Behandla en grupp vars
/Intentinnehåller vare sig/Vieweller/Allsom synlig, eftersom den inte deltar i synlighet för view-intention - Till sist köra den valda konfigurationens
/AS-array, vars poster för den matchande händelsen sätter tillståndet för de grupper de listar
Det tredje steget är det som bränner folk. En fil sparad av ett layoutverktyg bär ofta /ViewState /ON på varje OCG, och PDFium ignorerar då din noggrant redigerade /OFF-array: sparningen lyckas, filen öppnas rent, och lagret målar fortfarande. För Print och Export läser OcExplicitUsageState PrintState eller ExportState först och faller tillbaka på ViewState när den specifika posten saknas, så ett ensamt ViewState /ON fäster lagret även vid utskrift. Markerat innehåll som refererar en OCMD (§8.11.2.2) löses sedan mot dessa per grupp-resultat, genom /P-policyn eller, när den finns, /VE-synlighetsuttrycket
Hur listar du lagren som PDFium faktiskt visar?
TPdf.InspectOptionalContent returnerar en TPdfOptionalContentInventory vars Groups-array bär varje OCG:s objektnummer, namn, intentioner, de tre Usage-tillstånden, språk, zoomintervall, Locked-flagga, radiogruppindex och det beräknade EffectiveVisible. Metoden låter först PDFium spara det aktuella dokumentet i minnet, expanderar objektströmmar och skannar resultatet, så ändringar gjorda tidigare i sessionen speglas. Konfigurationsindex 0 är alltid standardordboken /D och posterna i /Configs följer från index 1; standardargumentet -1 väljer index 0. Ett dokument utan /OCProperties får metoden att returnera False med orsaken i ErrorMessage i stället för att kasta
procedure TFormMain.ListLayers;
var
Inv: TPdfOptionalContentInventory;
G: TPdfOptionalContentGroup;
begin
// Usage har ocuView som standard; -1 väljer konfiguration 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;
Arrayen Memberships rapporterar varje OCMD med sin Policy (ocmpAnyOn, ocmpAllOn, ocmpAnyOff, ocmpAllOff), råtexten VisibilityExpression och sitt eget EffectiveVisible. Några kantregler är medvetna. /P har /AnyOn som standard, och en OCMD utan grupper räknas som synlig. En referens till ett objektnummer som inte är en känd OCG behandlas som synlig i stället för att fälla hela uttrycket. /VE-evalueringen stannar vid ett nästlingsdjup på 32 och behandlar allt djupare som dolt, vilket hindrar ett fientligt eller själv-refererande uttryck från att förvandla inspektionen till en stack overflow
Skriva ett nytt lagertillstånd med SaveAsOptionalContentConfigured
TPdf.SaveAsOptionalContentConfigured tar en array av TPdfOptionalContentStateChange-poster (gruppobjektnummer plus Visible) och skriver ett dokument där den valda konfigurationen producerar exakt det tillståndet. Den valda konfigurationen får /BaseState /ON plus kompletta /ON- och /OFF-arrayer som täcker varje grupp, och varje OCG som redan har en Usage-ordbok får en explicit ViewState (eller PrintState / ExportState, enligt Options.Usage) som matchar sitt nya tillstånd. Med TPdfOptionalContentConfigureOptions.Default tas nyckeln /AS i den valda konfigurationen bort så att en open-, print- eller export-händelse inte kan vända tillbaka lagren
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;
// Konfiguration 0, ocuView, DisableAutomaticState och 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;
Skrivvägen behåller PDFiums egna sparade utdata som en byte-för-byte-prefix och lägger bara till den omskrivna konfigurationsägaren och de OCG-objekt som bär Usage-ordböcker, följt av en ny xref-sektion och trailer. Innan en enda byte når din destination öppnas resultatet på nytt i en separat TPdf under den strikta laddningspolicyn, och metoden misslyckas om korsreferenstabellen inte validerar. Filöverlagringen går ett steg längre: den skriver till en tillfällig fil bredvid målet och ersätter målet först när verifieringen lyckats, så en avvisad uppdatering lämnar aldrig en halvskriven ritning efter sig. Det är samma verifierade inkrementella revisionsmetod som redigeraren för PDF-namnträd och nummerträd i PDFium Component använder
Vad vägrar den konfigurerade sparningen göra?
Den konfigurerade sparningen vägrar varje ändring dokumentet själv förbjuder eller inte säkert kan representera, och varje vägran sker innan destinationen vidrörs. Ett objektnummer som inte finns i /OCGs faller rakt av. Att ändra en grupp som listas i konfigurationens /Locked-array misslyckas, även om att återge dess aktuella värde är tillåtet. Med EnforceRadioGroups påslagen avvisas varje /RBGroups-mängd som skulle sluta med mer än en synlig medlem i stället för att tyst slå av de andra. Krypterade dokument avvisas för att inkrementella objekt i klartext inte kan bära den aktiva säkerhetshanteraren. Undertecknade dokument kastar EPdfError om du inte skickar AllowSignedDocument = True, eftersom en ändring av vad en sida visar kan bryta signaturtäckning eller en certifieringspolicy
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örsta posten i /Configs, inte /D
try
Result := Pdf.SaveAsOptionalContentConfigured(Target, Changes, Options,
Report); // AllowSignedDocument förblir False
if not Result then
ShowMessage(Report.ErrorMessage);
except
on E: EPdfError do
begin
ShowMessage(E.Message); // undertecknad fil: inget skrivet till Target
Result := False;
end;
end;
end;
Känn avvägningarna innan du kopplar in detta i ett batchjobb. Den tillagda revisionen vilar ovanpå PDFiums fullständiga omsparning, inte dina ursprungliga filbyte, vilket är precis varför undertecknad indata kräver uttryckligt samtycke. Omskrivningen normaliserar också den valda konfigurationen till /BaseState /ON, så en upphovspersons /Unchanged eller /OFF-baslinje ersätts av explicita arrayer med samma resulterande synlighet. Att släppa /AS tar bort utskriftsverk-trick som ett vattenstämpellager som bara dyker upp på papper; sätt DisableAutomaticState till False för att behålla de reglerna, med vetskap om att de kan sänka ditt begärda tillstånd för den händelsen. På plussidan förbjuder både PDF/A-2 (ISO 19005-2 klausul 6.9) och PDF/UA (ISO 14289-1 klausul 7.10) /AS i konfigurationsordböcker, så standardutdatan tar bort ett fel som din PDF/A-preflightvalidering med PDFium Component annars skulle rapportera
Var lagrkontroll passar in i en Delphi-PDF-visare
I en visare är lagrkontroll en checklista driven av inventeringen plus en omloadning av det sparade resultatet. Fyll checklistan från Groups, inaktivera posterna som är Locked, behandla medlemmar som delar en RadioGroupIndex som ömsesidigt uteslutande, och vid verkställighet skriv till en TMemoryStream och ladda den strömmen tillbaka i TPdf så att vyn målar det nya tillståndet. Kopplingen mellan TPdf och TPdfView tas upp i att bygga en funktionsrik PDF-visare med PDFium VCL i Delphi. Licensiering, utvärderingsnedladdningar och resten av funktionsuppsättningen finns på produktsidan för PDFium Component for Delphi