Pada dasarnya, PDF adalah wadah teks biasa. Buka sebagian besar file di editor hex dan bagian atasnya dapat dibaca: komentar versi, kemudian serangkaian objek bernomor, kemudian indeks kecil dan sebuah penunjuk di bagian paling bawah yang memberi tahu pembaca dari mana harus mulai. Hilangkan kompresi dan format tersebut cukup mudah didekati sehingga Anda dapat mengetikkan dokumen yang berfungsi ke editor teks dan membukanya di viewer. Melakukan itu sekali mengajarkan Anda lebih banyak tentang cara PDF terhubung daripada membaca spesifikasinya, karena Anda harus menghubungkan objek-objek tersebut satu sama lain secara manual dan file tersebut menolak untuk dibuka sampai Anda mendapatkan koneksi yang benar
Panduan ini membangun PDF terkecil yang benar-benar merender sesuatu: satu halaman, kata-kata "Hello, World!" dalam font bawaan, di kertas US Letter. File yang telah selesai membutuhkan tepat lima objek dan beberapa baris pembukuan di sekitarnya. Kita akan menulis objek-objeknya terlebih dahulu, kemudian merakit header, tabel referensi silang, dan trailer yang mengikatnya menjadi file yang akan diterima oleh reader
Lima objek yang diharuskan oleh viewer
Reader tidak memindai PDF dari atas ke bawah mencari konten. Reader mulai dari trailer, mengikuti referensi ke katalog dokumen, dan berjalan melalui rantai objek dari sana. Setiap objek dalam rantai tersebut harus ada atau pembukaannya gagal. Untuk dokumen satu halaman, rantainya pendek, dan setiap tautan memiliki satu pekerjaan:
- Catalog adalah root. Objek inilah yang ditunjuk trailer, dan satu-satunya entri yang diperlukan di sini adalah referensi ke pohon halaman
- Pages adalah node pohon halaman. Ia mencantumkan halaman-halaman dalam dokumen dan melaporkan berapa banyak yang ada
- Page menggambarkan satu halaman fisik: ukurannya, sumber daya yang digunakannya, dan aliran konten yang melukisnya
- Content stream menyimpan operator gambar, perintah postfix yang menempatkan teks dan grafis di halaman tersebut
- Font mendeklarasikan jenis huruf yang dirujuk oleh content stream. Gunakan salah satu dari 14 font standar dan Anda tidak perlu menyematkan apa pun
Setiap objek diberi nomor dan dapat ditangani. Objek tidak langsung ditulis sebagai N 0 obj ... endobj, di mana N adalah nomor objek dan 0 adalah nomor generasinya (selalu 0 dalam file yang Anda tulis baru). Di tempat lain dalam file, Anda menunjuk objek itu dengan referensi: 5 0 R berarti "objek 5." Referensi-referensi tersebut adalah koneksinya. Catalog menyimpan 2 0 R dalam penomoran kita untuk mencapai pohon halaman, pohon halaman menyimpan referensi kembali ke halaman, dan seterusnya. Salahkan nomor dan reader mengikuti penunjuk yang menggantung ke ketiadaan
Nama, kamus, dan aliran
Tiga bagian sintaks membawa hampir semuanya. Sebuah nama dimulai dengan garis miring: /Type, /Page, /F0. Nama adalah pengidentifikasi yang peka huruf besar-kecil, bukan string, dan PDF menggunakannya untuk kunci kamus dan untuk menandai jenis objek. Sebuah kamus adalah sekumpulan pasangan kunci-nilai yang dibungkus dalam tanda kurung sudut ganda, di mana setiap kunci adalah nama: << /Type /Page /MediaBox [0 0 612 792] >>. Nilai dapat berupa angka, nama, array dalam tanda kurung siku, referensi, atau kamus bersarang. Sebagian besar objek PDF adalah kamus
Sebuah aliran adalah kamus yang diikuti oleh blok byte antara kata kunci stream dan endstream. Di situlah operator gambar halaman berada, dan dalam file nyata di situlah gambar terkompresi dan font tersemat juga berada. Kamus aliran mendeskripsikan byte tersebut; dalam file produksi harus membawa entri /Length yang memberikan jumlah byte yang tepat, dan sering kali sebuah /Filter seperti /FlateDecode ketika data dikompresi. Kita akan mengandalkan alat untuk mengisi /Length, karena menghitung byte secara manual adalah bagian dari latihan ini yang tidak memiliki nilai edukasi dan memiliki kemungkinan tinggi mengalami kesalahan satu byte yang merusak file
Menulis objek-objeknya
Berikut adalah lima objek secara berurutan. Detail koordinat yang perlu diingat sebelum membaca content stream: PDF mengukur dari sudut kiri bawah halaman dalam poin, di mana satu poin adalah 1/72 inci, dan Y tumbuh ke atas. Halaman US Letter berukuran 612 kali 792 poin, jadi 50 700 berada di dekat kiri atas, bukan di bawah
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]
/Resources << /Font << /F0 4 0 R >> >>
/Contents 5 0 R
>>
endobj
4 0 obj
<< /Type /Font
/Subtype /Type1
/BaseFont /Helvetica
>>
endobj
5 0 obj
<< /Length 44 >>
stream
BT
/F0 36 Tf
50 700 Td
(Hello, World!) Tj
ET
endstream
endobj
Baca referensi dan strukturnya menjadi jelas. Objek 1, catalog, mengarahkan entri /Pages-nya ke objek 2. Objek 2, pohon halaman, mencantumkan objek 3 dalam /Kids dan mendeklarasikan /Count 1. Objek 3, halaman, mengarahkan /Parent kembali ke objek 2 (pohon dan halaman saling mereferensikan, yang diperlukan), mengukur dirinya sendiri dengan /MediaBox, mengekspos font di bawah nama lokal /F0 dalam /Resources-nya, dan menamai objek 5 sebagai kontennya. Objek 4 adalah font: /BaseFont /Helvetica memilih salah satu dari 14 jenis huruf standar yang sudah dimiliki setiap reader yang sesuai, sehingga tidak ada yang perlu disematkan. Objek 5 adalah content stream
Apa yang sebenarnya dikatakan content stream
Isi aliran adalah program kecil dalam bahasa deskripsi halaman PDF, yang menggunakan notasi postfix: operand datang lebih dulu, kemudian operator yang mengonsumsinya. Lima baris melakukan pekerjaan. BT dan ET membuka dan menutup objek teks; semua yang memposisikan atau menampilkan teks harus berada di antara keduanya. /F0 36 Tf menetapkan font saat ini ke sumber daya bernama /F0 pada 36 poin (Tf adalah "set text font and size"). 50 700 Td memindahkan posisi teks ke (50, 700) dalam koordinat halaman. (Hello, World!) Tj menampilkan string, yang ditulis PDF sebagai teks literal dalam tanda kurung, menggunakan Tj untuk melukisnya di posisi saat ini. Hilangkan BT/ET dan reader ketat menolak operator teks; lupakan mengatur font sebelum Tj dan tidak ada font saat ini untuk menggambar
/Length 44 dalam kamus aliran adalah hitungan byte antara stream dan endstream, dan harus tepat. Ini adalah nilai yang layak diserahkan ke alat daripada menghitung baris baru secara manual, terutama karena apakah editor Anda menulis akhir baris sebagai LF atau CRLF mengubah totalnya
Header, xref, dan trailer
Objek-objek adalah kontennya. Tiga bagian struktural mengubahnya menjadi file. Yang pertama adalah header, baris pertama, menamai format dan versi:
%PDF-1.7
% memulai komentar dalam sintaks PDF, tetapi reader memperlakukan komentar khusus ini sebagai tanda tangan format dan membaca versinya dari situ. Penulis nyata langsung mengikutinya dengan baris komentar kedua dari byte high-bit, petunjuk ke alat transfer file bahwa file tersebut biner dan tidak boleh dirusak sebagai teks
Di akhir file datang tabel referensi silang, indeks yang memungkinkan akses acak. Ia merekam offset byte dari setiap objek dari awal file, sehingga reader dapat langsung mencari objek 3 tanpa mengurai objek 1 dan 2 terlebih dahulu. Tabelnya kaku: entri berukuran tetap, 20 byte masing-masing termasuk akhir baris, diformat sebagai offset 10 digit, generasi 5 digit, kata kunci (n untuk digunakan, f untuk bebas), dan terminator dua byte. Tabel yang benar untuk enam entri kita (objek 0 selalu merupakan kepala daftar bebas) terlihat seperti ini:
xref
0 6
0000000000 65535 f
0000000009 00000 n
0000000058 00000 n
0000000115 00000 n
0000000235 00000 n
0000000308 00000 n
trailer
<< /Size 6
/Root 1 0 R
>>
startxref
408
%%EOF
Offset-offset tersebut adalah bagian yang rapuh dari penulisan PDF secara manual. Setiap offset adalah posisi byte tepat di mana N 0 obj yang sesuai dimulai, dan setiap offset bergeser ketika Anda menambahkan karakter di mana saja di atasnya. Trailer adalah titik masuk yang digunakan reader terakhir dan pertama: /Root 1 0 R menamai catalog, /Size 6 menyatakan jumlah objek, dan startxref 408 memberikan offset byte dari kata xref itu sendiri. Reader membuka file, melompat ke akhir, membaca startxref, mencari ke tabel referensi silang, dan dari sana mencapai catalog dan segalanya di bawahnya. %%EOF menandai byte terakhir
Biarkan alat memperbaiki jumlah byte
Offset di atas bersifat ilustratif; dalam praktiknya akan salah pada saat Anda selesai mengetik, karena offset bergantung pada tata letak byte file Anda yang tepat. Daripada menghitungnya kembali, tulis struktur dengan nilai placeholder dan biarkan utilitas membangun ulang tabel referensi silang dan panjang aliran. pdftk yang gratis dan lintas platform melakukan ini dalam satu kali jalan:
pdftk hello-draft.pdf output hello.pdf
Ini mengurai objek Anda, menghitung ulang setiap offset byte, mengisi nilai /Length yang benar, menulis tabel xref dan trailer yang valid, dan mengeluarkan hello.pdf. Buka itu di viewer mana saja dan Anda mendapatkan satu halaman dengan "Hello, World!" dalam 36-poin Helvetica di dekat bagian atas. Qpdf melakukan pekerjaan yang sama, dan banyak viewer juga akan memperbaiki file yang sedikit cacat secara langsung. Intinya bersandar pada alat di sini bukan kemalasan; ini karena aritmatika offset adalah satu-satunya bagian dari format tanpa konten konseptual dan memiliki tingkat kesalahan tertinggi, sehingga mengotomatisasinya membiarkan struktur menjadi hal yang Anda pelajari
Mengapa ini berskala ke dokumen nyata
Tidak ada yang berubah tentang laporan seratus halaman dari bentuk yang baru saja Anda buat. Catalog masih berada di root, pohon halaman masih mengumpulkan halaman-halaman, dan setiap halaman masih menunjuk ke sumber dayanya dan content stream. Yang tumbuh adalah keluasannya, bukan tulang punggungnya: pohon halaman bercabang sehingga reader dapat melewati seluruh subpohon, content stream membawa ratusan operator alih-alih lima, font disematkan sebagai objek aliran sendiri dengan tabel lebar dan pengkodean, dan gambar datang sebagai aliran dengan filter khusus gambar. File modern juga cenderung mengemas banyak objek ke dalam aliran objek terkompresi dan mengganti tabel xref biasa dengan aliran referensi silang, itulah mengapa membuka PDF nyata di editor teks biasanya menampilkan dinding biner. Model di bawahnya identik dengan yang ada dalam file buatan tangan Anda. Untuk grafik objek yang lebih luas dan bagaimana catalog, pohon halaman, dan kamus sumber daya berhubungan dalam dokumen yang lebih besar, tur mendalam tentang struktur dokumen PDF melanjutkan dari sini, dan tinjauan struktur file mencakup pembaruan inkremental dan bagaimana trailer berrantai di seluruh revisi
Dari penulisan manual ke library
Mengetikkan objek secara manual adalah latihan pembelajaran, bukan teknik produksi. Segera setelah Anda membutuhkan font nyata, teks yang dibungkus, gambar, atau lebih dari sekadar halaman sederhana, pembukuan byte yang diperbaiki oleh pdftk untuk Anda menjadi seluruh pekerjaan, dan Anda menginginkan library yang memilikinya. Lima objek yang sama tetap ditulis, tetapi library menghitung setiap offset, mengelola kamus font dan sumber daya, dan mengompresi content stream tanpa Anda melacak satu byte pun. Dalam Delphi dan C++Builder, HotPDF Component mereduksi seluruh file ini menjadi segelintir panggilan: atur dokumennya, panggil BeginDoc, SetFont dan TextOut untuk menempatkan ucapan yang sama, kemudian EndDoc untuk menulis catalog, pohon halaman, xref, dan trailer yang benar. Memahami objek-objek di baliknya adalah apa yang memungkinkan Anda untuk bernalar tentang output ketika dokumen tidak dirender seperti yang Anda harapkan