Pro edição
Diff — Referência Profunda
Visão geral
Seção intitulada “Visão geral”Esta página é a referência em nível de contrato do módulo de diff do NextPDF Pro, NextPDF\Pro\Diff. O módulo compara dois documentos PDF e relata alterações de texto, imagem e metadados. PdfDiffer produz um diff de linhas de Myers alinhado por página. StructuredDiffer adiciona agrupamento em parágrafos, comparação de imagens e comparação de metadados. DiffFormatter serializa o resultado estruturado em JSON ou em um fragmento HTML. Esta página descreve a API pública, o contrato de comportamento observável, os limites de recursos e os modos de falha. A configuração orientada a tarefas e os exemplos estão na página da capacidade Diff.
Disponibilidade e licenciamento
Seção intitulada “Disponibilidade e licenciamento”Esta capacidade é fornecida no NextPDF Pro (nextpdf/pro) e é ativada com um envelope de licença de nível Pro. Uma implantação sem esse direito não carrega as classes da capacidade. Compare edições e obtenha uma licença.
Nenhum sinalizador de capacidade em tempo de execução restringe este módulo. As classes de diff podem ser usadas sempre que nextpdf/pro estiver instalado e licenciado.
Superfície da API pública
Seção intitulada “Superfície da API pública”| Símbolo | Parâmetros | Comportamento padrão | Retorna | Lança ou falha com | Notas |
|---|---|---|---|---|---|
PdfDiffer::compare() | string $sourcePdf, string $targetPdf | Extrai o texto de cada página e, então, compara a página i da origem com a página i do destino | DiffResult | InvalidArgumentException quando um buffer não tem o cabeçalho %PDF ou o leitor opcional falha ao analisar; OverflowException ao atingir um limite de recursos | Ponto de entrada estático |
PdfDiffer::compareTexts() | array $sourcePages, array $targetPages (list<string> cada) | Compara textos de página pré-extraídos, ignorando a extração | DiffResult | OverflowException ao atingir um limite de recursos | Estático; use quando o texto já estiver disponível |
PdfDiffer::extractText() | string $contentStream | Analisa os operadores de exibição de texto de um fluxo de conteúdo bruto | string | — (tolerante a falhas; entrada não analisável resulta em uma string vazia) | Estático |
StructuredDiffer::__construct() | ?ImageDiffer $imageDiffer = null, ?MetadataDiffer $metadataDiffer = null | argumentos null constroem os differs padrão | — | — | Injeção via construtor para testes |
StructuredDiffer::compare() | string $sourcePdf, string $targetPdf | Executa a comparação de texto, parágrafo, imagem e metadados e, então, monta um resumo | StructuredDiffResult | Propaga InvalidArgumentException e OverflowException do caminho de texto | Orquestrador de todo o módulo |
DiffFormatter::toJson() | StructuredDiffResult $result | Documento JSON formatado (pretty-print) | string | JsonException quando a codificação falha | — |
DiffFormatter::toHtml() | StructuredDiffResult $result | Fragmento HTML com seções de resumo, parágrafo e metadados; os valores de texto têm entidades escapadas | string | — | Apenas fragmento, não um documento completo |
DiffFormatter::toArray() | StructuredDiffResult $result | Array de serialização que sustenta toJson() | array<string, mixed> | — | Chaves snake_case estáveis |
ImageDiffer::diff() | string $sourcePdf, string $targetPdf | Faz o hash dos XObjects de imagem e relata imagens adicionadas, removidas e modificadas | list<ImageDiff> | — (estruturas não decodificáveis são ignoradas com falha fechada) | A identidade é o bucket de página mais o número do objeto |
MetadataDiffer::diff() | string $sourcePdf, string $targetPdf | Compara oito campos /Info (Title, Author, Subject, Keywords, Creator, Producer, CreationDate, ModDate) | list<MetadataChange> | — (nunca lança em entrada não conforme) | Valores comparados como strings decodificadas |
DiffEngine::diff() | array $sourceLines, array $targetLines, int $pageIndex = 0, int $maxLines = 10000 | Diff de linhas de Myers sobre duas listas de linhas | list<DiffRegion> | OverflowException quando as linhas combinadas excedem $maxLines ou a distância de edição excede o limite de memória | Estático; o produtor de regiões para todos os caminhos de texto |
TextExtractor::fromContentStream() | string $contentStream | Tokeniza o fluxo e executa a máquina de estado de texto | list<TextBlock> | — | Estático |
TextExtractor::fromOperations() | array $operations (list<ContentStreamOp>) | Executa a máquina de estado de texto sobre operações pré-analisadas | list<TextBlock> | — | Estático |
ContentStreamParser::parse() | o construtor recebe string $data | Tokeniza operadores e operandos; ignora dicionários e comentários; tolerante a falhas | list<ContentStreamOp> | — | Bytes não reconhecidos são ignorados, nunca fatais |
ContentStreamOp | string $operator, list<mixed> $operands | Objeto de valor de operação readonly; isTextOp() classifica operadores relacionados a texto | — | — | — |
DiffResult | list<DiffRegion> $regions, int $sourcePagesCount, int $targetPagesCount | Agrupa regiões em $added, $removed, $modified; expõe isIdentical(), hasDifferences(), totalChanges() | — | — | Readonly; regiões Unchanged permanecem apenas em $regions |
StructuredDiffResult | diff de texto, parágrafos, imagens, alterações de metadados, resumo | Resultado agregado; hasDifferences(), isIdentical() delegam ao resumo | — | — | Readonly |
DiffSummary | contagens por categoria mais contagens de páginas | hasDifferences() e totalChanges() sobre as contagens de texto, imagem e metadados | — | — | Readonly |
DiffRegion | DiffType $type, string $text, int $pageIndex, int $lineIndex, ?string $counterpartText = null | Uma alteração em nível de linha | — | — | $counterpartText permanece null no motor distribuído |
ParagraphDiff | tipo, texto, índice de página, linha inicial/final, regiões | Regiões consecutivas de mesmo tipo em uma página; lineCount() | — | — | Readonly |
ImageDiff | tipo, índice de página, hash de origem, hash de destino, id do objeto | Uma entrada de alteração de imagem | — | — | Os hashes são strings vazias no lado ausente |
MetadataChange | string $field, ?string $sourceValue, ?string $targetValue | Uma alteração de campo; isAdded(), isRemoved(), isModified() | — | — | null significa que o campo está ausente |
TextBlock | texto, x, y, nome da fonte, tamanho da fonte, índice de linha | Um trecho de texto extraído com posição aproximada | — | — | Readonly |
DiffType | enum: Added, Removed, Modified, Unchanged | Classificação de alteração baseada em string para texto | — | — | Veja a nota sobre Modified no contrato de comportamento |
ImageDiffType | enum: Added, Removed, Modified, Unchanged | Classificação de alteração baseada em string para imagens | — | — | — |
Assinaturas dos pontos de entrada
Seção intitulada “Assinaturas dos pontos de entrada”public static function compare(string $sourcePdf, string $targetPdf): DiffResult
public static function compareTexts(array $sourcePages, array $targetPages): DiffResult
public static function extractText(string $contentStream): stringpublic function __construct( ?ImageDiffer $imageDiffer = null, ?MetadataDiffer $metadataDiffer = null,)
public function compare(string $sourcePdf, string $targetPdf): StructuredDiffResultpublic function toJson(StructuredDiffResult $result): string
public function toHtml(StructuredDiffResult $result): string
public function toArray(StructuredDiffResult $result): arraypublic static function diff( array $sourceLines, array $targetLines, int $pageIndex = 0, int $maxLines = self::MAX_DIFF_LINES,): arrayContrato de comportamento
Seção intitulada “Contrato de comportamento”Alinhamento de páginas e diff de linhas
Seção intitulada “Alinhamento de páginas e diff de linhas”PdfDiffer::compare() extrai o texto de cada página e, então, compara a página i da origem com a página i do destino. Quando as contagens de páginas diferem, o lado ausente é tratado como texto vazio nas páginas excedentes. Dentro de cada par de páginas, o texto é dividido nas quebras de linha e um diff de linhas de Myers é executado por página. O motor emite regiões Added, Removed e Unchanged. Uma linha alterada aparece como uma região Removed mais uma Added; o motor distribuído nunca emite regiões de texto Modified. O caso Modified e o bucket DiffResult::$modified atendem a resultados construídos pelo chamador, já que o construtor de DiffResult é público. totalChanges() conta as regiões adicionadas, removidas e modificadas; regiões inalteradas são excluídas.
Caminhos de extração
Seção intitulada “Caminhos de extração”A extração tem dois caminhos:
- Leitor opcional do Artisan presente. Quando a classe opcional
NextPDF\Parser\PdfReaderestá instalada, os fluxos de conteúdo das páginas são lidos por meio dela para obter texto preciso por página. A contagem de páginas do trailer controla o laço. Uma página que falha ao ser lida contribui com texto vazio em vez de abortar a comparação. - Fallback. Um scanner em nível de bytes com limites definidos localiza os pares
stream/endstreamcomstrpos, descompacta dados FlateDecode com um limite rígido de saída de 50 MB e aplica o filtro reverso de um preditor PNG quando o dicionário de fluxo o solicita por meio de/DecodeParmsconforme ISO 32000-2:2020 §7.4.4.4. Um preditor malformado ou não suportado deixa os bytes decodificados inalterados. O fallback concatena todo o texto recuperado em um único bucket de página, portanto o alinhamento em nível de página só é preciso por página no caminho do leitor.
Ambos os caminhos analisam os operadores de exibição de texto da §9.4 Tj, TJ e '. A máquina de estado rastreia BT/ET, Tm (apenas origem), Td/TD, T* e Tf.
Comparação estruturada
Seção intitulada “Comparação estruturada”StructuredDiffer::compare() executa o diff de texto, agrupa em parágrafos as regiões consecutivas de mesmo tipo na mesma página (incluindo os trechos inalterados), então executa a comparação de imagens e metadados e monta um DiffSummary. As contagens de parágrafos do resumo cobrem apenas parágrafos adicionados, removidos e modificados.
A comparação de imagens enumera os objetos PDF estruturalmente. A extensão do corpo de um fluxo é governada por sua entrada /Length conforme §7.3.8.2, de modo que bytes binários que apenas se assemelham à sintaxe de objeto nunca são registrados como objetos fantasmas. Fluxos de objetos comprimidos (/Type /ObjStm) são decodificados conforme §7.5.7 para que os XObjects de imagem aninhados neles sejam visíveis. Cada imagem detectada tem seu conteúdo hasheado com a função não criptográfica xxh128; a identidade é o par bucket de página e número do objeto. Imagens sem página proprietária na ordem do fluxo são atribuídas à página 0.
A comparação de metadados resolve o dicionário /Info real por meio do trailer quando possível, de modo que um token de campo falso dentro de um fluxo de conteúdo não seja confundido com metadados do documento. Os valores dos campos são decodificados como strings PDF: a forma literal conforme §7.3.4.2 e a forma hexadecimal conforme §7.3.4.3. Sem um trailer resolvível, a busca recai sobre toda a entrada. As datas são comparadas como strings decodificadas, não como timestamps analisados.
Saída do relatório
Seção intitulada “Saída do relatório”DiffFormatter::toJson() retorna JSON formatado (pretty-print) e codifica com JSON_THROW_ON_ERROR, de modo que uma falha de codificação levanta JsonException em vez de retornar false. toHtml() retorna um fragmento <div class="nextpdf-diff">; o texto dos parágrafos e os valores de metadados passam por escape de entidades HTML. Não há saída de PDF com redline lado a lado visual. Para entradas idênticas, as regiões e a saída formatada são determinísticas.
Casos extremos e modos de falha
Seção intitulada “Casos extremos e modos de falha”- O alinhamento de páginas é posicional. Uma única página inserida ou excluída desloca o alinhamento de todas as páginas subsequentes e infla as contagens de alterações a jusante.
- No caminho de extração de fallback, todo o texto cai no índice de página 0. Comparar um documento extraído pelo leitor com expectativas do caminho de fallback resulta em uma atribuição de página diferente.
- Um buffer de origem ou destino que não começa com
%PDFfalha comInvalidArgumentExceptionantes de qualquer comparação. - Mais de 10.000 linhas combinadas em um par de páginas falha com
OverflowException(limite de contagem de linhas). - Dois textos de página que compartilham poucas linhas falham com
OverflowExceptionquando a distância de edição de Myers excede o limite de memória. Revisões legítimas compartilham a maioria das linhas e não são afetadas; entradas adversárias de baixa semelhança acionam o limite. - A saída de fluxo descompactada do fallback maior que 50 MB falha com
OverflowException(limite de bomba de descompactação). O scanner usastrpos, não regex sem limites, portanto entradas forjadas não conseguem acionar retrocesso catastrófico. - O operador de exibição de texto
"é tokenizado mas não produz bloco de texto na 3.1.0; texto exibido apenas por meio de"não participa do diff. - PDFs digitalizados, somente com imagem, produzem pouco ou nenhum diff de texto. Nenhum OCR é executado.
- A detecção de alterações de imagem é estrutural, não perceptiva. Ela não rasteriza páginas, e uma imagem recodificada com pixels idênticos é relatada como modificada quando seus bytes diferem.
- Uma imagem cujo bucket de página ou número de objeto muda entre revisões é relatada como um par removido-mais-adicionado, não como modificada.
- Fluxos de objetos comprimidos com filtros diferentes de FlateDecode são ignorados com falha fechada; suas imagens membro não são comparadas.
- Nenhuma operação criptográfica ocorre neste módulo, portanto não existe nenhum comportamento específico de modo FIPS. O hash de imagem serve apenas para detecção de alterações e não carrega nenhum peso de integridade ou probatório.
Conformidade
Seção intitulada “Conformidade”| Alegação | Norma | Cláusula |
|---|---|---|
Os operadores de exibição de texto Tj e TJ são analisados para extração | ISO 32000-2:2020 | §9.4 |
Os dados de fluxo do fallback começam após o CRLF ou LF que segue a palavra-chave stream | ISO 32000-2:2020 | §7.3.8.1 |
As extensões dos fluxos na varredura de imagens são governadas pela entrada /Length do dicionário | ISO 32000-2:2020 | §7.3.8.2 |
Os membros dos fluxos de objetos são localizados por meio da tabela de pares /N e do offset /First | ISO 32000-2:2020 | §7.5.7 |
A reversão do preditor PNG segue o parâmetro Predictor de /DecodeParms | ISO 32000-2:2020 | §7.4.4.4 |
| Os valores de metadados decodificam as formas de string literal e hexadecimal | ISO 32000-2:2020 | §7.3.4.2, §7.3.4.3 |
| Saída de PDF com redline lado a lado visual | — | Não suportado (apenas JSON/HTML) |
Todas as cláusulas são parafraseadas; o NextPDF não reproduz texto normativo. Estas são declarações de capacidade, não certificações; o NextPDF não possui nenhuma certificação e não concede nenhuma. A recuperação de texto reconstrói o texto da linha a partir dos operadores de exibição de texto. Ela não executa a máquina de estado de texto completa da §9.4, portanto o diff é em nível de conteúdo, não em nível de geometria.
Notas de desenvolvimento
Seção intitulada “Notas de desenvolvimento”- Disponibilidade dentro do pacote Pro:
PdfDiffer,DiffEngine,TextExtractore seus objetos de valor desde a 1.8.0;StructuredDiffer,DiffFormatter,ImageDiffer,MetadataDiffere os deles desde a 2.2.0. Todos estão atuais nonextpdf/pro3.1.0. - Prefira
PdfDiffer::compareTexts()quando o texto da página já estiver disponível; ele ignora completamente a extração e seus modos de falha. - O leitor opcional do Artisan melhora a precisão da extração e a atribuição de páginas. Ele é detectado em tempo de execução e nunca é obrigatório.
- Capture
OverflowExceptionao comparar entradas não confiáveis; os limites são rejeições deliberadas com falha fechada, não erros transitórios. DiffFormatter::toHtml()emite nomes de classe (diff-added,diff-removed,diff-modified,diff-unchanged) mas nenhuma folha de estilo; forneça seu próprio CSS.- Construa
StructuredDiffercom differs stub nos testes para isolar o caminho de texto da varredura de imagens e metadados.
Limite de publicação
Seção intitulada “Limite de publicação”Esta página documenta apenas o comportamento observável externamente e a superfície da API pública suportada. Caminhos de namespace internos, classes auxiliares, tabelas de mecanismos, nomes de arquivo de runbook e prefixos de tickets estão fora de escopo.
Veja também
Seção intitulada “Veja também”- Diff (capacidade) — instalação, início rápido e exemplos de produção.
- Converter — Referência Profunda
- Filter — Referência Profunda