Ga naar inhoud

Writer: PDF 2.0-serializer + xref

De Writer-module serialiseert een document naar Portable Document Format (PDF)-bytes. De module kiest een versiestrategie, schrijft de objectgraaf en genereert de cross-reference-structuur en de trailer.

Terminal window
composer require nextpdf/core:^3

Gebruik PdfWriter als toegangspunt. Geef een DocumentData-waardeobject mee aan write(). De methode retourneert de volledige PDF als byte-string. De writer stelt de objectgraaf samen, kent objectnummers toe, registreert byte-offsets en schrijft als laatste de cross-reference-structuur.

Bij elke aanroep gebruikt de writer één serialisatiestrategie. De interface PdfSerializationStrategy definieert vier methoden: writeHeader(), getCatalogVersion(), writeXrefAndTrailer() en usesXrefStream(). Deze interface heeft drie implementaties. Pdf20StreamStrategy schrijft de %PDF-2.0-header, stelt de catalogusversie in op /2.0 en genereert een cross-reference-stream. Pdf17TableStrategy schrijft %PDF-1.7 en een klassieke cross-reference-tabel. Pdf14TableStrategy schrijft %PDF-1.4 en een cross-reference-tabel. PdfWriter kiest de strategie met een match op DocumentData::$outputProfile. Standaard is Pdf20StreamStrategy.

De enum PdfOutputProfile bevat de drie doelversies: Pdf20, Pdf17 en Pdf14. De enum stelt headerVersion(), catalogVersion(), allowsObjectStreams() en usesXrefStream() beschikbaar. Een archiveringsconformiteitsmodus overschrijft het gekozen profiel vóór de strategieselectie. Pdf14FeatureGuard weigert PDF 2.0-functies wanneer het profiel Pdf14 is.

Een cross-reference-stream wijst elk objectnummer toe aan zijn byte-offset, zoals gedefinieerd in ISO 32000-2 §7. Incrementele updates voegen nieuwe objecten toe aan het einde van het bestand, zoals gedefinieerd in ISO 32000-2 §7.5.6. De writer escapet elke literal-string via het canonieke PdfStringEscaper::escapeLiteral()-pad, dat de normatieve escape-tabel in ISO 32000-2 §7.3.4.2 (ADR-015) volgt.

De writer ondersteunt deterministische uitvoer. setDeterministicMode() legt object-identifiers en de volgorde van dictionary-sleutels vast. setReproducibleClock() legt de tijdstempel van het document vast. Wanneer beide instellingen zijn vastgelegd, levert vaste invoer byte-identieke uitvoer op. De methode writeChunked() retourneert een generator die de PDF in chunks van vaste grootte levert. Streaming/StreamingPdfWriter schrijft één pagina tegelijk naar een door de aanroeper aangeleverde stream voor documenten die het geheugenbudget overschrijden.

Linearizer herschrijft een voltooide PDF naar een gelineariseerde lay-out. De Linearizer plaatst de eerste pagina vroeg in het bestand, zodat een viewer deze kan tonen voordat de volledige download is voltooid. shadowValidate() controleert de herschrijving zonder de invoer te wijzigen.

Let op. PdfWriter.php en Linearizer.php zijn cruciaal voor byte-offsets en de objectgraaf (in het manifest als gevarenzones aangeduid). Wijzig de objectnummering of de xref-offsetberekening niet zonder de golden-suite van de Writer.

ClassBelangrijkste methodenRol
PdfWriterwrite(DocumentData): string, writeChunked(DocumentData, int): Generator, setDeterministicMode(), setReproducibleClock(), setOutputColorProfile(), getLastXrefOffset(), getFileId()Primaire serializer
PdfSerializationStrategy (interface)writeHeader(), getCatalogVersion(), writeXrefAndTrailer(), usesXrefStream()Contract voor versiestrategie
Pdf20StreamStrategywriteHeader()%PDF-2.0, getCatalogVersion()/2.0, usesXrefStream()truePDF 2.0-xref-stream-strategie
Pdf17TableStrategywriteHeader()%PDF-1.7, xref-tabelPDF 1.7-xref-tabel-strategie
Pdf14TableStrategywriteHeader()%PDF-1.4, xref-tabelPDF 1.4-xref-tabel-strategie
PdfOutputProfile (enum)Pdf20, Pdf17, Pdf14; headerVersion(), catalogVersion(), allowsObjectStreams()Selector voor doelversie
PdfXrefWritergenerateFileId(), finalizeTrailerAndXref()File ID + trailer/xref-finalisatie
Linearizerlinearize(string): string, shadowValidate(string): arrayHerschrijving voor fast web view
Streaming\StreamingPdfWriteropen(), newPage(), close()Streaming-writer in één doorloop

Voer composer docs:generate-api-php -- --module=Writer uit om de volledige PHPDoc-tabel te genereren.

Bron: 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);

