PDF Library for Delphi, sayfa etiketi aralıklarını AddPageLabels ile yazar ve v3.539.10'dan bu yana o çağrı, /PageLabels number tree'si /Kids düğümlerine bölünmüş yüklü dosyalarda da çalışır: yeni aralık girmeden önce kök tek bir /Nums yaprağına düzleştirilir, böylece etiket sessizce yok sayılmak yerine görüntüleyicide gerçekten görünür. Klasik kurban, bir dizgi aracından çıkan kitap tarzı bir PDF'tir: ön bölümde roma rakamları, gövdede arap rakamları ve A-1, A-2 olarak etiketlenmiş bir ek; yalnızca eki yeniden etiketlemek istediniz ve hiçbir şey değişmedi
PDF sayfa etiketleri nedir ve nasıl saklanır?
Sayfa etiketleri, görüntüleyicinin sayfa kutusunda fiziksel sayfa indeksi yerine gösterdiği dizelerdir ve ISO 32000-1 §12.4.2 bunları katalog anahtarı /PageLabels altında bir number tree olarak saklar. Her anahtar, bir etiketleme aralığı başlatan 0 tabanlı bir sayfa indeksidir ve her değer, en çok üç girdili bir sayfa etiketi sözlüğüdür: numaralandırma stili için /S (D, R, r, A ya da a), önek dizesi için /P ve aralıktaki ilk sayfanın sayısal değeri için /St; varsayılanı 1'dir. Bir aralık sıradaki anahtara kadar sürer ve spesifikasyon, ağacın 0 sayfa indeksi için bir değer içermesini ister; böylece her sayfayı bir aralık kapsar
var
Lib: TPDFlib;
begin
Lib := TPDFlib.Create;
try
if Lib.LoadFromFile('handbook.pdf', '') <> 1 then
Exit;
// Sayfalar 1-4: i, ii, iii, iv (küçük harf roma)
Lib.AddPageLabels(1, 3, 1, '');
// Sayfalar 5-120: 1, 2, 3 ... (ondalık)
Lib.AddPageLabels(5, 1, 1, '');
// Sayfalar 121 ve sonrası: A-1, A-2 ... (önekli ondalık)
Lib.AddPageLabels(121, 1, 1, 'A-');
WriteLn(Lib.GetPageLabel(5)); // 1
WriteLn(Lib.GetPageLabel(122)); // A-2
Lib.SaveToFile('handbook-labeled.pdf');
finally
Lib.Free;
end;
end;
TPDFlib.AddPageLabels(Start, Style, Offset, Prefix), argümanlarını üç kuralı bildikten sonra sürprizsiz biçimde o sözlüğe eşler. Start, kütüphanedeki diğer her sayfa argümanı gibi 1 tabanlıdır ve ağaca Start - 1 olarak yazılır. Style 0'dan 5'e gider: 0 yalnızca önek demektir ve 1 ile 5, /S değerleri D, R, r, A ve a olur; bu aralığın dışındaki her şey 0 döndürür ve hiçbir şeye dokunmaz. Offset, sıfırdan büyük olduğunda /St olur; 0 geçmek anahtarı atlar ve görüntüleyici varsayılan 1'e döner. Sayfa etiketleri PDF 1.3'te geldiği için çağrı ayrıca EnsureMinVersion('1.3', '/PageLabels') çalıştırır; bu, kayıt sürümünü açıkça kilitlemediyseniz eski bir dosyanın çıktı sürümünü yükseltir
Ağaç /Kids taşıdığında yeni sayfa etiketleri neden kaybolur?
Yeni etiketler kaybolur, çünkü ISO 32000-1 §7.9.7 (Tablo 37) bir number tree'nin kökünün ya /Kids ya /Nums taşımasını, ikisini birden asla taşımamasını ister ve önceki NumTreeSet yardımcısı yalnızca /Nums aramayı biliyordu. Uzun belgeler üreten yazılımlar ağacı sık sık ara düğümlere böler; her biri bir /Limits çifti taşır ve yalnızca /Kids içeren bir köke asılır. Eski kod o kökte /Nums bulamayınca mevcut /Kidsin yanında taze bir tane oluşturuyor ve yeni aralığı oraya ekliyordu. Sonuç, iki karşılıklı dışlayıcı giriş noktalı bir köktü. Görüntüleyiciler /Kids üzerinden iner ve başıboş diziyi hiç bakmaz; kütüphanenin kendi EnumNumTree'si de önce /Kids'e bakar ve NumTreeLookup, HasKids xor HasNums false olan bir düğümü reddeder. AddPageLabels yine de 1 döndürüyordu ve kaydedilen dosya yine de sorunsuz açılıyordu; en kötü türden başarısızlık: hiçbir şey şikâyet etmez, etiketler sadece aynı kalır
NumTreeSet'teki düzeltme, bir şey eklemeden önce kökü yaprağa dönüştürür. Kök /Kids taşıyorduğunda EnumNumTree her yaprağı sırayla gezer ve her anahtar-değer çiftini toplar; o listeden yeni bir düz /Nums dizisi kurulur ve düz dizi eklenmeden önce /Kids, /Limits ve bayat bir /Nums varsa kökten temizlenir. /Limits'i düşürmek kozmetik değil, çünkü Tablo 37 o girdiye yalnızca ara ve yaprak düğümlerde izin verir, kökte asla. O noktadan sonra ekleme, tek bir diziye sıradan bir sıralı eklemedir ve mevcut aralıklar özgün etiket sözlükleriyle hayatta kalır. Takas bilinçlidir: ağaç sonradan dengeli /Kids düğümlerine yeniden kurulmaz. Sayfa etiketleri için bunun maliyeti sıfırdır, çünkü büyük bir başvuru kılavuzu bile nadiren birkaç düzineden fazla aralık taşır ve çoğu üretici zaten tek yaprak yazar
// /PageLabels kökü /Kids kullanan bir dosyada eki yeniden etiketle
if Lib.LoadFromFile('vendor-manual.pdf', '') = 1 then
begin
WriteLn('Before: ', Lib.GetPageLabel(121)); // örn. A-1
// 121. sayfada başlayan aralığı değiştir: App-a, App-b ...
if Lib.AddPageLabels(121, 5, 1, 'App-') = 1 then
Lib.SaveToFile('vendor-manual-relabeled.pdf');
// Mevcut roma ve ondalık aralıklar hâlâ düzleştirilmiş yaprakta
WriteLn('After: ', Lib.GetPageLabel(121)); // App-a
WriteLn('Front: ', Lib.GetPageLabel(2)); // ii, değişmedi
end;
Bir /Nums dizisi nasıl yanlış anahtar olarak okunabilir?
Bir /Nums dizisi, kod onu tek tek eleman gezerken yanlış okunur; çünkü dizi [key0 value0 key1 value1 ...] biçiminde dönüşümlü çiftlerden oluşan düz bir dizidir ve yalnızca çift konumlar anahtardır. Eski NumTreeSet döngüsü her elemanı sayısal tür açısından sınıyordu; o anda sayı olan bir değer anahtarmış gibi karşılaştırılıyordu ve bir küçüktür isabeti ekleme noktasını tek numaralı bir indekse taşıyıp yeni çifti mevcut bir çiftin ortasına bırakabiliyordu, sonraki her çifti faz dışına iterek. EnumNumTree de aynı tek adımlı yürüyüşe sahipti. İkisi de artık çiftleri adım ikiyle geziyor, anahtarı X * 2'de değeri X * 2 + 1'de okuyor ve tam anahtar eşleşmesi değeri değiştirip Break ile çıkıyor. Adaletten yana söylemek gerekirse sayfa etiketi değerleri sözlüktür, bu yüzden ikinci hata /PageLabels üzerinde nadiren tetiklendi; ama yanlış adımla okuyan bir number tree yardımcısı, herhangi bir değer sayısal olduğu anda bozuktur ve aynı geçişte düzeltildi
Etiketleri geri okumak ve kayıpsız gidiş dönüş
TPDFlib.GetPageLabel(Page), 1 tabanlı bir sayfanın etiketini döndürür ve bilinmesinde fayda olan iki yedeği vardır. Hiç /PageLabels girdisi yoksa ondalık sayfa numarasını döndürür, böylece çağıran onu koşulsuz kullanabilir. Ağaç var ama sayfayı kapsayan aralık yoksa boş dize döndürür; dosya zorunlu 0 indeks girdisini atladığında olan tam olarak budur. Referans dokümantasyonu, etiketlerin doğru görüntülenmesi için 1. sayfada başlayan bir aralığın bulunması gerektiğini söyler ve kod o gerekliliği görünür kılar. Harf stilleri elektronik tablo sütunlarını değil spesifikasyonu izler: Z'den sonra AA, sonra BB gelir; taşıma yapmak yerine harf yinelenir
var
P: Integer;
Data: WideString;
begin
// Görüntüleyicinin sayfa kutusunda göstereceğinin hızlı denetimi
for P := 1 to Lib.PageCount do
WriteLn(P, ' -> ', Lib.GetPageLabel(P));
// 4 seçenek değeri yalnızca etiket aralıklarını PageLabelBegin kayıtları olarak dışa aktarır
Data := Lib.ExportDocumentData(4);
// İçe aktarma onları ClearPageLabels + AddPageLabels üzerinden oynatır
Lib.ImportDocumentData(Data, 0);
end;
Toplu düzenlemeler için seçenek değeri 4 ile ExportDocumentData, her aralığı PageLabelNewIndex, PageLabelStart, PageLabelPrefix ve PageLabelNumStyle satırları taşıyan bir PageLabelBegin bloğu olarak yazar; ImportDocumentData ise gördüğü ilk etiket kaydını tam değişim sayar: bir kez ClearPageLabels çağırır ve sonra her kaydı AddPageLabels'e verir. Bu, özgün dosya /Kids ağacı kullanmış olsa bile metin gidiş dönüşünü deterministik kılar; çünkü temizleme tüm katalog girdisini kaldırır ve yeniden kurulan ağaç baştan tek yapraktır
Düzeltme hâlâ neyi garanti etmiyor?
Düzleştirme tek yönlüdür ve bulduğu sıraya güvenir. EnumNumTree çiftleri dosya sırasında toplar ve GetPageLabel, anahtarı sayfa indeksine eşit ya da ondan küçük olan son aralığı uygular; yaprakları sıra dışı olan yabancı bir dosya — §7.9.7'nin yasakladığı ama dolaşımda olan tür — siz aralıkları ClearPageLabels ile taze AddPageLabels çağrılarıyla yeniden kurana kadar yanlış etiketler verebilir. Etiketler ayrıca sayfa nesnelerine değil sayfa indekslerine bağlıdır, dolayısıyla sayfa sayısını ya da sırasını değiştiren her işlem aralıkları bulundukları yerde bırakır. Nesne numaralarını koruyarak sayfa değiştirmek gibi yerinde bir takas sayıyı ve dolayısıyla etiket hizasını korur; iç içe dubleks taramaları harmanlamak gibi bir birleştirme ise yeni yazılmış bir aralık setini hak eden yeni bir sayfa dizisi üretir
Burada anlatılan sayfa etiketi çağrıları, number tree işleme ve belge verisi dışa-içe aktarma, Delphi, C++Builder ve Lazarus için PDF Library for Delphi ile gelir; AddPageLabelsin referans girdisi stil değerlerini ve dönüş kodlarını belgeler