Lewati ke konten
getnextpdf.com

PDF adalah sebuah wadah: berkas tertanam dan data terasosiasi

Spec: ISO 32000-2, §7.11.4Spec: ISO 32000-2, §14.13Spec: ISO 19005-3, PDF/A-3

Kebanyakan orang membayangkan PDF sebagai tumpukan halaman. Itu bagian yang Anda lihat. Tetapi PDF juga adalah sebuah wadah, dan ia dapat membawa berkas-berkas lain secara utuh di dalamnya — sebuah spreadsheet, sebuah payload XML, dokumen sumber aslinya — dibundel ke dalam satu berkas tunggal yang sama yang Anda serahkan kepada orang lain.

Halaman ini menjelaskan bagaimana itu bekerja: embedded-file stream yang menyimpan byte-nya, name tree yang mendaftarnya, dan satu kunci yang memutuskan apakah sebuah lampiran hanya sekadar ada di situ atau benar-benar bermakna sesuatu.

Sebuah lampiran tak bertipe dan lampiran bertipe terlihat identik bagi manusia. Keduanya adalah berkas yang menumpang di dalam PDF, dan — pada mesin ini — keduanya terasosiasi dengan dokumen. Bedanya adalah salah satunya memberi tahu mesin untuk apa ia ada, sedangkan yang lain membiarkan relasinya kosong untuk ditebak mesin.

Perbedaan itu adalah segalanya bagi sebuah e-invoice hibrida. Sebuah platform pajak tidak membaca halaman faktur Anda; ia membaca XML yang Anda tanamkan. Jika XML itu dilampirkan sebagai gumpalan tak terbedakan alih-alih sebagai data faktur untuk dokumen yang terlihat, sebuah pembaca yang sesuai tidak punya cara yang andal untuk tahu bahwa itu adalah payload yang harus diproses. Halamannya terlihat sempurna. Fakturnya ditolak. Kegagalan itu datang beberapa hari kemudian, dengan sebuah pembayaran yang tertahan di belakangnya.

Membuat relasinya benar, di lapisan yang menghasilkan berkas, jauh lebih murah daripada menemukannya satu faktur yang ditolak pada satu waktu.

  • Sebuah PDF dapat menanamkan byte berkas apa pun sebagai embedded file stream (Spec: ISO 32000-2, §7.11.4). Stream itu membawa datanya ditambah sebuah parameters dictionary kecil: ukuran asli, tanggal, dan sebuah checksum.
  • Berkas tertanam dikatalogkan dalam EmbeddedFiles name tree, sehingga sebuah pembaca dapat mengenumerasinya berdasarkan nama tanpa memindai seluruh dokumen.
  • Sebuah associated file melangkah satu langkah lebih jauh: ia mendeklarasikan sebuah AFRelationship (Spec: ISO 32000-2, §7.11.3) — salah satu dari delapan nilai standar (Source, Data, Alternative, Supplement, EncryptedPayload, FormData, Schema, Unspecified), atau sebuah nilai kustom — yang menyatakan bagaimana berkas itu berhubungan dengan konten tempat ia dilampirkan.
  • Relasi bertipe itu adalah mekanisme di balik e-invoice hibrida (ZUGFeRD / Factur-X) dan lampiran PDF/A-3 (Spec: ISO 19005-3, PDF/A-3).
  • NextPDF mendukung primitif wadah mentah di inti: embedFile() dan embedFileFromString() dengan sebuah relasi eksplisit. Edisi Advanced menambahkan embedder e-invoice EN 16931 / ZUGFeRD / Factur-X khusus di atas primitif-primitif ini.

Anggaplah ini sebagai dua lapisan yang ditumpuk satu di atas yang lain.

Lapisan bawah adalah penyimpanan. Sebuah embedded file stream (Spec: ISO 32000-2, §7.11.4) adalah byte berkas asli yang dibungkus dalam sebuah objek stream PDF, dengan sebuah parameters dictionary yang mencatat ukuran asli, tanggal modifikasi, dan sebuah checksum dari data yang tidak terkompresi. Stream itu dijangkau melalui sebuah file specification dictionary yang /EF dictionary-nya menunjuk ke embedded file stream — stream itu sendiri tidak membawa /EF. Sebuah pembaca dapat menarik kembali berkas itu byte demi byte. Agar berkas-berkas ini dapat ditemukan, document catalog menyimpan sebuah EmbeddedFiles name tree — sebuah peta terurut dari sebuah nama ke setiap file specification — sehingga sebuah penampil dapat mendaftar “ini ada 3 berkas di dalam PDF ini” tanpa menelusuri setiap halaman.

