Salta ai contenuti
getnextpdf.com

Pro edizione

Diff — Riferimento approfondito

Questa pagina è il riferimento a livello di contratto per il modulo di diff di NextPDF Pro, NextPDF\Pro\Diff. Il modulo confronta due documenti PDF e segnala le modifiche di testo, immagini e metadati. PdfDiffer produce un diff per righe di Myers allineato alle pagine. StructuredDiffer aggiunge il raggruppamento in paragrafi, il confronto delle immagini e il confronto dei metadati. DiffFormatter serializza il risultato strutturato in JSON o in un frammento HTML. Questa pagina definisce l’API pubblica, il contratto di comportamento osservabile, i limiti sulle risorse e le modalità di errore. La configurazione orientata ai compiti e gli esempi si trovano nella pagina della funzionalità Diff.

Questa funzionalità è distribuita in NextPDF Pro (nextpdf/pro) e si attiva con un envelope di licenza di livello Pro. Un deployment privo di tale entitlement non carica le classi della funzionalità. Confronta le edizioni e ottieni una licenza.

Nessun flag di capacità a runtime applica un gate a questo modulo. Le classi di diff sono utilizzabili ogni volta che nextpdf/pro è installato e provvisto di licenza.

SimboloParametriComportamento predefinitoRestituisceGenera o fallisce conNote
PdfDiffer::compare()string $sourcePdf, string $targetPdfEstrae il testo pagina per pagina, quindi confronta con diff la pagina i dell’origine con la pagina i della destinazioneDiffResultInvalidArgumentException quando un buffer è privo dell’intestazione %PDF o il lettore opzionale non riesce ad analizzare; OverflowException al superamento di un limite sulle risorsePunto d’ingresso statico
PdfDiffer::compareTexts()array $sourcePages, array $targetPages (list<string> ciascuno)Esegue il diff di testi di pagina già estratti, saltando l’estrazioneDiffResultOverflowException al superamento di un limite sulle risorseStatico; da usare quando il testo è già disponibile
PdfDiffer::extractText()string $contentStreamAnalizza gli operatori di visualizzazione del testo da un singolo content stream grezzostring— (tollerante ai guasti; un input non analizzabile produce una stringa vuota)Statico
StructuredDiffer::__construct()?ImageDiffer $imageDiffer = null, ?MetadataDiffer $metadataDiffer = nullGli argomenti null costruiscono i differ predefinitiIniezione tramite costruttore per il testing
StructuredDiffer::compare()string $sourcePdf, string $targetPdfEsegue il confronto di testo, paragrafi, immagini e metadati, quindi costruisce un riepilogoStructuredDiffResultPropaga InvalidArgumentException e OverflowException dal percorso del testoOrchestratore sull’intero modulo
DiffFormatter::toJson()StructuredDiffResult $resultDocumento JSON formattato in modo leggibilestringJsonException quando la codifica fallisce
DiffFormatter::toHtml()StructuredDiffResult $resultFrammento HTML con sezioni di riepilogo, paragrafi e metadati; i valori di testo sono sottoposti a escaping delle entitàstringSolo frammento, non un documento completo
DiffFormatter::toArray()StructuredDiffResult $resultArray di serializzazione alla base di toJson()array<string, mixed>Chiavi snake_case stabili
ImageDiffer::diff()string $sourcePdf, string $targetPdfCalcola l’hash degli XObject immagine e segnala le immagini aggiunte, rimosse e modificatelist<ImageDiff>— (le strutture non decodificabili vengono ignorate con approccio fail-closed)L’identità è il bucket di pagina più il numero dell’oggetto
MetadataDiffer::diff()string $sourcePdf, string $targetPdfConfronta otto campi /Info (Title, Author, Subject, Keywords, Creator, Producer, CreationDate, ModDate)list<MetadataChange>— (non genera mai eccezioni su input non conforme)Valori confrontati come stringhe decodificate
DiffEngine::diff()array $sourceLines, array $targetLines, int $pageIndex = 0, int $maxLines = 10000Diff per righe di Myers su due liste di righelist<DiffRegion>OverflowException quando le righe combinate superano $maxLines o la distanza di edit supera il limite legato alla memoriaStatico; il produttore di regioni per tutti i percorsi del testo
TextExtractor::fromContentStream()string $contentStreamTokenizza lo stream ed esegue la macchina a stati del testolist<TextBlock>Statico
TextExtractor::fromOperations()array $operations (list<ContentStreamOp>)Esegue la macchina a stati del testo su operazioni già analizzatelist<TextBlock>Statico
ContentStreamParser::parse()il costruttore accetta string $dataTokenizza operatori e operandi; ignora dizionari e commenti; tollerante ai guastilist<ContentStreamOp>I byte non riconosciuti vengono ignorati, mai fatali
ContentStreamOpstring $operator, list<mixed> $operandsValue object di operazione readonly; isTextOp() classifica gli operatori relativi al testo
DiffResultlist<DiffRegion> $regions, int $sourcePagesCount, int $targetPagesCountRaggruppa le regioni in $added, $removed, $modified; espone isIdentical(), hasDifferences(), totalChanges()Readonly; le regioni Unchanged rimangono solo in $regions
StructuredDiffResultdiff del testo, paragrafi, immagini, modifiche ai metadati, riepilogoRisultato aggregato; hasDifferences(), isIdentical() delegano al riepilogoReadonly
DiffSummaryconteggi per categoria più conteggi di paginahasDifferences() e totalChanges() sui conteggi di testo, immagini e metadatiReadonly
DiffRegionDiffType $type, string $text, int $pageIndex, int $lineIndex, ?string $counterpartText = nullUna singola modifica a livello di riga$counterpartText rimane null nel motore distribuito
ParagraphDifftipo, testo, indice di pagina, riga iniziale/finale, regioniRegioni consecutive dello stesso tipo su una pagina; lineCount()Readonly
ImageDifftipo, indice di pagina, hash di origine, hash di destinazione, id dell’oggettoUna singola voce di modifica immagineGli hash sono stringhe vuote sul lato assente
MetadataChangestring $field, ?string $sourceValue, ?string $targetValueUna singola modifica di campo; isAdded(), isRemoved(), isModified()null indica che il campo è assente
TextBlocktesto, x, y, nome del font, dimensione del font, indice di rigaUna singola sequenza di testo estratta con posizione approssimativaReadonly
DiffTypeenum: Added, Removed, Modified, UnchangedClassificazione delle modifiche basata su stringa per il testoVedere la nota su Modified nel contratto di comportamento
ImageDiffTypeenum: Added, Removed, Modified, UnchangedClassificazione delle modifiche basata su stringa per le immagini
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() estrae il testo pagina per pagina, quindi confronta con diff la pagina i dell’origine con la pagina i della destinazione. Quando il numero di pagine differisce, per le pagine in eccesso il lato mancante viene trattato come testo vuoto. All’interno di ciascuna coppia di pagine, il testo viene suddiviso in base ai ritorni a capo e viene eseguito un diff per righe di Myers per ogni pagina. Il motore emette regioni Added, Removed e Unchanged. Una riga modificata emerge come una regione Removed più una Added; il motore distribuito non emette mai regioni di testo Modified. Il caso Modified e il bucket DiffResult::$modified servono per i risultati costruiti dal chiamante, poiché il costruttore di DiffResult è pubblico. totalChanges() conta le regioni aggiunte, rimosse e modificate; le regioni invariate sono escluse.

