PDFium Component valdo PDF pasirenkamo turinio sluoksnius (OCG) Delphi per du TPdf metodus: InspectOptionalContent išvardija kiekvieną sluoksnį kartu su matomumu, kurį PDFium iš tikrųjų atvaizduos, o SaveAsOptionalContentConfigured rašo patikrintą kopiją, kurioje jūsų pasirinkti sluoksniai įjungti arba išjungti. Antrasis metodas dar neutralizuoja Usage ir /AS taisykles, kurios kitu atveju tyliai panaikintų jūsų redagavimą. Abu dirba su dokumentu, jau atidarytu TPdf, tad nėra antro analizatoriaus, kurį reikėtų laikyti sinchronyje su tuo, ką rodo žiūryklė
Užklausa paprastai atkeliauja iš CAD ar GIS dirbtuvių: brėžinių rinkinys keliauja su matmenimis, pastabomis ir pavadinimų bloku atskiruose sluoksniuose, o klientas nori kopijos su paslėptais matmenimis, dar jai nukeliavus pas tiekėją. PDFium pasirenkamą turinį atvaizduoja teisingai, bet jo viešasis ABI neturi funkcijos išvardyti OCG, parinkti konfigūraciją ar perversti sluoksnio būseną. Tad nusileidžiate iki objektų lygio, redaguojate /OCProperties, išsaugote, perkraunate — ir sluoksnis tebečia. Priežastis yra PDFium matomumo logika, ir verta ją suprasti, dar nelietus jokių baitų
Kodėl /ON ir /OFF redagavimas nekeičia to, ką PDFium atvaizduoja?
Konfigūracijos žodyno /ON ir /OFF masyvų redagavimo neužtenka, nes PDFium leidžia aiškiai būsenai OCG paties /Usage žodyne nugalėti tuos masyvus, o /AS automatinės būsenos taisyklė gali tada perrašyti abu. ISO 32000-1 §8.11.4 konfigūracijas ir usage žodynus aprašo kaip atskirus mechanizmus; PDFium atvaizduoklis juos susuka į vieną sprendimą, o InspectOptionalContent jį atkuria šia tvarka:
- Pradedama nuo konfigūracijos
/BaseState, kur/ONir/Unchangedabu laikomi matomais ir tik/OFFslepia - Taikomas konfigūracijos
/ONmasyvas, tada jo/OFFmasyvas, tad abiejuose įrašyta grupė galiausiai paslepiama - Taikoma grupės aiški Usage būsena prašomam usage, pavyzdžiui
/Usage << /View << /ViewState /OFF >> >>, kuri perrašo visa, kas aukščiau - Grupė, kurios
/Intentneturi nei/View, nei/All, laikoma matoma, nes ji nedalyvauja view-intent matomume - Galiausiai paleidžiamas pasirinktos konfigūracijos
/ASmasyvas, kurio įrašai atitinkamam įvykiui nustato savo įrašomų grupių būseną
Trečiasis žingsnis — tas, kuris degina žmones. Maketavimo įrankio išsaugotas failas dažnai neša /ViewState /ON kiekvienam OCG, ir PDFium tada ignoruoja jūsų kruopščiai redaguotą /OFF masyvą: įrašymas pavyksta, failas švariai atsidaro, o sluoksnis tebepiešiamas. Print ir Export atveju OcExplicitUsageState pirmiausia skaito PrintState arba ExportState ir nusileidžia į ViewState, kai konkretaus įrašo nėra, tad vienas ViewState /ON prisega sluoksnį ir spausdinimui. Pažymėtas turinys, rodantis į OCMD (§8.11.2.2), tada išsprendžiamas pagal šiuos grupės rezultatus — per /P politiką arba, jei yra, /VE matomumo išraišką
Kaip išvardyti sluoksnius, kuriuos PDFium iš tikrųjų rodys?
TPdf.InspectOptionalContent grąžina TPdfOptionalContentInventory, kurio Groups masyvas neša kiekvieno OCG objekto numerį, pavadinimą, intentus, tris Usage būsenas, kalbą, zoom diapazoną, Locked žymę, radiogrupės indeksą ir apskaičiuotąjį EffectiveVisible. Metodas pirmiausia liepia PDFium išsaugoti esamą atmintyje esantį dokumentą, išskleidžia objektų srautus ir nuskanuoja rezultatą, tad sesijoje anksčiau atlikti redagavimai atsispindi. Konfigūracijos indeksas 0 visada yra numatytasis /D žodynas, o /Configs įrašai eina nuo indekso 1; numatytasis -1 argumentas parenka indeksą 0. Dokumentas be /OCProperties priverčia metodą grąžinti False su priežastimi ErrorMessage, užuot kėlus išimtį
procedure TFormMain.ListLayers;
var
Inv: TPdfOptionalContentInventory;
G: TPdfOptionalContentGroup;
begin
// Usage pagal nutylėjimą ocuView; -1 parenka konfigūraciją 0, /D žodyną
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 masyvas praneša kiekvieną OCMD su jo Policy (ocmpAnyOn, ocmpAllOn, ocmpAnyOff, ocmpAllOff), žaliuoju VisibilityExpression tekstu ir savuoju EffectiveVisible. Kelios kraštinės taisyklės sąmoningos. /P pagal nutylėjimą /AnyOn, o OCMD be grupių skaitomas matomu. Nuoroda į objekto numerį, kuris nėra žinomas OCG, laikoma matoma, užuot sugriovusi visą išraišką. /VE vertinimas sustoja prie 32 lizdavimo gylio ir bet ką giliau laiko paslėpta, tad priešiška ar į save rodanti išraiška negali apžiūros paversti steko perpildymu
Naujos sluoksnio būsenos rašymas su SaveAsOptionalContentConfigured
TPdf.SaveAsOptionalContentConfigured ima TPdfOptionalContentStateChange įrašų masyvą (grupės objekto numeris plius Visible) ir rašo dokumentą, kuriame pasirinkta konfigūracija duoda būtent tą būseną. Pasirinkta konfigūracija gauna /BaseState /ON plius pilnus /ON ir /OFF masyvus, dengiančius kiekvieną grupę, o kiekvienas OCG, jau turintis Usage žodyną, gauna aiškią ViewState (arba PrintState / ExportState, pagal Options.Usage), atitinkančią jo naują būseną. Su TPdfOptionalContentConfigureOptions.Default pasirinktos konfigūracijos /AS raktas pašalinamas, tad atidarymo, spausdinimo ar eksporto įvykis negali sluoksnių grąžinti atgal
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;
// Konfigūracija 0, ocuView, DisableAutomaticState ir 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;
Rašymo kelias PDFium patį išsaugotą išvestį laiko baitas į baitą priešdėliu ir prijungia tik perrašytą konfigūracijos savininką ir OCG objektus, nešančius Usage žodynus, po jų — naują xref sekciją ir trailer. Dar nė vienas baitas nepasiekus jūsų paskirties vietos, rezultatas atveriamas atskirame TPdf pagal griežtą įkėlimo politiką, ir metodas žlunga, jei kryžminių nuorodų lentelė nepraena patikros. Failo perkrova žengia žingsniu toliau: rašo į laikinąjį failą šalia tikslo ir pakeičia tikslą tik sėkmingai patikrinus, tad atmestas atnaujinimas niekada nepalieka pusiau parašyto brėžinio. Tai tas pats patikrintų inkrementinių revizijų būdas, kurį naudoja PDF vardų ir numerių medžių redaktorius PDFium Component
Ką konfigūruotas įrašymas atsisako daryti?
Konfigūruotas įrašymas atsisako bet kokio pakeitimo, kurio pats dokumentas draudžia ar negali saugiai atvaizduoti, ir kiekvienas atsisakymas nutinka dar nelietus paskirties vietos. Objekto numeris, nesantis /OCGs, žlunga iškart. Grupės, įrašytos konfigūracijos /Locked masyve, keitimas žlunga, nors jos dabartinės reikšmės pakartojimas leidžiamas. Su įjungtu EnforceRadioGroups bet kokia /RBGroups aibė, kuri galiausiai turėtų daugiau nei vieną matomą narį, atmetama, užuot tyliai išjungus kitus. Užšifruoti dokumentai atmetami, nes atvirųjų duomenų inkrementiniai objektai negali nešti aktyvaus saugumo apdorotojo. Pasirašyti dokumentai kelia EPdfError, nebent perduodate AllowSignedDocument = True, nes tai, ką rodo puslapis, pakeitus gali sulaužyti parašo aprėptį ar sertifikavimo politiką
function TFormMain.SavePrintPreset(Target: TStream;
const Changes: TPdfOptionalContentStateChanges): Boolean;
var
Options: TPdfOptionalContentConfigureOptions;
Report: TPdfOptionalContentConfigureReport;
begin
Options := TPdfOptionalContentConfigureOptions.Default;
Options.Usage := ocuPrint; // rašo /Print << /PrintState ... >>
Options.ConfigurationIndex := 1; // pirmasis /Configs įrašas, ne /D
try
Result := Pdf.SaveAsOptionalContentConfigured(Target, Changes, Options,
Report); // AllowSignedDocument lieka False
if not Result then
ShowMessage(Report.ErrorMessage);
except
on E: EPdfError do
begin
ShowMessage(E.Message); // pasirašytas failas: į Target nerašoma nieko
Result := False;
end;
end;
end;
Susipažinkite su kompromisais, dar prieš tai prijungdami prie paketinės užduoties. Pridėta revizija stovi ant PDFium pilno pakartotino išsaugojimo, o ne jūsų originalių failo baitų, ir būtent todėl pasirašytai įvestiai reikia aiškaus pritarimo. Perrašymas taip pat suvienodina pasirinktą konfigūraciją iki /BaseState /ON, tad autoriaus /Unchanged arba /OFF bazinė linija pakeičiama aiškiais masyvais su ta pačia gautina matomumu. /AS išmetimas pašalina tik spausdinimui skirtus triukus, tokius kaip vandens ženklelio sluoksnis, atsirandantis tik popieriuje; nustatykite DisableAutomaticState į False, norėdami tuos taisykles palikti, sutikdami, kad jos tam įvykiui gali perrašyti jūsų prašomą būseną. Iš gerosios pusės, PDF/A-2 (ISO 19005-2 6.9 punktas) ir PDF/UA (ISO 14289-1 7.10 punktas) abu draudžia /AS konfigūracijos žodynuose, tad numatytasis išvesties pašalina vieną problemą, kurią jūsų PDF/A preflight patikra su PDFium Component kitu atveju praneštų
Kur sluoksnių valdymas telpa Delphi PDF žiūryklėje
Žiūryklėje sluoksnių valdymas yra varnelėmis valdomas sąrašas, varomas inventoriaus plius išsaugoto rezultato perkrovimo. Užpildykite sąrašą iš Groups, išjunkite Locked įrašus, laikykite tuos pačius RadioGroupIndex dalijančius narius vienas kitam išimamais ir taikydami rašykite į TMemoryStream, o tą srautą vėl įkelkite į TPdf, kad vaizdas nupieštų naują būseną. Sujungimą tarp TPdf ir TPdfView dengia funkcijomis turtingos PDF žiūryklės su PDFium VCL Delphi statymas. Licencijavimas, bandomieji parsisiuntimai ir likusi funkcijų dalis — PDFium Component for Delphi produkto puslapyje