Geometar otvara situacioni plan i želi da konture budu skrivene dok instalacije ostaju prikazane. Recenzent želi da crvene oznake za korekciju budu vidljive na ekranu, ali uklonjene sa odštampanog lista. List sa podacima o proizvodu se isporučuje na tri jezika u jednoj datoteci, a čitalac bira koji će jezik biti prikazan. Sva tri primera koriste istu PDF funkciju, a panel koji ih kontroliše u Acrobat-u zove se Layers. Funkcija koja leži iza tog panela je opcioni sadržaj, i to je ono što omogućava da jedna stranica nosi nekoliko nezavisnih vizuelnih slojeva koje čitač uključuje i isključuje.
Opcioni sadržaj je specifikovan u standardu ISO 32000-1 §8.11. Jedinica vidljivosti je opciona grupa sadržaja, OCG, rečnik tipa /OCG koji nosi ime. Označeni sadržaj na stranici je povezan sa grupom, a čitač odlučuje da li je ta grupa trenutno prikazana. Srodna struktura, rečnik članstva opcionog sadržaja ili OCMD, omogućava da vidljivost zavisi od logičke kombinacije nekoliko grupa, ali svakodnevni slučaj je jedna imenovana grupa koja predstavlja jedan sloj. Dokument povezuje ceo mehanizam kroz jedan unos kataloga, /OCProperties, koji je opisan u nastavku.
Šta katalog mora da sadrži
OCG je sam po sebi inertan. Da bi čitač izlistao sloj i zapamtio njegovo stanje, katalogu dokumenta je potreban rečnik /OCProperties, a §8.11.4 tačno propisuje šta ide u njega. Postoji niz /OCGs koji imenuje svaku grupu u datoteci, kao i unos /D koji drži podrazumevanu konfiguraciju. Podrazumevana konfiguracija je deo koji čitač primenjuje kada se datoteka prvi put otvori. Ona beleži koje grupe počinju kao uključene a koje kao isključene, koji unosi su zaključani protiv korisničkog menjanja i, kroz niz /Order, kako su nazivi slojeva raspoređeni i ugnežđeni u panelu.
Praktična posledica je da kreiranje sloja nikada nije čisto lokalni čin. Grupa se mora iscrtati na stranici, a takođe mora biti registrovana u strukturi na nivou kataloga koja ranije nije postojala. PDFlibPas radi oba koraka umesto vas. Prvi poziv koji kreira grupu dodaje unos /OCProperties u katalog i postavlja podrazumevanu konfiguraciju, tako da je sloj i nacrtan i izlistan bez posebnog vođenja evidencije sa vaše strane.
Zašto režim usklađenosti može uskratiti ovu funkciju
Pre nego što se pokrene bilo koji kod za slojeve, cilj usklađenosti dokumenta odlučuje da li je opcioni sadržaj uopšte dozvoljen. PDF/A-1, arhivski profil definisan u ISO 19005-1, u potpunosti zabranjuje unos /OCProperties u §6.1.13. Razlog odgovara svrsi formata. Arhivska datoteka mora da se renderuje identično za svakog čitaoca daleko u budućnosti, a sadržaj čiju vidljivost čitalac može da promeni je sadržaj čiji izgled nije fiksiran, pa profil zabranjuje ovu strukturu umesto da dozvoli dvosmislenu arhivu. PDF/A-2 i PDF/A-3, definisani u ISO 19005-2 i ISO 19005-3, zauzimaju suprotan stav u svom §6.9 i dozvoljavaju opcioni sadržaj, uz pravila o podrazumevanoj vidljivosti.
Ta razlika se direktno odražava u API-ju. Kada je dokument u PDF/A-1 režimu, NewOptionalContentGroup odbija da kreira grupu i vraća nulu, jer bi ispunjavanje zahteva proizvelo datoteku koja ne prolazi sopstvenu deklarisanu usklađenost. U režimu PDF/A-2 ili PDF/A-3, kao i u običnom neograničenom PDF-u, isti poziv uspeva i vraća ne-nulti ID grupe. Nulti rezultat stoga nije generička greška koju treba kasnije ispitati; to vam biblioteka govori da aktivni nivo usklađenosti nema mesta za tu funkciju.
var
Pdf: TPDFlib;
LayerID: Integer;
begin
Pdf := TPDFlib.Create(nil);
try
Pdf.NewDocument;
Pdf.SetPDFAMode(1); // PDF/A-1a: OCProperties forbidden
LayerID := Pdf.NewOptionalContentGroup('Utilities');
if LayerID = 0 then
// refused under PDF/A-1; not a transient error, the mode bans layers
ShowMessage('Optional content is not available in PDF/A-1 mode.');
finally
Pdf.Free;
end;
end;
Dva stanja po sloju, a ne jedno
Sloj nije jednostavno vidljiv ili nevidljiv. Podrazumevana konfiguracija beleži njegovo stanje na ekranu i posebno stanje štampanja, jer §8.11.4 razlikuje ono što čitač prikazuje od onoga što sistem za štampanje emituje. Ta dva stanja su namerno nezavisna. Vodeni žig nacrta može biti prikazan na ekranu i izostavljen sa papira, a sloj sa linijama sečenja može biti skriven na ekranu, ali poslat na ploter. Spajanje ova dva stanja primoralo bi jedno da prati drugo i izgubila bi se upravo ona kontrola zbog koje funkcija i postoji.
PDFlibPas izlaže ovaj par kroz dva setera. SetOptionalContentGroupVisible uzima ID grupe i zastavicu, gde jedan znači vidljivo a nula skriveno, i upravlja podrazumevanim stanjem na ekranu. SetOptionalContentGroupPrintable uzima ID grupe i zastavicu za to da li se sloj emituje kada se dokument štampa. Odgovarajući geteri, GetOptionalContentGroupVisible i GetOptionalContentGroupPrintable, vraćaju jedan ili nulu, tako da možete zasebno pročitati raspored sloja na ekranu i pri štampanju, umesto da zaključujete jedno iz drugog.
Izgradnja dva sloja na stranici
Kreiranje sloja i njegovo popunjavanje prate fiksni redosled. Nacrtate sadržaj za sloj na trenutnoj stranici, a zatim pozovete SetContentStreamOptional sa ID-om grupe, što obavija trenutni tok sadržaja stranice tako da sve što je do sada nacrtano pripada toj grupi. Pošto poziv snima sve što je na toku u tom trenutku, pravilo je da postavite oznake jednog sloja, dodelite ih, pa tek onda započnete sledeći sloj. Primer ispod postavlja instalacije na prvu stranicu, a recenzentske ispravke na drugu stranicu, podešava stanje ekrana i štampanja za svaki sloj i čuva datoteku.
var
Pdf: TPDFlib;
FontID, UtilLayer, RedlineLayer: Integer;
begin
Pdf := TPDFlib.Create(nil);
try
Pdf.NewDocument; // unconstrained PDF: layers allowed
Pdf.SetPageDimensions(595, 842); // A4 in points
FontID := Pdf.AddStandardFont(0); // Helvetica
Pdf.SelectFont(FontID);
// Layer 1: utilities, drawn then assigned to its own group
Pdf.SetTextColor(0.10, 0.30, 0.65);
Pdf.DrawText(72, 770, 'Utilities: water main, valve chamber');
UtilLayer := Pdf.NewOptionalContentGroup('Utilities');
Pdf.SetContentStreamOptional(UtilLayer);
Pdf.SetOptionalContentGroupVisible(UtilLayer, 1); // shown on screen
Pdf.SetOptionalContentGroupPrintable(UtilLayer, 1); // and on paper
// Layer 2: reviewer redline on a fresh page
Pdf.InsertPages(2, 1); // append one page after page 1
Pdf.SetTextColor(0.80, 0.10, 0.10);
Pdf.DrawText(72, 770, 'REVIEW: revise valve spec before issue');
RedlineLayer := Pdf.NewOptionalContentGroup('Reviewer markup');
Pdf.SetContentStreamOptional(RedlineLayer);
Pdf.SetOptionalContentGroupVisible(RedlineLayer, 1); // visible while reviewing
Pdf.SetOptionalContentGroupPrintable(RedlineLayer, 0); // never printed
Pdf.SaveToFile('SitePlan_Layers.pdf');
finally
Pdf.Free;
end;
end;
Sloj sa recenzentskim ispravkama je primer vredan pažnje. Prikazuje se na ekranu kako bi recenzent video belešku, a njegova zastavica za štampanje je nula, tako da odštampana verzija iste datoteke ne sadrži tekst recenzije. Ta asimetrija je cela poenta držanja ova dva stanja odvojeno.
Čitanje konfiguracije nazad
Čitanje slojeva je drugačiji prolazak kroz istu strukturu. Nakon što se datoteka učita, GetOptionalContentConfigCount izveštava o tome koliko rečnika konfiguracije dokument sadrži; prva podrazumevana konfiguracija je ID konfiguracije 1. Unutar konfiguracije, GetOptionalContentConfigOrderCount daje broj unosa u stablu redosleda, a vi ih indeksirate počevši od 1. Za svaki unos, GetOptionalContentConfigOrderItemLabel vraća njegov tekst za prikaz, a GetOptionalContentConfigOrderItemLevel vraća njegovu dubinu ugnežđivanja, tako da se kontura panela sa pod-slojevima uvučenim ispod naslova može rekonstruisati doslovno.
Svaki unos takođe ima tip. GetOptionalContentConfigOrderItemType razlikuje stvarnu opcionu grupu sadržaja od obične tekstualne oznake koja postoji samo da bi vodila odeljak stabla. Ta razlika je važna jer upiti o stanju po grupi imaju smisla samo za stvarne grupe. Za unos grupe, GetOptionalContentConfigState izveštava da li je konfiguracija pokreće kao uključenu, isključenu ili je ostavlja nepromenjenom, a GetOptionalContentConfigLocked izveštava da li je korisniku zabranjeno da je menja. Petlja ispod prikazuje stablo redosleda sa stanjem i statusom zaključavanja svake grupe, uvlačeći ih po nivoima.
var
Pdf: TPDFlib;
Cfg, Count, I, ItemType, GroupID, Indent: Integer;
Line: string;
begin
Pdf := TPDFlib.Create(nil);
try
if Pdf.LoadFromFile('SitePlan_Layers.pdf', '') = 0 then Exit;
if Pdf.GetOptionalContentConfigCount = 0 then Exit;
Cfg := 1; // the default configuration
Count := Pdf.GetOptionalContentConfigOrderCount(Cfg);
for I := 1 to Count do
begin
Indent := Pdf.GetOptionalContentConfigOrderItemLevel(Cfg, I);
Line := StringOfChar(' ', Indent * 2)
+ Pdf.GetOptionalContentConfigOrderItemLabel(Cfg, I);
ItemType := Pdf.GetOptionalContentConfigOrderItemType(Cfg, I);
if ItemType = 1 then // 1 = optional content group
begin
GroupID := Pdf.GetOptionalContentConfigOrderItemID(Cfg, I);
case Pdf.GetOptionalContentConfigState(Cfg, GroupID) of
1: Line := Line + ' [on]';
2: Line := Line + ' [off]';
3: Line := Line + ' [unchanged]';
end;
if Pdf.GetOptionalContentConfigLocked(Cfg, GroupID) = 1 then
Line := Line + ' (locked)';
end;
// ItemType = 2 is a text label heading; it has no per-group state
Writeln(Line);
end;
finally
Pdf.Free;
end;
end;
Dva detalja održavaju ovu petlju ispravnom. Indeks redosleda počinje od jedan, od 1 do ukupnog broja, što odgovara načinu na koji biblioteka interno numeriše stablo. Takođe, pozivi po grupi se pokreću samo kada je tip stavke grupa, jer je tekstualna oznaka zapravo naslov sa imenom i nivoom, ali bez stanja uključenosti, isključenosti ili zaključanosti koje bi se moglo ispitati. Preskočite tu zaštitu i pitaćete oznaku za stanje koje ona nema.
Gde se ovo uklapa
Slojevi su mehanizam prezentacije, tako da mehanizam mora da ih poštuje na svakoj putanji koja renderuje stranicu, a strana renderovanja je pokrivena u našem vodiču kroz renderovanje sa više mehanizama u Delphi-ju. Oni se takođe presecaju sa strukturom dokumenta, jer je ime sloja tekst okrenut autoru, a čitalac ima koristi od strukturirane konture slojeva, što se povezuje sa radom u našem članku o označenom PDF-u i strukturi pristupačnosti. Oba se uparuju sa API-jima za opcioni sadržaj koji su ovde opisani, a koji se isporučuju kao deo Delphi PDF biblioteke zajedno sa funkcijama za stranice, tekst, fontove i usklađenost o kojima se govori na drugim mestima na ovom blogu.