İçeriğe geç

PDF aslında nedir?

Evidence: Standard-backed

Bir PDF, tesadüfen bir dosyada bulunan bir sayfa tanımı değildir. Yazıcı eklenmiş küçük bir çizge veritabanıdır. Bu sayfa, her PDF’nin sahip olduğu dört bölümü — başlık, gövde, çapraz başvuru tablosu, treyler — ve okuyucunun her nesneyi tahmin yürütmeden bulabilmesi için NextPDF’in bunları nasıl yazdığını anlatır.

PDF hatalarının çoğu işleme hatası değildir. Bunlar yapı hatalarıdır: işaret etmesi gereken nesneden bir karakter sonrasını gösteren bir bayt uzaklığı, yanlış kökü gösteren bir treyler, nesnenin gerçekte bulunduğu yerle uyuşmayan bir çapraz başvuru girdisi. Bir okuyucu dosyada farklı bir yol izleyip dosya sonunu aşana kadar, bunların hiçbiri bir sayfanın görünümünü değiştirmez.

Bir PDF’yi opak bir bütün olarak ele alırsanız, bu sorunlar rastgele görünür. Nesne modelini biliyorsanız, tam olarak oldukları şey olarak görünürler: bir konumla eşleşmeyen bir sayı. Biçimi bilmek, “PDF bozuk” ile “yazıcı akış uzunluğunu kesinleştirmeden önce ölçüm yaptığı için 14 numaralı nesnenin uzaklığı geçerliliğini yitirdi” arasındaki farktır.

Bir PDF, dosya sırasına göre dört bölümden oluşur:

  1. Bir başlık — sürümü belirten tek bir satır (%PDF-2.0).
  2. Bir gövde — numaralandırılmış dolaylı nesnelerden oluşan bir dizi: sözlükler, akışlar, diziler, sayılar, dizgeler, adlar.
  3. Bir çapraz başvuru tablosu (ya da PDF 2.0’da bir çapraz başvuru akışı) — nesne numarasından bayt uzaklığına giden bir arama yapısıdır; böylece herhangi bir nesneye dosyayı taramadan erişilebilir.
  4. Bir treyler — belgenin kök nesnesini belirten ve çapraz başvuru bölümünün nerede başladığını gösteren küçük bir sözlük.

Bir okuyucu, bir PDF’yi baştan sona okumaz. Önce son satırı okur, startxref ifadesini bulur, çapraz başvuru bölümüne atlar ve onu gövdeye yönelik bir dizin olarak kullanır. Biçim, geriye doğru okunacak şekilde kurulmuştur. Bu tek olgu, tasarımının çoğunu açıklar.

NextPDF, bir PDF’yi biçimin okunduğu mantıkla kurar: önce nesne, ardından uzaklığın kaydı, en son da tablonun yazımı.

Her dolaylı nesneye tek bir kayıt defteri numara atar (src/Core/ObjectRegistry.php). Kayıt defteri, allocate() ile ardışık numaralar dağıtır ve bir nesnenin baytları çıkış arabelleğine yazıldıktan sonra bayt uzaklığını register() ile kaydeder. Uzaklıklar asla önceden tahmin edilmez; nesne başlığının yazıldığı anda BinaryBuffer::getOffset() üzerinden gözlemlenir. Bu nedenle NextPDF’in bir çapraz başvuru girdisi, işaret ettiği nesneden sapamaz: uzaklık, arabelleğin o andaki gerçek konumudur.

Gövde tamamlandığında, sürüme özgü bir serileştirme stratejisi (src/Writer/PdfSerializationStrategy.php) çapraz başvuru bölümünü ve treyleri yazar:

  • Pdf20StreamStrategy, sıkıştırılmış bir çapraz başvuru akışı (/Type /XRef) yazar — PDF 2.0 varsayılanı.
  • Pdf17TableStrategy ve Pdf14TableStrategy, geleneksel 20 baytlık bir çapraz başvuru tablosu ile ayrı bir treyler sözlüğü yazar — eski dosya yapısını zorunlu tutan PDF/A profillerinin gerektirdiği biçim.