Lapisan atas adalah makna. Dengan sendirinya, sebuah berkas tertanam hanya sekadar hadir. Mekanisme associated-files (Spec: ISO 32000-2, §14.13) melampirkan sebuah berkas ke sesuatu — seluruh dokumen, sebuah halaman, sebuah objek grafis — dan menstempelnya dengan sebuah AFRelationship. ISO 32000-2 mendefinisikan sebuah kosakata kecil berisi delapan nilai standar (Spec: ISO 32000-2, §7.11.3), dan mengizinkan nilai kustom juga; setiap nilai standar menjawab sebuah pertanyaan yang presisi:

AFRelationshipApa yang ia tegaskan tentang berkas
SourceIni adalah materi sumber yang darinyalah konten yang terlihat dihasilkan (misalnya, dokumen word-processor aslinya).
DataIni adalah data terstruktur yang terikat pada konten yang terlihat — kasus kanonisnya adalah XML faktur di balik halaman faktur yang dirender.
AlternativeIni adalah representasi alternatif dari konten yang sama (misalnya, sebuah versi audio atau video).
SupplementIni adalah materi pelengkap yang memperluas konten tetapi bukan bagian darinya.
EncryptedPayloadBerkas tertanam adalah sebuah payload terenkripsi yang dibungkus PDF sebagai gumpalan buram.
FormDataBerkas adalah data formulir (FDF, XFDF, atau sebuah payload formulir XML).
SchemaBerkas adalah sebuah skema yang menggambarkan struktur dari sebuah berkas Data (misalnya, sebuah XSD untuk data XML atau sebuah JSON Schema).
UnspecifiedRelasi sengaja tidak dinyatakan. Jujur, tetapi ia tidak memberi tahu mesin apa pun.

Di luar kedelapan nilai ini, standar juga mengizinkan nilai relasi kustom yang spesifik untuk aplikasi, sehingga kosakatanya dapat diperluas alih-alih tetap.

Sebuah associated file didefinisikan oleh dua hal yang bekerja bersama, bukan satu kunci saja. Asosiasi /AF mengikat file specification ke sebuah bagian dokumen; kunci AFRelationship dalam file specification kemudian menyatakan relasi semantiknya. Entri /AF pada titik asosiasi (document catalog, sebuah halaman, atau sebuah objek) adalah sebuah larik — larik itu memuat satu atau lebih file specification dictionary, biasanya sebagai referensi tidak langsung; /AF bukan sebuah referensi tunggal. Sebuah associated file tingkat-dokumen adalah file specification yang tercantum dalam larik /AF document catalog, yang membawa AFRelationship-nya. Tandai spreadsheet itu Unspecified dan Anda telah mengasosiasikannya dengan dokumen tetapi tidak memberi tahu mesin apa pun tentang mengapa. Tandai spreadsheet yang sama dengan Data dan Anda telah memberi tahu setiap pembaca yang sesuai apa ia dan untuk apa ia. Byte-nya sama. Semantiknya tidak.

Inilah sebabnya kasus e-invoice bukanlah “lampirkan sebuah berkas XML”. Ia adalah “tanamkan XML ini sebagai associated file Data untuk dokumen ini, di dalam sebuah pembawa PDF/A-3 yang sesuai” — dengan validitas faktur dan penerimaan hukum tetap menjadi pemeriksaan terpisah yang tidak dilakukan pembawanya. Alurnya memiliki empat tahap, dan urutannyalah yang menjaga semuanya tetap benar.

  1. Store the bytesThe file is wrapped in an embedded file stream with its size, dates, and a checksum (ISO 32000-2 §7.11.4).
  2. Register it by nameThe file specification is added to the EmbeddedFiles name tree so a reader can enumerate attachments without scanning the document.
  3. Declare the relationshipAn AFRelationship value (one of the eight standard values such as Source or Data) marks how the file relates to the content, associated at the document level (ISO 32000-2 §14.13.3).
  4. Make it archivalA PDF/A-3 carrier permits the embedded payload to ride inside one conforming archival PDF/A document; invoice validity and legal acceptance remain separate checks (ISO 19005-3).
How a typed attachment becomes a hybrid file end to end: the engine stores the bytes, registers the file by name, declares the relationship, and the archival profile permits it all to ride inside one conforming archival document.

