Akışlar ve filtreler
ISO 32000-2 §7.4 Evidence: Standard-backed
Bir bakışta
“Bir bakışta” başlıklı bölümGerçek bir PDF’nin baytlarının çoğu akışların içindedir: sayfa içeriği, yazı tipleri, görüntüler ve çapraz başvuru akışının kendisi. Bu baytların neredeyse hiçbiri ham olarak saklanmaz; önce bir veya daha fazla filtreden geçer. Bu sayfa, karşılaşacağınız filtreleri, her birinin ne işe yaradığını, nerede sorun çıkardığını ve NextPDF’nin aynı girdinin her zaman aynı baytları üretmesi için sıkıştırmayı neden sabitlediğini ele alır.
Bunun neden önemli olduğu
“Bunun neden önemli olduğu” başlıklı bölümBir akış ile filtresi arasındaki ilişki bir sözleşmedir: “bu baytlar deflate ile sıkıştırıldı, ardından base-85 ile kodlandı — gerçek veriyi elde etmek için bu sırayla kodlarını çözün.” /Filter girdisi baytların gerçekte ne olduğuyla çelişiyorsa ya da /Length yanlışsa veya iki filtre yanlış sırada listelenmişse akışın kodu çözülemez ve taşıdığı nesne kaybolur. Bir okuyucu sezgisel tahmin yürütmez; sözlüğün söylediğini yapar.
İkinci ve daha sessiz bir maliyet daha vardır. Bir kitaplığın sıkıştırıcısı belirlenimci değilse — farklı zlib derlemesi, farklı düzey, farklı iç blok sınırları — aynı PDF’yi üretmesi gereken iki çalıştırma iki farklı dosya üretir. Bu, bayt düzeyinde yeniden üretilebilirliği bozar. Bozulan yeniden üretilebilirlik de altın dosya testlerini, imzalı yapı doğrulamasını ve çıktı farkı alan her ardışık düzeni bozar. Filtreler hem PDF’nin doğru olup olmadığını hem de PDF’nin aynı kalıp kalmadığını belirler.
Kısa özet
“Kısa özet” başlıklı bölüm- Bir akış nesnesi,
stream…endstreamiçine sarılmış bir sözlük ile bir bayt bloğudur; bir/Lengthve genellikle bir/Filtertaşır. - Bu
/Filtergirdisi, kod çözme filtresini — veya bir ardışık düzen olarak sırayla uygulanan bir filtre dizisini — adlandırır. - Filtreler iki aileye ayrılır: sıkıştırma (FlateDecode, LZWDecode, RunLengthDecode, DCTDecode, JPXDecode, JBIG2Decode) ve ASCII taşıma (ASCIIHexDecode, ASCII85Decode); ayrıca şifreleme için özel Crypt filtresi vardır.
- En sık göreceğiniz filtre FlateDecode’dur — zlib/deflate. İçerik, yazı tipleri ve çapraz başvuru akışı için varsayılan filtredir.
- NextPDF, aynı girdi baytlarının her zaman aynı çıktı baytlarına sıkıştırılması için Flate çıktısını sabit bir düzeye ve biçime sabitler.
NextPDF buna nasıl yaklaşır
“NextPDF buna nasıl yaklaşır” başlıklı bölümNextPDF, akış nesnelerini tek bir arabellek yardımcısı üzerinden yazar ve tek bir sabitlenmiş sıkıştırıcı üzerinden sıkıştırır — bunu bilinçli olarak yapar.
BinaryBuffer::writeStream() (src/Support/BinaryBuffer.php), akış içeriğini sözlüğüyle birlikte sarar; her zaman gerçek bayt uzunluğuna eşit bir /Length yazar ve çağıranın sağladığı, örneğin /Filter gibi ek girdileri birleştirir. Bildirilen uzunluğun yazılan baytlarla çelişmesine yol açacak bir kod yolu yoktur, çünkü uzunluk içerik dizesinin kendisinden alınır.
Sıkıştırma PinnedZlibCompressor (src/Writer/PinnedZlibCompressor.php) üzerinden gerçekleşir. Bu sınıf tek bir amaçla vardır. Açık bir düzey verilmeden çağrılan gzcompress, derlemeler arasında geçmişte farklılık göstermiş olan zlib çalışma zamanı varsayılanına bırakır. 2 baytlık zlib başlığı düzeyi dolaylı da olsa kodlar; dolayısıyla “varsayılan” kararlı bir çıktı değildir. Sıkıştırıcı, düzeyi RFC 1951 üst sınırına sabitler ve her zaman zlib ile sarmalanmış deflate (RFC 1950 başlığı + Adler-32 kuyruğu) üretir; bu da tam olarak /Filter /FlateDecode’in beklediği şeydir. zlib’ten gelen ciddi bir hata, sessizce sıkıştırılmamış çıktıya geri dönmek yerine türü belirlenmiş bir özel duruma dönüşür — bir akış asla sessizce ham olarak yazılmaz.
Çapraz başvuru akışının kendisi tüm bunların somut bir örneğidir: CrossReferenceStream (src/Core/CrossReferenceStream.php) ikili bir tablo oluşturur, onu sıkıştırır ve /Type /XRef, bir /W alan genişliği dizisi ve /Filter /FlateDecode ile bir akış nesnesi olarak yazar. Bir okuyucunun her nesneyi bulmasını sağlayan dizinin kendisi de filtrelenmiş bir akıştır.
| Filtre | Aile | Ne işe yaradığı | Nerede sorun çıkardığı |
|---|---|---|---|
| FlateDecode | Sıkıştırma | zlib/deflate; içerik, yazı tipleri ve xref akışları için varsayılan | Belirlenimci olmayan bir zlib derlemesi, “aynı” PDF’lerin bayt bayt farklı olmasına neden olur |
| LZWDecode | Sıkıştırma | Daha eski Lempel–Ziv–Welch sıkıştırması | Eski; yerini Flate aldı, eski dosyalarda ara sıra hâlâ görülür |
| DCTDecode | Sıkıştırma | JPEG ile kodlanmış renkli/gri tonlamalı görüntüler | Kayıplı — zaten DCT olan bir görüntüyü yeniden kodlamak onu yine bozar |
| JPXDecode | Sıkıştırma | JPEG 2000 dalgacık görüntü verisi | Bazı arşivleme profilleri tarafından izin verilmez; geniş destek tutarsızdır |
| JBIG2Decode | Sıkıştırma | İki düzeyli (1 bit) görüntü sıkıştırması | Satır içi görüntülerle kullanılmamalıdır; kayıplı kipler taramaları değiştirebilir |
| RunLengthDecode | Sıkıştırma | Bayt yönelimli çalışma uzunluğu | Yalnızca uzun tek baytlı diziler içeren verilerde işe yarar; başka verileri büyütebilir bile |
| ASCIIHexDecode | Taşıma | On altılık rakamlar olarak ikili veri | Boyutu ikiye katlar; yalnızca 7 bit güvenli kanallar için, asla boyut için değil |
| ASCII85Decode | Taşıma | base-85 ASCII olarak ikili veri | ~%25 ek yük; sıkıştırma değil, bir taşıma kolaylığı |
| Crypt | Güvenlik | Belgenin güvenlik işleyicisini uygular | Bir çapraz başvuru akışı bir Crypt filtresini asla kullanmamalıdır |
PDF standart filtre kümesi, ailelere göre ve her birinin ilişkili olduğu tipik hatayla birlikte. NextPDF, içerik, yazı tipleri ve çapraz başvuru akışı için FlateDecode yazar; ASCII taşıma filtreleri 7 bitlik kanallar içindir, asla boyut küçültmek için değil.
Kanıtların söylediği
“Kanıtların söylediği” başlıklı bölümFiltre mekanizması Spec: ISO 32000-2, §7.4 ISO 32000-2 §7.4 tarafından tanımlanır. Bir akış sözlüğü, filtrelerini /Filter aracılığıyla adlandırır. Girdi birden fazla filtre listelediğinde, bu filtreler bir kod çözme ardışık düzeni oluşturur ve sırayla uygulanır. Bir yazıcı, bir akışı sıkıştırmak veya onu 7 bit güvenli yapmak için kodlar. Bir okuyucu, özgün veriyi geri elde etmek için karşılık gelen kod çözme filtrelerini çağırır. Evidence: Standard-backed
Standardın filtre tablosu her filtreyi sınıflandırır. FlateDecode, zlib/deflate ile kodlanmış veriyi açar ve özgün metni veya ikili veriyi yeniden üretir. DCTDecode, JPEG aracılığıyla özgününe yaklaşan görüntü örneklerini yeniden üretir — “yaklaşan” sözcüğü, standardın bunun kayıplı olduğunu belirtme biçimidir. LZWDecode, RunLengthDecode, JBIG2Decode, JPXDecode ve Crypt filtresinin her biri de orada tanımlanır; JBIG2 satır içi görüntülerde açıkça yasaklanmıştır.
Çapraz başvuru akışı, biçimin kendi mekanizmasını yine kendisine uygular: bu bir akış nesnesidir (/Type /XRef,
Spec: ISO 32000-2, §7.5.8 ISO 32000-2 §7.5.8 ) ve onun /W dizisi
her giriş alanının bayt genişliğini kodu çözülmüş akışta belirtir. Standart,
bu akışın şifrelenmemesini ve bir Crypt filtresi kullanmamasını gerektirir.
NextPDF’nin CrossReferenceStream sınıfı buna tam olarak uyar — FlateDecode,
açık /W, şifreleme yok.
Pratik örnek
“Pratik örnek” başlıklı bölümFlate ile sıkıştırılmış bir sayfa içerik akışı. Son derece yaygın biçim budur: /Length ve /Filter içeren bir sözlük, ardından stream ile endstream arasındaki sıkıştırılmış baytlar.
<?php
declare(strict_types=1);
use NextPDF\Writer\PinnedZlibCompressor;
// The marking operators a page content stream carries, uncompressed.$content = "BT /F1 12 Tf 72 712 Td (Hello) Tj ET\n";
// NextPDF compresses through the pinned compressor: fixed level,// fixed zlib-wrapped format. The same $content always yields the// same $compressed bytes, on any supported PHP/zlib build.$compressed = PinnedZlibCompressor::compress($content);
// Emitted as a stream object. /Length is the real byte length of// $compressed; /Filter names the decode the reader must apply.// N 0 obj// << /Length <strlen($compressed)> /Filter /FlateDecode >>// stream// <$compressed bytes>// endstream// endobjBir okuyucu tersini yapar: /Length kadar bayt okur, /Filter öyle dediği için onları FlateDecode’dan geçirir ve özgün işleçleri geri elde eder. Sıkıştırıcıyı sabitlediğinizde bu gidiş gelişin yalnızca doğru olmakla kalmadığını da görürsünüz. Her seferinde aynıdır; altın dosya ve imzalı yapı denetimleri tam olarak buna dayanır.
Yaygın yanlış anlama
“Yaygın yanlış anlama” başlıklı bölümYaygın tuzak, ASCII filtrelerini sıkıştırma olarak görmektir. ASCIIHexDecode ve ASCII85Decode bir akışı büyütür — sırasıyla kabaca iki katına ve kabaca %25 oranında. Yer kazanmak için değil, ikili veriyi yalnızca 7 bitlik metin için güvenli olan bir kanaldan geçirmek için vardırlar. Bir PDF’yi “küçültmek” için ASCII85 seçmek bunun tam tersini yapar. Aynı yanlış anlamanın ikinci yarısı, FlateDecode’un görüntüler için “bedavaya” kayıpsız olduğuna inanmaktır. Flate kayıpsız olsa da görüntü zaten DCT (JPEG) ile kodlanmışsa onu yeniden sarmak veya kayıplı bir filtreden geçirerek dönüştürmek, Flate çevresinde ne yaparsanız yapın onu bozar. Filtre ardışık düzeni, ona verdiğiniz şeyi tam olarak korur — buna kazara verdiğiniz bir yeniden sıkıştırma artefaktı da dahildir.
Sınırlar ve kapsam dışı
“Sınırlar ve kapsam dışı” başlıklı bölümBu sayfa, filtrelerin nasıl bildirildiğini ve uygulandığını ele alır; her birinin içerdiği bit düzeyindeki algoritmayı değil. Belirlenimcilik güvencesi özellikle NextPDF’nin yazdığı akışlar için Flate çıktısıyla ilgilidir. Bu güvence, PHP ara sürümleri ve standarda uyumlu zlib derlemeleri arasında geçerlidir; ancak standart, bir deflate kodlayıcısının farklı iç blok sınırları seçmesine açıkça izin verir, dolayısıyla gerçekten farklı zlib uygulamaları arasında (örneğin standart zlib ile zlib-ng) bayt bayt aynı çıktı vaat edilmez. Yapı ortamı tam da bu nedenle sabitlenmiştir.
NextPDF, yazdığı veri için FlateDecode’u ve ASCII taşıma filtrelerini seçer. Bir görüntü dönüştürücüsü değildir. Gelen rastgele bir JPEG2000 veya JBIG2 akışını yeniden paketlemeyi vaat etmez; kayıplı görüntü ödünleşimleri, bir yazıcının telafi edebileceği bir şey değil, kaynak verinin bir özelliğidir.
Mini SSS
“Mini SSS” başlıklı bölümFlateDecode neden her yerde? Kayıpsızdır, genel amaçlıdır, iyi desteklenir ve çoğu PDF’nin metin ve işleç içeriği için iyi bir seçimdir. İçerik akışları, gömülü yazı tipleri ve çapraz başvuru akışı için güvenli varsayılandır.
Sıkıştırmayı kapatabilir miyim? /Filter’ı atlayıp ham baytları saklayabilirsiniz ve bir okuyucu bunu kabul eder. Dosya büyür ve başka hiçbir şey iyileşmez; hata ayıklama dışında nadiren bir neden vardır.
Sıkıştırma düzeyi neden sabitleniyor? Çıktının yeniden üretilebilir olması için. Sabitlenmemiş bir düzey (veya zlib derlemesi), açılmış içeriği değiştirmeden sıkıştırılmış baytları değiştirebilir — çıktı doğru olabilir, ama aynı değildir; bu da bayt düzeyinde doğrulamayı boşa çıkarır.
İlgili belgeler
“İlgili belgeler” başlıklı bölüm- Bir PDF aslında nedir — bu sayfadaki akışların parçası olduğu nesne modeli.
- Yazı tipleri: zor kısım — gömülü yazı tipi programları, kendi hata kiplerine sahip filtrelenmiş akışlardır.
- PDF 2.0: neler değişti — 2.0 temelinin akışları ve NextPDF’nin varsayılan olarak kullandığı çapraz başvuru akışını nasıl ele aldığı.
Sözlük
“Sözlük” başlıklı bölüm- Akış nesnesi —
streamileendstreamarasındaki bir bayt bloğu ve ona eşlik eden bir sözlük; bir/Lengthve genellikle bir/Filtertaşır. - Filtre — bir okuyucunun bir akışın baytlarına uyguladığı, adlandırılmış bir kod çözme dönüşümü (örneğin
FlateDecode). - Filtre ardışık düzeni — sırayla uygulanan bir filtre dizisi; dizi sırası, kod çözme sırasıdır.
- FlateDecode — zlib/deflate filtresi; içerik, yazı tipleri ve çapraz başvuru akışları için varsayılan sıkıştırma.
- DCTDecode — JPEG görüntü filtresi; kayıplı olduğundan yeniden kodlamak görüntüyü yine bozar.
- ASCII taşıma filtresi — ASCIIHexDecode / ASCII85Decode; veriyi boyut pahasına 7 bit güvenli yapar — sıkıştırma değildir.
- Belirlenimci sıkıştırma — aynı girdi için bayt bayt aynı sıkıştırılmış çıktı üretmek; sıkıştırıcının düzeyini ve biçimini sabitleyerek sağlanır.