Lewati ke konten
getnextpdf.com

Ekonomi ukuran berkas PDF

Spec: ISO 32000-2, §7.5.7

Dua PDF dapat tampak identik piksel-demi-piksel di layar dan berbeda sepuluh kali lipat di disk. Perbedaannya hampir tidak pernah konten yang Anda lihat; ia adalah bagaimana berkas itu dirakit di baliknya. Halaman ini adalah sebuah tur ekonomi-ukuran: ke mana byte sebuah PDF sebenarnya pergi, dan empat tuas yang dibelanjakan seorang penulis dari anggaran byte.

Ia adalah pendamping mengapa berkas besar untuk Streams and filters, yang membahas bagaimana sebuah filter men-decode. Yang ini tetap pada anggarannya.

Ukuran berkas jarang merupakan metrik kesombongan. Ia adalah bandwidth pada setiap unduhan, penyimpanan pada setiap arsip, dan latensi pada setiap pratinjau. Sebuah faktur 12 MB yang seharusnya 400 KB bukanlah masalah kosmetik ketika Anda menghasilkan sejuta darinya sebulan — ia adalah tagihan tiga puluh kali lipat.

Bagian yang menjengkelkan adalah bahwa kembung biasanya tak terlihat. Dokumen merender dengan benar, terbuka dengan baik, mencetak dengan baik. Tidak ada yang memberi tahu Anda bahwa artefak yang sama dapat menjadi pecahan dari ukurannya, karena byte yang terbuang bersifat struktural, bukan visual. Untuk menemukannya Anda harus melihat anggarannya, bukan halamannya.

Bayangkan sebuah PDF sebagai sebuah anggaran yang Anda belanjakan pada empat butir baris.

  • Overhead per-objek. Setiap objek tak langsung membawa sebuah amplop N G obj / endobj, dan dilacak oleh satu cross-reference entry. Sebuah dokumen yang berat-halaman memiliki ribuan objek kecil, dan amplop-amplop itu bertumpuk. Object stream membuang amplop itu untuk satu grup objek penuh; setiap objek yang dikemas tetap menyimpan cross-reference entry-nya sendiri, tetapi sebagai sebuah compact type-2 entry.
  • Byte gambar. Untuk dokumen apa pun dengan foto atau pindaian, gambar-gambar mendominasi, dan tuas tunggal terbesar adalah pilihan filter — sebuah codec lossless versus sebuah codec gambar lossy adalah perbedaan antara megabyte dan kilobyte.
  • Byte font. Sebuah font tersemat penuh adalah ratusan kilobyte glif yang tidak pernah Anda gunakan. Subsetting hanya menyimpan glif yang sebenarnya digambar dokumen.
  • Indeks. Cross-reference table yang membiarkan seorang pembaca menemukan setiap objek itu sendiri dapat menjadi sebuah compressed stream alih-alih teks-polos.

Buat keempatnya benar dan berkasnya kecil. Lewatkan satu dan ia mendominasi segala sesuatu lain yang Anda lakukan dengan baik.

Writer NextPDF adalah sebuah serializer streaming single-pass: ia menambahkan byte setiap objek begitu mereka diproduksi dan merekam sebuah cross-reference entry in-use klasik untuk masing-masing. Default itu cepat, dapat diprediksi, dan menghasilkan sebuah berkas yang stabil-byte — tetapi ia bukan tata letak terkecil yang mungkin, dan NextPDF jujur tentang itu.

Sebuah PDF adalah sebuah graf objek tak langsung. Sebagian besar dari mereka adalah dictionary kecil: node halaman, dictionary anotasi, elemen structure-tree, entri outline. Masing-masing membayar sebuah pajak tetap — keyword obj / endobj, nomor objek dan generasi, dan sebuah cross-reference entry yang menempatkannya. Pada sebuah dokumen dengan ribuan objek kecil, pajak itu adalah sebuah iris yang bermakna dari berkasnya.

