Writer: serializator PDF 2.0 + xref
W skrócie
Dział zatytułowany „W skrócie”Moduł Writer serializuje dokument do bajtów w formacie PDF (Portable Document Format). Dobiera strategię wersji, zapisuje graf obiektów oraz emituje strukturę odsyłaczy i zwiastun (trailer).
Instalacja
Dział zatytułowany „Instalacja”composer require nextpdf/core:^3Przegląd koncepcyjny
Dział zatytułowany „Przegląd koncepcyjny”Jako punkt wejścia użyj PdfWriter. Do write() przekaż obiekt wartości DocumentData. Metoda zwraca kompletny PDF jako ciąg bajtów. Writer składa graf obiektów, przypisuje numery obiektów, zapisuje przesunięcia bajtowe, a na końcu emituje strukturę odsyłaczy.
Przy każdym wywołaniu Writer korzysta z jednej strategii serializacji. Interfejs PdfSerializationStrategy definiuje cztery metody: writeHeader(), getCatalogVersion(), writeXrefAndTrailer() oraz usesXrefStream(). Implementują go trzy strategie. Pdf20StreamStrategy zapisuje nagłówek %PDF-2.0, ustawia wersję katalogu na /2.0 i emituje strumień odsyłaczy. Pdf17TableStrategy zapisuje %PDF-1.7 oraz klasyczną tablicę odsyłaczy. Pdf14TableStrategy zapisuje %PDF-1.4 oraz tablicę odsyłaczy. PdfWriter wybiera strategię za pomocą wyrażenia match na DocumentData::$outputProfile. Domyślną strategią jest Pdf20StreamStrategy.
Enum PdfOutputProfile definiuje trzy wersje docelowe: Pdf20, Pdf17 oraz Pdf14. Udostępnia headerVersion(), catalogVersion(), allowsObjectStreams() oraz usesXrefStream(). Tryb zgodności archiwizacyjnej nadpisuje wybrany profil przed wyborem strategii. Pdf14FeatureGuard odrzuca funkcje PDF 2.0, gdy profil to Pdf14.
Strumień odsyłaczy mapuje każdy numer obiektu na jego przesunięcie bajtowe, zgodnie z definicją w ISO 32000-2 §7. Aktualizacje przyrostowe dołączają nowe obiekty na końcu pliku, zgodnie z definicją w ISO 32000-2 §7.5.6. Writer przepuszcza każdy ciąg literalny przez kanoniczną ścieżkę ucieczki PdfStringEscaper::escapeLiteral(), która stosuje normatywną tabelę znaków ucieczki z ISO 32000-2 §7.3.4.2 (ADR-015).
Writer obsługuje deterministyczne generowanie danych wyjściowych. setDeterministicMode() ustala identyfikatory obiektów i kolejność kluczy w słowniku. setReproducibleClock() ustala znacznik czasu dokumentu. Po ustawieniu obu blokad to samo wejście daje wynik identyczny co do bajta. Metoda writeChunked() zwraca generator, który dostarcza PDF w porcjach o stałym rozmiarze. Streaming/StreamingPdfWriter zapisuje jedną stronę naraz do strumienia dostarczonego przez wywołującego; użyj go dla dokumentów przekraczających budżet pamięci.
Linearizer przepisuje gotowy PDF do układu linearyzowanego. Umieszcza pierwszą stronę na początku, dzięki czemu przeglądarka może ją wyświetlić, zanim zakończy się pełne pobieranie. shadowValidate() sprawdza przepisanie bez zmiany wejścia.
Uwaga.
PdfWriter.phpiLinearizer.phpmają krytyczne znaczenie dla przesunięć bajtowych i grafu obiektów (strefy zagrożenia w manifeście). Nie zmieniaj numeracji obiektów ani arytmetyki przesunięć xref bez golden suite dla modułu Writer.
Powierzchnia API
Dział zatytułowany „Powierzchnia API”| Klasa | Kluczowe metody | Rola |
|---|---|---|
PdfWriter | write(DocumentData): string, writeChunked(DocumentData, int): Generator, setDeterministicMode(), setReproducibleClock(), setOutputColorProfile(), getLastXrefOffset(), getFileId() | Główny serializator |
PdfSerializationStrategy (interfejs) | writeHeader(), getCatalogVersion(), writeXrefAndTrailer(), usesXrefStream() | Kontrakt strategii wersji |
Pdf20StreamStrategy | writeHeader() → %PDF-2.0, getCatalogVersion() → /2.0, usesXrefStream() → true | Strategia strumienia xref dla PDF 2.0 |
Pdf17TableStrategy | writeHeader() → %PDF-1.7, tablica xref | Strategia tablicy xref dla PDF 1.7 |
Pdf14TableStrategy | writeHeader() → %PDF-1.4, tablica xref | Strategia tablicy xref dla PDF 1.4 |
PdfOutputProfile (enum) | Pdf20, Pdf17, Pdf14; headerVersion(), catalogVersion(), allowsObjectStreams() | Selektor wersji docelowej |
PdfXrefWriter | generateFileId(), finalizeTrailerAndXref() | Identyfikator pliku + finalizacja trailer/xref |
Linearizer | linearize(string): string, shadowValidate(string): array | Przepisanie do trybu fast web view |
Streaming\StreamingPdfWriter | open(), newPage(), close() | Jednoprzebiegowy writer strumieniowy |
Uruchom composer docs:generate-api-php -- --module=Writer, aby wygenerować pełną tabelę PHPDoc.
Przykład kodu — szybki start
Dział zatytułowany „Przykład kodu — szybki start”Źródło: 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);Domyślnym profilem jest PDF 2.0. Dane wyjściowe zaczynają się od %PDF-2.0, a kończą się strumieniem odsyłaczy.
Przykład kodu — produkcja
Dział zatytułowany „Przykład kodu — produkcja”Ten przykład ustawia determinizm i stały zegar, aby uzyskać wynik identyczny co do bajta, a następnie strumieniuje go w porcjach o stałym rozmiarze.
<?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);Przypadki brzegowe i pułapki
Dział zatytułowany „Przypadki brzegowe i pułapki”- Przy każdym wywołaniu
write()uruchamia się tylko jedna strategia. Writer resetuje strategię na podstawie profilu przy każdym wywołaniu. Wersja z wcześniejszego wywołania nie przechodzi do kolejnego. - Tryb zgodności archiwizacyjnej nadpisuje żądany profil. Kompilacja PDF/A-3 wymusza PDF 1.7. Kompilacja PDF/A-4 wymusza PDF 2.0.
- Wynik identyczny co do bajta wymaga obu blokad. Ustaw tryb deterministyczny oraz powtarzalny zegar. Sama jedna blokada nie wystarczy.
writeChunked()zwraca generator. Skonsumuj go do końca. Częściowy odczyt daje obcięty, nieprawidłowy PDF.Linearizerprzepisuje przesunięcia odsyłaczy. W potoku, który nie toleruje nieudanego przepisania, najpierw uruchomshadowValidate().Pdf14TableStrategyjestfinal readonly. Ścieżka PDF 1.4 odrzuca funkcje PDF 2.0 za pośrednictwemPdf14FeatureGuard; nie degraduje ich.
Wydajność
Dział zatytułowany „Wydajność”Serializacja jest liniowa względem liczby obiektów i całkowitego rozmiaru w bajtach. Strumień odsyłaczy dodaje jeden przebieg przez tablicę obiektów. writeChunked() przechowuje złożony dokument, ale dostarcza go w fragmentach o ograniczonym rozmiarze, więc szczytowe zużycie pamięci to rozmiar dokumentu plus jedna porcja. Streaming\StreamingPdfWriter nie przechowuje całego dokumentu; użyj go dla wejść większych niż budżet pamięci. Budżet dla obciążenia referencyjnego to 1500 ms czasu rzeczywistego i 64 MB szczytowego zużycia pamięci. Linearyzacja dodaje drugi pełny przebieg oraz przebieg pomiarowy. Uwzględnij to wprost w budżecie.
Uwagi dotyczące bezpieczeństwa
Dział zatytułowany „Uwagi dotyczące bezpieczeństwa”Writer serializuje zaufany graf obiektów przechowywany w pamięci. Jego dane wejściowe wyznaczają główną granicę zagrożenia. Każdy ciąg literalny przechodzi przez kanoniczne PdfStringEscaper::escapeLiteral() (ADR-015), dzięki czemu osadzone bajty sterujące nie mogą wydostać się poza token ciągu. Szyfrowanie obsługują PdfEncryptionWriter oraz wpis /Encrypt w zwiastunie. Szyfrowanie kluczem publicznym jest odrzucane z jawnym wyjątkiem, a nie po cichu degradowane. Tryb deterministyczny i tryb powtarzalnego zegara usuwają z danych wyjściowych kanały boczne związane ze znacznikiem czasu i kolejnością. Zobacz /modules/core/security/, aby poznać model zagrożeń dokumentu i granicę zaufania szyfrowania.
Zgodność
Dział zatytułowany „Zgodność”Writer wytwarza struktury plików PDF 2.0: nagłówek %PDF-2.0, wersję katalogu /2.0, strumień odsyłaczy oraz znaki ucieczki w ciągach literalnych zgodnie z tabelą znaków ucieczki w ISO 32000-2 §7.3.4.2. Są to fakty dotyczące implementacji. Dowody znajdują się w src/Writer/Pdf20StreamStrategy.php, src/Writer/PdfSerializationStrategy.php oraz w wyborze strategii w src/Writer/PdfWriter.php. To zachowanie sprawdzają tests/Unit/Writer/ (192 testy, w tym zestawy Pdf20StreamStrategy, PdfXrefWriter oraz Linearizer*) oraz bazowa linia tests/Golden/PdfWriter/PdfWriterGoldenBaselineSmokeTest.
To nie jest deklaracja pełnej zgodności z PDF 2.0. Pełna zgodność z ISO 32000-2 jest właściwością kompletnego dokumentu zweryfikowanego przez zewnętrzną wyrocznię, a nie samego serializatora. Zgodność end-to-end stwierdza się tylko tam, gdzie potwierdza ją wyrocznia: tests/Integration/Accessibility/VeraPdfUa2GoldenTest weryfikuje wygenerowaną próbkę względem veraPDF dla profilu PDF/UA-2, a tests/Standards/Profile/PdfRConformanceTest obejmuje profil PDF/R. Test golden veraPDF jest pomijany, gdy plik binarny veraPDF jest nieobecny na maszynie uruchomieniowej, więc jest opcjonalną bramką wyroczni, a nie bramką bezwarunkową. Aby go uruchomić, ustaw VERAPDF_BINARY. Wybór profilu archiwizacyjnego (PDF/A-3 → PDF 1.7, PDF/A-4 → PDF 2.0) rozstrzygają ADR-011 i tryb zgodności; weryfikują go zestawy zgodności w /modules/core/conformance/. Poza tymi profilami popartymi wyrocznią stwierdzaj, że Writer „wytwarza struktury PDF 2.0; zgodność jest weryfikowana przez veraPDF dla profilu PDF/UA-2”, zamiast deklarować zgodność bez zastrzeżeń.