Writer: ตัวซีเรียลไลซ์ PDF 2.0 + xref
ภาพรวมโดยสังเขป
หัวข้อที่มีชื่อว่า “ภาพรวมโดยสังเขป”โมดูล Writer ทำหน้าที่ซีเรียลไลซ์เอกสารเป็นไบต์ในรูปแบบ Portable Document Format (PDF) โดยเลือกกลยุทธ์เวอร์ชัน เขียนกราฟอ็อบเจ็กต์ แล้วสร้างโครงสร้างการอ้างอิงไขว้และเทรลเลอร์
การติดตั้ง
หัวข้อที่มีชื่อว่า “การติดตั้ง”composer require nextpdf/core:^3ภาพรวมเชิงแนวคิด
หัวข้อที่มีชื่อว่า “ภาพรวมเชิงแนวคิด”ใช้ PdfWriter เป็นจุดเริ่มต้น โดยส่งค่าอ็อบเจ็กต์ DocumentData ไปยัง write() เมธอดนี้จะคืนค่า PDF ฉบับสมบูรณ์เป็นสตริงไบต์ Writer จะประกอบกราฟอ็อบเจ็กต์ กำหนดหมายเลขอ็อบเจ็กต์ บันทึกออฟเซ็ตไบต์ และเขียนโครงสร้างการอ้างอิงไขว้เป็นขั้นตอนสุดท้าย
ทุกครั้งที่เรียกใช้ Writer จะใช้กลยุทธ์การซีเรียลไลซ์เพียงกลยุทธ์เดียว อินเทอร์เฟซ PdfSerializationStrategy นิยามเมธอดไว้สี่เมธอด ได้แก่ writeHeader(), getCatalogVersion(), writeXrefAndTrailer() และ usesXrefStream() กลยุทธ์ที่อิมพลีเมนต์อินเทอร์เฟซนี้มีสามแบบ Pdf20StreamStrategy เขียนเฮดเดอร์ %PDF-2.0 ตั้งค่าเวอร์ชันในแคตาล็อกเป็น /2.0 และสร้างการอ้างอิงไขว้แบบ สตรีม Pdf17TableStrategy เขียน %PDF-1.7 และการอ้างอิงไขว้แบบ ตาราง ดั้งเดิม Pdf14TableStrategy เขียน %PDF-1.4 และตารางการอ้างอิงไขว้ PdfWriter เลือกกลยุทธ์ด้วย match บน DocumentData::$outputProfile โดยค่าเริ่มต้นจะใช้ Pdf20StreamStrategy เป็นกลยุทธ์
enum PdfOutputProfile ระบุเวอร์ชันเป้าหมายทั้งสาม ได้แก่ Pdf20, Pdf17 และ Pdf14 enum นี้เปิดเผยเมธอด headerVersion(), catalogVersion(), allowsObjectStreams() และ usesXrefStream() โหมดการสอดคล้องสำหรับการจัดเก็บถาวรจะลบล้างโปรไฟล์ที่เลือกไว้ก่อนเลือกกลยุทธ์ ระหว่างการเขียน Pdf14FeatureGuard จะปฏิเสธคุณลักษณะของ PDF 2.0 เมื่อโปรไฟล์ที่ใช้งานอยู่คือ Pdf14
สตรีมการอ้างอิงไขว้จะแมปหมายเลขอ็อบเจ็กต์แต่ละหมายเลขไปยังออฟเซ็ตไบต์ของอ็อบเจ็กต์นั้น ตามที่นิยามไว้ใน ISO 32000-2 §7 การอัปเดตแบบเพิ่มหน่วยจะผนวกอ็อบเจ็กต์ใหม่เข้าที่ท้ายไฟล์ ตามที่นิยามไว้ใน ISO 32000-2 §7.5.6 Writer จะหลีกอักขระสตริงลิเทอรัลทั้งหมดผ่านพาธมาตรฐาน PdfStringEscaper::escapeLiteral() ซึ่งเป็นไปตามตารางการหลีกอักขระที่กำหนดไว้ใน ISO 32000-2 §7.3.4.2 (ADR-015)
Writer รองรับเอาต์พุตแบบกำหนดผลได้แน่นอน setDeterministicMode() จะตรึงตัวระบุอ็อบเจ็กต์และลำดับคีย์ในดิกชันนารี setReproducibleClock() จะตรึงไทม์สแตมป์ของเอกสาร เมื่อตั้งค่าการตรึงครบทั้งสองส่วนแล้ว อินพุตที่คงที่จะให้เอาต์พุตที่เหมือนกันในระดับไบต์ เมธอด writeChunked() จะคืนค่าเจเนอเรเตอร์ที่ทยอยส่ง PDF ออกมาเป็นชังก์ขนาดคงที่ ส่วน Streaming/StreamingPdfWriter จะเขียนทีละหน้าไปยังสตรีมที่ผู้เรียกจัดเตรียมไว้ สำหรับเอกสารที่เกินงบประมาณหน่วยความจำ
Linearizer จะเขียน PDF ที่เสร็จสมบูรณ์แล้วใหม่เป็นเค้าโครงแบบลิเนียไรซ์ โดยวางหน้าแรกไว้ที่ต้นไฟล์เพื่อให้โปรแกรมดูแสดงหน้านั้นได้ก่อนที่การดาวน์โหลดทั้งไฟล์จะเสร็จสมบูรณ์ shadowValidate() จะตรวจสอบการเขียนใหม่โดยไม่เปลี่ยนแปลงอินพุต
ข้อควรระวัง
PdfWriter.phpและLinearizer.phpมีความสำคัญอย่างยิ่งต่อออฟเซ็ตไบต์ และต่อกราฟอ็อบเจ็กต์ (โซนอันตรายตามที่ระบุในแมนิเฟสต์) อย่าเปลี่ยนแปลงการกำหนดหมายเลขอ็อบเจ็กต์ หรือการคำนวณออฟเซ็ต xref หากไม่ได้ใช้ชุดทดสอบ golden ของ Writer
พื้นผิว API
หัวข้อที่มีชื่อว่า “พื้นผิว API”| คลาส | เมธอดสำคัญ | บทบาท |
|---|---|---|
PdfWriter | write(DocumentData): string, writeChunked(DocumentData, int): Generator, setDeterministicMode(), setReproducibleClock(), setOutputColorProfile(), getLastXrefOffset(), getFileId() | ตัวซีเรียลไลซ์หลัก |
PdfSerializationStrategy (อินเทอร์เฟซ) | writeHeader(), getCatalogVersion(), writeXrefAndTrailer(), usesXrefStream() | สัญญาของกลยุทธ์ตามเวอร์ชัน |
Pdf20StreamStrategy | writeHeader() → %PDF-2.0, getCatalogVersion() → /2.0, usesXrefStream() → true | กลยุทธ์ xref-stream ของ PDF 2.0 |
Pdf17TableStrategy | writeHeader() → %PDF-1.7, ตาราง xref | กลยุทธ์ xref-table สำหรับ PDF 1.7 |
Pdf14TableStrategy | writeHeader() → %PDF-1.4, ตาราง xref | กลยุทธ์ xref-table สำหรับ PDF 1.4 |
PdfOutputProfile (enum) | Pdf20, Pdf17, Pdf14; headerVersion(), catalogVersion(), allowsObjectStreams() | ตัวเลือกเวอร์ชันเป้าหมาย |
PdfXrefWriter | generateFileId(), finalizeTrailerAndXref() | การสร้าง File ID + การสรุป trailer/xref |
Linearizer | linearize(string): string, shadowValidate(string): array | การเขียนใหม่เพื่อ Fast-web-view |
Streaming\StreamingPdfWriter | open(), newPage(), close() | ตัวเขียนแบบสตรีมมิงผ่านครั้งเดียว |
รัน composer docs:generate-api-php -- --module=Writer เพื่อสร้างตาราง PHPDoc แบบเต็ม
ตัวอย่างโค้ด — เริ่มต้นอย่างรวดเร็ว
หัวข้อที่มีชื่อว่า “ตัวอย่างโค้ด — เริ่มต้นอย่างรวดเร็ว”แหล่งที่มา: examples/02-pdf-factory.php (ไฟล์ตัวอย่าง)
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Writer\PdfWriter;
$writer = new PdfWriter();$pdfBytes = $writer->write($documentData);
file_put_contents('out.pdf', $pdfBytes);โปรไฟล์เริ่มต้นคือ PDF 2.0 เอาต์พุตจะเริ่มด้วย %PDF-2.0 และจบด้วยสตรีมการอ้างอิงไขว้
ตัวอย่างโค้ด — การใช้งานจริง
หัวข้อที่มีชื่อว่า “ตัวอย่างโค้ด — การใช้งานจริง”ตัวอย่างนี้ตรึงโหมดกำหนดผลแน่นอนและนาฬิกาแบบคงที่เพื่อให้ได้เอาต์พุตที่เหมือนกันในระดับไบต์ จากนั้นจึงสตรีมผลลัพธ์ออกมาเป็นชังก์ขนาดคงที่
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use DateTimeImmutable;use NextPDF\Writer\PdfWriter;use NextPDF\Writer\ReproducibleClock;
$pinned = new DateTimeImmutable('2026-01-01T00:00:00Z');
$writer = new PdfWriter();$writer->setDeterministicMode($pinned, 'nextpdf-fixed-file-id');$writer->setReproducibleClock(new ReproducibleClock($pinned));
$out = fopen('php://output', 'wb');foreach ($writer->writeChunked($documentData, chunkSize: 65536) as $chunk) { fwrite($out, $chunk);}fclose($out);กรณีขอบและข้อควรระวัง
หัวข้อที่มีชื่อว่า “กรณีขอบและข้อควรระวัง”- ในการเรียก
write()แต่ละครั้งจะมีกลยุทธ์ทำงานเพียงกลยุทธ์เดียว Writer จะรีเซ็ตกลยุทธ์จากโปรไฟล์ทุกครั้งที่เรียกใช้ การเรียกครั้งก่อนหน้าจะไม่ปล่อยให้เวอร์ชันของตนรั่วไหลออกมา - โหมดการสอดคล้องสำหรับการจัดเก็บถาวรจะลบล้างโปรไฟล์ที่ร้องขอ การสร้าง PDF/A-3 จะบังคับใช้ PDF 1.7 การสร้าง PDF/A-4 จะบังคับใช้ PDF 2.0
- เอาต์พุตที่เหมือนกันในระดับไบต์จำเป็นต้องใช้การตรึงทั้งสองส่วน ให้ตั้งค่าโหมดกำหนดผลแน่นอน และ นาฬิกาแบบทำซ้ำได้ การตรึงเพียงอย่างเดียวยังไม่เพียงพอ
writeChunked()จะคืนค่าเป็นเจเนอเรเตอร์และต้องบริโภคเจเนอเรเตอร์นี้จนหมด การอ่านเพียงบางส่วนจะทำให้ได้ PDF ที่ถูกตัดทอนและไม่ถูกต้องLinearizerจะเขียนออฟเซ็ตการอ้างอิงไขว้ใหม่ ให้รันshadowValidate()ก่อนในไปป์ไลน์ที่ยอมรับความล้มเหลวจากการเขียนใหม่ไม่ได้Pdf14TableStrategyเป็นfinal readonlyพาธ PDF 1.4 จะปฏิเสธคุณลักษณะของ PDF 2.0 ผ่านPdf14FeatureGuardโดยไม่ลดทอนคุณลักษณะเหล่านั้น
ประสิทธิภาพ
หัวข้อที่มีชื่อว่า “ประสิทธิภาพ”การซีเรียลไลซ์เป็นแบบเชิงเส้นตามจำนวนอ็อบเจ็กต์และขนาดไบต์รวม สตรีมการอ้างอิงไขว้จะเพิ่มการวนผ่านตารางอ็อบเจ็กต์อีกหนึ่งรอบ writeChunked() จะเก็บเอกสารที่ประกอบแล้วไว้ แต่ทยอยส่งออกมาเป็นสไลซ์ที่มีขอบเขตจำกัด ดังนั้นหน่วยความจำสูงสุดจึงเท่ากับขนาดเอกสารบวกหนึ่งชังก์ Streaming\StreamingPdfWriter จะไม่เก็บเอกสารทั้งฉบับไว้ ให้ใช้กับอินพุตที่ใหญ่กว่างบประมาณหน่วยความจำ งบประมาณของเวิร์กโหลดอ้างอิงคือ 1500 ms wall และ 64 MB peak การลิเนียไรซ์จะเพิ่มการวนผ่านทั้งหมดอีกหนึ่งรอบและการวนวัดผลอีกหนึ่งรอบ จึงควรกันงบประมาณสำหรับการลิเนียไรซ์ไว้อย่างชัดเจน
หมายเหตุด้านความปลอดภัย
หัวข้อที่มีชื่อว่า “หมายเหตุด้านความปลอดภัย”Writer ทำหน้าที่ซีเรียลไลซ์กราฟอ็อบเจ็กต์ในหน่วยความจำที่เชื่อถือได้ อินพุตของ Writer คือขอบเขตภัยคุกคามหลัก สตริงลิเทอรัลทุกตัวจะผ่าน PdfStringEscaper::escapeLiteral() มาตรฐาน (ADR-015) ดังนั้นไบต์ควบคุมที่ฝังอยู่จึงไม่สามารถหลุดออกจากโทเค็นสตริงได้ การเข้ารหัสลับทำงานผ่าน PdfEncryptionWriter และรายการ /Encrypt ในเทรลเลอร์ การเข้ารหัสลับด้วยคีย์สาธารณะจะถูกปฏิเสธพร้อมข้อยกเว้นที่ชัดเจน แทนที่จะถูกลดระดับลงอย่างเงียบ ๆ โหมดกำหนดผลแน่นอนและโหมดนาฬิกาแบบทำซ้ำได้จะลบช่องทางข้างเคียงด้านไทม์สแตมป์และลำดับออกจากเอาต์พุต ดู /modules/core/security/ สำหรับแบบจำลองภัยคุกคามของเอกสารและขอบเขตความเชื่อถือด้านการเข้ารหัสลับ
การสอดคล้อง
หัวข้อที่มีชื่อว่า “การสอดคล้อง”Writer สร้าง โครงสร้างไฟล์แบบ PDF 2.0 ได้แก่ เฮดเดอร์ %PDF-2.0 เวอร์ชันแคตาล็อก /2.0 สตรีมการอ้างอิงไขว้ และการหลีกอักขระสตริงลิเทอรัลตามตารางการหลีกอักขระใน ISO 32000-2 §7.3.4.2 สิ่งเหล่านี้เป็นข้อเท็จจริงของการอิมพลีเมนต์ หลักฐานอยู่ใน src/Writer/Pdf20StreamStrategy.php, src/Writer/PdfSerializationStrategy.php และการเลือกกลยุทธ์ใน src/Writer/PdfWriter.php พฤติกรรมนี้ได้รับการทดสอบโดย tests/Unit/Writer/ (192 การทดสอบ รวมถึงชุดทดสอบ Pdf20StreamStrategy, PdfXrefWriter และ Linearizer*) รวมถึงเบสไลน์ tests/Golden/PdfWriter/PdfWriterGoldenBaselineSmokeTest
นี่ ไม่ใช่ การอ้างว่าสอดคล้องกับ PDF 2.0 อย่างสมบูรณ์ การสอดคล้องกับ ISO 32000-2 อย่างสมบูรณ์เป็นคุณสมบัติของเอกสารฉบับสมบูรณ์ที่ได้รับการตรวจสอบโดยออราเคิลภายนอก ไม่ใช่คุณสมบัติของตัวซีเรียลไลซ์เพียงลำพัง การสอดคล้องแบบครบวงจรจะยืนยันเฉพาะในจุดที่ออราเคิลยืนยันเท่านั้น คือ tests/Integration/Accessibility/VeraPdfUa2GoldenTest จะตรวจสอบฟิกซ์เจอร์ที่สร้างขึ้นเทียบกับ veraPDF สำหรับ PDF/UA-2 และ tests/Standards/Profile/PdfRConformanceTest ครอบคลุมโปรไฟล์ PDF/R การทดสอบ golden ของ veraPDF จะ ข้ามเมื่อไม่มีไบนารี veraPDF อยู่บน runner ดังนั้นจึงเป็นเกตแบบออราเคิลที่เลือกเปิดได้ ไม่ใช่แบบไม่มีเงื่อนไข ตั้งค่า VERAPDF_BINARY เพื่อรันการทดสอบนี้ การเลือกโปรไฟล์การจัดเก็บถาวร (PDF/A-3 → PDF 1.7, PDF/A-4 → PDF 2.0) ตัดสินโดย ADR-011 และโหมดการสอดคล้อง และได้รับการตรวจสอบโดยชุดทดสอบการสอดคล้องใน /modules/core/conformance/ นอกเหนือจากโปรไฟล์ที่ออราเคิลรองรับเหล่านั้น ให้ระบุว่า Writer “สร้างโครงสร้าง PDF 2.0 ส่วนการสอดคล้องได้รับการตรวจสอบโดย veraPDF สำหรับโปรไฟล์ PDF/UA-2” แทนที่จะอ้างการสอดคล้องแบบไม่มีเงื่อนไข