Sebuah object stream mengumpulkan banyak dari objek non-stream kecil itu ke dalam satu stream dan mengompresinya bersama (Spec: ISO 32000-2, §7.5.7). Amplop obj / endobj dibuang untuk grup penuhnya; nilai-nilai di dalamnya disimpan saling-membelakangi tanpa keyword per-objek, lalu di-deflate sebagai sebuah blok tunggal — yang juga mengompresi lebih baik, karena kompresor yang men-deduplikasi sekarang melihat semua dictionary serupa itu sekaligus. Cross-reference entry tidak hilang — setiap objek yang dikemas masih membutuhkannya — tetapi ia menyusut menjadi sebuah compact binary type-2 entry di cross-reference stream (lebih lanjut tentang itu di Tuas 2).

Di NextPDF ini disajikan oleh ObjectStreamPacker, sebuah post-processor mandiri yang mengambil sebuah PDF cross-reference-stream yang selesai dan menulis ulang objek yang memenuhi syarat ke dalam sebuah /Type /ObjStm tunggal. Aturan yang ia ikuti datang langsung dari standar: sebuah objek yang memenuhi syarat adalah sebuah objek generation-zero, non-stream, setelah mengecualikan objek khusus yang harus tetap dapat dialamatkan secara langsung. §7.5.7 melarang menyimpan sebuah objek stream di dalam sebuah object stream, sehingga objek stream — konten, font, gambar — menyimpan entri mereka sendiri; dan ObjectStreamPacker selain itu menolak objek cross-reference stream dokumen itu sendiri (ia ditulis ulang) dan dictionary /Encrypt, keduanya harus tetap dapat dialamatkan secara langsung. Segala sesuatu lainnya yang generation-zero dan non-stream dikemas.

Object stream packing (ObjStm) — edition availability
EditionAvailability
CoreDukungan penuh dalam core open-source via ObjectStreamPacker. Ia bersifat opt-in: default writer single-pass memancarkan entri in-use klasik, sehingga keluaran tetap identik per byte kecuali Anda mengaktifkan packing. Packer-nya deterministik, dengan golden baseline-nya sendiri yang dapat direproduksi.
ProNot in this edition
EnterpriseNot in this edition

Opt-in adalah sebuah sikap yang disengaja, bukan sebuah keterbatasan yang disembunyikan. Keluaran default bersifat stabil-byte dan cocok dengan golden baseline yang ada; menyalakan packing memilih masuk ke sebuah tata letak yang berbeda, lebih kecil, dan sama-sama deterministik. Anda memilih trade-nya, dan mesin tidak pernah membuatnya di belakang Anda.

Tuas 2 — indeks juga dapat menjadi sebuah stream

Bagian berjudul “Tuas 2 — indeks juga dapat menjadi sebuah stream”

Begitu objek hidup di dalam sebuah object stream, indeks yang menunjuk ke mereka berubah bentuk. Seorang pembaca menemukan sebuah objek yang dikemas melalui sebuah compressed cross-reference entry — sebuah type-2 entry yang menamai object stream dan indeks di dalamnya (Spec: ISO 32000-2, §7.5.8.3). Karena seluruh cross-reference itu sendiri adalah sebuah /Type /XRef stream, indeks untuk ribuan objek di-binary-pack dan di-deflate alih-alih ditulis sebagai baris teks-polos. Peta menyusut bersama wilayahnya.

ObjectStreamPacker membangun ulang persis ini: ia memancarkan object stream tunggal, lalu sebuah cross-reference stream yang ditulis ulang yang membawa sebuah compact type-1 entry untuk setiap objek yang dipertahankan dan sebuah type-2 entry untuk setiap yang dikemas, mempertahankan setiap nomor objek sehingga referensi yang ada tetap valid.

Untuk dokumen apa pun dengan gambar sungguhan, tuas ini mengerdilkan yang lainnya. Byte-nya adalah gambar yang sama; codec-nya adalah anggarannya. Nama filter hidup di standard filter set (Spec: ISO 32000-2, §7.4), dan pilihan di antara mereka adalah sebuah keputusan ukuran:

  • FlateDecode bersifat lossless. Sempurna untuk line art, screenshot, dan apa pun dengan warna datar — dan menghancurkan untuk sebuah foto, di mana lossless berarti setiap byte dari aslinya.
  • DCTDecode adalah JPEG: lossy, dan untuk foto adalah pilihan yang tepat dengan margin lebar, sering kali sebuah pengurangan sepuluh kali lipat untuk sebuah penurunan kualitas yang tidak diperhatikan siapa pun.
  • JPXDecode adalah JPEG 2000: kompresi wavelet dengan sebuah kurva kualitas/ukuran yang berbeda, tetapi dukungan yang tidak merata dan dilarang oleh beberapa profil arsip.

