PDFium Component upravlja PDF optional content slojevima (OCG) u Delphi-ju kroz dve TPdf metode: InspectOptionalContent izlistava svaki sloj zajedno sa vidljivošću koju PDFium zaista renderuje, a SaveAsOptionalContentConfigured piše verifikovanu kopiju u kojoj su slojevi koje izaberete prebačeni na uključeno ili isključeno. Druga metoda takođe neutrališe Usage i /AS pravila koja bi inače tiho poništila Vašu izmenu. Obe rade na dokumentu već otvorenom u TPdf, pa nema drugog parsera koji treba držati usklađenim sa onim što viewer pokazuje
Zahtev obično stiže iz CAD ili GIS radionice: set crteža putuje sa dimenzijama, anotacijama i blokom naslova na odvojenim slojevima, a kupac želi kopiju sa sakrivenim dimenzijama pre nego što ode dobavljaču. PDFium renderuje optional content ispravno, ali njegov javni ABI nema funkciju za enumeraciju OCG-ova, izbor konfiguracije ili okretanje stanja sloja. Pa silazite na nivo objekata, izmenite /OCProperties, sačuvate, ponovo učitate, i sloj je i dalje tu. Razlog je logika vidljivosti u PDFium-u, i vredi je razumeti pre nego što dirate ijedan bajt
Zašto izmena /ON i /OFF ne menja ono što PDFium renderuje?
Izmena nizova /ON i /OFF rečnika konfiguracije nije dovoljna, jer PDFium dozvoljava da eksplicitno stanje unutar sopstvenog /Usage rečnika OCG-a pobedi te nizove, i da ih zatim oba pregazi /AS auto-state pravilo. ISO 32000-1 §8.11.4 opisuje konfiguracije i usage rečnike kao odvojene mehanizme; PDFium renderer ih skuplja u jednu odluku, i InspectOptionalContent je reprodukuje ovim redosledom:
- Krenite od
/BaseStatekonfiguracije, gde/ONi/Unchangedoba računaju se kao vidljivo i samo/OFFskriva - Primenite
/ONniz konfiguracije, pa njen/OFFniz, pa grupa navedena u oba na kraju bude skrivena - Primenite eksplicitno Usage stanje grupe za traženu upotrebu, poput
/Usage << /View << /ViewState /OFF >> >>, koje pregazi sve iznad - Grupu čiji
/Intentne sadrži ni/Viewni/Alltretirajte kao vidljivu, pošto ne učestvuje u view-intent vidljivosti - Napokon pokrenite
/ASniz izabrane konfiguracije, čiji unosi za odgovarajući event postavljaju stanje grupa koje izlistavaju
Treći korak je onaj koji peče ljude. Fajl sačuvan layout alatom često nosi /ViewState /ON na svakom OCG-u, i PDFium tada ignoriše Vaš pažljivo uređeni /OFF niz: čuvanje uspe, fajl se čisto otvara, i sloj se i dalje crta. Za Print i Export, OcExplicitUsageState prvo čita PrintState ili ExportState i vraća se na ViewState kad specifičnog unosa nema, pa usamljen ViewState /ON prikačuje sloj i za štampu. Marked content koji referencira OCMD (§8.11.2.2) zatim se razrešava prema ovim rezultatima po grupi, kroz /P politiku ili, kad postoji, /VE izraz vidljivosti
Kako izlistati slojeve koje će PDFium zaista pokazati?
TPdf.InspectOptionalContent vraća TPdfOptionalContentInventory čiji niz Groups nosi broj objekta svakog OCG-a, ime, intente, tri Usage stanja, jezik, zoom opseg, zastavicu Locked, indeks radio grupe i izračunatu EffectiveVisible. Metoda prvo natera PDFium da sačuva trenutni dokument u memoriji, proširi object stream-ove, i skenira rezultat, pa se izmene ranije u sesiji ogledaju. Indeks konfiguracije 0 je uvek podrazumevani rečnik /D a unosi /Configs slede od indeksa 1; podrazumevani argument -1 bira indeks 0. Dokument bez /OCProperties tera metodu da vrati False sa razlogom u ErrorMessage umesto da podigne izuzetak
procedure TFormMain.ListLayers;
var
Inv: TPdfOptionalContentInventory;
G: TPdfOptionalContentGroup;
begin
// Usage podrazumeva ocuView; -1 bira konfiguraciju 0, rečnik /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;
Niz Memberships izveštava svaki OCMD sa njegovom Policy (ocmpAnyOn, ocmpAllOn, ocmpAnyOff, ocmpAllOff), sirovim tekstom VisibilityExpression i sopstvenom EffectiveVisible. Par rubnih pravila je namerno. /P podrazumeva /AnyOn, i OCMD bez grupa računa se kao vidljiv. Referenca na broj objekta koji nije poznati OCG tretira se kao vidljiva umesto da obori ceo izraz. Evaluacija /VE staje na dubini ugnježđivanja 32 i sve dublje tretira kao skriveno, što sprečava neprijateljski ili samoreferencirajući izraz da pretvori inspekciju u stack overflow
Pisanje novog stanja sloja sa SaveAsOptionalContentConfigured
TPdf.SaveAsOptionalContentConfigured prima niz record-a TPdfOptionalContentStateChange (broj objekta grupe plus Visible) i piše dokument u kojem izabrana konfiguracija proizvodi baš to stanje. Izabrana konfiguracija dobija /BaseState /ON plus kompletne /ON i /OFF nizove koji pokrivaju svaku grupu, i svaki OCG koji već ima Usage rečnik prima eksplicitan ViewState (ili PrintState / ExportState, po Options.Usage) koji odgovara njegovom novom stanju. Sa TPdfOptionalContentConfigureOptions.Default, ključ /AS izabrane konfiguracije se uklanja da open, print ili export event ne može da vrati slojeve nazad
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;
// Konfiguracija 0, ocuView, DisableAutomaticState i 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;
Putanja upisa zadržava sopstveni sačuvani izlaz PDFium-a kao prefiks bajt po bajt i dodaje samo preslovljenog vlasnika konfiguracije i OCG objekte koji nose Usage rečnike, iza kojih ide novi xref odeljak i trailer. Pre nego što ijedan bajt stigne do odredišta, rezultat se ponovo otvara u posebnom TPdf-u pod strogom politikom učitavanja, i metoda pada ako tabela unakrsnih referenci ne validira. File overload ide korak dalje: piše u privremeni fajl pored cilja i zamenjuje cilj tek kad verifikacija uspe, pa odbijena izmena nikada ne ostavlja upola napisan crtež. To je isti pristup verifikovane inkrementalne revizije koji koristi PDF name tree i number tree editor u PDFium Component-u
Šta konfigurisano čuvanje odbija da učini?
Konfigurisano čuvanje odbija svaku izmenu koju sam dokument zabranjuje ili ne može bezbedno da predstavi, i svako odbijanje dešava se pre nego što se odredište dirne. Broj objekta koji nije u /OCGs pada odmah. Izmena grupe navedene u /Locked nizu konfiguracije pada, mada je ponavljanje njene trenutne vrednosti dozvoljeno. Sa uključenim EnforceRadioGroups, svaki /RBGroups skup koji bi na kraju imao više od jednog vidljivog člana odbija se umesto da se ostali tiho isključe. Šifrovani dokumenti se odbijaju jer otvoreni tekst inkrementalnih objekata ne može da nosi aktivni security handler. Potpisani dokumenti podižu EPdfError osim ako prosledite AllowSignedDocument = True, pošto izmena onoga što stranica pokazuje može da slomi pokriće potpisa ili 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; // prvi unos /Configs, ne /D
try
Result := Pdf.SaveAsOptionalContentConfigured(Target, Changes, Options,
Report); // AllowSignedDocument ostaje False
if not Result then
ShowMessage(Report.ErrorMessage);
except
on E: EPdfError do
begin
ShowMessage(E.Message); // potpisan fajl: ništa nije upisano u Target
Result := False;
end;
end;
end;
Znajte kompromise pre nego što ovo uvežete u batch posao. Dodata revizija sedi na vrhu punog re-save-a PDFium-a, ne na Vašim originalnim bajtovima fajla, i baš zato potpisani ulaz traži eksplicitnu saglasnost. Preslovljavanje takođe normalizuje izabranu konfiguraciju na /BaseState /ON, pa se autorov /Unchanged ili /OFF baseline zamenjuje eksplicitnim nizovima sa istom rezultujućom vidljivošću. Ispuštanje /AS skida trikove samo za štampu poput sloja vodenog žiga koji se pojavi samo na papiru; postavite DisableAutomaticState na False da zadržite ta pravila, prihvativši da mogu da pregaze Vaše traženo stanje za taj event. S pozitivne strane, PDF/A-2 (ISO 19005-2 klauza 6.9) i PDF/UA (ISO 14289-1 klauza 7.10) oba zabranjuju /AS u rečnicima konfiguracije, pa podrazumevani izlaz skida jedan problem koji bi Vaša PDF/A preflight validacija sa PDFium Component-om inače prijavila
Gde kontrola slojeva staje u Delphi PDF viewer
U viewer-u, kontrola slojeva je checklist-a koja se pokreće inventarom plus ponovnim učitavanjem sačuvanog rezultata. Napunite checklist-u iz Groups, onemogućite unose koji su Locked, tretirajte članove koji dele RadioGroupIndex kao međusobno isključive, i pri primeni upišite u TMemoryStream i učitajte taj stream nazad u TPdf da view nacrta novo stanje. Ožičenje između TPdf-a i TPdfView-a pokriveno je u gradnji PDF viewer-a bogatog fičerima sa PDFium VCL u Delphi-ju. Licenciranje, trial preuzimanja i ostatak seta fičera su na stranici proizvoda PDFium Component za Delphi