L’estrazione ha due percorsi:

  • Lettore Artisan opzionale presente. Quando la classe opzionale NextPDF\Parser\PdfReader è installata, i content stream delle pagine vengono letti attraverso di essa per ottenere testo accurato a livello di pagina. Il numero di pagine del trailer guida il ciclo. Una pagina che non riesce a essere letta contribuisce con testo vuoto anziché interrompere il confronto.
  • Fallback. Uno scanner a livello di byte, con limiti, individua le coppie stream/endstream tramite strpos, decomprime i dati FlateDecode con un limite rigido di output di 50 MB e applica il filtro inverso a un predittore PNG quando il dizionario dello stream ne richiede uno tramite /DecodeParms secondo ISO 32000-2:2020 §7.4.4.4. Un predittore malformato o non supportato lascia invariati i byte decodificati. Il fallback concatena tutto il testo recuperato in un singolo bucket di pagina, pertanto l’allineamento a livello di pagina è accurato solo nel percorso del lettore.

Entrambi i percorsi analizzano gli operatori di visualizzazione del testo del §9.4 Tj, TJ e '. La macchina a stati traccia BT/ET, Tm (solo origine), Td/TD, T* e Tf.

StructuredDiffer::compare() esegue il diff del testo, raggruppa in paragrafi le regioni consecutive dello stesso tipo sulla stessa pagina (incluse le sequenze invariate), quindi esegue il confronto di immagini e metadati e assembla un DiffSummary. I conteggi dei paragrafi del riepilogo coprono solo i paragrafi aggiunti, rimossi e modificati.

