PDFium Component ovláda PDF optional content vrstvy (OCG) v Delphi cez dve metódy TPdf: InspectOptionalContent vypíše každú vrstvu spolu s viditeľnosťou, ktorú PDFium reálne vyrenderuje, a SaveAsOptionalContentConfigured zapíše overenú kópiu, v ktorej sú zvolené vrstvy zapnuté alebo vypnuté. Druhá metóda zároveň neutralizuje Usage a /AS pravidlá, ktoré by inak poticho vrátili vašu úpravu späť. Obe pracujú na dokumente už otvorenom v TPdf, takže niet druhého parsera, ktorý by trebalo držať v synchronizácii s tým, čo viewer ukazuje
Požiadavka obyčajne prichádza z CAD či GIS dielne: výkresová sada odchádza s rozmermi, anotáciami a title blockom na samostatných vrstvách a zákazník chce kópiu so skrytými rozmermi skôr, než putuje k dodávateľovi. PDFium renderuje optional content správne, ale jeho verejné ABI nemá žiadnu funkciu na enumeráciu OCG, výber konfigurácie alebo preklopenie stavu vrstvy. Takže zostúpite na úroveň objektov, upravíte /OCProperties, uložíte, znova načítate a vrstva je stále tam. Dôvodom je viditeľnostná logika PDFium a stojí za pochopenie skôr, než sa siahne na akýkoľvek bajt
Prečo úprava /ON a /OFF nemení to, čo PDFium renderuje?
Úprava polí /ON a /OFF konfiguračného slovníka nestačí, pretože PDFium nechá explicitný stav vo vnútri vlastného slovníka /Usage OCG zvýaziť nad tými poľami a pravidlo auto-stavu /AS potom môže preklopiť oboje. ISO 32000-1 §8.11.4 popisuje konfigurácie a usage slovníky ako oddelené mechanizmy; renderer PDFium ich zlepí do jedného rozhodnutia a InspectOptionalContent ho reprodukuje v tomto poradí:
- Začnite od
/BaseStatekonfigurácie, kde/ONaj/Unchangedpočítajú ako viditeľné a len/OFFskrýva - Aplikujte pole
/ONkonfigurácie, potom jej pole/OFF, takže skupina uvedená v oboch skončí skrytá - Aplikujte explicitný Usage stav skupiny pre požadované použitie, ako
/Usage << /View << /ViewState /OFF >> >>, ktorý preklopí všetko vyššie - Berte skupinu, ktorej
/Intentneobsahuje ani/View, ani/All, ako viditeľnú, keďže sa nezúčastňuje view-intent viditeľnosti - Nakoniec pusťte pole
/ASzvolenej konfigurácie, ktorého záznamy pre sediacu udalosť nastavia stav skupín, ktoré vypisujú
Tretí krok je ten, ktorý páli ľudí. Súbor uložený layoutovým nástrojom často nesie /ViewState /ON na každom OCG a PDFium potom ignoruje vaše starostlivo upravené pole /OFF: uloženie sa podarí, súbor sa čisto znova otvorí a vrstva sa stále kreslí. Pre Print a Export číta OcExplicitUsageState najprv PrintState alebo ExportState a padne späť na ViewState, keď konkrétny záznam chýba, takže osamelé ViewState /ON pripína vrstvu aj pre tlač. Marked content odkazujúci na OCMD (§8.11.2.2) sa potom vyhodnocuje proti týmto výsledkom na skupinu, cez politiku /P alebo, keď je prítomná, cez viditeľnostný výraz /VE
Ako vypíšete vrstvy, ktoré PDFium reálne ukáže?
TPdf.InspectOptionalContent vracia TPdfOptionalContentInventory, ktorého pole Groups nesie objektové číslo každého OCG, meno, intenty, tri Usage stavy, jazyk, zoom range, flag Locked, radio-group index a vypočítané EffectiveVisible. Metóda najprv nechá PDFium uložiť aktuálny dokument v pamäti, expanduje object streams a preskánuje výsledok, takže sa odzrkadlia úpravy urobené skôr v session. Konfiguračný index 0 je vždy default slovník /D a záznamy /Configs nasledujú od indexu 1; default argument -1 vyberá index 0. Dokument bez /OCProperties donúti metódu vrátiť False s dôvodom v ErrorMessage namiesto výnimky
procedure TFormMain.ListLayers;
var
Inv: TPdfOptionalContentInventory;
G: TPdfOptionalContentGroup;
begin
// Usage má default ocuView; -1 vyberá konfiguráciu 0, slovník /D
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;
Pole Memberships hlási každý OCMD s jeho Policy (ocmpAnyOn, ocmpAllOn, ocmpAnyOff, ocmpAllOff), surovým textom VisibilityExpression a jeho vlastným EffectiveVisible. Pár hraničných pravidiel je zámerných. /P má default /AnyOn a OCMD bez skupín počíta ako viditeľný. Odkaz na objektové číslo, ktoré nie je známe OCG, sa berie ako viditeľný namiesto toho, aby zlyhal celý výraz. Vyhodnocovanie /VE zastaví na vnorení hĺbky 32 a čokoľvek hlbšie berie ako skryté, čo bráni nepriateľskému alebo seba-odkazujúcemu výrazu zmeniť inšpekciu na stack overflow
Zápis nového stavu vrstvy cez SaveAsOptionalContentConfigured
TPdf.SaveAsOptionalContentConfigured berie pole recordov TPdfOptionalContentStateChange (objektové číslo skupiny plus Visible) a zapisuje dokument, v ktorom zvolená konfigurácia produkuje presne ten stav. Zvolená konfigurácia dostane /BaseState /ON plus kompletné polia /ON a /OFF pokrývajúce každú skupinu a každý OCG, ktorý už má Usage slovník, dostane explicitný ViewState (alebo PrintState / ExportState, podľa Options.Usage) sediaci s jeho novým stavom. S TPdfOptionalContentConfigureOptions.Default sa kľúč /AS zvolenej konfigurácie odstráni, takže udalosť open, print alebo export nedokáže vrstvy preklopiť späť
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;
// Konfigurácia 0, ocuView, DisableAutomaticState a 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;
Zápisová cesta drží vlastný uložený výstup PDFium ako bajt-za-bajtom prefix a pripája len prepísaného vlastníka konfigurácie a OCG objekty nesúce Usage slovníky, za nimi novú xref sekciu a trailer. Skôr než jediný bajt dosiahne na váš cieľ, výsledok sa znova otvorí v samostatnom TPdf pod prísnou load politikou a metóda zlyhá, keď cross-reference tabuľka nevaliduje. File overload ide ešte ďalej: zapisuje do dočasného súboru vedľa cieľa a nahradí cieľ až po úspešnej verifikácii, takže odmietnutá aktualizácia nikdy nezanechá polozapísaný výkres. Je to ten istý prístup overenej inkrementálnej revízie, aký používa editor PDF name tree a number tree v PDFium Component
Čo odmieta konfigurované ukladanie robiť?
Konfigurované ukladanie odmietne každú zmenu, ktorú dokument sám zakazuje alebo nedokáže bezpečne reprezentovať, a každé odmietnutie sa stane skôr, než sa siahne na cieľ. Objektové číslo, ktoré nie je v /OCGs, zlyhá rovno. Zmena skupiny uvedenej v poli /Locked konfigurácie zlyhá, hoci zopakovanie jej aktuálnej hodnoty je povolené. So zapnutým EnforceRadioGroups sa odmietne každá sada /RBGroups, ktorá by skončila s viac než jedným viditeľným členom, namiesto tichého vypnutia ostatných. Šifrované dokumenty sa odmietajú, pretože plaintextové inkrementálne objekty nedokážu niesť aktívny security handler. Podpísané dokumenty vyhodia EPdfError, pokiaľ nepošlete AllowSignedDocument = True, keďže zmena toho, čo stránka ukazuje, dokáže zlomiť pokrytie podpisu alebo certification politiku
function TFormMain.SavePrintPreset(Target: TStream;
const Changes: TPdfOptionalContentStateChanges): Boolean;
var
Options: TPdfOptionalContentConfigureOptions;
Report: TPdfOptionalContentConfigureReport;
begin
Options := TPdfOptionalContentConfigureOptions.Default;
Options.Usage := ocuPrint; // zapisuje /Print << /PrintState ... >>
Options.ConfigurationIndex := 1; // prvý záznam /Configs, nie /D
try
Result := Pdf.SaveAsOptionalContentConfigured(Target, Changes, Options,
Report); // AllowSignedDocument zostáva False
if not Result then
ShowMessage(Report.ErrorMessage);
except
on E: EPdfError do
begin
ShowMessage(E.Message); // podpísaný súbor: do Target sa nič nezapíše
Result := False;
end;
end;
end;
Poznajte trade-offy skôr, než toto zapojíte do batch jobu. Pripojená revízia sedí na vrchu úplného re-save PDFium, nie pôvodných bajtov vášho súboru, a presne preto podpísaný vstup potrebuje explicitný súhlas. Prepis tiež normalizuje zvolenú konfiguráciu na /BaseState /ON, takže baseline /Unchanged alebo /OFF od autora sa nahradí explicitnými poľami s rovnakou výslednou viditeľnosťou. Zahodenie /AS odstráni print-only triky ako watermark vrstvu, ktorá sa ukáže len na papieri; nastavte DisableAutomaticState na False, aby tie pravidlá zostali, s tým, že môžu preklopiť váš požadovaný stav pre tú udalosť. Na druhej strane PDF/A-2 (ISO 19005-2 clause 6.9) aj PDF/UA (ISO 14289-1 clause 7.10) zakazujú /AS v konfiguračných slovníkoch, takže default výstup odstráni jeden problém, ktorý by inak hlásila PDF/A preflight validácia s PDFium Component
Kde sa ovládanie vrstiev hodí do Delphi PDF vieweru
Vo vieweri je ovládanie vrstiev checklist hnaný inventárom plus reloadom uloženého výsledku. Naplňte checklist z Groups, zneprístupnite záznamy, ktoré sú Locked, berte členov zdieľajúcich RadioGroupIndex ako vzájomne sa vylučujúcich a pri aplikovaní zapisujte do TMemoryStream a načítajte ten stream späť do TPdf, aby view nakreslil nový stav. Prepojenie medzi TPdf a TPdfView pokrýva stavba bohatého PDF vieweru s PDFium VCL v Delphi. Licencovanie, trial sťahovania a zvyšok funkcionality sú na produktovej stránke PDFium Component for Delphi