Teknik Makale

PDF Sayfa Sıralaması: Sayfa Ağacı Sayfa Sırasını Nasıl Belirler

1 numaralı nesne, 1. sayfa değildir. Bu tek gerçek, PDF işleme kodunu biçimin başka hiçbir yönünden daha çok yanıltır ve nedenini anlamak, bir görüntüleyicinin size gösterdiğinin ötesine, görüntüleyicinin gerçekte okuduğu nesne grafiğine bakmayı gerektirir

Bir PDF dosyası, numaralandırılmış dolaylı nesnelerden oluşan bir koleksiyondur. Her nesne bir nesne numarası ve bir kuşak numarası taşır ve diğer nesneler ona N G R biçiminde yazılmış bir referansla işaret eder: 3 0 R, 3 numaralı nesnenin güncel sürümü demektir. Sayfalar da bu nesneler arasındadır, ancak görüntülenme sıraları ne dosyanın neresinde oturduklarıyla ne de taşıdıkları numaralarla ilgilidir. Görüntülenme sırası tamamen /Pages ağacı tarafından belirlenir; bu, belge kataloğunda köklenmiş bağlı bir yapıdır. Ağacı görmezden gelir ve nesneleri sayısal olarak tararsanız, gerçek dünya dosyalarının önemli bir kısmı için sayfaları yanlış sırada bir araya getirirsiniz

Sayfa ağacı: sırayı aslında belirleyen şey

Her PDF bir belge kataloğuyla başlar (ISO 32000-2 §7.7.2). Katalog, sayfa ağacının kök düğümüne işaret eden bir /Pages girdisi tutar. Bu kök düğüm, /Type /Pages içeren bir sözlüktür; bir /Kids dolaylı referans dizisine ve altındaki toplam yaprak sayfa sayısını veren bir /Count değerine sahiptir. Görüntülenme sırası, o ağacın önce-derinlik, soldan-sağa dolaşımıdır, nokta

Minimal üç sayfalık bir dosya bunu somutlaştırır:

%PDF-1.7

1 0 obj
<< /Type /Catalog /Pages 2 0 R >>
endobj

2 0 obj
<< /Type /Pages /Kids [20 0 R  4 0 R  9 0 R] /Count 3 >>
endobj

% Object 4 is stored third in the file but is page 2 in display order
4 0 obj
<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792]
   /Contents 5 0 R /Resources << /Font << /F1 6 0 R >> >> >>
endobj

% Object 9 is stored fourth but is page 3
9 0 obj
<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792]
   /Contents 10 0 R /Resources << /Font << /F1 6 0 R >> >> >>
endobj

% Object 20 is stored last but is page 1; Kids[0] decides, not object number
20 0 obj
<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792]
   /Contents 21 0 R /Resources << /Font << /F1 6 0 R >> >> >>
endobj

/Kids dizisi [20 0 R 4 0 R 9 0 R] şeklindedir, dolayısıyla 20 numaralı nesne 1. sayfa, 4 numaralı nesne 2. sayfa ve 9 numaralı nesne 3. sayfadır. Nesne numaralandırması alakasızdır. Nesneleri sayısal sırada dolaşan ve /Type /Page içerenleri toplayan herhangi bir kod, bu dosyada yanlış sıra üretecektir

Üreticiler neden ardışık olmayan düzenler üretir? Birkaç nedeni var. İçeriklerini yazmadan önce tüm sayfalar için nesne numaralarını önceden ayıran bir kütüphane, onları oluşturulma sırasına göre numaralandırır, sonra gerçek baytları serileştiriciye uygun gelen sırada yazar. Belgeleri birbirine diken bir birleştirme aracı, çakışmaları önlemek için her kaynak belgeden gelen nesneleri yeniden numaralandırır; yeniden numaralandırılan sayfa nesneleri birleştirilmiş nesne tablosuna dağılmış olarak biterken yeni kök /Kids dizisi doğru görüntülenme sırasını tutar. Artımlı güncellemeler yeni nesneleri dosyanın sonuna taze numaralarla ekler, dolayısıyla bir revizyon olarak eklenen bir sayfa, görüntülenme sırasında 1. konuma ait olsa bile bayt akışının sonuna yakın yaşar

