Bir sürveyan bir vaziyet planını açar ve altyapı hatları açık kalırken eşyükselti eğrilerinin gizlenmesini ister. Bir incelemeci, kırmızı çizgili ek açıklamaların ekranda görünmesini ancak çıktıdan silinmesini ister. Bir ürün sayfası tek bir dosyadan üç dilde gönderilir ve okuyucu hangi dilin gösterileceğini seçer. Her üçü de aynı PDF özelliğidir ve Acrobat'ta bunları yöneten panele Katmanlar (Layers) adı verilir. Bu panelin altındaki özellik isteğe bağlı içeriktir (optional content) ve tek bir sayfanın, görüntüleyicinin açıp kapattığı birkaç bağımsız görsel katmanı taşımasını sağlayan şeydir.
İsteğe bağlı içerik ISO 32000-1 §8.11'de belirtilmiştir. Görünürlük birimi, bir ad taşıyan /OCG türünde bir sözlük olan isteğe bağlı içerik grubudur (OCG). Bir sayfadaki işaretli içerik bir grupla ilişkilendirilir ve görüntüleyici o grubun şu anda gösterilip gösterilmeyeceğine karar verir. İlgili bir yapı olan isteğe bağlı içerik üyeliği sözlüğü (OCMD), görünürlüğün birkaç grubun mantıksal kombinasyonuna bağlı olmasına izin verir, ancak günlük durum tek bir katmanı temsil eden tek bir adlandırılmış gruptur. Belge tüm bu mekanizmayı, aşağıda açıklanan tek bir katalog girişi olan /OCProperties aracılığıyla birbirine bağlar.
Kataloğun taşıması gerekenler
Bir OCG tek başına etkisizdir. Bir görüntüleyicinin bir katmanı listelemesi ve durumunu hatırlaması için belge kataloğunun bir /OCProperties sözlüğüne ihtiyacı vardır ve §8.11.4 bunun içine tam olarak neyin gireceğini belirtir. Dosyadaki her grubu adlandıran bir /OCGs dizisi ve varsayılan yapılandırmayı tutan bir /D girişi vardır. Varsayılan yapılandırma, dosya ilk açıldığında bir okuyucunun uyguladığı kısımdır. Hangi grupların açık, hangilerinin kapalı başlayacağını, hangi girişlerin kullanıcının bunları değiştirmesine karşı kilitlendiğini ve bir /Order dizisi aracılığıyla katman adlarının panelde nasıl düzenlendiğini ve iç içe yerleştirildiğini kaydeder。
Bunun pratik sonucu, bir katman oluşturmanın asla tamamen yerel bir eylem olmamasıdır. Grubun hem sayfaya çizilmesi hem de daha önce var olmayan katalog düzeyinde bir yapıda kaydedilmesi gerekir. PDFlibPas does both for you. Bir grup oluşturan ilk çağrı, kataloğa /OCProperties girişini ekler ve varsayılan yapılandırmayı başlatır, böylece katman sizin tarafınızda ayrı bir kayıt tutma işlemi gerektirmeden hem çizilir hem de listelenir.
Bir uyumluluk modunun özelliği neden engelleyebileceği
Herhangi bir katman kodu çalışmadan önce, belgenin uyumluluk hedefi isteğe bağlı içeriğin yasal olup olmadığına karar verir. PDF/A-1, ISO 19005-1'de tanımlanan arşiv profili, §6.1.13'te /OCProperties girişini doğrudan yasaklar. Bunun gerekçesi biçimin amacına uygundur. Arşivsel bir dosya, gelecekteki her okuyucu için aynı şekilde işlenmelidir ve görünürlüğü bir görüntüleyici tarafından değiştirilebilen içerik, görünümü sabit olmayan içeriktir, bu nedenle profil, belirsiz bir arşive izin vermek yerine bu yapıyı yasaklar. PDF/A-2 ve PDF/A-3, sırasıyla ISO 19005-2 ve ISO 19005-3'te tanımlanan, §6.9'larında aksi yönde bir görüş bildirir ve varsayılan görünürlükle ilgili kurallarla birlikte isteğe bağlı içeriğe izin verir.
Bu fark doğrudan API'de kendini gösterir. Belge PDF/A-1 modundayken, NewOptionalContentGroup grubu oluşturmayı reddeder ve sıfır döndürür, çünkü isteği yerine getirmek, beyan edilen kendi uyumluluk testinde başarısız olan bir dosya üretecektir. PDF/A-2 veya PDF/A-3 modunda ve sıradan kısıtlanmamış PDF'de, aynı çağrı başarılı olur ve sıfır olmayan bir grup kimliği (ID) döndürür. Bu nedenle sıfır sonucu, daha sonra incelenecek genel bir hata değildir; kütüphanenin size etkin uyumluluk düzeyinde bu özelliğe yer olmadığını söylemesidir.
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;
Katman başına bir değil, iki durum
Bir katman sadece görünür veya görünmez değildir. Varsayılan yapılandırma, ekran üzerindeki durumunu ve ayrı bir yazdırma durumunu kaydeder, çünkü §8.11.4 bir görüntüleyicinin ne gösterdiği ile bir yazdırma işlem hattının ne çıkardığını birbirinden ayırır. İkisi bilerek birbirinden bağımsızdır. Bir taslak filigranı ekranda gösterilebilir ve kağıttan çıkarılabilir ve bir kesim çizgisi katmanı ekranda gizlenebilir ancak yine de bir çiziciye gönderilebilir. İkisini birleştirmek, birinin diğerini takip etmesini zorunlu kılacak ve tam da bu özelliğin sunmak için var olduğu kontrolün kaybedilmesine neden olacaktır.
PDFlibPas bu ikiliyi iki ayarlayıcı (setter) aracılığıyla sunar. SetOptionalContentGroupVisible, grup kimliğini ve bir bayrağı alır (burada bir görünür, sıfır ise gizli anlamına gelir) ve varsayılan ekran durumunu yönetir. SetOptionalContentGroupPrintable, grup kimliğini ve belge yazdırıldığında katmanın çıkarılıp çıkarılmayacağına dair bir bayrağı alır. Eşleşen alıcılar (getter) olan GetOptionalContentGroupVisible ve GetOptionalContentGroupPrintable, her biri bir veya sıfır döndürür, böylece bir katmanın ekran ve yazdırma durumunu birini diğerinden çıkarmak yerine ayrı ayrı okuyabilirsiniz。
Bir sayfada iki katman oluşturma
Bir katman oluşturmak ve onu doldurmak belirli bir sırayı takip eder. Katmanın içeriğini geçerli sayfaya çizer, ardından grup kimliğiyle SetContentStreamOptional çağrısı yaparsınız. Bu çağrı, sayfanın geçerli içerik akışını sarar ve şu ana kadar çizilen her şeyin o gruba ait olmasını sağlar. Çağrı o anda akışta ne varsa onu yakaladığından, kural bir katmanın işaretlerini koymak, bunları atamak ve ancak ondan sonra sonraki katmana başlamaktır. Aşağıdaki örnek, altyapıyı ilk sayfaya ve bir incelemecinin kırmızı çizgisini ikinci sayfaya koyar, her katmanın ekran ve yazdırma durumunu ayarlar ve kaydeder.
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;
Kırmızı çizgi katmanı dikkat çekici bir durumdur. Bir incelemecinin notu görmesi için ekranda gösterilir ve yazdırılabilirlik bayrağı sıfırdır, böylece aynı dosyanın çıktısı herhangi bir inceleme metni taşımaz. Bu asimetri, iki durumu ayrı tutmanın asıl amacıdır.
Yapılandırmayı geri okuma
Katmanları okumak, aynı yapı içinde farklı bir gezintidir. Bir dosya yüklendikten sonra, GetOptionalContentConfigCount belgenin kaç yapılandırma sözlüğü tuttuğunu bildirir; ilk varsayılan yapılandırma yapılandırma kimliği (config ID) 1'dir. Bir yapılandırma içinde, GetOptionalContentConfigOrderCount sıra ağacındaki girişlerin sayısını verir ve bunları 1'den başlayarak dizinlersiniz. Her giriş için, GetOptionalContentConfigOrderItemLabel bunun görüntüleme metnini döndürür ve GetOptionalContentConfigOrderItemLevel bunun iç içe yerleştirme derinliğini döndürür, böylece başlıkların altında girintili alt katmanlara sahip bir panel taslağı aynen yeniden oluşturulabilir。
Her girişin bir türü de vardır. GetOptionalContentConfigOrderItemType, gerçek bir isteğe bağlı içerik grubunu, yalnızca ağacın bir bölümüne başlık oluşturmak için var olan düz metin etiketinden ayırır. Bu ayrım önemlidir çünkü grup başına durum sorguları yalnızca gerçek gruplar için anlamlıdır. Bir grup girişi için, GetOptionalContentConfigState yapılandırmanın onu açık, kapalı mı başlattığını yoksa değiştirmeden mi bıraktığını bildirir ve GetOptionalContentConfigLocked kullanıcının bunu değiştirmesinin engellenip engellenmediğini bildirir. Aşağıdaki döngü, seviyeye göre girintileyerek, her grubun durumu ve kilit statüsüyle birlikte sıra ağacını işler.
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;
İki ayrıntı bu döngüyü doğru tutar. Sıra dizini, kütüphanenin ağacı dahili olarak nasıl numaralandırdığıyla eşleşecek şekilde 1'den başlayıp sayıya kadar gider. Ve grup başına çağrılar yalnızca öğe türü bir grup olduğunda çalışır, çünkü bir metin etiketi bir ada ve seviyeye sahip bir başlıktır, ancak sorgulanacak bir açık, kapalı veya kilitli durumu yoktur. Bu korumayı atlarsanız, bir etiketten sahip olmadığı bir durumu istemiş olursunuz.
Bunun yeri neresidir
Katmanlar bir sunum mekanizmasıdır, bu nedenle motor bir sayfayı işleyen her yolda bunlara uymak zorundadır ve işleme tarafı Delphi'de çok motorlu işleme kılavuzumuzda ele alınmıştır. Ayrıca belge yapısıyla da kesişirler, çünkü bir katmanın adı yazar odaklı metindir ve okuyucu yapılandırılmış bir katman taslağından yararlanır; bu da etiketli PDF ve erişilebilirlik yapısı hakkındaki makalemizdeki çalışmaya bağlanır. Her ikisi de, bu blogun başka yerlerinde tartışılan sayfa, metin, yazı tipi ve uyumluluk olanaklarının yanı sıra Delphi PDF Kütüphanesinin bir parçası olarak sunulan burada açıklanan isteğe bağlı içerik API'leriyle eşleşir。