PDFium Component hallitsee PDF:n valinnaisia sisältökerroksia (OCG:t) Delphissä kahden TPdf-metodin kautta: InspectOptionalContent luetteloi jokaisen kerroksen yhdessä sen näkyvyyden kanssa jonka PDFium oikeasti renderöi, ja SaveAsOptionalContentConfigured kirjoittaa verifioidun kopion jossa valitsemasi kerrokset on kytketty päälle tai pois. Toinen metodi myös neutraloi Usage- ja /AS-säännöt jotka muuten kumoaisivat muokkauksesi hiljaa. Molemmat toimivat asiakirjalla joka on jo auki TPdfissä, joten toista jäsentä ei ole pidettävä synkassa sen kanssa mitä katselin näyttää
Pyyntö saapuu yleensä CAD- tai GIS-talolta: piirustussetti toimitetaan mitoilla, annotaatioilla ja otsikkolohkolla erillisillä kerroksilla, ja asiakas haluaa kopion josta mitat on piilotettu ennen kuin se menee toimittajalle. PDFium renderöi valinnaisen sisällön oikein, mutta sen julkinen ABI ei tarjoa funktiota OCG:ien luetteloimiseen, konfiguraation valintaan tai kerrostilan vaihtamiseen. Niin putoat objektitasolle, muokkaat /OCPropertiesia, tallennat, lataat uudelleen, ja kerros on yhä siellä. Syy on PDFiumin näkyvyyslogiikka, ja se kannattaa ymmärtää ennen mihinkään tavuihin koskemista
Miksi /ON:n ja /OFF:n muokkaaminen ei muuta sitä mitä PDFium renderöi?
Konfiguraatiosanakirjan /ON- ja /OFF-aulokkojen muokkaaminen ei riitä, koska PDFium antaa OCG:n oman /Usage-sanakirjan sisällä olevan eksplisiittisen tilan voittaa kyseiset aulokot, ja /AS-automaattitilasääntö voi sitten kumota molemmat. ISO 32000-1 §8.11.4 kuvaa konfiguraatiot ja usage-sanakirjat erillisinä mekanismeina; PDFiumin renderöijä taittaa ne yhdeksi päätökseksi, ja InspectOptionalContent toistaa sen tässä järjestyksessä:
- Aloita konfiguraation
/BaseStateista, jossa/ONja/Unchangedlasketaan molemmat näkyviksi ja vain/OFFpiilottaa - Sovella konfiguraation
/ON-aulokkoa, sitten sen/OFF-aulokkoa, joten molemmissa lueteltu ryhmä päätyy piilotetuksi - Sovella ryhmän eksplisiittistä Usage-tilaa pyydetylle käyttötarkoitukselle, kuten
/Usage << /View << /ViewState /OFF >> >>, joka kumoaa kaiken yllä olevan - Käsittele ryhmää jonka
/Intentei sisällä/Viewia eikä/Allia näkyvänä, koska se ei osallistu view-intent-näkyvyyteen - Aja viimeksi valitun konfiguraation
/AS-aulokko, jonka merkinnät osuvalle tapahtumalle asettavat luettelemiensa ryhmien tilan
Kolmas askel on se joka polttaa ihmisiä. Layout-työkalun tallentama tiedosto kantaa usein /ViewState /ONia jokaisella OCG:lla, ja PDFium sivuuttaa silloin huolella muokkaamasi /OFF-aulokon: tallennus onnistuu, tiedosto avautuu siististi ja kerros maalaa silti. Printille ja Exportille OcExplicitUsageState lukee ensin PrintStatein tai ExportStatein ja palaa ViewStatein kun kyseinen merkintä puuttuu, joten yksinäinen ViewState /ON kiinnittää kerroksen myös tulostusta varten. OCMD:hen (§8.11.2.2) viittaava merkitty sisältö ratkaistaan sitten näitä ryhmäkohtaisia tuloksia vasten, /P-käytännön kautta tai, jos läsnä, /VE-näkyvyyslausekkeen kautta
Miten luettelet kerrokset jotka PDFium oikeasti näyttää?
TPdf.InspectOptionalContent palauttaa TPdfOptionalContentInventoryin jonka Groups-aulokko kantaa jokaisen OCG:n objektinumeron, nimen, intentit, kolme Usage-tilaa, kielen, zoom-alueen, Locked-lipun, radioaryhmäindeksin ja lasketun EffectiveVisiblein. Metodi käskee ensin PDFiumin tallentamaan nykyisen muistissa olevan asiakirjan, laajentaa objektistreamit ja skannaa tuloksen, joten istunnon aikana aiemmin tehdyt muokkaukset heijastuvat. Konfiguraatioindeksi 0 on aina oletusarvoinen /D-sanakirja ja /Configsin merkinnät seuraavat indeksistä 1; oletusargumentti -1 valitsee indeksin 0. Asiakirja ilman /OCPropertiesia saa metodin palauttamaan arvon False syynä ErrorMessageissa poikkeuksen nostamisen sijaan
procedure TFormMain.ListLayers;
var
Inv: TPdfOptionalContentInventory;
G: TPdfOptionalContentGroup;
begin
// Usage on oletuksena ocuView; -1 valitsee konfiguraation 0, /D-sanakirjan
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-aulokko raportoi jokaisen OCMD:n sen Policyllä (ocmpAnyOn, ocmpAllOn, ocmpAnyOff, ocmpAllOff), raakan VisibilityExpression-tekstin ja omalla EffectiveVisibleillaan. Muutama reunasääntö on tahallinen. /P on oletuksena /AnyOn, ja OCMD ilman ryhmiä lasketaan näkyväksi. Viittaus objektinumeroon joka ei ole tunnettu OCG käsitellään näkyvänä sen sijaan että koko lauseke epäonnistuisi. /VEin evaluointi pysähtyy sisäkkäisyyssyvyyteen 32 ja käsittelee syvemmän piilotettuna, mikä estää vihamielistä tai itseensä viittaavaa lauseketta muuttamasta tarkastusta stack overflowiksi
Uuden kerrostilan kirjoittaminen SaveAsOptionalContentConfiguredilla
TPdf.SaveAsOptionalContentConfigured ottaa vastaan aulokon TPdfOptionalContentStateChange-recordeja (ryhmän objektinumero plus Visible) ja kirjoittaa asiakirjan jossa valittu konfiguraatio tuottaa täsmälleen kyseisen tilan. Valittu konfiguraatio saa /BaseState /ONin plus täydelliset /ON- ja /OFF-aulokot kattaen jokaisen ryhmän, ja jokainen OCG jolla on jo Usage-sanakirja saa eksplisiittisen ViewStatein (tai PrintStatein / ExportStatein, Options.Usagen mukaan) vastaamaan uutta tilaansa. TPdfOptionalContentConfigureOptions.Defaultilla valitun konfiguraation /AS-avain poistetaan jotta avaus-, tulostus- tai export-tapahtuma ei voi kytkeä kerroksia takaisin
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;
// Konfiguraatio 0, ocuView, DisableAutomaticState ja EnforceRadioGroups tosi
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;
Kirjoituspolku pitää PDFiumin oman tallennetun tuotoksen tavu tavulta etuliitteenä ja liittää vain uudelleenkirjoitetun konfiguraation omistajan ja Usage-sanakirjoja kantavat OCG-objektit, joita seuraa uusi xref-osa ja traileri. Ennen kuin yksi ainoa tavu saapuu kohteeseesi, tulos avataan uudelleen erillisessä TPdfissä tiukan latauskäytännön alla, ja metodi epäonnistuu jos ristiviittaustaulu ei validoidu. Tiedosto-overload menee askeleen pidemmälle: se kirjoittaa väliaikaiseen tiedostoon kohteen viereen ja korvaa kohteen vasta kun verifiointi onnistuu, joten hylätty päivitys ei koskaan jätä puoliksi kirjoitettua piirustusta jälkeensä. Se on sama verifioitu inkrementaalinen revisiolähestymistapa jota PDF-nimipuun ja numeropuun editori PDFium Componentissa käyttää
Mitä konfiguroitu tallennus kieltäytyy tekemästä?
Konfiguroitu tallennus kieltäytyy mistä tahansa muutoksesta jonka asiakirja itse kieltää tai ei voi esittää turvallisesti, ja jokainen kieltäytyminen tapahtuu ennen kuin kohdetta kosketaan. Objektinumero joka ei ole /OCGsissa epäonnistuu täysin. Ryhmän muuttaminen joka on lueteltu konfiguraation /Locked-aulokossa epäonnistuu, vaikka sen nykyisen arvon toistaminen on sallittua. EnforceRadioGroupsin ollessa päällä mikä tahansa /RBGroups-joukko joka päätyisi useampaan kuin yhteen näkyvään jäseneen hylätään sen sijaan että muut kytkettäisiin hiljaa pois. Salatut asiakirjat hylätään koska selkotekstiset inkrementaaliobjektit eivät voi kantaa aktiivista turvauskäsittelijää. Allekirjoitetut asiakirjat nostavat EPdfErrorin ellet välitä AllowSignedDocument = True:ta, koska sivun sisällön muuttaminen voi rikkoa allekirjoituskattavuuden tai sertifiointikäytännön
function TFormMain.SavePrintPreset(Target: TStream;
const Changes: TPdfOptionalContentStateChanges): Boolean;
var
Options: TPdfOptionalContentConfigureOptions;
Report: TPdfOptionalContentConfigureReport;
begin
Options := TPdfOptionalContentConfigureOptions.Default;
Options.Usage := ocuPrint; // kirjoittaa /Print << /PrintState ... >>
Options.ConfigurationIndex := 1; // /Configs:n ensimmäinen merkintä, ei /D
try
Result := Pdf.SaveAsOptionalContentConfigured(Target, Changes, Options,
Report); // AllowSignedDocument pysyy arvossa False
if not Result then
ShowMessage(Report.ErrorMessage);
except
on E: EPdfError do
begin
ShowMessage(E.Message); // allekirjoitettu tiedosto: mitään ei kirjoiteta Targetiin
Result := False;
end;
end;
end;
Tunne kompromissit ennen kuin kytket tämän eräajoon. Liitetty revisio istuu PDFiumin täyden uudelleentallennuksen päällä, ei alkuperäisten tiedostotavujesi, mikä on juuri syy sille että allekirjoitettu syöte tarvitsee eksplisiittisen suostumuksen. Uudelleenkirjoitus normalisoi myös valitun konfiguraation muotoon /BaseState /ON, joten tekijän /Unchanged- tai /OFF-baselini korvautuu eksplisiittisillä aulokoilla samalla lopputuloksen näkyvyydellä. /AS:n pudottaminen poistaa vain tulostukseen tarkoitetut temput kuten vesileimakerroksen joka ilmestyy vain paperille; aseta DisableAutomaticState arvoon False säilyttääksesi kyseiset säännöt hyväksyen että ne voivat kumota pyytämäsi tilan kyseiselle tapahtumalle. Positiivisena puolena PDF/A-2 (ISO 19005-2 clause 6.9) ja PDF/UA (ISO 14289-1 clause 7.10) kieltävät molemmat /ASin konfiguraatiosanakirjoissa, joten oletustuotos poistaa yhden ongelman jonka PDF/A-preflight-validointi PDFium Componentilla muuten raportoisi
Missä kerrosten hallinta istuu Delphi-PDF-katselimessa
Katselimessa kerrosten hallinta on tarkistuslista jota ohjaa inventaario plus tallennetun tuloksen uudelleenlataus. Täytä tarkistuslista Groupsista, poista käytöstä Locked-merkinnät, käsittele samaa RadioGroupIndexia jakavat jäsenet toisiaan poissulkevina, ja sovittaessa kirjoita TMemoryStreamiin ja lataa kyseinen stream takaisin TPdfiin jotta näkymä maalaa uuden tilan. Kytkentä TPdfin ja TPdfViewin välillä käsitellään artikkelissa ominaisrikkaan PDF-katselimen rakentaminen PDFium VCL:llä Delphissä. Lisensointi, kokeilulataukset ja loput ominaisuusjoukosta ovat PDFium Component for Delphi -tuotesivulla