Tahap keempat itulah sebabnya PDF/A-3 ada sebagai profil tersendiri. Profil arsip sebelumnya membatasi apa yang dapat ditanamkan; PDF/A-3 (Spec: ISO 19005-3, PDF/A-3) adalah bagian yang mengizinkan berkas dengan format apa pun untuk menumpang di dalam sebuah dokumen arsip yang sesuai. Ia mengizinkan payload tertanam — ia tidak memvalidasi payload itu atau memberikan status hukum. Tanpanya, faktur hibrida — satu berkas yang sekaligus merupakan halaman yang dibaca seseorang dan data yang diuraikan sistem pajak — tidak akan bisa menjadi sebuah dokumen arsip PDF/A yang sesuai sama sekali; apakah faktur itu valid dan secara hukum diterima tetap merupakan pertanyaan terpisah. Embedder e-invoice khusus yang ditambahkan edisi Advanced adalah jahitan kenyamanan tepat di atas hal ini: ia menanamkan payload, menyetel relasi ke Data, dan mendaftarkannya dengan benar, sehingga Anda tidak perlu merakit pipa wadahnya dengan tangan. Mekanika fakturasi dan pengarsipan yang lebih dalam ada pada dua halaman tetangga yang ditautkan di bawah; halaman ini adalah tentang wadah yang menjadi pijakan keduanya.

Sebuah program kecil dan lengkap. Dua panggilan yang penting adalah pembeda antara associated file tak bertipe dan yang bertipe — dan relasinya adalah sebuah argumen eksplisit yang sebaiknya Anda setel. Pada mesin ini, kedua panggilan menghasilkan sebuah associated file: embedFile() dan embedFileFromString() selalu mendaftarkan file specification dalam larik /AF document catalog, sehingga satu-satunya hal yang diubah relasi adalah apa arti asosiasi itu. Ia berdefault ke Unspecified, yang mengasosiasikan berkas tetapi tidak memberi tahu mesin apa pun tentang mengapa; untuk sebuah payload e-invoice Anda menyetelnya ke Data agar sebuah pembaca dapat menemukannya.

<?php
declare(strict_types=1);
use NextPDF\Core\Document;
use NextPDF\Navigation\AFRelationship;
$document = Document::createStandalone();
$document->addPage();
$document->setFont('helvetica', 'B', 16);
$document->cell(0, 12, 'Invoice INV-2026-0042', newLine: true);
// An UNTYPED associated file: the bytes are embedded AND the file spec is
// added to the document catalog's /AF array, but the relationship says
// nothing about why. A reader can open it; a machine cannot tell its role.
// The relationship is left Unspecified (its default); the second argument is
// the human-readable description. embedFile accepts the AFRelationship enum.
$document->embedFile(
'/srv/invoices/INV-2026-0042-source.docx',
'Original source document',
AFRelationship::Unspecified,
);
// A TYPED associated file: the invoice XML is declared as the DATA behind
// the visible page. This is the relationship a hybrid e-invoice reader
// looks for — the same intent the dedicated e-invoice embedder sets.
// embedFileFromString takes the data, a filename, a description, and a
// relationship as a PDF-name string ('/Data').
$invoiceXml = $generateCiiXml(); // your ERP authors this; the engine never does
$document->embedFileFromString(
$invoiceXml,
'factur-x.xml',
'Factur-X invoice data',
'/Data',
);
$bytes = $document->getPdfData();

Relasi '/Data' tidak dapat disalahartikan. Lampiran pertama — yang dibiarkan Unspecified — tetap terasosiasi, hanya tanpa makna yang dinyatakan. Untuk kedua panggilan, mesin menulis embedded file stream, menambahkan berkas ke EmbeddedFiles name tree, mencantumkan file specification-nya dalam larik /AF document catalog, dan mencatat relasi yang Anda nyatakan — ia tidak memilihkannya untuk Anda. Mesin ini tidak memiliki mode name-tree-only: setiap berkas yang Anda tanamkan dengan cara ini adalah associated file tingkat-dokumen, sehingga relasi adalah satu-satunya tuas yang Anda kendalikan.

Asumsi yang sering muncul adalah bahwa “embedded” dan “associated” adalah dua kata untuk hal yang sama. Mereka bukan. Embedded adalah tentang penyimpanan — byte-nya berada di dalam PDF. Associated adalah tentang pengikatan — file specification tercantum dalam sebuah larik /AF pada sebuah bagian dokumen, dan ia membawa sebuah AFRelationship. Dalam model PDF abstrak, sebuah berkas dapat ditanamkan ke dalam name tree tanpa pernah terasosiasi; jalur embedFile() NextPDF tidak meninggalkannya begitu saja — ia selalu menulis asosiasi /AF — sehingga untuk mesin ini, pertanyaan terbukanya tidak pernah apakah sebuah berkas terasosiasi melainkan apa yang dinyatakan relasinya.

