Teknik Makale

PDF Sayfa Ağacı Biçimi: Fan-Out, Düzleştirme ve /Count Bütünlüğü

Buna eşlik eden PDF sayfa sıralaması açıklamamız temel kuralı ele alır: görüntüleme sırası, nesne numaralarından değil, /Pages ağacındaki /Kids dizilerinin derinlik öncelikli, soldan sağa dolaşımından gelir. Bu makale ağaca farklı bir açıdan bakar — biçimine. Olgun PDF yazıcıları, tek bir düz dizi tamamen yasal olabilecekken neden ara düğümlerden oluşan hiyerarşiler üretir? Bir araç ağacı düzleştirdiğinde veya yeniden inşa ettiğinde aslında ne değişir? Ve tüm yapıyı hızlı kılan /Count defter tutması gerçeği söylemeyi bıraktığında ne olur

Fan-out bir performans kararıdır

Hiçbir şey bir yazıcıyı iç içe geçmeye zorlamaz. Tek bir kök /Pages düğümü ve tek bir /Kids dizisinde 10.000 yaprak referansı olan 10.000 sayfalık bir belge spesifikasyona uyar. Yine de PDF Referansı büyük belgeler için dengeli bir ağaç önerir ve yaygın üreticiler bu tavsiyeye mütevazı bir fan-out ile, tipik olarak ara düğüm başına birkaç düzine çocukla uyar

Bunun nedeni, bir görüntüleyicinin herhangi bir şey gösterebilmeden önce ne okuması gerektiğidir. O 10.000 sayfalık dosyanın doğrudan 8.214. sayfasına atlamayı düşünün. Düz bir ağaçla görüntüleyici önce kök düğümü ayrıştırmalıdır ve o kök düğüm tek bir devasa dizidir: dolaylı referans başına yaklaşık sekiz baytla, 8.213 numaralı girdi çözülmeden önce baştan sona simgeleştirilmesi gereken 80 KB'lık bir nesne. Fan-out'u 32 olan dengeli bir ağaçla, aynı atlama kökü okur, doğru çocuğu seçmek için çalışan /Count toplamlarını karşılaştırır ve iner — toplamda üç veya dört küçük sözlük, her biri birkaç yüz bayt. Ağacın sağlaması için tasarlandığı O(log n) rastgele erişim budur ve ara düğümlerde /Count'un var olmasının bütün nedeni de budur: bir okuyucunun içindeki tek bir nesneyi bile açmadan bütün bir alt ağacı atlamasına izin verir

Ağaç biçimi ayrıca düzenleme maliyetini de belirler. Bir sayfa ekleyen artımlı bir güncelleme, /Kids veya /Count değeri değişen her düğümü yeniden yazmalıdır; bu da yeni yaprağın ebeveyninden köke kadar olan yol demektir. Dengeli bir ağaçta bu yol, dosyaya eklenen bir avuç küçük sözlüktür. Düz bir ağaçta ise "yol", her revizyonda tam olarak çoğaltılan tek dev kök dizisidir. Otuz gözden geçirme ve açıklama döngüsünden geçen bir sözleşme, bayt akışında aynı 80 KB'lık dizinin otuz eskimiş kopyasını taşımaya varabilir

İç düğümler kalıtsal öznitelikler taşır

Ara düğümler yalnızca yönlendirme değildir. Dört kalıtsal sayfa özniteliği — /Resources, /MediaBox, /CropBox ve /Rotate — herhangi bir /Pages düğümüne kaldırılabilir; burada, bir alt öğe onları geçersiz kılmadıkça altındaki her yaprağa uygulanırlar. Yatay bir ek içeren bir rapor üreten bir yazıcı, bu düzeni ağacın kendisinde ifade edebilir:

5 0 obj   % document root
<< /Type /Pages /Count 6 /Kids [6 0 R  7 0 R] >>
endobj