Düz ağaçlar ve iç içe alt ağaçlar

Şartname sayfa ağacı için iki biçime izin verir. Basit üreticiler düz bir yapı üretir: /Kids dizisi yalnızca /Page yaprak nesnelerini içeren tek bir kök /Pages düğümü. Bunu dolaşmak kolaydır: bir seviye derinlik, bir geçiş

Büyük belgeler bunun yerine rutin olarak dengeli bir ağaç kullanır. Kök /Pages düğümünün /Kids dizisi ara /Pages düğümleri içerir; bunların her biri de sırayla kendi /Kids dizisini tutar. Her ara düğümdeki /Count, alt ağacındaki toplam yaprak sayfa sayısını bildirir, böylece bir görüntüleyici, bir sayfaya indeksle atlarken her nesneyi ayrıştırmadan tüm alt ağaçları atlayabilir. Yaprak düğüm başına 10 sayfa ile dengeli bir ağaç olarak yapılandırılmış 1000 sayfalık bir belge, 750 /Kids girdisini taramak yerine üç veya dört sözlük araması üzerinden ikili aramayla 750. sayfayı bulabilir

İşleme kodu için sonuç: /Kids'in ilk seviyesinin /Page nesneleri içerdiğini varsayamazsınız. Her çocuk denetlenmelidir. /Type/Pages ise, içine özyinele. /Type/Page ise, o bir yapraktır. İlk seviyede durmak, üreticinin iç içe geçmeyi seçtiği herhangi bir belgede tüm alt ağaçları sessizce düşürür. Yazarların ilk etapta neden derin ağaçlar seçtiği, düzleştirme araçlarının neyden vazgeçtiği ve /Count bozulmasının pratikte nasıl ortaya çıktığı, sayfa ağacı biçimi, dallanma ve /Count bütünlüğü hakkındaki tamamlayıcı yazımızda ele alınmıştır

Kalıtsal sayfa öznitelikleri

Sayfa ağacı ayrıca bir kaynak paylaşım mekanizması taşır. Belirli sayfa öznitelikleri: /MediaBox, /CropBox, /Resources ve /Rotate kalıtsaldır (ISO 32000-2 §7.7.3.4). Bir /Page sözlüğü bunlardan birini atlarsa, bir okuyucu özniteliği bulana veya köke ulaşana kadar /Parent zincirini yukarı yürür. Paylaşılan bir yazı tipi sözlüğünü her yaprak sayfaya kopyalamak yerine kök /Pages düğümüne yerleştirmek, aynı yazı biçimlerini baştan sona kullanan belgeler için dosya boyutunu belirgin şekilde azaltabilir

Kalıtım kuralı, sayfa özelliklerini okuyan kod için bir incelik yaratır. /MediaBox'ı doğrudan bir /Page nesnesinden okumak ve eksik bir anahtarı hata olarak ele almak yanlıştır; anahtar basitçe kalıtsal olabilir. Sayfa geometrisini doğru şekilde çözen kod, ebeveyn zincirini takip etmelidir. Ayrıca bir döngü koruması da gerekir: bozuk bir dosya, zaten ziyaret edilmiş bir düğüme geri işaret eden bir /Parent referansına sahip olabilir; bu, ziyaret edilen nesne denetimi olmadan sonsuza dek döngüye girer

Xref tablosu ve çapraz referans akışları