Jebakan kedua: mengasumsikan sebuah penampil akan “mencari tahu” lampiran mana yang merupakan fakturnya. Sebuah pembaca yang sesuai tidak seharusnya menebak. Ia mencari berkas yang relasinya menyatakan Data. Biarkan relasinya Unspecified dan Anda telah mengasosiasikan payload itu sambil tidak memberi tahu mesin apa pun yang berguna tentang perannya.

Mekanisme wadah ini kuat dengan cara yang layak untuk dikatakan secara jujur: embedFile() membaca path apa pun yang dapat dibaca oleh proses PHP. Itu adalah fiturnya — dan itu juga batasnya. Mesin melampirkan byte yang diberikan kepadanya; ia tidak, dan tidak dapat, memutuskan untuk Anda apakah sebuah path adalah path yang Anda maksudkan untuk diekspos.

Embedding a file from a caller-supplied path — edition availability
EditionAvailability
Core

embedFile() reads any path the PHP process has access to and embeds its bytes verbatim. Validating that the path is safe and intended — not a user-controlled value, a traversal, or a secret outside the document’s scope — is the integrator’s responsibility. This is a documented security contract, not an oversight: the engine will not silently guess which paths are legitimate, because that guess belongs to your application, which knows the trust boundary the engine cannot see. Pass attacker-influenced bytes through a string with embedFileFromString() so the path layer is never in play.

ProNot in this edition
EnterpriseNot in this edition

Dua batas lagi yang layak dinyatakan secara terus terang:

  • Menanamkan bukan memvalidasi. Mesin membawa byte yang Anda berikan kepadanya. Apakah XML tertanam itu adalah payload faktur yang sesuai adalah pertanyaan terpisah, yang dijawab oleh sebuah validator — lihat halaman fakturasi.
  • Sebuah lampiran bertipe bukanlah berkas arsip yang sesuai dengan sendirinya. Menjadikan berkas hibrida itu sebuah dokumen PDF/A-3 yang legal memerlukan mode arsip dan sebuah pemeriksaan kesesuaian yang independen — lihat halaman pengarsipan.
  • Invoices and e-invoicing — kasus penggunaan yang dimungkinkan mekanisme ini: sebuah PDF hibrida yang membawa sebuah faktur yang dapat dibaca mesin sebagai associated file Data-nya.
  • Archival and PDF/A — mengapa pembawanya adalah berkas PDF/A-3 dan apa yang dijanjikan maupun tidak dijanjikan oleh kesesuaian.
  • The anatomy of a PDF file — di mana name tree dan document catalog berada dalam struktur berkas.
  • Streams and filters — bagaimana byte sebuah berkas tertanam disimpan dan dikompresi di dalam sebuah objek stream.
  • Embedded file stream — sebuah objek stream PDF yang menampung byte dari sebuah berkas eksternal, dengan sebuah parameters dictionary yang mencatat ukuran asli, tanggal, dan sebuah checksum (ISO 32000-2 §7.11.4).
  • EmbeddedFiles name tree — peta terurut dalam document catalog yang mendaftarkan berkas tertanam berdasarkan nama, sehingga sebuah pembaca dapat mengenumerasi lampiran tanpa memindai seluruh dokumen.
  • Associated file — sebuah berkas tertanam yang terikat ke sebuah bagian dokumen oleh asosiasi /AF (pada document catalog, sebuah halaman, atau sebuah objek) dan membawa sebuah AFRelationship yang menyatakan bagaimana ia berhubungan dengan konten itu; kasus tingkat-dokumen — file specification dalam larik /AF katalog — adalah yang menjadi pusat halaman ini (ISO 32000-2 §14.13.3).
  • AFRelationship — kunci file-specification yang nilainya menamai relasi (ISO 32000-2 §7.11.3). Ia mengambil salah satu dari delapan nilai standar (Source, Data, Alternative, Supplement, EncryptedPayload, FormData, Schema, Unspecified) atau sebuah nilai kustom; Data adalah nilai yang dipakai sebuah payload e-invoice hibrida.
  • PDF/A-3 — profil arsip ISO 19005-3 yang mengizinkan berkas dengan format apa pun untuk ditanamkan, memungkinkan sebuah dokumen hibrida yang sesuai.
  • Hybrid invoice — satu berkas PDF yang sekaligus merupakan halaman yang dapat dibaca manusia dan sebuah payload faktur tertanam yang dapat dibaca mesin.