Przejdź do głównej zawartości

Writer: serializator PDF 2.0 + xref

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).

Okno terminala
composer require nextpdf/core:^3

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.php i Linearizer.php mają 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.

KlasaKluczowe metodyRola
PdfWriterwrite(DocumentData): string, writeChunked(DocumentData, int): Generator, setDeterministicMode(), setReproducibleClock(), setOutputColorProfile(), getLastXrefOffset(), getFileId()Główny serializator
PdfSerializationStrategy (interfejs)writeHeader(), getCatalogVersion(), writeXrefAndTrailer(), usesXrefStream()Kontrakt strategii wersji
Pdf20StreamStrategywriteHeader()%PDF-2.0, getCatalogVersion()/2.0, usesXrefStream()trueStrategia strumienia xref dla PDF 2.0
Pdf17TableStrategywriteHeader()%PDF-1.7, tablica xrefStrategia tablicy xref dla PDF 1.7
Pdf14TableStrategywriteHeader()%PDF-1.4, tablica xrefStrategia tablicy xref dla PDF 1.4
PdfOutputProfile (enum)Pdf20, Pdf17, Pdf14; headerVersion(), catalogVersion(), allowsObjectStreams()Selektor wersji docelowej
PdfXrefWritergenerateFileId(), finalizeTrailerAndXref()Identyfikator pliku + finalizacja trailer/xref
Linearizerlinearize(string): string, shadowValidate(string): arrayPrzepisanie do trybu fast web view
Streaming\StreamingPdfWriteropen(), newPage(), close()Jednoprzebiegowy writer strumieniowy

Uruchom composer docs:generate-api-php -- --module=Writer, aby wygenerować pełną tabelę PHPDoc.

Ź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.

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);
  • 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.
  • Linearizer przepisuje przesunięcia odsyłaczy. W potoku, który nie toleruje nieudanego przepisania, najpierw uruchom shadowValidate().
  • Pdf14TableStrategy jest final readonly. Ścieżka PDF 1.4 odrzuca funkcje PDF 2.0 za pośrednictwem Pdf14FeatureGuard; nie degraduje ich.

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.

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.

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ń.