Document: DPart การแยก / รวม และส่วนขยายของผู้จำหน่าย
ภาพรวมโดยย่อ
หัวข้อที่มีชื่อว่า “ภาพรวมโดยย่อ”โมดูล Document ทำงานกับไฟล์ Portable Document Format (PDF) ทั้งไฟล์ ไม่ใช่เนื้อหาระดับหน้า โมดูลนี้สร้างลำดับชั้น Document Part ที่เวิร์กโฟลว์ภายใต้การกำกับดูแลใช้แนบเมทาดาทา แยก PDF เป็นเซกเมนต์ตามช่วงหน้า รวม PDF หลายไฟล์เป็นไฟล์เดียว และลงทะเบียนส่วนขยายสำหรับนักพัฒนาในแคตาล็อกเอกสาร
การติดตั้ง
หัวข้อที่มีชื่อว่า “การติดตั้ง”composer require nextpdf/core:^3ภาพรวมเชิงแนวคิด
หัวข้อที่มีชื่อว่า “ภาพรวมเชิงแนวคิด”โมดูลนี้อยู่เหนือชั้นเนื้อหาระดับหน้า ขณะที่ Graphics และ Content ปล่อยตัวดำเนินการออกมา Document จะทำงานในระดับโครงสร้าง ได้แก่ แผนผังหน้า แคตาล็อกเอกสาร และแผนผัง Document Part
คำว่า Document Part (DPart) หมายถึงพาร์ทิชันเชิงตรรกะของ PDF ISO 32000-2 กำหนดลำดับชั้น DPart ซึ่งโหนดต่าง ๆ เก็บ Document Part Metadata (DPM) ไว้ เวิร์กโฟลว์ภายใต้การกำกับดูแล เช่น งานเภสัชกรรม งานกฎหมาย หรืองานจัดเก็บถาวร จึงเชื่อมโยงเมทาดาทากับช่วงย่อยของหน้าได้ แทนที่จะผูกกับทั้งไฟล์ — §14.12 DPart เป็นโหนดแบบ readonly ที่เปลี่ยนค่าไม่ได้ โหนดใบอ้างอิงชุดดัชนีหน้าที่ต่อเนื่องกัน และโหนดระดับกลางจัดกลุ่มโหนดลูก DPart เป็นแผนผัง DPartRoot คือรากของแผนผังที่ Writer ซีเรียลไลซ์ รายการ /Start และ /End ของโหนดใบเป็นการอ้างอิงทางอ้อมไปยังอ็อบเจกต์หน้า ไม่ใช่จำนวนเต็มดัชนีหน้า — §14.12 DPart::resolveWithPageObjects() รีโซลฟ์รายการเหล่านี้เทียบกับแมปดัชนีหน้า→หมายเลขอ็อบเจกต์ที่ writer จัดเตรียมให้ และคืนค่ารูปแบบการอ้างอิง /Start (รวมถึง /End หากมี) เมธอดนี้จะถอยไปใช้รูปแบบจำนวนเต็มเฉพาะในเส้นทางทดสอบที่ไม่มีแมปให้ใช้งานเท่านั้น
PdfMerger และ PdfSplitter เป็นส่วนต่อประสานสำหรับประกอบเอกสาร PdfMerger รวมอ็อบเจกต์หน้าจาก PDF อินพุตหลายไฟล์ กำหนดหมายเลขอ็อบเจกต์ใหม่เพื่อหลีกเลี่ยงการชนกัน และสร้างแผนผังหน้ากับตารางอ้างอิงไขว้ขึ้นใหม่เป็นชุดเดียว แผนผังหน้าที่สร้างขึ้นเป็นโหนด Pages แบบสมดุล พร้อม Kids และ Count รวมถึงโมเดลแอตทริบิวต์ที่สืบทอดได้ซึ่ง PDF กำหนดไว้สำหรับโหนดแผนผังหน้า — §7.7.3 PdfSplitter ทำงานในทิศทางตรงกันข้าม โดยแยกช่วงหน้าเป็นอ็อบเจกต์ SplitDocument ที่เป็นอิสระ PageRange คือ value object ที่ทั้งสองคลาสใช้ โดยเริ่มนับจาก 1 ตรวจสอบความถูกต้องของขอบเขต และตอบสนองต่อ contains(), count() และ toArray() ได้
VendorExtensionRegistry, ExtensionsDictionary และ DeveloperExtensionEntry เป็นโมเดลของดิกชันนารีส่วนขยายสำหรับนักพัฒนาในแคตาล็อกเอกสาร เอนจินใช้ดิกชันนารีนี้เพื่อประกาศระดับส่วนขยายของผู้จำหน่ายที่อยู่นอกเหนือข้อกำหนดพื้นฐาน รีจิสทรีจะปฏิเสธการลงทะเบียนซ้ำของพรีฟิกซ์ผู้จำหน่ายเดียวกันที่ขัดแย้งกันด้วย VendorExtensionRegistryConflictException CollectionDictionary และ CollectionSort เป็นโมเดลของรายการแคตาล็อก collection ของ PDF (portable collection หรือ portfolio)
ส่วนต่อประสาน API
หัวข้อที่มีชื่อว่า “ส่วนต่อประสาน API”| คลาส | เมธอดสำคัญ | บทบาท |
|---|---|---|
DPart | isLeaf(), hasMetadata(), resolveWithPageObjects(), write() | โหนด Document Part ที่เปลี่ยนแปลงค่าไม่ได้ (@since 1.12.0) |
DPartRoot | isEmpty(), write() | รากของแผนผัง DPart ที่ Writer ทำการซีเรียลไลซ์ (@since 1.12.0) |
PdfMerger | merge(array $pdfFiles, int $maxFiles = 100, int $maxTotalBytes = 200_000_000), append() | การรวม PDF หลายไฟล์พร้อมกำหนดหมายเลขอ็อบเจกต์ใหม่ (@since 1.9.0) |
PdfSplitter | split(), splitEvery(), extractPages() | การแยกตามช่วงหน้าเป็น SplitDocument (@since 1.9.0) |
PageRange | contains(int $page), count(), toArray() | value object สำหรับช่วงหน้าที่เริ่มนับจาก 1 |
MergeResult / SplitResult | isValid(), count(), document(), totalOutputSize() | อ็อบเจกต์ผลลัพธ์ของการประกอบเอกสาร |
VendorExtensionRegistry | การลงทะเบียนส่วนขยาย | รีจิสทรีส่วนขยายสำหรับนักพัฒนา (@since 2.2.0) |
ExtensionsDictionary | withEntry(), entries(), isEmpty(), toPdfDictionary() | ตัวสร้างดิกชันนารีส่วนขยายที่เปลี่ยนแปลงค่าไม่ได้ (@since 2.0.0) |
CollectionDictionary | toPdfDictionary() | รายการแคตาล็อกของ portable-collection (@since 2.0.0) |
เรียกใช้ composer docs:generate-api-php -- --module=Document เพื่อสร้างตาราง PHPDoc ฉบับเต็ม
ตัวอย่างโค้ด — การเริ่มต้นใช้งานอย่างรวดเร็ว
หัวข้อที่มีชื่อว่า “ตัวอย่างโค้ด — การเริ่มต้นใช้งานอย่างรวดเร็ว”แยก PDF เป็นเอกสารหน้าเดียว จากนั้นตรวจสอบผลลัพธ์
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Document\PageRange;use NextPDF\Document\PdfSplitter;
$splitter = new PdfSplitter();$result = $splitter->splitEvery(file_get_contents('/srv/in/report.pdf'), 1);
foreach (range(0, $result->count() - 1) as $index) { $segment = $result->document($index); file_put_contents("/srv/out/page-{$index}.pdf", $segment->pdfData);}
$singlePage = $splitter->extractPages( file_get_contents('/srv/in/report.pdf'), new PageRange(2, 4),);ตัวอย่างโค้ด — ระดับโปรดักชัน
หัวข้อที่มีชื่อว่า “ตัวอย่างโค้ด — ระดับโปรดักชัน”รวม PDF หลายไฟล์ภายในงบประมาณอินพุตที่กำหนดอย่างชัดเจน จากนั้นตรวจสอบความถูกต้องของผลลัพธ์ก่อนเขียนเอาต์พุตที่รวมเสร็จแล้ว
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Document\PdfMerger;use NextPDF\Exception\PageLayoutException;
/** @var list<string> $sources Raw PDF byte strings to combine. */$sources = array_map( static fn (string $path): string => file_get_contents($path), glob('/srv/batch/*.pdf') ?: [],);
$merger = new PdfMerger();
try { // Bound the merge: at most 50 files, 100 MB total. $merged = $merger->merge($sources, maxFiles: 50, maxTotalBytes: 100_000_000);} catch (PageLayoutException $e) { throw new \RuntimeException('Merge rejected: empty or invalid input set.', previous: $e);}
if (!$merged->isValid()) { throw new \RuntimeException('Merged document failed structural validation.');}
file_put_contents('/srv/out/combined.pdf', $merged->pdfData);กรณีขอบและข้อควรระวัง
หัวข้อที่มีชื่อว่า “กรณีขอบและข้อควรระวัง”PdfMerger::merge()และPdfSplitter::split()บังคับใช้ขอบเขตอินพุตผ่านResourceGuardอินพุตที่มีจำนวนไฟล์มากเกินไปหรือจำนวนไบต์มากเกินไปจะทำให้เกิดข้อยกเว้น แทนที่จะถูกตัดทอนแบบเงียบ ๆ กำหนดmaxFiles/maxTotalBytesอย่างตั้งใจให้เหมาะกับเวิร์กโหลดที่ใช้งาน- รายการไฟล์ที่ว่างเปล่าหรือรายการช่วงที่ว่างเปล่าจะทำให้เกิด
PageLayoutExceptionให้ถือว่ากรณีเหล่านี้เป็นข้อผิดพลาดด้านการกำหนดค่า ไม่ใช่ผลลัพธ์ว่าง PageRangeเริ่มนับจาก 1 และนับรวมปลายช่วง โหนดใบDPartมีรายการpagesเป็นดัชนีหน้าที่เริ่มนับจาก 0 นามธรรมทั้งสองใช้ฐานดัชนีต่างกัน ให้แปลงค่าอย่างชัดเจนเมื่อข้ามระหว่างนามธรรมทั้งสองDPartเป็นreadonlyหากต้องการสร้างแผนผังรูปแบบอื่น ให้สร้างโหนดใหม่แทนการเปลี่ยนแปลงโหนดที่มีอยู่resolveWithPageObjects()จะคืนค่ารูปแบบสำรองที่เป็นดัชนีจำนวนเต็มเฉพาะเมื่อแมปอ็อบเจกต์หน้าว่างเปล่าเท่านั้น อย่าพึ่งพาเส้นทางดังกล่าวในเอาต์พุตระดับโปรดักชันVendorExtensionRegistryจะทำให้เกิดVendorExtensionRegistryConflictExceptionสำหรับพรีฟิกซ์ผู้จำหน่ายที่ซ้ำกัน ลงทะเบียนแต่ละพรีฟิกซ์เพียงครั้งเดียว
ประสิทธิภาพ
หัวข้อที่มีชื่อว่า “ประสิทธิภาพ”การแยกและการรวมขยายขนาดเชิงเส้นตามจำนวนหน้า โดยภาระหลักอยู่ที่การแจงและการกำหนดหมายเลขอ็อบเจกต์ใหม่ ไม่ใช่การจัดการบันทึกภายในของโมดูลเอง เวิร์กโหลดอ้างอิงเริ่มต้นอยู่ภายในงบประมาณเวลา wall-clock 1500 ms / หน่วยความจำสูงสุด 64 MB การรวมขนาดใหญ่ถูกจำกัดเป็นหลักด้วยจำนวนไบต์อินพุตทั้งหมด ขอบเขต maxTotalBytes ช่วยควบคุมหน่วยความจำสูงสุดให้อยู่ในขอบเขต โปรไฟล์ความสามารถในการทำซ้ำเป็นแบบ structural โดย PDF ที่ผ่านการรวมหรือแยกจะมี trailer และ /ID ที่สร้างขึ้นใหม่ ดังนั้นการรันสองครั้งจึงเท่ากันในเชิงโครงสร้างแต่ไม่เหมือนกันในระดับไบต์
หมายเหตุด้านความปลอดภัย
หัวข้อที่มีชื่อว่า “หมายเหตุด้านความปลอดภัย”PdfMerger::merge() และ PdfSplitter::split() รับไบต์ PDF ที่ไม่น่าเชื่อถือเข้ามาประมวลผล ก่อนการแจง ทั้งสองจะส่งอินพุตผ่าน ResourceGuard::assertSize() / assertCount() เพื่อจำกัดการโจมตีปฏิเสธการให้บริการที่ขยายผลจากการคลายบีบอัดหรือจำนวนอ็อบเจกต์ ให้กำหนดอาร์กิวเมนต์ maxFiles, maxTotalBytes และ maxBytes ให้รัดกุมตามสภาพแวดล้อมการดีพลอย แทนการพึ่งพาค่าเริ่มต้น ให้ถือว่า PDF อินพุตทุกไฟล์เป็นภัยคุกคาม เมื่อแหล่งข้อมูลมาจากผู้ใช้ ให้รันการประกอบเอกสารแบบแบตช์ในเวิร์กเกอร์ที่ถูกจำกัดทรัพยากร ดูแบบจำลองภัยคุกคามของเอนจินใน /modules/core/security/ สำหรับขอบเขตความเชื่อถือ
ความสอดคล้องตามมาตรฐาน
หัวข้อที่มีชื่อว่า “ความสอดคล้องตามมาตรฐาน”แผนผัง DPart ที่โมดูลนี้สร้างขึ้นเป็นไปตามแบบจำลอง Document Part ใน ISO 32000-2 §14.12 โดยปล่อยรายการ /Start และ /End ของโหนดใบเป็นการอ้างอิงทางอ้อมไปยังอ็อบเจกต์หน้าภายใต้ข้อกำหนดเดียวกัน เอาต์พุตที่รวมแล้วใช้โครงสร้างโหนดแผนผังหน้าตามที่กำหนดไว้ใน §7.7.3 ข้อความเหล่านี้เป็นข้อเท็จจริงของการนำไปใช้ที่สร้างโดย src/Document/ และทดสอบโดย tests/Unit/Document/ (DPartTest, DPartRootTest, DPartPageRefTest, DocumentPdfMergerDeepTest, DocumentPageRangeParseDeepTest) ไม่ใช่คำยืนยันว่า PDF 2.0 สอดคล้องครบวงจร ความสอดคล้องของเอกสารทั้งฉบับได้รับการตรวจสอบโดยชุดทดสอบ oracle และ golden ที่อธิบายไว้ใน /modules/core/conformance/ แล้ว
ดูเพิ่มเติม
หัวข้อที่มีชื่อว่า “ดูเพิ่มเติม”- โมดูล Core
- โมดูล Writer — ทำการซีเรียลไลซ์แผนผัง DPart และแผนผังหน้า
- โมดูล Metadata — Extensible Metadata Platform (XMP) ที่จับคู่กับ DPM
- โมดูล Navigation
- ภาพรวมความสอดคล้องตามมาตรฐาน
- แบบจำลองความปลอดภัยของเอนจิน