Ini persis di mana bagaimana sebuah filter men-decode penting, dan itu adalah tugas artikel Streams and filters. Poin ekonominya lebih sempit: sebuah foto yang disimpan lossless adalah penyebab tunggal paling umum dari sebuah PDF yang besar tanpa perlu, dan tidak ada jumlah object-stream packing yang akan menyelamatkan sebuah berkas yang bobot sesungguhnya adalah satu pindaian yang tidak di-JPEG.

Sebuah font tersemat adalah sebuah program. Yang penuh dapat beberapa ratus kilobyte, karena ia membawa setiap glif yang pernah digambar perancang typeface — ribuan karakter melintasi skrip yang tidak akan pernah Anda gunakan di dokumen ini. Subsetting hanya menyematkan glif yang sebenarnya digambar dokumen, mengubah program itu menjadi sebuah pecahan kecil dari dirinya. Sebuah surat satu halaman tidak membutuhkan seluruh sebuah font yang berkemampuan-CJK; ia membutuhkan beberapa lusin glif yang ia set. Mekanika bagaimana glif dipilih dan diindeks-ulang adalah subjeknya sendiri — lihat Fonts: the hard part.

  1. Image bytesFor media-heavy files, the largest line item. The filter choice — lossless Flate vs a lossy image codec like DCT (or JPX in a lossy mode) — is the dominant size lever.
  2. Font bytesA full embedded font is mostly glyphs you never draw. Subsetting keeps only the used glyphs, often shrinking the program by an order of magnitude.
  3. Per-object overheadThousands of small dictionaries each pay an obj/endobj envelope plus a cross-reference entry. Object streams (ObjStm) drop the envelope for the whole group; each object keeps a compact type-2 cross-reference entry.
  4. The indexA compressed cross-reference stream with type-2 entries binary-packs and deflates the map of every object, replacing plaintext index rows.
Ke mana byte-nya pergi: empat tuas yang dibelanjakan seorang penulis dari anggaran ukuran sebuah PDF, dalam urutan mereka biasanya mendominasi sebuah berkas nyata.

Tidak ada API yang dibuat-buat untuk ditunjukkan di sini, karena keputusan ukuran yang paling penting dibuat sebelum byte-nya mencapai writer — dan satu tuas struktural yang diekspos NextPDF adalah sebuah opt-in tunggal. Secara konseptual, anggarannya terbaca seperti ini:

  • Berikan foto sebagai JPEG sehingga mereka mendarat di bawah DCTDecode, bukan di-encode-ulang secara lossless. Kemenangan terbesar adalah sebuah pilihan tentang data sumber, bukan sebuah flag writer.
  • Biarkan writer men-subset font tersemat sehingga hanya glif yang digambar yang dikirim.
  • Untuk sebuah dokumen dengan banyak objek kecil, memilih masuk ke object-stream packing, yang merutekan berkas yang selesai melalui ObjectStreamPacker untuk mengelompokkan objek non-stream kecil dan menulis ulang cross-reference stream.

Jalur default — entri in-use klasik, tanpa ObjStm — adalah baseline yang tepat: deterministik, stabil-byte, dan mudah diverifikasi. Packing adalah peningkatan yang dipertimbangkan ketika jumlah objek, bukan bobot gambar, yang menggelembung berkasnya.

Jebakannya adalah menjangkau sebuah tombol “kompres PDF” dan mengharapkannya memperbaiki segalanya. Kompresi bukan satu tuas; ia empat, dan mereka tidak saling menggantikan. Object-stream packing tidak dapat menyusutkan sebuah foto — itu adalah tugas filter gambar. Sebuah JPEG yang sempurna tidak dapat mengimbangi sebuah font yang lupa Anda subset. Dan tidak satu pun dari itu membantu jika kembung sesungguhnya adalah sebuah pindaian 10 MB yang disimpan lossless karena tidak ada yang memilih DCTDecode untuknya.

