Document: DPart, podział i scalanie oraz rozszerzenia producenta
W skrócie
Dział zatytułowany „W skrócie”Moduł Document działa na całych plikach w formacie Portable Document Format (PDF), a nie na treści stron. Buduje hierarchię części dokumentu używaną w regulowanych procesach do dołączania metadanych. Dzieli plik PDF na segmenty według zakresów stron, scala kilka plików PDF w jeden i rejestruje rozszerzenia deweloperskie w katalogu dokumentu.
Instalacja
Dział zatytułowany „Instalacja”composer require nextpdf/core:^3Przegląd koncepcyjny
Dział zatytułowany „Przegląd koncepcyjny”Ten moduł działa powyżej warstwy treści stron. Tam, gdzie moduły Graphics i Content emitują operatory, moduł Document działa na poziomie strukturalnym: drzewa stron, katalogu dokumentu oraz drzewa części dokumentu.
Część dokumentu (DPart) to logiczna partycja pliku PDF. ISO 32000-2 definiuje hierarchię DPart, której węzły przechowują metadane części dokumentu (DPM). Regulowany proces, na przykład farmaceutyczny, prawny lub archiwalny, może powiązać metadane z podzakresem stron zamiast z całym plikiem — §14.12. DPart to niezmienny węzeł readonly: węzeł-liść odwołuje się do ciągłego zakresu indeksów stron, a węzeł pośredni grupuje podrzędne węzły DPart w drzewo. DPartRoot to korzeń drzewa serializowany przez moduł Writer. Wpisy /Start i /End węzła-liścia są pośrednimi odwołaniami do obiektów stron, a nie całkowitoliczbowymi indeksami stron — §14.12. DPart::resolveWithPageObjects() rozwiązuje te wpisy na podstawie dostarczonej przez Writer mapy indeks strony→numer obiektu i zwraca postać odwołania /Start (oraz opcjonalnie /End). Postać całkowitoliczbową zwraca tylko na ścieżkach testowych, gdzie mapa jest niedostępna.
PdfMerger i PdfSplitter tworzą powierzchnię kompozycji dokumentu. PdfMerger łączy obiekty stron z wielu wejściowych plików PDF, ponownie numeruje obiekty, aby uniknąć kolizji, oraz odbudowuje pojedyncze drzewo stron i tablicę odsyłaczy. Tworzone przez niego drzewo stron jest zrównoważonym węzłem Pages z Kids i Count oraz modelem atrybutów dziedziczonych, który PDF definiuje dla węzłów drzewa stron — §7.7.3. PdfSplitter wykonuje operację odwrotną: wyodrębnia zakresy stron do samodzielnych obiektów SplitDocument. PageRange to obiekt wartości używany przez obie klasy. Jest indeksowany od 1, weryfikuje swoje granice oraz odpowiada na contains(), count() i toArray().
VendorExtensionRegistry, ExtensionsDictionary i DeveloperExtensionEntry modelują słownik rozszerzeń deweloperskich w katalogu dokumentu. Silnik używa tego słownika do zadeklarowania poziomu rozszerzenia producenta wykraczającego poza specyfikację bazową. Rejestr odrzuca sprzeczną ponowną rejestrację tego samego prefiksu producenta, zgłaszając VendorExtensionRegistryConflictException. CollectionDictionary i CollectionSort modelują wpis katalogu kolekcji PDF (kolekcji przenośnej lub portfolio).
Powierzchnia API
Dział zatytułowany „Powierzchnia API”| Klasa | Kluczowe metody | Rola |
|---|---|---|
DPart | isLeaf(), hasMetadata(), resolveWithPageObjects(), write() | Niezmienny węzeł części dokumentu (@since 1.12.0) |
DPartRoot | isEmpty(), write() | Korzeń drzewa DPart, który serializuje moduł Writer (@since 1.12.0) |
PdfMerger | merge(array $pdfFiles, int $maxFiles = 100, int $maxTotalBytes = 200_000_000), append() | Scalanie wielu plików PDF z ponownym numerowaniem obiektów (@since 1.9.0) |
PdfSplitter | split(), splitEvery(), extractPages() | Podział według zakresu stron na SplitDocument (@since 1.9.0) |
PageRange | contains(int $page), count(), toArray() | Obiekt wartości zakresu stron indeksowany od 1 |
MergeResult / SplitResult | isValid(), count(), document(), totalOutputSize() | Obiekty wyników kompozycji |
VendorExtensionRegistry | rejestracja rozszerzeń | Rejestr rozszerzeń deweloperskich (@since 2.2.0) |
ExtensionsDictionary | withEntry(), entries(), isEmpty(), toPdfDictionary() | Niezmienny konstruktor słownika rozszerzeń (@since 2.0.0) |
CollectionDictionary | toPdfDictionary() | Wpis katalogu kolekcji przenośnej (@since 2.0.0) |
Uruchom composer docs:generate-api-php -- --module=Document, aby wygenerować pełną tabelę PHPDoc.
Przykład kodu — szybki start
Dział zatytułowany „Przykład kodu — szybki start”Podziel plik PDF na dokumenty jednostronicowe, a następnie sprawdź wynik.
<?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),);Przykład kodu — produkcja
Dział zatytułowany „Przykład kodu — produkcja”Scal kilka plików PDF przy jawnym budżecie wejściowym, a następnie zweryfikuj wynik przed zapisaniem połączonych danych wyjściowych.
<?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);Przypadki brzegowe i pułapki
Dział zatytułowany „Przypadki brzegowe i pułapki”PdfMerger::merge()iPdfSplitter::split()wymuszają limity wejścia za pośrednictwemResourceGuard. Dane wejściowe obejmujące zbyt wiele plików lub zbyt wiele bajtów powodują zgłoszenie wyjątku, zamiast być po cichu obcinane. UstawmaxFiles/maxTotalBytesrozważnie, odpowiednio do obciążenia.- Pusta lista plików lub pusta lista zakresów powoduje zgłoszenie
PageLayoutException. Traktuj je jako błędy konfiguracji, a nie puste wyniki. PageRangejest indeksowany od 1 i obejmuje obie granice. Listapageswęzła-liściaDPartzawiera indeksy stron liczone od 0. Te dwie abstrakcje używają różnych baz indeksowania. Przy przechodzeniu między nimi przeliczaj wartości jawnie.DPartjestreadonly. Aby zbudować inne drzewo, twórz nowe węzły zamiast modyfikować istniejące.resolveWithPageObjects()zwraca zapasową postać z indeksem całkowitoliczbowym tylko wtedy, gdy mapa obiektów stron jest pusta. Nie polegaj na tej ścieżce w produkcyjnych danych wyjściowych.VendorExtensionRegistryzgłaszaVendorExtensionRegistryConflictExceptiondla zduplikowanego prefiksu producenta. Rejestruj każdy prefiks tylko raz.
Wydajność
Dział zatytułowany „Wydajność”Podział i scalanie skalują się liniowo wraz z liczbą stron; dominują analiza składniowa i ponowne numerowanie obiektów, a nie wewnętrzna ewidencja modułu. Domyślne obciążenie referencyjne mieści się w budżecie 1500 ms czasu rzeczywistego / 64 MB szczytowego zużycia. Duże operacje scalania są ograniczone głównie przez łączną liczbę bajtów wejścia. Zabezpieczenie maxTotalBytes utrzymuje szczytowe zużycie pamięci w granicach. Profil odtwarzalności ma wartość structural: scalony lub podzielony plik PDF zawiera świeży zwiastun i /ID, dlatego dwa przebiegi są strukturalnie równoważne, ale nie identyczne bajtowo.
Uwagi dotyczące bezpieczeństwa
Dział zatytułowany „Uwagi dotyczące bezpieczeństwa”PdfMerger::merge() i PdfSplitter::split() przetwarzają niezaufane bajty plików PDF. Przed analizą składniową obie klasy przepuszczają dane wejściowe przez ResourceGuard::assertSize() / assertCount(), co ogranicza atak odmowy usługi oparty na wzmocnieniu dekompresji lub liczbie obiektów. Utrzymuj argumenty maxFiles, maxTotalBytes i maxBytes ściśle dopasowane do wdrożenia, zamiast polegać na wartościach domyślnych. Traktuj każdy wejściowy plik PDF jako wrogi. Gdy źródła pochodzą od użytkownika, uruchamiaj wsadową kompozycję w ograniczonym procesie roboczym. Informacje o granicy zaufania znajdują się w modelu zagrożeń silnika w /modules/core/security/.
Zgodność
Dział zatytułowany „Zgodność”Drzewo DPart budowane przez ten moduł jest zgodne z modelem części dokumentu z ISO 32000-2 §14.12, a wpisy /Start i /End liścia są emitowane jako pośrednie odwołania do obiektów stron na podstawie tej samej klauzuli. Scalone dane wyjściowe wykorzystują strukturę węzłów drzewa stron zdefiniowaną w §7.7.3. To fakty implementacyjne wynikające z src/Document/ i weryfikowane przez tests/Unit/Document/ (DPartTest, DPartRootTest, DPartPageRefTest, DocumentPdfMergerDeepTest, DocumentPageRangeParseDeepTest). Nie stanowią deklaracji kompleksowej zgodności z PDF 2.0. Zgodność całego dokumentu jest weryfikowana przez wyrocznię oraz zestawy wzorcowe opisane w /modules/core/conformance/.
Zobacz też
Dział zatytułowany „Zobacz też”- Moduł Core
- Moduł Writer — serializuje drzewo DPart i drzewo stron.
- Moduł Metadata — platforma Extensible Metadata Platform (XMP), która współpracuje z DPM.
- Moduł Navigation
- Przegląd zgodności
- Model bezpieczeństwa silnika