Document: DPart, tách / gộp và phần mở rộng của nhà cung cấp
Tổng quan nhanh
Phần tiêu đề “Tổng quan nhanh”Module Document làm việc với toàn bộ tệp Portable Document Format (PDF), không phải với nội dung trang. Module này dựng cây phân cấp Document Part để các quy trình làm việc chịu sự quản lý gắn metadata. Module này tách một PDF thành các phần theo dải trang, gộp nhiều PDF thành một, và đăng ký các phần mở rộng dành cho nhà phát triển trong document catalog.
Cài đặt
Phần tiêu đề “Cài đặt”composer require nextpdf/core:^3Tổng quan khái niệm
Phần tiêu đề “Tổng quan khái niệm”Module này nằm ở lớp phía trên nội dung trang. Trong khi Graphics và Content phát ra các toán tử, Document làm việc ở cấp cấu trúc: cây trang, document catalog và cây Document Part.
Một Document Part (DPart) là một phân vùng logic của một PDF. ISO 32000-2 định nghĩa cây phân cấp DPart, trong đó các nút mang Document Part Metadata (DPM). Một quy trình làm việc chịu sự quản lý, chẳng hạn như quy trình dược phẩm, pháp lý hoặc lưu trữ, có thể gắn metadata với một dải trang con thay vì toàn bộ tệp — §14.12. DPart là một nút bất biến readonly: nút lá tham chiếu đến một dãy chỉ mục trang liên tiếp, còn nút trung gian nhóm các nút DPart con thành một cây. DPartRoot là gốc của cây mà Writer tuần tự hóa. Các mục /Start và /End của nút lá là tham chiếu gián tiếp đến đối tượng trang, không phải số nguyên chỉ mục trang — §14.12. DPart::resolveWithPageObjects() phân giải các mục đó dựa trên ánh xạ chỉ-mục-trang→số-đối-tượng do writer cung cấp và trả về dạng tham chiếu /Start (và tùy chọn /End). Phương thức này chỉ quay về dạng số nguyên trong các luồng kiểm thử khi không có ánh xạ.
PdfMerger và PdfSplitter là bề mặt soạn tài liệu. PdfMerger kết hợp các đối tượng trang từ nhiều PDF đầu vào, đánh số lại đối tượng để tránh xung đột, rồi dựng lại một cây trang và một bảng tham chiếu chéo duy nhất. Cây trang mà nó tạo ra là một nút Pages cân bằng với Kids và Count, cùng mô hình thuộc tính kế thừa mà PDF định nghĩa cho các nút cây trang — §7.7.3. PdfSplitter làm điều ngược lại: trích các dải trang thành các đối tượng SplitDocument độc lập. PageRange là value object được cả hai lớp sử dụng. Nó đánh chỉ mục từ 1, kiểm tra hợp lệ các giới hạn của chính nó, và trả lời contains(), count() và toArray().
VendorExtensionRegistry, ExtensionsDictionary và DeveloperExtensionEntry mô hình hóa từ điển phần mở rộng dành cho nhà phát triển trong document catalog. Engine dùng từ điển đó để khai báo cấp phần mở rộng của nhà cung cấp nằm ngoài đặc tả cơ sở. Sổ đăng ký từ chối việc đăng ký lại cùng một tiền tố nhà cung cấp nếu gây xung đột, bằng VendorExtensionRegistryConflictException. CollectionDictionary và CollectionSort mô hình hóa mục catalog collection của PDF (portable collection hoặc portfolio).
Bề mặt API
Phần tiêu đề “Bề mặt API”| Lớp | Phương thức chính | Vai trò |
|---|---|---|
DPart | isLeaf(), hasMetadata(), resolveWithPageObjects(), write() | Nút Document Part bất biến (@since 1.12.0) |
DPartRoot | isEmpty(), write() | Gốc cây DPart mà Writer tuần tự hóa (@since 1.12.0) |
PdfMerger | merge(array $pdfFiles, int $maxFiles = 100, int $maxTotalBytes = 200_000_000), append() | Gộp nhiều PDF kèm đánh số lại đối tượng (@since 1.9.0) |
PdfSplitter | split(), splitEvery(), extractPages() | Tách theo dải trang thành SplitDocument (@since 1.9.0) |
PageRange | contains(int $page), count(), toArray() | Value object dải trang đánh chỉ mục từ 1 |
MergeResult / SplitResult | isValid(), count(), document(), totalOutputSize() | Các đối tượng kết quả soạn tài liệu |
VendorExtensionRegistry | đăng ký phần mở rộng | Sổ đăng ký phần mở rộng dành cho nhà phát triển (@since 2.2.0) |
ExtensionsDictionary | withEntry(), entries(), isEmpty(), toPdfDictionary() | Trình dựng từ điển phần mở rộng bất biến (@since 2.0.0) |
CollectionDictionary | toPdfDictionary() | Mục catalog portable-collection (@since 2.0.0) |
Chạy composer docs:generate-api-php -- --module=Document để tạo bảng PHPDoc đầy đủ.
Mẫu mã — bắt đầu nhanh
Phần tiêu đề “Mẫu mã — bắt đầu nhanh”Tách một PDF thành các tài liệu một trang, rồi kiểm tra kết quả.
<?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),);Mẫu mã — môi trường production
Phần tiêu đề “Mẫu mã — môi trường production”Gộp nhiều PDF trong một ngân sách đầu vào rõ ràng, rồi kiểm tra tính hợp lệ của kết quả trước khi ghi đầu ra đã kết hợp.
<?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);Trường hợp đặc biệt & điểm cần lưu ý
Phần tiêu đề “Trường hợp đặc biệt & điểm cần lưu ý”PdfMerger::merge()vàPdfSplitter::split()áp đặt giới hạn đầu vào thông quaResourceGuard. Đầu vào có quá nhiều tệp hoặc quá nhiều byte sẽ phát ra exception thay vì âm thầm cắt bớt. Hãy đặtmaxFiles/maxTotalBytescó chủ đích theo khối lượng công việc của bạn.- Danh sách tệp rỗng hoặc danh sách dải rỗng sẽ phát ra
PageLayoutException. Hãy xem đây là lỗi cấu hình, chứ không phải kết quả rỗng. PageRangeđánh chỉ mục từ 1 và bao gồm cả hai đầu mút.DPartlá có danh sáchpageslà các chỉ mục trang đánh từ 0. Hai mức trừu tượng này dùng cơ sở chỉ mục khác nhau. Hãy chuyển đổi tường minh khi bạn chuyển qua lại giữa chúng.DPartlàreadonly. Để dựng một cây khác, hãy tạo nút mới thay vì thay đổi một nút sẵn có.resolveWithPageObjects()chỉ trả về dạng dự phòng chỉ-mục-số-nguyên khi ánh xạ đối tượng trang rỗng. Đừng dựa vào luồng đó cho đầu ra production.VendorExtensionRegistryphát raVendorExtensionRegistryConflictExceptionkhi gặp tiền tố nhà cung cấp trùng lặp. Đăng ký mỗi tiền tố một lần.
Hiệu năng
Phần tiêu đề “Hiệu năng”Tách và gộp tăng tuyến tính theo số trang, và chủ yếu bị chi phối bởi quá trình phân tích cú pháp và đánh số lại đối tượng, không phải bởi việc quản lý nội bộ của module. Khối lượng công việc tham chiếu mặc định nằm trong ngân sách 1500 ms thời gian thực / 64 MB đỉnh. Các phép gộp lớn bị ràng buộc chủ yếu bởi tổng số byte đầu vào. Bộ bảo vệ maxTotalBytes giữ cho bộ nhớ đỉnh nằm trong giới hạn. Hồ sơ khả tái lập là structural: một PDF đã gộp hoặc đã tách mang một trailer mới và /ID mới, nên hai lần chạy tương đương về cấu trúc nhưng không giống nhau từng byte.
Lưu ý bảo mật
Phần tiêu đề “Lưu ý bảo mật”PdfMerger::merge() và PdfSplitter::split() xử lý các byte PDF không đáng tin cậy. Trước khi phân tích cú pháp, cả hai đều cho đầu vào đi qua ResourceGuard::assertSize() / assertCount(), nhằm giới hạn các kiểu tấn công từ chối dịch vụ bằng khuếch đại giải nén hoặc khuếch đại số lượng đối tượng. Hãy đặt chặt chẽ các đối số maxFiles, maxTotalBytes và maxBytes cho lần triển khai thay vì dựa vào các giá trị mặc định. Hãy coi mọi PDF đầu vào là thù địch. Khi nguồn do người dùng cung cấp, hãy chạy việc soạn theo lô trong một worker bị giới hạn. Xem mô hình mối đe dọa của engine trong /modules/core/security/ để biết ranh giới tin cậy.
Tuân thủ
Phần tiêu đề “Tuân thủ”Cây DPart mà module này dựng tuân theo mô hình Document Part trong ISO 32000-2 §14.12, với các mục /Start và /End của nút lá được phát ra dưới dạng tham chiếu gián tiếp đến các đối tượng trang theo cùng điều khoản đó. Đầu ra đã gộp dùng cấu trúc nút cây trang được định nghĩa trong §7.7.3. Đây là các sự kiện triển khai do src/Document/ tạo ra và được kiểm thử bởi tests/Unit/Document/ (DPartTest, DPartRootTest, DPartPageRefTest, DocumentPdfMergerDeepTest, DocumentPageRangeParseDeepTest). Chúng không phải là tuyên bố về việc tuân thủ PDF 2.0 toàn trình. Mức tuân thủ của toàn tài liệu được kiểm tra hợp lệ bằng các bộ oracle và golden được mô tả trong /modules/core/conformance/.
Xem thêm
Phần tiêu đề “Xem thêm”- Module Core
- Module Writer — tuần tự hóa cây DPart và cây trang.
- Module Metadata — Extensible Metadata Platform (XMP) đi cùng với DPM.
- Module Navigation
- Tổng quan về tuân thủ
- Mô hình bảo mật của engine