Il confronto delle immagini enumera gli oggetti PDF in modo strutturale. L’estensione del corpo di uno stream è governata dalla sua voce /Length secondo §7.3.8.2, pertanto i byte binari che semplicemente somigliano alla sintassi degli oggetti non vengono mai registrati come oggetti fantasma. Gli object stream compressi (/Type /ObjStm) vengono decodificati secondo §7.5.7 affinché gli XObject immagine annidati al loro interno siano visibili. Ogni immagine rilevata viene sottoposta a hash del contenuto con la funzione non crittografica xxh128; l’identità è la coppia formata dal bucket di pagina e dal numero dell’oggetto. Le immagini prive di una pagina proprietaria nell’ordine dello stream vengono attribuite alla pagina 0.

Il confronto dei metadati risolve il dizionario /Info reale attraverso il trailer quando possibile, così che un token di campo civetta all’interno di un content stream non venga scambiato per metadati del documento. I valori dei campi vengono decodificati come stringhe PDF: la forma letterale secondo §7.3.4.2 e la forma esadecimale secondo §7.3.4.3. In assenza di un trailer risolvibile, la ricerca ripiega sull’intero input. Le date vengono confrontate come stringhe decodificate, non come timestamp analizzati.

DiffFormatter::toJson() restituisce JSON formattato in modo leggibile e codifica con JSON_THROW_ON_ERROR, così che un errore di codifica sollevi JsonException anziché restituire false. toHtml() restituisce un frammento <div class="nextpdf-diff">; il testo dei paragrafi e i valori dei metadati passano attraverso l’escaping delle entità HTML. Non esiste alcun output PDF visivo affiancato con revisioni evidenziate. Per input identici, le regioni e l’output formattato sono deterministici.

  • L’allineamento delle pagine è posizionale. Una singola pagina inserita o eliminata sposta l’allineamento di tutte le pagine successive e gonfia i conteggi delle modifiche a valle.
  • Nel percorso di estrazione di fallback, tutto il testo finisce all’indice di pagina 0. Eseguire il diff di un documento estratto tramite lettore rispetto ad aspettative provenienti dal percorso di fallback produce un’attribuzione di pagina differente.
  • Un buffer di origine o destinazione che non inizia con %PDF fallisce con InvalidArgumentException prima di qualsiasi confronto.
  • Più di 10,000 righe combinate in una coppia di pagine fallisce con OverflowException (limite sul numero di righe).
  • Due testi di pagina che condividono troppo poche righe falliscono con OverflowException quando la distanza di edit di Myers supera il limite legato alla memoria. Le revisioni legittime condividono la maggior parte delle righe e non ne sono influenzate; gli input ostili con scarsa comunanza fanno scattare il limite.
  • Un output dello stream di fallback decompresso superiore a 50 MB fallisce con OverflowException (limite anti-bomba di decompressione). Lo scanner usa strpos, non una regex illimitata, pertanto un input artefatto non può innescare un backtracking catastrofico.
  • L’operatore di visualizzazione del testo " viene tokenizzato ma non produce alcun blocco di testo in 3.1.0; il testo mostrato solo tramite " non partecipa al diff.
  • I PDF scansionati, contenenti solo immagini, producono poco o nessun diff di testo. Non viene eseguito alcun OCR.
  • Il rilevamento delle modifiche alle immagini è strutturale, non percettivo. Non rasterizza le pagine e un’immagine ricodificata con pixel identici viene segnalata come modificata quando i suoi byte differiscono.
  • Un’immagine il cui bucket di pagina o numero dell’oggetto cambia tra le revisioni viene segnalata come una coppia rimossa-più-aggiunta, non come modificata.
  • Gli object stream compressi con filtri diversi da FlateDecode vengono ignorati con approccio fail-closed; le loro immagini membro non vengono confrontate.
  • In questo modulo non avviene alcuna operazione crittografica, pertanto non esiste alcun comportamento specifico della modalità FIPS. L’hash dell’immagine serve solo al rilevamento delle modifiche e non ha alcun valore di integrità o probatorio.
