Lewati ke konten

ContentStream: pemancar content stream PDF

Modul ContentStream memancarkan operator marked-content Portable Document Format (PDF). Modul ini membuka dan menutup tag struktur serta artifact, melacak kedalaman penyarangan, lalu mengembalikan buffer operator.

Terminal window
composer require nextpdf/core:^3

ContentStreamBuilder adalah satu-satunya kelas dalam modul ini. Kelas ini membangun lapisan marked-content dari content stream sebuah halaman. Sebuah content stream mengodekan konten halaman sebagai rangkaian operator — ISO 32000-2 §8. Builder kemudian memancarkan operator marked-content di sekeliling konten tersebut.

append() menambahkan byte operator mentah secara verbatim. Builder tidak melakukan escaping pada masukan ini. Validitasnya menjadi tanggung jawab Anda. Gunakan batas ini ketika pipeline HTML dan modul Graphics perlu menyisipkan operator mereka sendiri.

beginTag() membuka rangkaian bertag struktur. Operasi ini memancarkan operator BDC dengan daftar properti MCID, sesuai ISO 32000-2 §14.6. endTag() memancarkan operator EMC yang sepadan. Builder menghitung kedalaman penyarangan. Jika Anda memanggil endTag() tanpa rangkaian yang terbuka, ia melempar PageLayoutException alih-alih menulis EMC yang tidak seimbang.

beginArtifact() membuka rangkaian artifact. Gunakan artifact untuk dekorasi paginasi — header, footer, nomor halaman, dan garis — yang harus berada di luar pohon struktur, sesuai ISO 32000-2 §14.8.2.2. Subtipe ini adalah salah satu dari empat nilai ISO: Pagination, Layout, Page, atau Background. Utamakan enum ArtifactSubtype yang sudah bertipe. Overload string divalidasi terhadap enum, sehingga nilai non-standar langsung ditolak.

relabelTag() menulis ulang tag yang sebelumnya telah dipancarkan di tempat. finish() mengembalikan buffer penuh dan melempar jika marked content tidak seimbang. drain() mengembalikan buffer sejauh ini tanpa pemeriksaan keseimbangan, untuk streaming bertahap. peek() mengembalikan buffer tanpa mengonsumsinya. reset() menghapus state.

MetodeTanda tanganPeran
append()append(string $raw): voidMenambahkan byte operator mentah secara verbatim (tanpa escaping)
beginTag()beginTag(string $structType, int $mcid): voidMembuka rangkaian struktur BDC
endTag()endTag(): voidMenutup rangkaian terdalam dengan EMC
beginArtifact()beginArtifact(ArtifactSubtype|string $type): voidMembuka rangkaian artifact
endArtifact()endArtifact(): voidMenutup artifact terdalam
getMarkedContentDepth()getMarkedContentDepth(): intMengembalikan kedalaman penyarangan saat ini
relabelTag()relabelTag(string $old, string $new, int $mcid): voidMenulis ulang tag yang telah dipancarkan di tempat
finish()finish(): stringMengembalikan buffer penuh; melempar jika tidak seimbang
drain()drain(): stringMengembalikan buffer tanpa pemeriksaan keseimbangan
peek()peek(): stringMengembalikan buffer tanpa mengonsumsinya
reset()reset(): voidMenghapus seluruh state

Jalankan composer docs:generate-api-php -- --module=ContentStream untuk menghasilkan tabel PHPDoc lengkap.

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\ContentStream\ContentStreamBuilder;
$builder = new ContentStreamBuilder();
$builder->beginTag('P', mcid: 0);
$builder->append("BT /F1 12 Tf 72 720 Td (Hello) Tj ET\n");
$builder->endTag();
$pageContent = $builder->finish();

Gunakan pola ini untuk membungkus sebuah paragraf dalam tag struktur dan sebuah footer dalam artifact. Pola ini mengalirkan buffer secara bertahap dengan drain().

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Accessibility\ArtifactSubtype;
use NextPDF\ContentStream\ContentStreamBuilder;
$builder = new ContentStreamBuilder();
$builder->beginTag('H1', mcid: 0);
$builder->append($titleOperators);
$builder->endTag();
$builder->beginArtifact(ArtifactSubtype::Pagination);
$builder->append($footerOperators);
$builder->endArtifact();
if ($builder->getMarkedContentDepth() !== 0) {
throw new RuntimeException('Unbalanced marked content before flush.');
}
$chunk = $builder->drain();
  • append() tidak melakukan escaping pada masukan. Berikan hanya byte operator yang valid. Builder mempercayai pemanggil.
  • endTag() dan endArtifact() melempar saat underflow. Jangan pernah menutup rangkaian yang tidak terbuka.
  • finish() memeriksa keseimbangan dan melempar ketika kedalaman bukan nol. drain() tidak memeriksa. Gunakan drain() hanya untuk streaming bertahap.
  • Penghitung kedalaman tidak membedakan tag dari artifact. EMC menutup rangkaian terdalam dari salah satu jenis. Sarangkan rangkaian dalam urutan yang ketat.
  • Overload string dari beginArtifact() divalidasi terhadap enum. Subtipe non-standar ditolak saat pemanggilan, bukan pada keluaran.
  • relabelTag() menulis ulang tag yang telah dipancarkan. Gunakan mcid yang sama dengan yang Anda gunakan untuk memancarkannya.

Setiap operasi berupa penambahan string O(1), kecuali relabelTag(), yang melakukan penulisan ulang O(buffer). Modul ini menyimpan satu buffer string dan satu penghitung kedalaman bertipe integer. Modul ini tidak melakukan parsing dan hanya mengalokasikan buffer. Anggaran beban kerja rujukan adalah 1500 ms wall dan 64 MB puncak. Modul ini tetap jauh di bawahnya.

append() adalah batas kepercayaan. Builder menulis byte secara verbatim, sehingga kode hulu harus melakukan escaping pada setiap string yang mencapai operator literal-string. Escaper kanonis adalah PdfStringEscaper::escapeLiteral() (ADR-015). Jangan pernah meneruskan teks dari pengguna yang tidak di-escape melalui append(). Pemeriksaan keseimbangan pada endTag(), endArtifact(), dan finish() mencegah pohon marked-content yang cacat mencapai Writer. Lihat /modules/core/security/ untuk model ancaman dokumen.

Modul ini memancarkan struktur operator marked-content yang konsisten dengan ISO 32000-2: pasangan BDC/EMC dengan daftar properti MCID sesuai §14.6, dan rangkaian artifact sesuai §14.8.2.2. Ini adalah fakta implementasi. Buktinya terdapat pada src/ContentStream/ContentStreamBuilder.php, enum src/Accessibility/ArtifactSubtype.php, serta tests/Unit/ContentStream/ContentStreamBuilderMarkedContentBalanceCoverageTest ditambah ContentStreamBuilderRelabelTagInvariantTest. Semua itu bukan klaim kesesuaian PDF/UA-2 atau PDF 2.0 secara menyeluruh. Sebuah oracle eksternal memvalidasi struktur tagged-PDF yang melibatkan operator-operator ini: tests/Integration/Accessibility/VeraPdfUa2GoldenTest memeriksa fixture yang dihasilkan terhadap veraPDF untuk profil PDF/UA-2. Tes oracle itu dilewati ketika biner veraPDF tidak tersedia, sehingga merupakan gate opsional. Nyatakan bahwa modul ini “menghasilkan struktur marked-content; kesesuaian PDF/UA-2 divalidasi oleh veraPDF” alih-alih menegaskan kesesuaian tanpa syarat.