Aliran dan filter
ISO 32000-2 §7.4 Evidence: Standard-backed
Sekilas pandang
Bagian berjudul “Sekilas pandang”Sebagian besar byte dalam PDF sebenarnya berada di dalam aliran (stream): konten halaman, fon, gambar, dan aliran referensi-silang itu sendiri. Nyaris tidak ada byte tersebut yang disimpan mentah; semuanya melewati satu atau beberapa filter terlebih dahulu. Halaman ini membahas filter apa saja yang akan Anda temui, kegunaan masing-masing, di mana masalah biasanya muncul, dan mengapa NextPDF mengunci kompresinya agar masukan yang sama selalu menghasilkan byte yang sama.
Mengapa ini penting
Bagian berjudul “Mengapa ini penting”Aliran beserta filternya adalah kontrak: “byte ini dikompresi dengan deflate, lalu dikodekan base-85 — dekode dengan urutan itu untuk mendapatkan data yang sebenarnya.” Jika entri /Filter tidak sesuai dengan isi byte yang sebenarnya, atau /Length salah, atau dua filter dicantumkan dengan urutan yang keliru, aliran tidak dapat didekode dan objek yang dibawanya hilang. Pembaca tidak menebak secara heuristik; ia melakukan apa yang diperintahkan oleh kamus.
Ada konsekuensi kedua yang lebih tersembunyi. Jika kompresor sebuah pustaka tidak deterministik — build zlib yang berbeda, level yang berbeda, batas blok internal yang berbeda — maka dua eksekusi yang seharusnya menghasilkan PDF identik justru menghasilkan dua berkas yang berbeda. Hal itu merusak reproduksibilitas pada tingkat byte. Reproduksibilitas yang rusak kemudian merusak pengujian golden-file, verifikasi signed-build, dan pipeline apa pun yang membandingkan keluaran. Filter menentukan apakah PDF valid dan apakah PDF sama persis.
Versi singkatnya
Bagian berjudul “Versi singkatnya”- Sebuah objek aliran adalah sebuah kamus ditambah sebuah blok byte, yang dibungkus dalam
stream…endstream, dengan sebuah/Lengthdan biasanya sebuah/Filter. - Entri
/Filtermenamai filter pendekode — atau sebuah larik filter yang diterapkan sebagai pipeline, secara berurutan. - Filter terbagi menjadi dua keluarga: kompresi (FlateDecode, LZWDecode, RunLengthDecode, DCTDecode, JPXDecode, JBIG2Decode) dan transport ASCII (ASCIIHexDecode, ASCII85Decode), ditambah filter Crypt khusus untuk enkripsi.
- Yang paling sering Anda lihat adalah FlateDecode — zlib/deflate. Filter ini menjadi standar untuk konten, fon, dan aliran referensi-silang.
- NextPDF mengunci keluaran Flate-nya pada level dan format tetap sehingga byte masukan yang sama selalu terkompresi menjadi byte keluaran yang sama.
Bagaimana NextPDF menanganinya
Bagian berjudul “Bagaimana NextPDF menanganinya”NextPDF menulis objek aliran melalui satu helper buffer dan mengompresi melalui satu kompresor yang dikunci — secara sengaja.
BinaryBuffer::writeStream() (src/Support/BinaryBuffer.php) membungkus konten aliran dalam kamusnya, selalu menuliskan /Length yang sama dengan panjang byte sebenarnya dan menggabungkan entri tambahan apa pun yang disediakan pemanggil, seperti /Filter. Tidak ada jalur yang dapat membuat panjang terdeklarasi tidak sesuai dengan byte yang ditulis, karena panjang tersebut diambil dari string konten itu sendiri.
Kompresi berlangsung melalui PinnedZlibCompressor (src/Writer/PinnedZlibCompressor.php). Kelas ini dibuat untuk satu alasan. gzcompress tanpa level eksplisit mengikuti default runtime zlib, yang secara historis bervariasi antar build. Header zlib 2-byte bahkan mengodekan level secara tidak langsung, sehingga “default” bukanlah keluaran yang stabil. Kompresor mengunci level pada maksimum RFC 1951 dan selalu mengeluarkan deflate yang dibungkus zlib (header RFC 1950 + trailer Adler-32), yang persis seperti yang diharapkan oleh /Filter /FlateDecode. Kegagalan keras dari zlib diubah menjadi eksepsi bertipe, bukan fallback diam-diam ke keluaran yang tidak terkompresi — sebuah aliran tidak pernah ditulis mentah secara diam-diam.
Aliran referensi-silang itu sendiri adalah contoh penerapan semua hal ini: CrossReferenceStream (src/Core/CrossReferenceStream.php) membangun tabel biner, mengompresinya, dan menulisnya sebagai objek aliran dengan /Type /XRef, larik lebar-bidang /W, dan /Filter /FlateDecode. Indeks yang memungkinkan pembaca menemukan setiap objek itu sendiri merupakan aliran yang difilter.
| Filter | Keluarga | Kegunaannya | Di mana ia keliru |
|---|---|---|---|
| FlateDecode | Kompresi | zlib/deflate; standar untuk konten, fon, aliran xref | Build zlib yang tidak deterministik membuat PDF yang tampak “identik” berbeda byte demi byte |
| LZWDecode | Kompresi | Kompresi Lempel–Ziv–Welch yang lebih lawas | Warisan format lama; digantikan oleh Flate, sesekali masih terlihat di berkas lama |
| DCTDecode | Kompresi | Gambar warna/grayscale berkode JPEG | Lossy — mengodekan ulang gambar yang sudah berkode DCT kembali menurunkan kualitasnya |
| JPXDecode | Kompresi | Data gambar wavelet JPEG 2000 | Tidak diizinkan oleh sebagian profil arsip; dukungan luasnya tidak merata |
| JBIG2Decode | Kompresi | Kompresi gambar bilevel (1-bit) | Tidak boleh digunakan dengan gambar inline; mode lossy dapat mengubah hasil pemindaian |
| RunLengthDecode | Kompresi | Run-length berorientasi byte | Hanya membantu data dengan deret byte tunggal yang panjang; dapat memperbesar data lainnya |
| ASCIIHexDecode | Transport | Biner sebagai digit heksadesimal | Menggandakan ukuran; hanya untuk kanal aman-7-bit, tidak pernah untuk mengecilkan ukuran |
| ASCII85Decode | Transport | Biner sebagai ASCII base-85 | Overhead ~25%; memudahkan transport, bukan kompresi |
| Crypt | Keamanan | Menerapkan penangan keamanan dokumen | Aliran referensi-silang tidak boleh menggunakan filter Crypt |
Kumpulan filter standar PDF menurut keluarga, beserta kegagalan yang relevan untuk masing-masing. NextPDF menulis FlateDecode untuk konten, fon, dan aliran referensi-silang; filter transport ASCII ditujukan untuk kanal 7-bit, bukan untuk mengurangi ukuran.
Apa kata bukti
Bagian berjudul “Apa kata bukti”Mekanisme filter didefinisikan oleh Spec: ISO 32000-2, §7.4 ISO 32000-2 §7.4 . Sebuah kamus aliran menamai filternya melalui /Filter. Jika entri tersebut mencantumkan lebih dari satu filter, filter-filter itu membentuk pipeline pendekodean dan diterapkan secara berurutan. Penulis mengodekan aliran untuk mengompresinya atau untuk membuatnya aman-7-bit. Pembaca menjalankan filter pendekode yang sesuai untuk memulihkan data asli. Evidence: Standard-backed
Tabel filter standar mengklasifikasikan setiap filter. FlateDecode mendekompresi data yang dienkode zlib/deflate, mereproduksi teks atau data biner asli. DCTDecode mereproduksi sampel gambar yang mendekati aslinya melalui JPEG — kata “mendekati” adalah cara standar menyatakan bahwa proses ini lossy. LZWDecode, RunLengthDecode, JBIG2Decode, JPXDecode, dan filter Crypt juga didefinisikan di sana, dengan JBIG2 secara eksplisit dilarang untuk gambar inline.
Aliran referensi-silang menerapkan mekanisme format yang sama pada dirinya sendiri: ia adalah objek aliran (/Type /XRef,
Spec: ISO 32000-2, §7.5.8 ISO 32000-2 §7.5.8 ) yang larik /W-nya
menyatakan lebar byte setiap bidang entri di dalam aliran yang telah didekode. Standar
mengharuskan aliran ini tidak dienkripsi dan tidak menggunakan filter Crypt.
CrossReferenceStream milik NextPDF mengikuti ketentuan ini secara persis — FlateDecode,
/W eksplisit, tanpa enkripsi.
Contoh praktis
Bagian berjudul “Contoh praktis”Berikut sebuah aliran konten halaman yang dikompresi dengan Flate. Ini bentuk yang sangat lumrah: sebuah kamus dengan /Length dan /Filter, lalu byte terkompresi di antara stream dan endstream.
<?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// endobjPembaca melakukan proses sebaliknya: membaca /Length byte, menjalankannya melalui FlateDecode karena /Filter mengatakan demikian, dan mendapatkan kembali operator aslinya. Saat kompresornya dikunci, perjalanan bolak-balik itu tidak hanya benar. Hasilnya identik setiap kali, yang menjadi dasar pemeriksaan golden-file dan signed-build.
Kesalahpahaman umum
Bagian berjudul “Kesalahpahaman umum”Jebakannya adalah memperlakukan filter ASCII sebagai kompresi. ASCIIHexDecode dan ASCII85Decode membuat sebuah aliran lebih besar — yang satu kira-kira dua kali lipat, yang lain kira-kira 25%. Keduanya ada untuk memindahkan data biner melalui kanal yang hanya aman untuk teks 7-bit, bukan untuk menghemat ruang. Memilih ASCII85 untuk “mengecilkan” sebuah PDF justru melakukan sebaliknya. Separuh kedua dari kesalahpahaman yang sama adalah meyakini bahwa FlateDecode bersifat lossless untuk gambar begitu saja. Flate memang lossless, tetapi jika gambar sudah berkode DCT (JPEG), membungkusnya lagi atau mentranskodekannya melalui filter lossy akan menurunkan kualitasnya, terlepas dari apa pun yang dilakukan Flate di sekitarnya. Pipeline filter mempertahankan persis apa yang Anda masukkan ke dalamnya — termasuk artefak rekompresi yang Anda masukkan secara tidak sengaja.
Batasan dan ruang lingkup
Bagian berjudul “Batasan dan ruang lingkup”Halaman ini membahas bagaimana filter dideklarasikan dan diterapkan, bukan algoritma tingkat bit di dalam masing-masingnya. Jaminan determinisme secara khusus berlaku untuk keluaran Flate NextPDF bagi aliran yang ditulisnya. Jaminan ini berlaku di seluruh versi minor PHP dan build zlib yang sesuai standar, tetapi standar secara eksplisit mengizinkan pengode deflate memilih batas blok internal yang berbeda, sehingga keluaran yang identik pada tingkat byte di seluruh implementasi zlib yang benar-benar berbeda (misalnya zlib bawaan versus zlib-ng) tidak dijanjikan. Lingkungan build dikunci karena alasan itu.
NextPDF memilih FlateDecode dan filter transport ASCII untuk data yang dikeluarkannya. Ia bukan transkoder gambar. Ia tidak menjanjikan akan mengemas ulang sembarang aliran JPEG2000 atau JBIG2 yang masuk, dan kompromi lossy pada gambar adalah sifat data sumber, bukan sesuatu yang dapat dibatalkan oleh penulis.
Mini-FAQ
Bagian berjudul “Mini-FAQ”Mengapa FlateDecode ada di mana-mana? Ia lossless, serbaguna, didukung dengan baik, dan cocok untuk konten teks-dan-operator pada sebagian besar PDF. Filter ini merupakan pilihan standar yang aman untuk aliran konten, fon tertanam, dan aliran referensi-silang.
Bisakah saya mematikan kompresi? Anda dapat menghilangkan /Filter dan menyimpan byte mentah, dan pembaca akan menerimanya. Berkas menjadi lebih besar dan tidak ada hal lain yang menjadi lebih baik; jarang ada alasan di luar proses debugging.
Mengapa mengunci level kompresi sama sekali? Agar keluarannya dapat direproduksi. Level yang tidak dikunci (atau build zlib) dapat mengubah byte terkompresi tanpa mengubah konten yang terdekompresi — tetap benar, tetapi tidak identik, yang menggagalkan verifikasi tingkat-byte.
Dokumen terkait
Bagian berjudul “Dokumen terkait”- Apa sebenarnya PDF itu — model objek tempat aliran di halaman ini berada.
- Fon: bagian yang sulit — program fon tertanam adalah aliran yang difilter, dengan mode kegagalannya sendiri.
- PDF 2.0: apa yang berubah — bagaimana baseline 2.0 memperlakukan aliran dan aliran referensi-silang yang menjadi standar NextPDF.
Glosarium
Bagian berjudul “Glosarium”- Objek aliran — sebuah kamus ditambah sebuah blok byte di antara
streamdanendstream, yang membawa sebuah/Lengthdan biasanya sebuah/Filter. - Filter — transformasi pendekodean bernama yang diterapkan pembaca pada byte sebuah aliran (misalnya
FlateDecode). - Pipeline filter — sebuah larik filter yang diterapkan secara berurutan; urutan larik adalah urutan pendekodean.
- FlateDecode — filter zlib/deflate; kompresi standar untuk konten, fon, dan aliran referensi-silang.
- DCTDecode — filter gambar JPEG; lossy, sehingga pengodean ulang akan menurunkan kualitas gambar lagi.
- Filter transport ASCII — ASCIIHexDecode / ASCII85Decode; membuat data aman-7-bit dengan ukuran yang lebih besar — bukan kompresi.
- Kompresi deterministik — menghasilkan keluaran terkompresi yang identik byte untuk masukan yang identik, dicapai dengan mengunci level dan format kompresor.