Pular para o conteúdo
getnextpdf.com

Pro edição

Diff — Referência Profunda

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.

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.

SímboloParâmetrosComportamento padrãoRetornaLança ou falha comNotas
PdfDiffer::compare()string $sourcePdf, string $targetPdfExtrai o texto de cada página e, então, compara a página i da origem com a página i do destinoDiffResultInvalidArgumentException quando um buffer não tem o cabeçalho %PDF ou o leitor opcional falha ao analisar; OverflowException ao atingir um limite de recursosPonto de entrada estático
PdfDiffer::compareTexts()array $sourcePages, array $targetPages (list<string> cada)Compara textos de página pré-extraídos, ignorando a extraçãoDiffResultOverflowException ao atingir um limite de recursosEstático; use quando o texto já estiver disponível
PdfDiffer::extractText()string $contentStreamAnalisa os operadores de exibição de texto de um fluxo de conteúdo brutostring— (tolerante a falhas; entrada não analisável resulta em uma string vazia)Estático
StructuredDiffer::__construct()?ImageDiffer $imageDiffer = null, ?MetadataDiffer $metadataDiffer = nullargumentos null constroem os differs padrãoInjeção via construtor para testes
StructuredDiffer::compare()string $sourcePdf, string $targetPdfExecuta a comparação de texto, parágrafo, imagem e metadados e, então, monta um resumoStructuredDiffResultPropaga InvalidArgumentException e OverflowException do caminho de textoOrquestrador de todo o módulo
DiffFormatter::toJson()StructuredDiffResult $resultDocumento JSON formatado (pretty-print)stringJsonException quando a codificação falha
DiffFormatter::toHtml()StructuredDiffResult $resultFragmento HTML com seções de resumo, parágrafo e metadados; os valores de texto têm entidades escapadasstringApenas fragmento, não um documento completo
DiffFormatter::toArray()StructuredDiffResult $resultArray de serialização que sustenta toJson()array<string, mixed>Chaves snake_case estáveis
ImageDiffer::diff()string $sourcePdf, string $targetPdfFaz o hash dos XObjects de imagem e relata imagens adicionadas, removidas e modificadaslist<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 $targetPdfCompara 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 = 10000Diff de linhas de Myers sobre duas listas de linhaslist<DiffRegion>OverflowException quando as linhas combinadas excedem $maxLines ou a distância de edição excede o limite de memóriaEstático; o produtor de regiões para todos os caminhos de texto
TextExtractor::fromContentStream()string $contentStreamTokeniza o fluxo e executa a máquina de estado de textolist<TextBlock>Estático
TextExtractor::fromOperations()array $operations (list<ContentStreamOp>)Executa a máquina de estado de texto sobre operações pré-analisadaslist<TextBlock>Estático
ContentStreamParser::parse()o construtor recebe string $dataTokeniza operadores e operandos; ignora dicionários e comentários; tolerante a falhaslist<ContentStreamOp>Bytes não reconhecidos são ignorados, nunca fatais
ContentStreamOpstring $operator, list<mixed> $operandsObjeto de valor de operação readonly; isTextOp() classifica operadores relacionados a texto
DiffResultlist<DiffRegion> $regions, int $sourcePagesCount, int $targetPagesCountAgrupa regiões em $added, $removed, $modified; expõe isIdentical(), hasDifferences(), totalChanges()Readonly; regiões Unchanged permanecem apenas em $regions
StructuredDiffResultdiff de texto, parágrafos, imagens, alterações de metadados, resumoResultado agregado; hasDifferences(), isIdentical() delegam ao resumoReadonly
DiffSummarycontagens por categoria mais contagens de páginashasDifferences() e totalChanges() sobre as contagens de texto, imagem e metadadosReadonly
DiffRegionDiffType $type, string $text, int $pageIndex, int $lineIndex, ?string $counterpartText = nullUma alteração em nível de linha$counterpartText permanece null no motor distribuído
ParagraphDifftipo, texto, índice de página, linha inicial/final, regiõesRegiões consecutivas de mesmo tipo em uma página; lineCount()Readonly
ImageDifftipo, índice de página, hash de origem, hash de destino, id do objetoUma entrada de alteração de imagemOs hashes são strings vazias no lado ausente
MetadataChangestring $field, ?string $sourceValue, ?string $targetValueUma alteração de campo; isAdded(), isRemoved(), isModified()null significa que o campo está ausente
TextBlocktexto, x, y, nome da fonte, tamanho da fonte, índice de linhaUm trecho de texto extraído com posição aproximadaReadonly
DiffTypeenum: Added, Removed, Modified, UnchangedClassificação de alteração baseada em string para textoVeja a nota sobre Modified no contrato de comportamento
ImageDiffTypeenum: Added, Removed, Modified, UnchangedClassificação de alteração baseada em string para imagens
public static function compare(string $sourcePdf, string $targetPdf): DiffResult
public static function compareTexts(array $sourcePages, array $targetPages): DiffResult
public static function extractText(string $contentStream): string
public function __construct(
?ImageDiffer $imageDiffer = null,
?MetadataDiffer $metadataDiffer = null,
)
public function compare(string $sourcePdf, string $targetPdf): StructuredDiffResult
public function toJson(StructuredDiffResult $result): string
public function toHtml(StructuredDiffResult $result): string
public function toArray(StructuredDiffResult $result): array
public static function diff(
array $sourceLines,
array $targetLines,
int $pageIndex = 0,
int $maxLines = self::MAX_DIFF_LINES,
): array

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.

A extração tem dois caminhos:

  • Leitor opcional do Artisan presente. Quando a classe opcional NextPDF\Parser\PdfReader está 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/endstream com strpos, 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 /DecodeParms conforme 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.

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.

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.

  • 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 %PDF falha com InvalidArgumentException antes 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 OverflowException quando 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 usa strpos, 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.
AlegaçãoNormaCláusula
Os operadores de exibição de texto Tj e TJ são analisados para extraçãoISO 32000-2:2020§9.4
Os dados de fluxo do fallback começam após o CRLF ou LF que segue a palavra-chave streamISO 32000-2:2020§7.3.8.1
As extensões dos fluxos na varredura de imagens são governadas pela entrada /Length do dicionárioISO 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 /FirstISO 32000-2:2020§7.5.7
A reversão do preditor PNG segue o parâmetro Predictor de /DecodeParmsISO 32000-2:2020§7.4.4.4
Os valores de metadados decodificam as formas de string literal e hexadecimalISO 32000-2:2020§7.3.4.2, §7.3.4.3
Saída de PDF com redline lado a lado visualNã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.

  • Disponibilidade dentro do pacote Pro: PdfDiffer, DiffEngine, TextExtractor e seus objetos de valor desde a 1.8.0; StructuredDiffer, DiffFormatter, ImageDiffer, MetadataDiffer e os deles desde a 2.2.0. Todos estão atuais no nextpdf/pro 3.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 OverflowException ao 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 StructuredDiffer com differs stub nos testes para isolar o caminho de texto da varredura de imagens e metadados.

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.