Kesalahpahaman kedua adalah bahwa lebih kecil selalu pasti lebih baik. Tidak. Object stream tidak kompatibel dengan linearization — tata letak fast-web-view yang mematok penempatan objek absolut sehingga halaman pertama mengalir masuk lebih awal. Dan beberapa profil arsip membatasi filter gambar mana yang bahkan diizinkan. Ukuran adalah satu sumbu. Ia bertukar terhadap streaming, konformansi arsip, dan reproduksibilitas, dan titik yang tepat pada kurva bergantung pada untuk apa dokumen itu.

Keempat tuas itu adalah ekonomi struktural dari ukuran berkas. Mereka bukan sebuah jaminan “buat ia lebih kecil” yang universal, dan NextPDF tidak berpura- pura menjadi sebuah re-optimizer untuk PDF masuk yang sembarang.

ObjectStreamPacker adalah sebuah optimisasi upaya-terbaik yang tidak pernah mempertaruhkan kebenaran. Ia menolak — mengembalikan input tanpa perubahan — ketika berkasnya bukan sebuah PDF cross-reference-stream, ketika ia terenkripsi, ketika ia membawa sebuah tanda tangan digital (menata-ulang objek akan menggeser rentang byte yang dilindungi sebuah tanda tangan), ketika ia sudah berisi object stream, atau ketika tidak ada objek yang memenuhi syarat untuk dikemas. Ia memancarkan persis satu object stream untuk seluruh dokumen; ia tidak terpecah menjadi sebuah koleksi /Extends, yang berada di luar cakupan dan konform untuk ukuran dokumen yang dihasilkan NextPDF.

Tuas gambar dan font sebagian besar adalah keputusan tentang input-nya. Writer NextPDF tidak secara implisit men-transcode sebuah bitmap lossless menjadi sebuah stream gambar lossy — meng-encode-ulang sebuah foto menjadi DCTDecode atau JPXDecode adalah sebuah keputusan image-encoding hulu yang eksplisit, bukan sesuatu yang dilakukan serializer di belakang Anda — ataupun ia merebut kembali glif dari sebuah font yang diminta pemanggil untuk disematkan secara penuh. Kemenangan ukuran terbesar dibuat di hulu byte serializer; tugas mesin adalah untuk tidak memboroskan anggaran yang Anda bawa kepadanya.

  • Streams and filters — pendamping bagaimana sebuah filter men-decode; halaman ini sengaja melengkapinya, bukan menduplikasinya.
  • What a PDF actually is — model objek- tak-langsung yang overhead per-objek-nya dikurangi oleh tuas object-stream.
  • The anatomy of a PDF file — struktur cross-reference yang menjadi sebuah compressed stream.
  • Fonts: the hard part — bagaimana subsetting memilih dan mengindeks-ulang glif yang digambar sebuah dokumen.
  • Object stream (ObjStm) — sebuah stream tunggal yang menampung banyak objek tak langsung non-stream kecil, dikompresi bersama, sehingga amplop obj / endobj dibuang untuk grupnya. Setiap objek yang dikemas tetap menyimpan cross-reference entry-nya sendiri, sebagai sebuah compact type-2 entry. Opt-in di NextPDF.
  • Indirect object — sebuah objek bernomor di graf sebuah PDF, dibungkus dalam sebuah amplop obj / endobj dan dilacak oleh indeks cross-reference. Amplop itu adalah overhead per-objek.
  • Cross-reference stream/Type /XRef stream yang mengindeks setiap objek, di-binary-pack dan di-deflate alih-alih ditulis sebagai baris teks-polos.
  • Compressed (type-2) entry — sebuah cross-reference entry yang menunjuk ke sebuah objek yang hidup di dalam sebuah object stream, menamai stream dan indeks di dalamnya.
  • Font subsetting — menyematkan hanya glif yang sebenarnya digambar sebuah dokumen, alih-alih typeface penuh, menyusutkan program font tersemat.
  • Lossless vs lossy filter — sebuah codec lossless (FlateDecode) mereproduksi setiap byte; sebuah codec gambar lossy (DCTDecode, atau JPXDecode dalam sebuah mode lossy) membuang detail yang tidak terlihat untuk sebuah hasil yang jauh lebih kecil. (JPEG 2000 / JPX juga dapat dikonfigurasi lossless.) Pilihannya adalah tuas dominan pada sebuah berkas yang berat-media.