PDFium Component controlează straturile de conținut opțional PDF (OCG) în Delphi prin două metode TPdf: InspectOptionalContent listează fiecare strat împreună cu vizibilitatea pe care PDFium o va randa efectiv, iar SaveAsOptionalContentConfigured scrie o copie verificată în care straturile alese de dvs. sunt pornite sau oprite. A doua metodă neutralizează de asemenea regulile Usage și /AS care altfel ar desface în tăcere editarea dvs. Ambele lucrează pe documentul deja deschis în TPdf, deci nu există un al doilea parser de ținut în sincron cu ce arată viewer-ul
Cererea vine de obicei dintr-un atelier de CAD sau GIS: setul de desene se livrează cu dimensiuni, adnotări și un bloc de titlu pe straturi separate, iar clientul vrea o copie cu dimensiunile ascunse înainte să ajungă la un furnizor. PDFium randează corect conținutul opțional, dar ABI-ul lui public nu are nicio funcție de enumerare a OCG-urilor, de alegere a unei configurații sau de răsturnare a stării unui strat. Deci coborâți la nivel de obiect, editați /OCProperties, salvați, reîncărcați, iar stratul este tot acolo. Motivul este logica de vizibilitate a PDFium și merită înțeleasă înainte să atingeți vreun byte
De ce nu schimbă editarea /ON și /OFF ce randează PDFium?
Editarea tablourilor /ON și /OFF ale dicționarului de configurație nu este de ajuns, pentru că PDFium lasă o stare explicită din propriul dicționar /Usage al OCG-ului să învingă acele tablouri, iar o regulă auto-state /AS le poate suprascrie apoi pe ambele. ISO 32000-1 §8.11.4 descrie configurațiile și dicționarele de usage ca mecanisme separate; renderer-ul PDFium le împăturește într-o singură decizie, iar InspectOptionalContent o reproduce în ordinea aceasta:
- Plecați de la
/BaseStateal configurației, unde/ONși/Unchangedcontează ambele ca vizibile și doar/OFFascunde - Aplicați tabloul
/ONal configurației, apoi tabloul lui/OFF, astfel încât un grup listat în ambele ajunge ascuns - Aplicați starea Usage explicită a grupului pentru usage-ul cerut, precum
/Usage << /View << /ViewState /OFF >> >>, care suprascrie tot ce este mai sus - Tratați ca vizibil un grup al cărui
/Intentnu conține nici/View, nici/All, fiindcă nu ia parte la vizibilitatea cu intent de vizualizare - În final rulați tabloul
/ASal configurației selectate, ale cărui intrări pentru evenimentul potrivit setează starea grupurilor pe care le listează
Al treilea pas este cel care arde oamenii. Un fișier salvat de un instrument de layout poartă adesea /ViewState /ON pe fiecare OCG, iar PDFium ignoră apoi tabloul /OFF editat cu grijă: salvarea reușește, fișierul se redeschide curat, iar stratul pictează în continuare. Pentru Print și Export, OcExplicitUsageState citește întâi PrintState sau ExportState și cade pe ViewState când intrarea specifică lipsește, deci un ViewState /ON singuratic fixează stratul și pentru tipărire. Conținutul marcat care face referință la un OCMD (§8.11.2.2) este apoi rezolvat contra acestor rezultate per grup, prin politica /P sau, când este prezentă, expresia de vizibilitate /VE
Cum listezi straturile pe care PDFium le va arăta efectiv?
TPdf.InspectOptionalContent întoarce un TPdfOptionalContentInventory al cărui tablou Groups poartă numărul de obiect al fiecărui OCG, numele, intent-urile, cele trei stări Usage, limba, intervalul de zoom, flag-ul Locked, indexul de grup radio și EffectiveVisible calculat. Metoda îl face întâi pe PDFium să salveze documentul curent din memorie, expandează fluxurile de obiecte și scanează rezultatul, astfel încât editările făcute mai devreme în sesiune se reflectă. Indexul de configurație 0 este întotdeauna dicționarul implicit /D, iar intrările lui /Configs urmează de la indexul 1; argumentul implicit de -1 selectează indexul 0. Un document fără /OCProperties face ca metoda să întoarcă False cu motivul în ErrorMessage în loc să ridice
procedure TFormMain.ListLayers;
var
Inv: TPdfOptionalContentInventory;
G: TPdfOptionalContentGroup;
begin
// Usage are implicit ocuView; -1 selectează configurația 0, dicționarul /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;
Tabloul Memberships raportează fiecare OCMD cu Policy-ul lui (ocmpAnyOn, ocmpAllOn, ocmpAnyOff, ocmpAllOff), textul brut VisibilityExpression și propriul lui EffectiveVisible. Câteva reguli de margine sunt deliberate. /P are implicit /AnyOn, iar un OCMD fără grupuri contează ca vizibil. O referință la un număr de obiect care nu este un OCG cunoscut este tratată ca vizibilă în loc să prăbușească întreaga expresie. Evaluarea /VE se oprește la o adâncime de imbricare de 32 și tratează orice mai adânc ca ascuns, ceea ce împiedică o expresie ostilă sau autoreferențială să transforme inspecția într-un stack overflow
Scrierea unei noi stări de strat cu SaveAsOptionalContentConfigured
TPdf.SaveAsOptionalContentConfigured primește un tablou de recorduri TPdfOptionalContentStateChange (număr de obiect de grup plus Visible) și scrie un document în care configurația selectată produce exact starea aceea. Configurația selectată primește /BaseState /ON plus tablouri complete /ON și /OFF care acoperă fiecare grup, iar fiecare OCG care are deja un dicționar Usage primește un ViewState explicit (sau PrintState / ExportState, după Options.Usage) care se potrivește cu noua lui stare. Cu TPdfOptionalContentConfigureOptions.Default, cheia /AS a configurației selectate este îndepărtată, astfel încât un eveniment de deschidere, tipărire sau export să nu poată răsturna straturile înapoi
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;
// Configurația 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;
Calea de scriere păstrează ieșirea salvată a PDFium ca prefix identic byte cu byte și adaugă doar proprietarul de configurație rescris și obiectele OCG care poartă dicționare Usage, urmate de o secțiune xref nouă și un trailer. Înainte ca un singur byte să ajungă la destinație, rezultatul este redeschis într-un TPdf separat sub politica strictă de încărcare, iar metoda eșuează dacă tabelul de referințe încrucișate nu se validează. Overload-ul pe fișier merge un pas mai departe: scrie într-un fișier temporar lângă țintă și înlocuiește ținta doar după ce verificarea reușește, astfel încât o actualizare respinsă nu lasă niciodată în urmă un desen pe jumătate scris. Este aceeași abordare de revizuire incrementală verificată folosită de editorul de arbori de nume și de numere PDF din PDFium Component
Ce refuză să facă salvarea configurată?
Salvarea configurată refuză orice schimbare pe care documentul însuși o interzice sau nu o poate reprezenta în siguranță, iar fiecare refuz se întâmplă înainte ca destinația să fie atinsă. Un număr de obiect care nu este în /OCGs eșuează categoric. Schimbarea unui grup listat în tabloul /Locked al configurației eșuează, deși reformularea valorii lui curente este permisă. Cu EnforceRadioGroups activat, orice mulțime /RBGroups care ar ajunge cu mai mult de un membru vizibil este respinsă în loc să oprească în tăcere celelalte. Documentele criptate sunt respinse pentru că obiectele incrementale în text clar nu pot duce handler-ul de securitate activ. Documentele semnate ridică EPdfError dacă nu pasați AllowSignedDocument = True, fiindcă schimbarea a ceea ce arată o pagină poate rupe acoperirea semnăturii sau o politică de certificare
function TFormMain.SavePrintPreset(Target: TStream;
const Changes: TPdfOptionalContentStateChanges): Boolean;
var
Options: TPdfOptionalContentConfigureOptions;
Report: TPdfOptionalContentConfigureReport;
begin
Options := TPdfOptionalContentConfigureOptions.Default;
Options.Usage := ocuPrint; // scrie /Print << /PrintState ... >>
Options.ConfigurationIndex := 1; // prima intrare a lui /Configs, nu /D
try
Result := Pdf.SaveAsOptionalContentConfigured(Target, Changes, Options,
Report); // AllowSignedDocument rămâne False
if not Result then
ShowMessage(Report.ErrorMessage);
except
on E: EPdfError do
begin
ShowMessage(E.Message); // fișier semnat: nimic scris în Target
Result := False;
end;
end;
end;
Cunoașteți compromisurile înainte să cuplați asta într-un job de batch. Revizuirea adăugată stă peste resalvarea completă a PDFium, nu peste byte-ii originali ai fișierului dvs., ceea ce este exact motivul pentru care input-ul semnat are nevoie de consimțământ explicit. Rescrierea normalizează de asemenea configurația selectată la /BaseState /ON, astfel încât o linie de bază /Unchanged sau /OFF a autorului este înlocuită de tablouri explicite cu aceeași vizibilitate rezultată. Îndepărtarea lui /AS elimină trucurile doar de tipar, precum un strat de filigran care apare doar pe hârtie; setați DisableAutomaticState la False ca să păstrați acele reguli, acceptând că vă pot suprascrie starea cerută pentru acel eveniment. Pe partea bună, PDF/A-2 (ISO 19005-2 clauza 6.9) și PDF/UA (ISO 14289-1 clauza 7.10) interzic ambele /AS în dicționarele de configurație, deci ieșirea implicită elimină o problemă pe care validarea de preflight PDF/A cu PDFium Component ar raporta-o altfel
Unde se încadrează controlul straturilor într-un viewer PDF Delphi
Într-un viewer, controlul straturilor este o listă de bifare condusă de inventar plus o reîncărcare a rezultatului salvat. Populați lista din Groups, dezactivați intrările care sunt Locked, tratați membrii care împart un RadioGroupIndex ca reciproc exclusivi, iar la aplicare scrieți într-un TMemoryStream și încărcați fluxul acela înapoi în TPdf ca vederea să picteze noua stare. Cablajul dintre TPdf și TPdfView este acoperit în construirea unui viewer PDF bogat în funcții cu PDFium VCL în Delphi. Licențierea, descărcările de probă și restul setului de funcții sunt pe pagina de produs PDFium Component pentru Delphi