A PDFium Component a PDF opcionális tartalomrétegeit (OCG-ket) Delphiben két TPdf metóduson keresztül kezeli: az InspectOptionalContent minden réteget felsorol a PDFium által ténylegesen kirajzolt láthatósággal együtt, a SaveAsOptionalContentConfigured pedig egy ellenőrzött másolatot ír, amelyben az Ön által választott rétegek be vagy ki vannak kapcsolva. A második metódus emellett hatástalanítja azokat a Usage és /AS szabályokat, amelyek egyébként csendben visszacsinálnák a módosítását. Mindkettő a TPdf-ben már megnyitott dokumentumon dolgozik, tehát nincs második elemző, amelyet a megjelenítővel szinkronban kellene tartani
A kérés jellemzően egy CAD vagy GIS műhelyből érkezik: a rajzkészlet méretezést, annotációkat és címsort külön rétegeken szállít, és a vevő olyan másolatot szeretne, amelyben a méretezés rejtve van, mielőtt egy beszállítóhoz kerül. A PDFium az opcionális tartalmat helyesen rajzolja, de a nyilvános ABI-jában nincs függvény az OCG-k felsorolására, konfiguráció választására vagy rétegállapot váltására. Így leereszkedik az objektumszintre, szerkeszti az /OCProperties-t, ment, újratölt, és a réteg még mindig ott van. Az ok a PDFium láthatósági logikája, és érdemes megérteni, mielőtt bármely bájthoz nyúlna
Miért nem változtatja meg a /ON és /OFF szerkesztése, amit a PDFium rajzol?
A konfigurációs szótár /ON és /OFF tömbjeinek szerkesztése önmagában nem elég, mert a PDFium hagyja, hogy az OCG saját /Usage szótárában lévő explicit állapot felülírja ezeket a tömböket, egy /AS automatikus állapotszabály pedig mindkettőt felülírhatja. Az ISO 32000-1 §8.11.4 a konfigurációkat és a használati szótárakat külön mechanizmusokként írja le; a PDFium rajzolója egyetlen döntéssé olvasztja őket össze, és az InspectOptionalContent ebben a sorrendben reprodukálja:
- Induljon a konfiguráció
/BaseState-éből, ahol a/ONés az/Unchangedegyaránt láthatónak számít, és csak a/OFFrejt - Alkalmazza a konfiguráció
/ONtömbjét, majd a/OFFtömbjét, tehát mindkettőben szereplő csoport rejtve végzi - Alkalmazza a csoport explicit Usage állapotát a kért használatra, például
/Usage << /View << /ViewState /OFF >> >>, amely felülír mindent, ami fentebb áll - Azon csoportot, amelynek
/Intent-jében sincs sem/View, sem/All, tekintse láthatónak, hiszen nem vesz részt a nézet-szándékú láthatóságban - Végül futtassa a kiválasztott konfiguráció
/AStömbjét, amelynek az egyező eseményre vonatkozó bejegyzései beállítják az általuk felsorolt csoportok állapotát
A harmadik lépés az, amely megégeti az embereket. Egy layout eszköz által mentett fájl gyakran /ViewState /ON-t hordoz minden OCG-n, és a PDFium ekkor figyelmen kívül hagyja az Ön gondosan szerkesztett /OFF tömbjét: a mentés sikerül, a fájl tisztán újranyílik, és a réteg mégis fest. Print és Export esetén az OcExplicitUsageState előbb a PrintState-et vagy az ExportState-et olvassa, és a ViewState-re esik vissza, ha a konkrét bejegyzés hiányzik, tehát egy magányos ViewState /ON a nyomtatáshoz is rögzíti a réteget. Az OCMD-re (§8.11.2.2) hivatkozó megjelölt tartalmat ezután ezekhez a csoportonkénti eredményekhez mérnek, a /P szabály által vagy, ha jelen van, a /VE láthatósági kifejezés által
Hogyan sorolhatja fel azokat a rétegeket, amelyeket a PDFium ténylegesen mutat?
A TPdf.InspectOptionalContent egy TPdfOptionalContentInventory-t ad vissza, amelynek Groups tömbje mindegyik OCG-hez hordozza az objektumszámát, nevét, szándékait, a három Usage állapotot, a nyelvet, a zoom tartományt, a Locked jelzőt, a rádiócsoport indexét és a számolt EffectiveVisible-t. A metódus előbb megkéri a PDFiumot, hogy mentse az aktuális, memóriabeli dokumentumot, kibontja az objektumfolyamokat, és átvizsgálja az eredményt, tehát a munkamenet korábbi szerkesztései tükröződnek. A 0-s konfigurációs index mindig az alapértelmezett /D szótár, a /Configs bejegyzései pedig az 1-es indextől következnek; a -1 alapértelmezett argumentum a 0-s indexet választja. Egy /OCProperties nélküli dokumentum esetén a metódus False-szal tér vissza, az ok az ErrorMessage-ben, kivétel dobása nélkül
procedure TFormMain.ListLayers;
var
Inv: TPdfOptionalContentInventory;
G: TPdfOptionalContentGroup;
begin
// A Usage alapértelmezése ocuView; a -1 a 0-s konfigurációt, a /D szótárt választja
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;
A Memberships tömb minden OCMD-et jelent a Policy-jével (ocmpAnyOn, ocmpAllOn, ocmpAnyOff, ocmpAllOff), a nyers VisibilityExpression szöveggel és a saját EffectiveVisible-jével. Néhány szélső szabály szándékos. A /P alapértelmezése /AnyOn, és egy csoport nélküli OCMD láthatónak számít. Egy olyan objektumszámra való hivatkozás, amely nem ismert OCG, láthatónak kezelik ahelyett, hogy az egész kifejezést buktatná. A /VE kiértékelés 32-es beágyazási mélységnél megáll, és mindent mélyebbet rejtettként kezel, ami megakadályozza, hogy egy ellenséges vagy önmagára hivatkozó kifejezés a vizsgálatot stack overflow-vá alakítsa
Új rétegállapot írása a SaveAsOptionalContentConfigured-dzsel
A TPdf.SaveAsOptionalContentConfigured egy TPdfOptionalContentStateChange rekordokból álló tömböt vesz (csoportobjektumszám plusz Visible), és olyan dokumentumot ír, amelyben a kiválasztott konfiguráció pontosan azt az állapotot adja. A kiválasztott konfiguráció /BaseState /ON-t kap, plusz teljes /ON és /OFF tömböket, amelyek minden csoportot lefednek, és minden olyan OCG, amelynek már van Usage szótára, explicit ViewState-t (vagy PrintState-et / ExportState-et, az Options.Usage-ot követve) kap az új állapotához illesztve. TPdfOptionalContentConfigureOptions.Default esetén a kiválasztott konfiguráció /AS kulcsa eltávolításra kerül, hogy egy nyitó, nyomtató vagy exportáló esemény ne kapcsolhassa vissza a rétegeket
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;
// 0-s konfiguráció, ocuView, DisableAutomaticState és 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;
Az írási út a PDFium saját mentett kimenetét tartja bájtonkénti előtagként, és csak az újraírt konfigurációtulajdonost és az Usage szótárakat hordozó OCG objektumokat fűzi hozzá, majd egy új xref szekciót és trailert. Mielőtt egyetlen bájt elérné a célját, az eredmény egy külön TPdf-ben újranyílik a szigorú betöltési szabályzattal, és a metódus elbukik, ha a kereszthivatkozási tábla nem érvényesíthető. A fájl túlterhelése még egy lépéssel megy tovább: a cél mellett egy ideiglenes fájlba ír, és a célt csak az ellenőrzés sikere után cseréli, tehát egy elutasított frissítés soha nem hagy félreírt rajzot maga után. Ugyanez az ellenőrzött növekményes revíziós megközelítés, amelyet a PDF névfa- és számfaszerkesztő a PDFium Component-ben használ
Mire nem hajlandó a konfigurált mentés?
A konfigurált mentés minden olyan változást megtagad, amelyet maga a dokumentum tilt, vagy amelyet nem tud biztonságosan ábrázolni, és minden megtagadás a cél megérintése előtt történik. Egy, az /OCGs-ben nem szereplő objektumszám egyenesen elbukik. A konfiguráció /Locked tömbjében szereplő csoport megváltoztatása elbukik, bár az aktuális értékének újramondása megengedett. Bekapcsolt EnforceRadioGroups esetén minden olyan /RBGroups készletet elutasítanak, amely egyetlen látható tagnál többel végezne, ahelyett hogy a többieket csendben kikapcsolnák. A titkosított dokumentumok elutasításra kerülnek, mert a nyílt szöveges növekményes objektumok nem hordozhatják az aktív biztonsági kezelőt. Az aláírt dokumentumok EPdfError-t dobnak, hacsak AllowSignedDocument = True-t nem ad át, hiszen az, ami egy oldalon látszik, megváltoztathatja az aláírás lefedettségét vagy egy tanúsítási szabályzatot
function TFormMain.SavePrintPreset(Target: TStream;
const Changes: TPdfOptionalContentStateChanges): Boolean;
var
Options: TPdfOptionalContentConfigureOptions;
Report: TPdfOptionalContentConfigureReport;
begin
Options := TPdfOptionalContentConfigureOptions.Default;
Options.Usage := ocuPrint; // /Print << /PrintState ... >>-t ír
Options.ConfigurationIndex := 1; // a /Configs első bejegyzése, nem a /D
try
Result := Pdf.SaveAsOptionalContentConfigured(Target, Changes, Options,
Report); // az AllowSignedDocument False marad
if not Result then
ShowMessage(Report.ErrorMessage);
except
on E: EPdfError do
begin
ShowMessage(E.Message); // aláírt fájl: semmi nem íródik a Target-be
Result := False;
end;
end;
end;
Ismerje meg a kompromisszumokat, mielőtt kötegelt feladatba kötné. A hozzáfűzött revízió a PDFium teljes újramentésén ül, nem az Ön eredeti fájljának bájtjain, és pontosan ezért kíván az aláírt bemenet explicit beleegyezést. Az újraírás a kiválasztott konfigurációt /BaseState /ON-ra is normalizálja, tehát egy szerző /Unchanged vagy /OFF bázisát azonos eredményű láthatósággal járó explicit tömbök váltják fel. Az /AS eldobása eltávolítja a csak nyomtatásra vonatkozó trükköket, például egy vízjelet hordozó réteget, amely csak papíron jelenik meg; ha ezeket a szabályokat meg szeretné tartani, állítsa a DisableAutomaticState-et False-ra, vállalva, hogy azok felülírhatják a kért állapotot az adott eseményre. Plusz oldalon a PDF/A-2 (ISO 19005-2 6.9 szakasz) és a PDF/UA (ISO 14289-1 7.10 szakasz) egyaránt tiltja az /AS-t a konfigurációs szótárakban, tehát az alapértelmezett kimenet egy problémát előz meg, amelyet egyébként a PDF/A preflight validáció a PDFium Component-tel jelentene
Hol ül a rétegvezérlés egy Delphi PDF nézegetőben
Egy nézegetőben a rétegvezérlés egy jelölőnégyzetlista, amelyet a leltár hajt, plusz a mentett eredmény újratöltése. Töltse fel a listát a Groups-ból, tiltsa le a Locked bejegyzéseket, az azonos RadioGroupIndex-et hordozó tagokat tekintse kölcsönösen kizárónak, és alkalmazáskor írjon egy TMemoryStream-be, majd töltse vissza azt a streamet a TPdf-be, hogy a nézet az új állapotot fesse. A TPdf és a TPdfView közti vezetékezést a funkciókban gazdag PDF nézegető építése PDFium VCL-lel Delphiben cikk tárgyalja. Licencelés, próbaletöltések és a funkciókészlet többi része a PDFium Component Delphihez termékoldalon érhető el