ContentStream: ตัวปล่อย content stream สำหรับ PDF
ภาพรวมโดยย่อ
หัวข้อที่มีชื่อว่า “ภาพรวมโดยย่อ”โมดูล ContentStream ปล่อยโอเปอเรเตอร์ marked-content ของ Portable Document Format (PDF) โดยเปิดและปิด structure tag และ artifact ติดตามความลึกของการซ้อน และคืนค่าบัฟเฟอร์โอเปอเรเตอร์
การติดตั้ง
หัวข้อที่มีชื่อว่า “การติดตั้ง”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() ล้างสถานะ
พื้นผิว API
หัวข้อที่มีชื่อว่า “พื้นผิว API”| เมธอด | ลายเซ็น | บทบาท |
|---|---|---|
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” แทนการยืนยันความสอดคล้องแบบไม่มีเงื่อนไข