AffermazioneStandardClausola
Gli operatori di visualizzazione del testo Tj e TJ vengono analizzati per l’estrazioneISO 32000-2:2020§9.4
I dati dello stream di fallback iniziano dopo il CRLF o LF che segue la parola chiave streamISO 32000-2:2020§7.3.8.1
Le estensioni dello stream nella scansione delle immagini sono governate dalla voce /Length del dizionarioISO 32000-2:2020§7.3.8.2
I membri dell’object stream vengono individuati tramite la tabella di coppie /N e l’offset /FirstISO 32000-2:2020§7.5.7
L’inversione del predittore PNG segue il parametro Predictor di /DecodeParmsISO 32000-2:2020§7.4.4.4
I valori dei metadati decodificano le forme di stringa letterale ed esadecimaleISO 32000-2:2020§7.3.4.2, §7.3.4.3
Output PDF visivo affiancato con revisioni evidenziateNon supportato (solo JSON/HTML)

Tutte le clausole sono parafrasate; NextPDF non riproduce il testo normativo. Si tratta di dichiarazioni di funzionalità, non di certificazioni; NextPDF non detiene alcuna certificazione e non ne concede alcuna. Il recupero del testo ricostruisce il testo delle righe a partire dagli operatori di visualizzazione del testo. Non esegue la macchina a stati completa del testo del §9.4, pertanto il diff è a livello di contenuto, non di geometria.

  • Disponibilità all’interno del pacchetto Pro: PdfDiffer, DiffEngine, TextExtractor e i relativi value object dalla 1.8.0; StructuredDiffer, DiffFormatter, ImageDiffer, MetadataDiffer e i relativi dalla 2.2.0. Tutti sono attuali in nextpdf/pro 3.1.0.
  • Preferire PdfDiffer::compareTexts() quando il testo della pagina è già disponibile; salta completamente l’estrazione e le sue modalità di errore.
  • Il lettore Artisan opzionale migliora l’accuratezza dell’estrazione e l’attribuzione delle pagine. Viene rilevato a runtime e non è mai obbligatorio.
  • Intercettare OverflowException quando si esegue il diff di input non attendibile; i limiti sono rifiuti deliberati fail-closed, non errori transitori.
  • DiffFormatter::toHtml() emette nomi di classe (diff-added, diff-removed, diff-modified, diff-unchanged) ma nessun foglio di stile; fornire il proprio CSS.
  • Costruire StructuredDiffer con differ stub nei test per isolare il percorso del testo dalla scansione di immagini e metadati.

Questa pagina documenta esclusivamente il comportamento osservabile dall’esterno e la superficie API pubblica supportata. I percorsi di namespace interni, le classi helper, le tabelle dei meccanismi, i nomi dei file di runbook e i prefissi dei ticket sono fuori ambito.