ข้ามไปยังเนื้อหา

ContentStream: ตัวปล่อย content stream สำหรับ PDF

โมดูล ContentStream ปล่อยโอเปอเรเตอร์ marked-content ของ Portable Document Format (PDF) โดยเปิดและปิด structure tag และ artifact ติดตามความลึกของการซ้อน และคืนค่าบัฟเฟอร์โอเปอเรเตอร์

Terminal window
composer require nextpdf/core:^3

ContentStreamBuilder เป็นคลาสเดียวของโมดูลนี้ คลาสนี้สร้างเลเยอร์ marked-content สำหรับ content stream ของหน้า content stream เข้ารหัสเนื้อหาของหน้าเป็นลำดับโอเปอเรเตอร์ — ISO 32000-2 §8 จากนั้น builder จะปล่อยโอเปอเรเตอร์ marked-content เพื่อล้อมรอบเนื้อหานั้น

append() เพิ่มไบต์ของโอเปอเรเตอร์ดิบแบบตามตัวอักษร builder จะไม่ escape ข้อมูลขาเข้านี้ ผู้เรียกใช้ต้องรับผิดชอบความถูกต้องของข้อมูลนั้นเอง ใช้ขอบเขตนี้เมื่อ HTML pipeline และโมดูล Graphics ต้องแทรกโอเปอเรเตอร์ของตนเอง

beginTag() เปิดลำดับที่มี structure tag เมธอดนี้ปล่อยโอเปอเรเตอร์ BDC พร้อม property list ของ MCID ตาม ISO 32000-2 §14.6 endTag() ปล่อยโอเปอเรเตอร์ EMC ที่จับคู่กัน builder นับความลึกของการซ้อน หากเรียก endTag() โดยไม่มีลำดับที่เปิดอยู่ จะเกิดข้อยกเว้น PageLayoutException แทนการเขียน EMC ที่ไม่สมดุล

beginArtifact() เปิดลำดับ artifact ใช้ artifact สำหรับองค์ประกอบตกแต่งของการแบ่งหน้า — ส่วนหัว ส่วนท้าย เลขหน้า และเส้น — ที่ต้องอยู่นอกโครงสร้าง structure tree ตาม ISO 32000-2 §14.8.2.2 subtype เป็นหนึ่งในสี่ค่าของ ISO ได้แก่ Pagination Layout Page หรือ Background ควรใช้ enum ArtifactSubtype แบบกำหนดชนิด การโอเวอร์โหลดแบบสตริงจะถูกตรวจสอบกับ enum ดังนั้นค่าที่ไม่เป็นมาตรฐานจะล้มเหลวทันที

relabelTag() เขียนทับ tag ที่ปล่อยออกไปก่อนหน้าในตำแหน่งเดิม finish() คืนค่าบัฟเฟอร์ทั้งหมดและจะเกิดข้อยกเว้นหาก marked content ไม่สมดุล drain() คืนค่าบัฟเฟอร์ที่มีอยู่ในขณะนั้นโดยไม่ตรวจสอบความสมดุล สำหรับการสตรีมแบบเพิ่มทีละส่วน peek() คืนค่าบัฟเฟอร์โดยไม่บริโภคบัฟเฟอร์นั้น reset() ล้างสถานะ

เมธอดลายเซ็นบทบาท
append()append(string $raw): voidเพิ่มไบต์ของโอเปอเรเตอร์ดิบแบบตามตัวอักษร (ไม่มีการ escape)
beginTag()beginTag(string $structType, int $mcid): voidเปิดลำดับ BDC โครงสร้าง
endTag()endTag(): voidปิดลำดับชั้นในสุดด้วย EMC
beginArtifact()beginArtifact(ArtifactSubtype|string $type): voidเปิดลำดับ artifact
endArtifact()endArtifact(): voidปิด artifact ชั้นในสุด
getMarkedContentDepth()getMarkedContentDepth(): intคืนค่าความลึกของการซ้อนปัจจุบัน
relabelTag()relabelTag(string $old, string $new, int $mcid): voidเขียนทับ tag ที่ปล่อยออกไปแล้วในตำแหน่งเดิม
finish()finish(): stringคืนค่าบัฟเฟอร์ทั้งหมด และเกิดข้อยกเว้นหากไม่สมดุล
drain()drain(): stringคืนค่าบัฟเฟอร์โดยไม่ตรวจสอบความสมดุล
peek()peek(): stringคืนค่าบัฟเฟอร์โดยไม่บริโภคบัฟเฟอร์นั้น
reset()reset(): voidล้างสถานะทั้งหมด

รัน composer docs:generate-api-php -- --module=ContentStream เพื่อสร้างตาราง PHPDoc แบบเต็ม

<?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();

