ContentStream: emissor de content streams de PDF
Visão geral
Seção intitulada “Visão geral”O módulo ContentStream emite operadores de conteúdo marcado (marked content) para Portable Document Format (PDF). Ele abre e fecha tags de estrutura e artefatos, acompanha a profundidade de aninhamento e retorna o buffer de operadores.
Instalação
Seção intitulada “Instalação”composer require nextpdf/core:^3Visão geral conceitual
Seção intitulada “Visão geral conceitual”ContentStreamBuilder é a única classe do módulo. Ela constrói a camada de conteúdo marcado de um content stream de página. Um content stream codifica o conteúdo da página como uma sequência de operadores — ISO 32000-2 §8. Em seguida, o builder emite operadores de conteúdo marcado em torno desse conteúdo.
append() adiciona literalmente bytes brutos de operador. O builder não faz escape dessa entrada. Você é responsável pela validade dela. Use essa fronteira quando o pipeline de HTML e o módulo Graphics precisarem intercalar seus próprios operadores.
beginTag() abre uma sequência com tag de estrutura. Ele emite um operador BDC com uma lista de propriedades MCID, conforme a ISO 32000-2 §14.6. endTag() emite o operador EMC correspondente. O builder contabiliza a profundidade de aninhamento. Se você chamar endTag() sem nenhuma sequência aberta, ele lança PageLayoutException em vez de escrever um EMC desbalanceado.
beginArtifact() abre uma sequência de artefato. Use artefatos para elementos decorativos de paginação — cabeçalhos, rodapés, números de página e linhas — que devem ficar fora da árvore de estrutura, conforme a ISO 32000-2 §14.8.2.2. O subtipo é um de quatro valores ISO: Pagination, Layout, Page ou Background. Prefira o enum tipado ArtifactSubtype. A sobrecarga de string é validada contra o enum; portanto, um valor fora do padrão falha imediatamente.
relabelTag() reescreve, no lugar, uma tag emitida anteriormente. finish() retorna o buffer completo e lança uma exceção se o conteúdo marcado estiver desbalanceado. drain() retorna o buffer acumulado até o momento, sem a verificação de balanceamento, para streaming incremental. peek() retorna o buffer sem consumi-lo. reset() limpa o estado.
Superfície da API
Seção intitulada “Superfície da API”| Método | Assinatura | Função |
|---|---|---|
append() | append(string $raw): void | Adiciona literalmente bytes brutos de operador (sem escape) |
beginTag() | beginTag(string $structType, int $mcid): void | Abre uma sequência BDC de estrutura |
endTag() | endTag(): void | Fecha a sequência mais interna com EMC |
beginArtifact() | beginArtifact(ArtifactSubtype|string $type): void | Abre uma sequência de artefato |
endArtifact() | endArtifact(): void | Fecha o artefato mais interno |
getMarkedContentDepth() | getMarkedContentDepth(): int | Retorna a profundidade de aninhamento atual |
relabelTag() | relabelTag(string $old, string $new, int $mcid): void | Reescreve uma tag emitida no lugar |
finish() | finish(): string | Retorna o buffer completo; lança uma exceção se estiver desbalanceado |
drain() | drain(): string | Retorna o buffer sem a verificação de balanceamento |
peek() | peek(): string | Retorna o buffer sem consumi-lo |
reset() | reset(): void | Limpa todo o estado |
Execute composer docs:generate-api-php -- --module=ContentStream 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”<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\ContentStream\ContentStreamBuilder;
$builder = new ContentStreamBuilder();
$builder->beginTag('P', mcid: 0);$builder->append("BT /F1 12 Tf 72 720 Td (Hello) Tj ET\n");$builder->endTag();
$pageContent = $builder->finish();Exemplo de código — Produção
Seção intitulada “Exemplo de código — Produção”Use este padrão para envolver um parágrafo em uma tag de estrutura e um rodapé em um artefato. O padrão envia o buffer incrementalmente por streaming com drain().
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Accessibility\ArtifactSubtype;use NextPDF\ContentStream\ContentStreamBuilder;
$builder = new ContentStreamBuilder();
$builder->beginTag('H1', mcid: 0);$builder->append($titleOperators);$builder->endTag();
$builder->beginArtifact(ArtifactSubtype::Pagination);$builder->append($footerOperators);$builder->endArtifact();
if ($builder->getMarkedContentDepth() !== 0) { throw new RuntimeException('Unbalanced marked content before flush.');}
$chunk = $builder->drain();Casos extremos e pegadinhas
Seção intitulada “Casos extremos e pegadinhas”append()não faz escape da entrada. Passe apenas bytes de operador válidos. O builder confia no código que o chama.endTag()eendArtifact()lançam exceção em caso de underflow. Nunca feche uma sequência que não esteja aberta.finish()verifica o balanceamento e lança uma exceção quando a profundidade não é zero.drain()não verifica. Usedrain()apenas para streaming incremental.- O contador de profundidade não distingue tags de artefatos.
EMCfecha a sequência mais interna de qualquer um dos tipos. Aninhe as sequências em uma ordem estrita. - A sobrecarga de string de
beginArtifact()é validada contra o enum. Um subtipo fora do padrão falha na chamada, não na saída. relabelTag()reescreve uma tag emitida. Use o mesmomcidque você usou para emiti-la.
Desempenho
Seção intitulada “Desempenho”Cada operação é um append de string O(1), exceto relabelTag(), que realiza uma reescrita O(buffer). O módulo mantém um buffer de string e um contador inteiro de profundidade. Ele não faz parsing e aloca apenas o buffer. O orçamento da carga de trabalho de referência é de 1500 ms de tempo de parede e 64 MB de pico. Este módulo permanece bem abaixo desse limite.
Notas de segurança
Seção intitulada “Notas de segurança”append() é a fronteira de confiança. O builder escreve os bytes literalmente; portanto, o código upstream deve fazer escape de qualquer string que chegue a um operador de string literal. O escaper canônico é PdfStringEscaper::escapeLiteral() (ADR-015). Nunca passe texto de usuário sem escape por append(). As verificações de balanceamento em endTag(), endArtifact() e finish() impedem que uma árvore de conteúdo marcado malformada chegue ao Writer. Consulte /modules/core/security/ para o modelo de ameaças do documento.
Conformidade
Seção intitulada “Conformidade”O módulo emite estruturas de operadores de conteúdo marcado consistentes com a ISO 32000-2: pares BDC/EMC com uma lista de propriedades MCID conforme a §14.6, e sequências de artefato conforme a §14.8.2.2. Estes são fatos de implementação. A evidência é src/ContentStream/ContentStreamBuilder.php, o enum src/Accessibility/ArtifactSubtype.php e tests/Unit/ContentStream/ContentStreamBuilderMarkedContentBalanceCoverageTest mais ContentStreamBuilderRelabelTagInvariantTest. Eles não constituem uma afirmação de conformidade ponta a ponta com PDF/UA-2 ou PDF 2.0. Um oráculo externo valida a estrutura de PDF marcado da qual esses operadores participam: tests/Integration/Accessibility/VeraPdfUa2GoldenTest verifica um fixture gerado contra o veraPDF para o perfil PDF/UA-2. Esse teste de oráculo é ignorado quando o binário do veraPDF está ausente; portanto, é um gate opcional. Declare que este módulo “produz estruturas de conteúdo marcado; a conformidade com PDF/UA-2 é validada pelo veraPDF” em vez de afirmar conformidade sem ressalvas.