6 0 obj   % report body: portrait A4, body font
<< /Type /Pages /Parent 5 0 R /Count 3
   /Kids [30 0 R  31 0 R  32 0 R]
   /MediaBox [0 0 595 842]
   /Resources << /Font << /F1 8 0 R >> >> >>
endobj

7 0 obj   % appendix: landscape A4, rotated, its own font
<< /Type /Pages /Parent 5 0 R /Count 3
   /Kids [40 0 R  41 0 R  42 0 R]
   /MediaBox [0 0 842 595] /Rotate 90
   /Resources << /Font << /F2 9 0 R >> >> >>
endobj

40 0 obj  % appendix page: inherits size, rotation, fonts
<< /Type /Page /Parent 7 0 R /Contents 43 0 R >>
endobj

40'tan 42'ye kadar olan nesneler neredeyse boştur. Sayfa boyutları, döndürmeleri ve font kaynaklarının tümü, düğüm 7'den kalıtım yoluyla gelir; bu da dosyayı derli toplu ve kendi kendini idame ettiren tutar: ek düğümünün altına dördüncü bir sayfa ekleyin, otomatik olarak yatay çıkar

Aynı mekanizma klasik sayfa taşıma tehlikesini yaratır. Bir aracın, iki /Kids dizisini düzenleyerek ve /Parent'ı düğüm 6'ya yeniden yönlendirerek nesne 40'ı rapor gövdesine taşıdığını varsayalım. Taşıma yapısal olarak geçerlidir, yine de nesne 40 artık dikey /MediaBox'ı, döndürme olmamasını ve /F1 fontunu miras alır — oysa içerik akışı hâlâ artık çözülmeyen /F2'yi seçmektedir. Sayfa tek bir düzenlemede küçülür, döndürmesini kaybeder ve metnini yitirir. Bu nedenle sağlam yeniden sıralama kodu, bir sayfayı yeniden ebeveynlemeden önce dört kalıtsal özniteliğin çözülmüş değerlerinin tümünü sayfa sözlüğüne somutlaştırır. Bir düzenleyicide bir sayfayı sürükleyip onun boyut veya yönelim değiştirdiğine hiç tanık olduysanız, işte gördüğünüz mekanizma budur

Düzleştirme: yasal, yaygın, ara sıra maliyetli

Pek çok araç ters yönde gider. Asgari yazıcılar basit olduğu için tek seviyeli bir ağaç üretir ve birçok birleştirme ile bölme aracı, okudukları ağacı tek bir düz /Kids dizisine yeniden inşa eder, çünkü dengeli yapı üretmek fazladan iş demektir ve düz çıktı her zaman uyumludur. Doğru bir yeniden inşa, kalıtımı aynı anda çözmelidir: bir yaprağın miras aldığı her öznitelik yaprağın üzerine kopyalanmalı ya da belge genelinde tekdüzeyse yeni köke kaldırılmalıdır — aksi takdirde çıktı, tam olarak sayfa taşıma durumundaki gibi geometriyi değiştirir

Tipik belgeler için düzleştirme zararsızdır. Ölçekte, zaten anlatılan iki şekilde zarar verir: kök dizi, her açılışın ve her sayfa atlamasının tam olarak ayrıştırması gereken tek büyük bir nesne haline gelir ve her yapısal düzenleme onu bütünüyle yeniden yazar. Düzleştirmenin yok etmediği şey ise dolaylı referanslar aracılığıyla paylaşımdır — 10.000 sayfanın tümünün aynı /Resources sözlük nesnesine işaret ettiği düz bir ağaç hâlâ tekilleştirilmiştir. Kaybedilen tek şey, girdiyi sayfadan çıkarıp bir atanın onu sağlamasına izin verme seçeneğidir

/Count yalan söylediğinde

/Count saf defter tutmadır: düğümün alt ağacındaki yaprak sayfaların sayısına eşit olmalıdır ve dosya biçiminde bunu zorlayan hiçbir şey yoktur. İki bozulma deseni, vahşi doğada görülen yalancı sayımların çoğunu açıklar