Strateji, çıkış profili tarafından seçilir; çıkarım yoluyla belirlenmez. Hangisi olursa olsun, son baytlar aynı biçimdedir: çapraz başvuru bölümü, ardından startxref, ardından bayt uzaklığı, ardından %%EOF. Bir okuyucunun ilk bulduğu şey, işte bu kuyruktur.

  1. Step 1 of 4: ISO 32000-2 §7.5.5 — %%EOF and startxref at the file end
  2. Step 2 of 4: ISO 32000-2 §7.5.4 / §7.5.8 — the cross-reference section maps object number to offset
  3. Step 3 of 4: ISO 32000-2 §7.5.5 — the trailer names /Root, the document catalog
  4. Step 4 of 4: ISO 32000-2 §7.3.10 — each indirect object is reached at its recorded offset
Bir okuyucunun bir NextPDF dosyasındaki bir nesneyi nasıl çözümlediği ve her adımı tanımlayan ISO 32000-2 maddesi: işlem dosyanın sonundan başlar ve içeri doğru ilerler.

Dört bölümlü yapı, NextPDF’e özgü bir tercih değildir; Spec: ISO 32000-2, §7.5 kapsamındaki dosya yapısı maddesinde tanımlanır. Standart, bir PDF’yi bir başlık, nesnelerden oluşan bir gövde, bir çapraz başvuru tablosu ve bir treyler olarak tanımlar ve okuyucunun dosyayı sondan başlayarak ayrıştırması gerektiğini belirtir. Son satır %%EOF ifadesidir ve ondan önceki iki satır, startxref anahtar sözcüğü ile çapraz başvuru bölümüne giden bayt uzaklığıdır.

Evidence: Standard-backed

Bir dolaylı nesne, boşlukla ayrılmış bir nesne numarası ve üretim numarasıyla başlar; nesnenin değeri de obj ve endobj anahtar sözcükleri arasına yerleştirilir. Nesne numarası ile üretim numarasının birleşimi, nesneyi benzersiz biçimde tanımlar; ona yapılan bir dolaylı başvuru ise nesne numarası, üretim numarası ve R anahtar sözcüğü olarak yazılır. NextPDF’in ObjectRegistry bileşeni bunu birebir yansıtır: ardışık bir numara, yeni yazılan nesneler için 0 üretim numarası ve kaydedilmiş bir uzaklık.

PDF 1.5 ve sonrası, nesnelerin bir nesne akışı içinde bulunmasına da izin verir; bu akışlarda nesneler obj/endobj anahtar sözcükleri olmadan depolanır ve üretimleri sıfır olmalıdır. Çapraz başvuru akışı (/Type /XRef, Spec: ISO 32000-2, §7.5.8 ), hem sıradan nesneleri hem de bu sıkıştırılmış nesneleri dizinleyen PDF 2.0 mekanizmasıdır. NextPDF’in CrossReferenceStream bileşeni, bunu bir /W alan genişliği dizisi ve FlateDecode sıkıştırmasıyla kurar.

Bu, en küçük PDF gövdesinin ve treylerinin biçimidir. Çapraz başvuru bölümündeki sayılar, bayt uzaklıklarıdır. Bunların tam olarak doğru olması gerekir; bu yüzden NextPDF bunları hesaplamak yerine doğrudan arabellekten kaydeder.

%PDF-2.0
1 0 obj
<< /Type /Catalog /Pages 2 0 R >>
endobj
2 0 obj
<< /Type /Pages /Kids [3 0 R] /Count 1 >>
endobj
3 0 obj
<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] >>
endobj
xref
0 4
0000000000 65535 f
0000000009 00000 n
0000000058 00000 n
0000000122 00000 n
trailer
<< /Size 4 /Root 1 0 R >>
startxref
196
%%EOF

Bir okuyucu bunu sondan açar: %%EOF, ardından startxref 196; sonra xref ifadesinin başladığı 196. bayta gider, 1 numaralı nesnenin 9. baytta bulunduğunu okur, kataloğa giden /Root 1 0 R başvurusunu izler ve oradan sayfa ağacında ilerler. 0 numaralı nesne, her zaman 65535 üretimine sahip serbest liste başıdır — biçimin en erken tasarımından devralınan ve okuyucular beklediği için aynen yeniden üretilen bir tuhaflık.

