Writer: serializador de PDF 2.0 + xref
Visão geral
Seção intitulada “Visão geral”O módulo Writer serializa um documento em bytes do Portable Document Format (PDF). Ele seleciona uma estratégia de versão, grava o grafo de objetos e emite a estrutura de referência cruzada junto com o trailer.
Instalação
Seção intitulada “Instalação”composer require nextpdf/core:^3Visão conceitual
Seção intitulada “Visão conceitual”Use PdfWriter como ponto de entrada. Passe um objeto de valor DocumentData para write(). O método retorna o PDF completo como uma string de bytes. O writer monta o grafo de objetos, atribui os números de objeto, registra os deslocamentos de bytes e grava a estrutura de referência cruzada por último.
Em cada chamada, o writer usa uma estratégia de serialização. A interface PdfSerializationStrategy define quatro métodos: writeHeader(), getCatalogVersion(), writeXrefAndTrailer() e usesXrefStream(). Três estratégias implementam essa interface. Pdf20StreamStrategy grava o cabeçalho %PDF-2.0, define a versão do catálogo como /2.0 e emite uma stream de referência cruzada. Pdf17TableStrategy grava %PDF-1.7 e uma tabela de referência cruzada clássica. Pdf14TableStrategy grava %PDF-1.4 e uma tabela de referência cruzada. PdfWriter escolhe a estratégia com um match em DocumentData::$outputProfile. O padrão é Pdf20StreamStrategy.
O enum PdfOutputProfile contém as três versões de destino: Pdf20, Pdf17 e Pdf14. Ele expõe headerVersion(), catalogVersion(), allowsObjectStreams() e usesXrefStream(). Um modo de conformidade de arquivamento sobrepõe o perfil escolhido antes da seleção da estratégia. Pdf14FeatureGuard rejeita recursos de PDF 2.0 quando o perfil é Pdf14.
Uma stream de referência cruzada mapeia cada número de objeto para seu deslocamento de bytes, conforme definido pela ISO 32000-2 §7. Atualizações incrementais acrescentam novos objetos ao final do arquivo, conforme definido pela ISO 32000-2 §7.5.6. O writer faz o escape de cada string literal pelo caminho canônico PdfStringEscaper::escapeLiteral(), que segue a tabela de escape normativa da ISO 32000-2 §7.3.4.2 (ADR-015).
O writer oferece suporte à saída determinística. setDeterministicMode() fixa os identificadores de objeto e a ordem das chaves do dicionário. setReproducibleClock() fixa o carimbo de data/hora do documento. Com as duas fixações definidas, uma entrada fixa produz uma saída byte a byte idêntica. O método writeChunked() retorna um gerador que produz o PDF em blocos de tamanho fixo. Streaming/StreamingPdfWriter grava uma página por vez em uma stream fornecida pelo chamador, para documentos que excedem o orçamento de memória.
Linearizer reescreve um PDF concluído em um layout linearizado. Ele posiciona a primeira página no início para que um visualizador possa exibi-la antes de o download completo terminar. shadowValidate() verifica a reescrita sem alterar a entrada.
Cuidado.
PdfWriter.phpeLinearizer.phpsão críticos para os deslocamentos de bytes e para o grafo de objetos (zonas críticas do manifesto). Não altere a numeração de objetos nem a aritmética de deslocamento do xref sem a golden suite do Writer.
Superfície da API
Seção intitulada “Superfície da API”| Classe | Métodos principais | Função |
|---|---|---|
PdfWriter | write(DocumentData): string, writeChunked(DocumentData, int): Generator, setDeterministicMode(), setReproducibleClock(), setOutputColorProfile(), getLastXrefOffset(), getFileId() | Serializador principal |
PdfSerializationStrategy (interface) | writeHeader(), getCatalogVersion(), writeXrefAndTrailer(), usesXrefStream() | Contrato para estratégia de versão |
Pdf20StreamStrategy | writeHeader() → %PDF-2.0, getCatalogVersion() → /2.0, usesXrefStream() → true | Estratégia de xref-stream para PDF 2.0 |
Pdf17TableStrategy | writeHeader() → %PDF-1.7, tabela xref | Estratégia de xref-table para PDF 1.7 |
Pdf14TableStrategy | writeHeader() → %PDF-1.4, tabela xref | Estratégia de xref-table para PDF 1.4 |
PdfOutputProfile (enum) | Pdf20, Pdf17, Pdf14; headerVersion(), catalogVersion(), allowsObjectStreams() | Seletor de versão de destino |
PdfXrefWriter | generateFileId(), finalizeTrailerAndXref() | Finalização do File ID + trailer/xref |
Linearizer | linearize(string): string, shadowValidate(string): array | Reescrita para Fast Web View |
Streaming\StreamingPdfWriter | open(), newPage(), close() | Writer de streaming em passagem única |
Execute composer docs:generate-api-php -- --module=Writer para gerar a tabela completa de PHPDoc.
Exemplo de código — Início rápido
Seção intitulada “Exemplo de código — Início rápido”Fonte: 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);O perfil padrão é PDF 2.0. A saída começa com %PDF-2.0 e termina com uma stream de referência cruzada.
Exemplo de código — Produção
Seção intitulada “Exemplo de código — Produção”Isto fixa o determinismo e um relógio fixo para uma saída byte a byte idêntica e, em seguida, transmite o resultado em blocos de tamanho fixo.
<?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);Casos extremos e pegadinhas
Seção intitulada “Casos extremos e pegadinhas”- Apenas uma estratégia é executada por chamada de
write(). O writer redefine a estratégia a partir do perfil em cada chamada. Uma chamada anterior não vaza sua versão. - Um modo de conformidade de arquivamento sobrepõe o perfil solicitado. Uma build de PDF/A-3 força PDF 1.7. Uma build de PDF/A-4 força PDF 2.0.
- A saída byte a byte idêntica exige as duas fixações. Defina o modo determinístico e um relógio reproduzível. Uma fixação sozinha não é suficiente.
writeChunked()produz um gerador. Consuma-o por completo. Uma leitura parcial produz um PDF truncado e inválido.Linearizerreescreve os deslocamentos de referência cruzada. Em um pipeline que não pode tolerar uma reescrita malsucedida, executeshadowValidate()primeiro.Pdf14TableStrategyéfinal readonly. O caminho de PDF 1.4 rejeita recursos de PDF 2.0 por meio dePdf14FeatureGuard; ele não os rebaixa.
Desempenho
Seção intitulada “Desempenho”A serialização é linear em relação à quantidade de objetos e ao tamanho total em bytes. A stream de referência cruzada adiciona uma passagem sobre a tabela de objetos. writeChunked() mantém o documento montado, mas o produz em fatias limitadas, de modo que o pico de memória é o tamanho do documento mais um bloco. Streaming\StreamingPdfWriter não mantém o documento inteiro; use-o para entradas maiores que o orçamento de memória. O orçamento da carga de trabalho de referência é de 1500 ms de tempo de relógio e 64 MB de pico. A linearização adiciona uma segunda passagem completa e uma passagem de medição. Reserve orçamento explicitamente para isso.
Notas de segurança
Seção intitulada “Notas de segurança”O writer serializa um grafo de objetos confiável em memória. Suas entradas são o principal limite de ameaça. Toda string literal passa pelo caminho canônico PdfStringEscaper::escapeLiteral() (ADR-015), de modo que bytes de controle incorporados não conseguem escapar de um token de string. A criptografia é conectada por meio de PdfEncryptionWriter e da entrada /Encrypt do trailer. A criptografia de chave pública é rejeitada com uma exceção explícita, em vez de ser rebaixada silenciosamente. Os modos determinístico e de relógio reproduzível removem da saída os canais laterais de carimbo de data/hora e de ordenação. Consulte /modules/core/security/ para o modelo de ameaça do documento e o limite de confiança da criptografia.
Conformidade
Seção intitulada “Conformidade”O Writer produz estruturas de arquivo PDF 2.0: o cabeçalho %PDF-2.0, uma versão de catálogo /2.0, uma stream de referência cruzada e o escape de strings literais conforme a tabela de escape da ISO 32000-2 §7.3.4.2. Esses são fatos de implementação. A evidência está em src/Writer/Pdf20StreamStrategy.php, src/Writer/PdfSerializationStrategy.php e na seleção de estratégia em src/Writer/PdfWriter.php. O comportamento é exercitado por tests/Unit/Writer/ (192 testes, incluindo as suítes Pdf20StreamStrategy, PdfXrefWriter e Linearizer*) e pela baseline tests/Golden/PdfWriter/PdfWriterGoldenBaselineSmokeTest.
Isto não é uma alegação de conformidade total com PDF 2.0. A conformidade total com a ISO 32000-2 é uma propriedade de um documento completo validado por um oráculo externo, não do serializador isoladamente. A conformidade de ponta a ponta é afirmada apenas onde um oráculo a confirma: tests/Integration/Accessibility/VeraPdfUa2GoldenTest valida um fixture gerado contra o veraPDF para PDF/UA-2, e tests/Standards/Profile/PdfRConformanceTest cobre o perfil PDF/R. O golden test do veraPDF é ignorado quando o binário do veraPDF está ausente no runner; portanto, é uma porta de oráculo opcional, não incondicional. Defina VERAPDF_BINARY para executá-lo. A seleção do perfil de arquivamento (PDF/A-3 → PDF 1.7, PDF/A-4 → PDF 2.0) é decidida pela ADR-011 e pelo modo de conformidade, e validada pelas suítes de conformidade em /modules/core/conformance/. Fora desses perfis respaldados por oráculo, afirme que o Writer “produz estruturas de PDF 2.0; a conformidade é validada pelo veraPDF para o perfil PDF/UA-2”, em vez de afirmar conformidade sem ressalvas.