PDFium Component upravlja PDF optional content slojevima (OCG) u Delphiju kroz dvije metode TPdf: InspectOptionalContent izlistava svaki sloj zajedno s vidljivošću koju PDFium stvarno renderira, a SaveAsOptionalContentConfigured piše verificiranu kopiju u kojoj su slojevi koje Vi izaberete uključeni ili isključeni. Druga metoda također neutralizira Usage i /AS pravila koja bi inače tiho poništila Vašu izmjenu. Obje rade nad dokumentom već otvorenim u TPdf, pa nema drugog parsera koji bi se držao u sunku s onim što viewer prikazuje
Zahtjev obično dolazi iz CAD ili GIS radionice: set crteža isporučuje se s dimenzijama, anotacijama i naslovnim blokom na odvojenim slojevima, a kupac želi kopiju sa skrivenim dimenzijama prije nego otide dobavljaču. PDFium ispravno renderira optional content, ali njegov javni ABI nema funkciju za enumeraciju OCG-ova, odabir konfiguracije ili preokret stanja sloja. Pa siđete na razinu objekata, uredite /OCProperties, spremite, učitate iznova, i sloj je još uvijek tu. Razlog je PDFium logika vidljivosti, i vrijedi je razumjeti prije nego dirate ijedan bajt
Zašto uređivanje /ON i /OFF ne mijenja ono što PDFium renderira?
Uređivanje polja /ON i /OFF rječnika konfiguracije nije dovoljno, jer PDFium pušta da eksplicitno stanje unutar vlastitog /Usage rječnika OCG-a pobijedi ta polja, a /AS auto-state pravilo može zatim nadjačati oboje. ISO 32000-1 §8.11.4 opisuje konfiguracije i usage rječnike kao odvojene mehanizme; PDFium ih renderer savija u jednu odluku, a InspectOptionalContent je reproducira ovim redoslijedom:
- Počnite od
/BaseStatekonfiguracije, gdje/ONi/Unchangedoba računaju se vidljivima i samo/OFFskriva - Primijenite polje
/ONkonfiguracije, zatim njeno polje/OFF, pa grupa navedena u oba završi skrivena - Primijenite eksplicitno Usage stanje grupe za traženu uporabu, poput
/Usage << /View << /ViewState /OFF >> >>, koje nadjačava sve gore navedeno - Grupu čiji
/Intentne sadrži ni/Viewni/Alltretirajte kao vidljivu, jer ne sudjeluje u vidljivosti view-intenta - Na kraju pokrenite polje
/ASodabrane konfiguracije, čiji unosi za odgovarajući događaj postavljaju stanje grupa koje izlistavaju
Treći je korak onaj koji ljude opeče. Datoteka spremljena layout alatom često nosi /ViewState /ON na svakom OCG-u, pa PDFium tada ignorira Vaše pažljivo uređeno polje /OFF: spremanje uspije, datoteka se čisto otvara, i sloj se i dalje crta. Za Print i Export OcExplicitUsageState prvo čita PrintState ili ExportState i pada na ViewState kad specifični unos ne postoji, pa usamljeni ViewState /ON pribija sloj i za ispis. Markirani sadržaj koji referencira OCMD (§8.11.2.2) zatim se razrješuje protiv ovih rezultata po grupi, kroz politiku /P ili, kad postoji, vidljivosni izraz /VE
Kako izlistati slojeve koje će PDFium stvarno pokazati?
TPdf.InspectOptionalContent vraća TPdfOptionalContentInventory čije polje Groups nosi broj objekta svakog OCG-a, ime, intentove, tri Usage stanja, jezik, zoom raspon, zastavicu Locked, indeks radio grupe i izračunati EffectiveVisible. Metoda najprije naredi PDFiumu da spremi trenutni dokument u memoriji, širi object streamove i skenira rezultat, pa se izmjene učinjene ranije u sesiji odraze. Indeks konfiguracije 0 uvijek je zadani rječnik /D, a unosi /Configs slijede od indeksa 1; zadani argument -1 odabire indeks 0. Dokument bez /OCProperties natjera metodu da vrati False s razlogom u ErrorMessage umjesto da digne iznimku
procedure TFormMain.ListLayers;
var
Inv: TPdfOptionalContentInventory;
G: TPdfOptionalContentGroup;
begin
// Usage je zadano ocuView; -1 odabire konfiguraciju 0, rječ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;
Polje Memberships javlja svaki OCMD s njegovim Policy (ocmpAnyOn, ocmpAllOn, ocmpAnyOff, ocmpAllOff), sirovim tekstom VisibilityExpression i vlastitim EffectiveVisible. Par rubnih pravila namjerno je takvo. /P je zadano /AnyOn, a OCMD bez grupa računa se vidljivim. Referenca na broj objekta koji nije poznati OCG tretira se kao vidljiva umjesto da obori cijeli izraz. Evaluacija /VE staje na dubini ugniježđenja 32 i sve dublje tretira kao skriveno, što neprijateljskom ili samose referencirajućem izrazu sprječava da inspekciju pretvori u stack overflow
Pisanje novog stanja sloja sa SaveAsOptionalContentConfigured
TPdf.SaveAsOptionalContentConfigured prima polje zapisa TPdfOptionalContentStateChange (broj objekta grupe plus Visible) i piše dokument u kojem odabrana konfiguracija proizvodi točno to stanje. Odabrana konfiguracija dobiva /BaseState /ON plus potpuna polja /ON i /OFF koja pokrivaju svaku grupu, a svaki OCG koji već ima Usage rječnik prima eksplicitni ViewState (ili PrintState / ExportState, prema Options.Usage) koji odgovara njegovom novom stanju. S TPdfOptionalContentConfigureOptions.Default ključ /AS odabrane konfiguracije uklanja se da open, print ili export događaj ne može slojeve vratiti natrag
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 pisanja zadržava PDFium vlastiti spremljeni izlaz kao prefiks identičan do bajta i dodaje samo prepravljenog vlasnika konfiguracije i OCG objekte koji nose Usage rječnike, iza kojih slijede novi xref odsječak i trailer. Prije nego ijedan bajt dođe do Vašeg odredišta, rezultat se ponovno otvara u zasebnom TPdf pod strogom politikom učitavanja, i metoda pada ako tablica unakrsnih referenci ne prolazi validaciju. Overload s datotekom ide korak dalje: piše u privremenu datoteku pokraj ciljne i ciljnu mijenja tek nakon što verifikacija uspije, pa odbijena izmjena nikad ne ostavlja napola napisani crtež za sobom. To je isti pristup verificirane inkrementalne revizije koji koristi uređivač PDF name i number stabala u PDFium Componentu
Što konfigurirano spremanje odbija učiniti?
Konfigurirano spremanje odbija svaku izmjenu koju sam dokument zabranjuje ili ne može sigurno predstaviti, i svako odbijanje događa se prije nego se odredište dotakne. Broj objekta koji nije u /OCGs pada odmah. Mijenjanje grupe navedene u polju /Locked konfiguracije pada, iako je ponovno navođenje njene trenutne vrijednosti dopušteno. Uključenim EnforceRadioGroups, svaki skup /RBGroups koji bi završio s više od jednog vidljivog člana odbija se umjesto da se ostali tiho isključe. Šifrirani dokumenti se odbijaju jer otvoreni inkrementalni objekti ne mogu nositi aktivni sigurnosni handler. Potpisani dokumenti dižu EPdfError osim ako ne proslijedite AllowSignedDocument = True, jer promjena onoga što stranica prikazuje može slomiti pokrivenost potpisa ili politiku certifikacije
function TFormMain.SavePrintPreset(Target: TStream;
const Changes: TPdfOptionalContentStateChanges): Boolean;
var
Options: TPdfOptionalContentConfigureOptions;
Report: TPdfOptionalContentConfigureReport;
begin
Options := TPdfOptionalContentConfigureOptions.Default;
Options.Usage := ocuPrint; // piše /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); // potpisana datoteka: ništa nije zapisano u Target
Result := False;
end;
end;
end;
Upoznajte kompromise prije nego ovo uključite u batch posao. Dodana revizija sjedi na vrhu PDFium punog re-savea, ne Vaših izvornih bajtova datoteke, i baš zato potpisani ulaz treba izričit pristanak. Prepravka također normalizira odabranu konfiguraciju na /BaseState /ON, pa se autorova /Unchanged ili /OFF baza zamjenjuje eksplicitnim poljima s istom rezultantnom vidljivošću. Ispuštanje /AS uklanja print-only trikove poput vodenog žiga koji se pojavljuje samo na papiru; postavite DisableAutomaticState na False da ta pravila zadržite, prihvaćajući da mogu nadjačati traženo stanje za taj događaj. Na plus strani, PDF/A-2 (ISO 19005-2 clause 6.9) i PDF/UA (ISO 14289-1 clause 7.10) oboje zabranjuju /AS u rječnicima konfiguracija, pa zadani izlaz uklanja jedan problem koji bi inače PDF/A preflight validacija s PDFium Componentom javila
Gdje se upravljanje slojevima uklapa u Delphi PDF viewer
U vieweru je upravljanje slojevima kontrolna lista koju pokreće inventar plus ponovno učitavanje spremljenog rezultata. Napunite kontrolnu listu iz Groups, onemogućite unose koji su Locked, članove koji dijele RadioGroupIndex tretirajte kao međusobno isključive, i pri primjeni pišite u TMemoryStream i učitajte taj stream natrag u TPdf da prikaz nacrta novo stanje. Povezivanje između TPdf i TPdfView pokriveno je u gradnji PDF viewera bogatog značajkama s PDFium VCL-om u Delphiju. Licenciranje, probna preuzimanja i ostatak skupa značajki su na stranici proizvoda PDFium Component za Delphi