Het standaardprofiel is PDF 2.0. De uitvoer begint met %PDF-2.0 en eindigt met een cross-reference-stream.

Hiermee leg je determinisme en een vaste klok vast voor byte-identieke uitvoer en stream je het resultaat daarna in vaste chunks.

<?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);
  • Per write()-aanroep wordt slechts één strategie uitgevoerd. De writer stelt de strategie bij elke aanroep opnieuw in op basis van het profiel. De versie van een eerdere aanroep sijpelt niet door.
  • Een archiveringsconformiteitsmodus overschrijft het gevraagde profiel. Een PDF/A-3-build dwingt PDF 1.7 af. Een PDF/A-4-build dwingt PDF 2.0 af.
  • Byte-identieke uitvoer vereist beide instellingen. Stel de deterministische modus en een reproduceerbare klok in. Eén instelling is op zichzelf niet voldoende.
  • writeChunked() levert een generator op. Verwerk de generator volledig. Als je slechts een deel uitleest, levert dat een afgekapte, ongeldige PDF op.
  • Linearizer herschrijft de cross-reference-offsets. Voer in een pijplijn die geen mislukte herschrijving kan verdragen eerst shadowValidate() uit.
  • Pdf14TableStrategy is final readonly. Het PDF 1.4-pad weigert PDF 2.0-functies via Pdf14FeatureGuard; het pad degradeert ze niet.

De serialisatie verloopt lineair met het aantal objecten en de totale bytegrootte. De cross-reference-stream voegt één doorloop over de objecttabel toe. writeChunked() houdt het samengestelde document in het geheugen, maar levert het in begrensde stukken, zodat het piekgeheugen gelijk is aan de documentgrootte plus één chunk. Streaming\StreamingPdfWriter houdt niet het hele document vast; gebruik deze writer voor invoer die niet binnen het geheugenbudget past. Het budget voor de referentiewerklast is 1500 ms wandkloktijd en 64 MB piek. Linearisatie voegt een tweede volledige doorloop en een meetdoorloop toe. Reserveer hiervoor expliciet budget.

De writer serialiseert een vertrouwde objectgraaf in het geheugen. De invoer is de belangrijkste bedreigingsgrens. Elke literal-string gaat door het canonieke PdfStringEscaper::escapeLiteral() (ADR-015), zodat ingesloten control-bytes niet uit een string-token kunnen breken. Versleuteling wordt afgehandeld via PdfEncryptionWriter en de /Encrypt-trailervermelding. Versleuteling met publieke sleutel wordt geweigerd met een expliciete exception in plaats van stilzwijgend gedegradeerd. De deterministische modus en de modus met reproduceerbare klok verwijderen tijdstempel- en volgordegebaseerde side channels uit de uitvoer. Zie /modules/core/security/ voor het bedreigingsmodel van het document en de vertrouwensgrens voor versleuteling.

De Writer produceert PDF 2.0-bestandsstructuren: de %PDF-2.0-header, een catalogusversie /2.0, een cross-reference-stream en literal-string-escaping volgens de escape-tabel uit ISO 32000-2 §7.3.4.2. Dit zijn implementatiefeiten. Het bewijs bevindt zich in src/Writer/Pdf20StreamStrategy.php, src/Writer/PdfSerializationStrategy.php en de strategieselectie in src/Writer/PdfWriter.php. Het gedrag wordt getest door tests/Unit/Writer/ (192 tests, waaronder de suites Pdf20StreamStrategy, PdfXrefWriter en Linearizer*) en de tests/Golden/PdfWriter/PdfWriterGoldenBaselineSmokeTest-baseline.

Dit is geen claim van volledige PDF 2.0-conformiteit. Volledige ISO 32000-2-conformiteit is een eigenschap van een volledig document dat door een externe oracle is gevalideerd, niet van de serializer alleen. End-to-end-conformiteit wordt alleen vastgesteld waar een oracle deze bevestigt: tests/Integration/Accessibility/VeraPdfUa2GoldenTest valideert een gegenereerde fixture tegen veraPDF voor PDF/UA-2, en tests/Standards/Profile/PdfRConformanceTest dekt het PDF/R-profiel. De veraPDF-golden-test wordt overgeslagen wanneer de veraPDF-binary ontbreekt op de runner, dus het is een opt-in-oracle-gate, geen onvoorwaardelijke gate. Stel VERAPDF_BINARY in om deze test uit te voeren. De selectie van het archiveringsprofiel (PDF/A-3 → PDF 1.7, PDF/A-4 → PDF 2.0) wordt bepaald door ADR-011 en de conformiteitsmodus, en gevalideerd door de conformiteitssuites in /modules/core/conformance/. Gebruik buiten die door een oracle gedekte profielen de formulering dat de Writer „PDF 2.0-structuren produceert; conformiteit wordt voor het PDF/UA-2-profiel gevalideerd door veraPDF” in plaats van ongekwalificeerde conformiteit te claimen.