Tuzak, PDF’nin kaynak kod gibi yukarıdan aşağıya okunduğunu sanmaktır. Öyle değildir. Gövde, nesneleri herhangi bir sırada içerebilir. Nesne numaralarının dosyada ardışık olması gerekmez ve okuyucu da bunların öyle olmasına asla güvenmez. Tek yetkili dizin çapraz başvuru bölümüdür ve onu bulmanın tek yolu, sondaki treylerdir. Tamamen geçerli bir gövdeye ve startxref içinde tek bir yanlış sayıya sahip bir PDF okunamaz. Nesneleri karışık bir sırada yazılmış ama çapraz başvuru tablosu doğru olan bir PDF ise sorunsuzdur. Konum anlamsızdır; kaydedilmiş konum her şeydir.

Bu sayfa, sayfa içeriğini değil, dosya yapısını anlatır. İşaretlerin sayfada nasıl konumlandığı — içerik akışları, grafik işleçleri, metin gösterimi — ayrı bir konudur. Bir dosya yazıldıktan sonra değiştirildiğinde ne olduğunu da kapsamaz. Bu, yazıcının ikinci bir çapraz başvuru bölümü eklediği ve treylerin geriye doğru zincirlendiği artımlı güncellemelerin işidir.

NextPDF bir yazıcıdır. Burada anlatılan davranış, kendi kurduğu bir belgeyi nasıl serileştirdiğini açıklar. Genel amaçlı bir PDF ayrıştırıcısı ya da onarım aracı değildir. Çapraz başvuru tablosu hasarlı olan herhangi bir üçüncü taraf dosyasını okumayı, yeniden oluşturmayı ya da kurtarmayı vaat etmez. Güvence dar kapsamlıdır ve bilinçli olarak böyle tutulmuştur. NextPDF’in yazdığı dosyaların uzaklıkları eşleşir, çünkü bunlar tahmin edilmez, ölçülür.

Yeni dosyalarda her zaman 0 kullanılıyorsa, üretim numaraları neden var? Üretim numaraları, güncellemeler boyunca nesnelerin yeniden kullanılması için vardır. Yeni yazılmış bir dosyada her nesne üretim olarak 0 değerindedir. Sıfırdan farklı üretimler, yalnızca bir dosya artımlı olarak güncellendiğinde ve bir nesne numarası geri dönüştürüldüğünde ortaya çıkar.

İki nesnenin aynı numaraya sahip olması mümkün mü? Tek bir çapraz başvuru bölümünde hayır. Artımlı güncellemeler boyunca bir dosya, aynı nesne numarasının fiziksel olarak birden çok kopyasını içerebilir. En güncel çapraz başvuru girdisi kazanır. Bu, bir sonraki sayfanın konusudur.

Dosyadaki nesne sırası, çıktı için önemli mi? Hayır. NextPDF, yeniden üretilebilir derlemeler için nesneleri belirlenimci bir sırada yazar, ancak okuyucu her şeyi çapraz başvuru bölümü üzerinden çözer; bu nedenle fiziksel sıranın anlamsal bir önemi yoktur.

  • Dolaylı nesne — gövde içinde numaralandırılmış bir nesne; N G obj … endobj biçiminde yazılır; burada N nesne numarası, G ise üretim numarasıdır.
  • Dolaylı başvuru — bir dolaylı nesneye yönelik bir işaretçi; N G R biçiminde yazılır.
  • Çapraz başvuru tablosu (xref) — nesne numarasını bayt uzaklığıyla eşleyen dizin. PDF 2.0’da bu, genellikle klasik girdi başına 20 baytlık metin tablosu yerine bir çapraz başvuru akışıdır (/Type /XRef).
  • Treyler — bir çapraz başvuru bölümünün sonunda yer alan, /Root (belge kataloğu) ve /Size ögelerini belirten ve startxref uzaklığı aracılığıyla bulunan sözlük.
  • Nesne akışı — kendisi başka dolaylı nesneler içeren (birlikte sıkıştırılmış) bir akış nesnesi; üyelerinin obj/endobj yoktur ve üretimleri sıfırdır.
  • Belge kataloğu/Root tarafından belirtilen nesne; sayfa ağacına ve belgedeki diğer her şeye giriş noktası.