ใช้รูปแบบนี้เพื่อห่อย่อหน้าไว้ใน structure tag และห่อส่วนท้ายไว้ใน artifact รูปแบบนี้สตรีมบัฟเฟอร์แบบเพิ่มทีละส่วนด้วย 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() ไม่ escape ข้อมูลขาเข้า ส่งเฉพาะไบต์ของโอเปอเรเตอร์ที่ถูกต้องเท่านั้น builder ไว้วางใจผู้เรียกใช้
  • endTag() และ endArtifact() จะเกิดข้อยกเว้นเมื่อเกิด underflow อย่าปิดลำดับที่ไม่ได้เปิดอยู่
  • finish() ตรวจสอบความสมดุลและจะเกิดข้อยกเว้นเมื่อความลึกไม่เป็นศูนย์ drain() ไม่ตรวจสอบ ใช้ drain() เฉพาะสำหรับการสตรีมแบบเพิ่มทีละส่วนเท่านั้น
  • ตัวนับความลึกไม่แยกแยะระหว่าง tag กับ artifact EMC ปิดลำดับชั้นในสุดไม่ว่าจะเป็นชนิดใด ซ้อนลำดับตามลำดับที่เข้มงวด
  • การโอเวอร์โหลดแบบสตริงของ beginArtifact() จะถูกตรวจสอบกับ enum subtype ที่ไม่เป็นมาตรฐานจะล้มเหลวที่จุดเรียกใช้ ไม่ใช่ในเอาต์พุต
  • relabelTag() เขียนทับ tag ที่ปล่อยออกไปแล้ว ใช้ mcid ค่าเดียวกับที่ใช้เมื่อปล่อย tag นั้น

แต่ละการดำเนินการเป็นการต่อสตริงแบบ O(1) ยกเว้น relabelTag() ซึ่งเขียนทับแบบ O(buffer) โมดูลนี้ถือบัฟเฟอร์สตริงหนึ่งตัวและตัวนับความลึกชนิดจำนวนเต็มหนึ่งตัว โมดูลนี้ไม่ parse และจัดสรรหน่วยความจำเฉพาะบัฟเฟอร์เท่านั้น งบประมาณภาระงานอ้างอิงคือ 1500 ms ของเวลานาฬิกาและ 64 MB ของหน่วยความจำสูงสุด โมดูลนี้ยังคงใช้ทรัพยากรต่ำกว่าขีดจำกัดดังกล่าวมาก

append() คือขอบเขตความไว้วางใจ (trust boundary) builder เขียนไบต์แบบตามตัวอักษร ดังนั้นโค้ดต้นทางต้อง escape สตริงใด ๆ ที่ไปถึงโอเปอเรเตอร์ literal-string ตัว escape มาตรฐานคือ PdfStringEscaper::escapeLiteral() (ADR-015) อย่าส่งข้อความของผู้ใช้ที่ยังไม่ได้ escape ผ่าน append() การตรวจสอบความสมดุลใน endTag() endArtifact() และ finish() ป้องกันไม่ให้โครงสร้าง marked-content ที่ผิดรูปแบบไปถึง Writer ดู /modules/core/security/ สำหรับ threat model ของเอกสาร

โมดูลนี้ ปล่อย โครงสร้างโอเปอเรเตอร์ marked-content ที่สอดคล้องกับ ISO 32000-2: คู่ BDC/EMC พร้อม property list ของ MCID ตาม §14.6 และลำดับ artifact ตาม §14.8.2.2 ข้อความเหล่านี้เป็นข้อเท็จจริงของการนำไปปฏิบัติ หลักฐานคือ src/ContentStream/ContentStreamBuilder.php enum src/Accessibility/ArtifactSubtype.php และ tests/Unit/ContentStream/ContentStreamBuilderMarkedContentBalanceCoverageTest รวมถึง ContentStreamBuilderRelabelTagInvariantTest ข้อความเหล่านี้ไม่ใช่การกล่าวอ้างความสอดคล้องแบบ end-to-end ของ PDF/UA-2 หรือ PDF 2.0 oracle ภายนอกตรวจสอบโครงสร้าง tagged-PDF ที่โอเปอเรเตอร์เหล่านี้มีส่วนร่วม: tests/Integration/Accessibility/VeraPdfUa2GoldenTest ตรวจสอบ fixture ที่สร้างขึ้นกับ veraPDF สำหรับโปรไฟล์ PDF/UA-2 การทดสอบ oracle นั้น ข้ามเมื่อไม่มีไบนารี veraPDF ดังนั้นจึงเป็น gate แบบ opt-in ให้ระบุว่าโมดูลนี้ “produces marked-content structures; PDF/UA-2 conformance is validated by veraPDF” แทนการยืนยันความสอดคล้องแบบไม่มีเงื่อนไข