Dolaylı nesne araması, çapraz referans tablosundan (veya onun ardılı, PDF 1.5'te tanıtılan çapraz referans akışından) geçer. Xref, her nesne numarasını dosya içindeki bir bayt konumuna eşler. Uyumlu bir okuyucu, herhangi bir nesneye doğrudan atlamak için xref'i kullanır; dosyayı sırayla taramaz. Bu rastgele erişim tasarımı, hızlı sayfa atlamayı mümkün kılan şeydir: görüntüleyici kataloğu okur, /Pages referansını xref üzerinden çözer, kök /Pages düğümünü okur, bir /Kids girdisini çözer ve bu böyle devam eder, yalnızca ihtiyaç duyduğu nesnelere dokunarak

Artımlı güncellemeler, dosyanın sonuna, öncekine geri zincirlenen bir fragman (trailer) içeren yeni bir xref bölümü ekler. Bir revizyonda güncellenen bir nesne, eklenen xref bölümünde yeni bir girdi alır; orijinal baytlar yerinde kalır ancak geçersiz kılınır. Dijital olarak imzalanmış PDF'lerin, ek açıklama veya form doldurma revizyonları eklendikten sonra bile doğrulanabilir kalmasının yolu budur: imzalı bayt aralığına asla dokunulmaz ve yeni içerik eklenen bölümde yaşar. Sayfa ağacı da güncellenebilir, dolayısıyla bir revizyondaki sayfa ekleme veya silmeler, gözden geçirilmiş bir /Kids dizisine sahip yeni bir /Pages kökü üretirken eski kök nesnesi hâlâ dosyadaki orijinal konumunu işgal eder. Doğrusallaştırılmış (web için optimize edilmiş) dosyalar bir bayt-yerleşim kıvrımı ekler: 1. sayfanın nesneleri, geri kalanı hâlâ indirilirken görüntüleyicinin ilk sayfayı gösterebilmesi için fiziksel olarak dosyanın önüne taşınır, yine de sayfa ağacı sıra üzerindeki tek yetki olarak kalır — yalnızca xref'te kayıtlı olan ofsetler değişir

Ağaç dolaşımı olmadan ne ters gider

Nesne tarama yaklaşımlarının başarısızlık biçimi sessizdir. Çıktı belgesi makul görünür: doğru sayıda sayfaya sahiptir ve her sayfa tanınabilir içerik içerir. Sadece sıra yanlıştır ve üreticiye, revizyon sayısına ve herhangi bir sayfanın dış kaynaklardan birleştirilip birleştirilmediğine bağlı bir şekilde yanlıştır. Tek bir araç tarafından üretilen dosyalardan oluşan bir test derlemesi tamamen geçebilir; farklı bir araçtan veya bir birleştirme iş akışından gelen dosyalar başarısız olacaktır. İşte bu tutarsızlık, sezgisel düzeltmelerin neden asla tutmadığının nedenidir. Gerçek bir müşteri belgesinde tam olarak bu başarısızlığın adım adım anlatımı için — belirti, yanlış teşhis ve dolaşım düzeltmesi — sayfa sırası hata ayıklama vaka çalışmamıza bakın

Artımlı güncelleme dosyaları buna özellikle yatkındır çünkü sonraki revizyonlarda eklenen veya yeniden düzenlenen sayfalar yüksek nesne numaraları taşırken görüntülenme sırası güncellenmiş /Kids dizisi tarafından kontrol edilir. Nesneleri sayısal sırada işleyen bir tarama, ağacın nereye ait olduklarını söylediğine bakılmaksızın o geç numaralı sayfaları sona yerleştirecektir

Düzeltme karmaşık değildir. Katalogdan başlayın, /Pages referansını çözün, /Kids dizisini özyinelemeli olarak yürüyün ve yaprakları karşılaştığınız sırada yayın. Bu, nesne numaraları, bayt ofsetleri veya dosya yapısı ne olursa olsun, tanım gereği görüntülenme sırasıdır. Çoğu olgun PDF kütüphanesi, bunu zaten doğru yapan bir sayfa sayısı ve indeksli bir sayfa erişimcisi sunar; risk, kütüphanenin sayfa modelini atlayıp doğrudan nesne katmanına dokunan koddadır

Açıkça ele alınmaya değer bir yapısal anormallik: ara bir /Pages düğümündeki /Count değeri, biçimsiz dosyalarda yanlış olabilir. Sınır denetimi için /Count'a güvenmek ve ardından tam bir dolaşımın gerisinde durmak, sayı eksik bildirildiğinde sayfaları sessizce atlar. /Count'u yalnızca kapasite ön ayırma veya ikili arama için bir performans ipucu olarak kullanmak ve gerçek sayıyı dolaşımdan türetmek, önemli belgeler için daha güvenli desendir

Sonraki Makale