İlki, artımlı bir güncellemenin geride bıraktığı bayat sayımdır. Bir düzenleyici bir sayfa ekler, yakın ebeveyni yeni bir /Kids ve güncellenmiş bir /Count ile yeniden yazar, ikisini de dosyaya ekler — ve atalara hiç dokunmaz:

% Original revision
12 0 obj
<< /Type /Pages /Count 9 /Kids [13 0 R  14 0 R  15 0 R] >>
endobj

14 0 obj
<< /Type /Pages /Parent 12 0 R /Count 3
   /Kids [50 0 R  51 0 R  52 0 R] >>
endobj

% Appended revision: one page inserted into the middle branch.
% Object 14 is superseded; object 12 is never rewritten
14 0 obj
<< /Type /Pages /Parent 12 0 R /Count 4
   /Kids [50 0 R  51 0 R  90 0 R  52 0 R] >>
endobj

Ağaç artık on yaprak tutuyor, ama kök hâlâ dokuz diyor. Köke güvenen bir görüntüleyici, sayfa sayacında dokuz sayfa bildirir. Bir sayfa atlamasını ikili aramayla bulmak için iç sayımları kullanan biri, ekleme noktasından sonraki her sayfa için yanlış bir indeks hesaplar. Tam bir dolaşım on bulur. Üç farklı yanıt, bir dosya

İkinci desen, asla doğru olamayacak sayımdır: negatif, dolu bir düğümde sıfır ya da saçma derecede büyük. Bunlar fuzzing'den, iletim hasarından ve ara sıra düzenleyicilerdeki aritmetik hatalardan gelir. Özellikle tahsis için /Count'a güvenen koda tehlikelidir — bir diziyi -3'lük bir /Count'tan boyutlandırmak en iyi ihtimalle bir aralık hatası doğurur ve bunu iki milyarlık bir /Count'tan yapmak bir hizmet reddi tahsisidir. Değer, dosyadaki her diğer sayı gibi, güvenilmez girdidir

Ayrıştırıcılar tüm bunlar üzerinde iki kampa ayrılır. Katı tüketiciler — preflight araçları, PDF/A doğrulayıcıları, arşivleme boru hatları — /Count'u dolaşım sonucuyla karşılaştırır ve dosyayı reddeder ya da işaretler. Etkileşimli görüntüleyiciler neredeyse evrensel olarak hoşgörülüdür: dolaşır, gerçek sayımı türetir ve saklanan sayımı sessizce yok sayar; işte tam da bu yüzden bayat sayımlı bir dosya, otomatik bir iş akışının içindeki daha katı bir ayrıştırıcıyla karşılaşana kadar yıllarca şikâyetsiz dolaşabilir. Kütüphane kodu için savunmacı orta yol, /Count'u bir ipucu olarak ele almaktır — ön tahsis için ve doğrulandıktan sonra alt ağaç atlaması için kullanışlı — dolaşımı ise gerçeğin kaynağı olarak bırakmaktır

Dolaşım algoritmasının kendisi, kalıtım arama kuralları ve katalogdan yaprağa dolaşım için sayfa sıralaması açıklamasıyla başlayın. Gerçek bir müşteri belgesi üretim koduna ulaştığında bu hata modlarının nasıl göründüğünü öğrenmek için, karıştırılmış sayfalar olayını belirtiden temel nedene kadar izleyen sayfa sırası hata ayıklama vaka çalışmasını okuyun

HotPDF Component tüm bunlarla dahili olarak ilgilenir: herhangi bir derinlikteki iç içe ağaçları dolaşır, sayfalar kopyalandığında veya taşındığında kalıtsal öznitelikleri çözer ve /Count'a güvenmek yerine onu gerçek yaprak sayımlarına karşı doğrular, böylece API'sindeki sayfa indeksleri her zaman mantıksal sayfalar anlamına gelir