Pro edizione
Diff — Riferimento approfondito
In breve
Sezione intitolata “In breve”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.
Disponibilità e licenza
Sezione intitolata “Disponibilità e licenza”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.
Superficie API pubblica
Sezione intitolata “Superficie API pubblica”| Simbolo | Parametri | Comportamento predefinito | Restituisce | Genera o fallisce con | Note |
|---|---|---|---|---|---|
PdfDiffer::compare() | string $sourcePdf, string $targetPdf | Estrae il testo pagina per pagina, quindi confronta con diff la pagina i dell’origine con la pagina i della destinazione | DiffResult | InvalidArgumentException quando un buffer è privo dell’intestazione %PDF o il lettore opzionale non riesce ad analizzare; OverflowException al superamento di un limite sulle risorse | Punto d’ingresso statico |
PdfDiffer::compareTexts() | array $sourcePages, array $targetPages (list<string> ciascuno) | Esegue il diff di testi di pagina già estratti, saltando l’estrazione | DiffResult | OverflowException al superamento di un limite sulle risorse | Statico; da usare quando il testo è già disponibile |
PdfDiffer::extractText() | string $contentStream | Analizza gli operatori di visualizzazione del testo da un singolo content stream grezzo | string | — (tollerante ai guasti; un input non analizzabile produce una stringa vuota) | Statico |
StructuredDiffer::__construct() | ?ImageDiffer $imageDiffer = null, ?MetadataDiffer $metadataDiffer = null | Gli argomenti null costruiscono i differ predefiniti | — | — | Iniezione tramite costruttore per il testing |
StructuredDiffer::compare() | string $sourcePdf, string $targetPdf | Esegue il confronto di testo, paragrafi, immagini e metadati, quindi costruisce un riepilogo | StructuredDiffResult | Propaga InvalidArgumentException e OverflowException dal percorso del testo | Orchestratore sull’intero modulo |
DiffFormatter::toJson() | StructuredDiffResult $result | Documento JSON formattato in modo leggibile | string | JsonException quando la codifica fallisce | — |
DiffFormatter::toHtml() | StructuredDiffResult $result | Frammento HTML con sezioni di riepilogo, paragrafi e metadati; i valori di testo sono sottoposti a escaping delle entità | string | — | Solo frammento, non un documento completo |
DiffFormatter::toArray() | StructuredDiffResult $result | Array di serializzazione alla base di toJson() | array<string, mixed> | — | Chiavi snake_case stabili |
ImageDiffer::diff() | string $sourcePdf, string $targetPdf | Calcola l’hash degli XObject immagine e segnala le immagini aggiunte, rimosse e modificate | list<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 $targetPdf | Confronta 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 = 10000 | Diff per righe di Myers su due liste di righe | list<DiffRegion> | OverflowException quando le righe combinate superano $maxLines o la distanza di edit supera il limite legato alla memoria | Statico; il produttore di regioni per tutti i percorsi del testo |
TextExtractor::fromContentStream() | string $contentStream | Tokenizza lo stream ed esegue la macchina a stati del testo | list<TextBlock> | — | Statico |
TextExtractor::fromOperations() | array $operations (list<ContentStreamOp>) | Esegue la macchina a stati del testo su operazioni già analizzate | list<TextBlock> | — | Statico |
ContentStreamParser::parse() | il costruttore accetta string $data | Tokenizza operatori e operandi; ignora dizionari e commenti; tollerante ai guasti | list<ContentStreamOp> | — | I byte non riconosciuti vengono ignorati, mai fatali |
ContentStreamOp | string $operator, list<mixed> $operands | Value object di operazione readonly; isTextOp() classifica gli operatori relativi al testo | — | — | — |
DiffResult | list<DiffRegion> $regions, int $sourcePagesCount, int $targetPagesCount | Raggruppa le regioni in $added, $removed, $modified; espone isIdentical(), hasDifferences(), totalChanges() | — | — | Readonly; le regioni Unchanged rimangono solo in $regions |
StructuredDiffResult | diff del testo, paragrafi, immagini, modifiche ai metadati, riepilogo | Risultato aggregato; hasDifferences(), isIdentical() delegano al riepilogo | — | — | Readonly |
DiffSummary | conteggi per categoria più conteggi di pagina | hasDifferences() e totalChanges() sui conteggi di testo, immagini e metadati | — | — | Readonly |
DiffRegion | DiffType $type, string $text, int $pageIndex, int $lineIndex, ?string $counterpartText = null | Una singola modifica a livello di riga | — | — | $counterpartText rimane null nel motore distribuito |
ParagraphDiff | tipo, testo, indice di pagina, riga iniziale/finale, regioni | Regioni consecutive dello stesso tipo su una pagina; lineCount() | — | — | Readonly |
ImageDiff | tipo, indice di pagina, hash di origine, hash di destinazione, id dell’oggetto | Una singola voce di modifica immagine | — | — | Gli hash sono stringhe vuote sul lato assente |
MetadataChange | string $field, ?string $sourceValue, ?string $targetValue | Una singola modifica di campo; isAdded(), isRemoved(), isModified() | — | — | null indica che il campo è assente |
TextBlock | testo, x, y, nome del font, dimensione del font, indice di riga | Una singola sequenza di testo estratta con posizione approssimativa | — | — | Readonly |
DiffType | enum: Added, Removed, Modified, Unchanged | Classificazione delle modifiche basata su stringa per il testo | — | — | Vedere la nota su Modified nel contratto di comportamento |
ImageDiffType | enum: Added, Removed, Modified, Unchanged | Classificazione delle modifiche basata su stringa per le immagini | — | — | — |
Firme dei punti d’ingresso
Sezione intitolata “Firme dei punti d’ingresso”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,): arrayContratto di comportamento
Sezione intitolata “Contratto di comportamento”Allineamento delle pagine e diff per righe
Sezione intitolata “Allineamento delle pagine e diff per righe”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.
Percorsi di estrazione
Sezione intitolata “Percorsi di estrazione”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/endstreamtramitestrpos, 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/DecodeParmssecondo 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.
Confronto strutturato
Sezione intitolata “Confronto strutturato”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.
Output del report
Sezione intitolata “Output del report”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.
Casi limite e modalità di errore
Sezione intitolata “Casi limite e modalità di errore”- 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
%PDFfallisce conInvalidArgumentExceptionprima 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
OverflowExceptionquando 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 usastrpos, 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.
Conformità
Sezione intitolata “Conformità”| Affermazione | Standard | Clausola |
|---|---|---|
Gli operatori di visualizzazione del testo Tj e TJ vengono analizzati per l’estrazione | ISO 32000-2:2020 | §9.4 |
I dati dello stream di fallback iniziano dopo il CRLF o LF che segue la parola chiave stream | ISO 32000-2:2020 | §7.3.8.1 |
Le estensioni dello stream nella scansione delle immagini sono governate dalla voce /Length del dizionario | ISO 32000-2:2020 | §7.3.8.2 |
I membri dell’object stream vengono individuati tramite la tabella di coppie /N e l’offset /First | ISO 32000-2:2020 | §7.5.7 |
L’inversione del predittore PNG segue il parametro Predictor di /DecodeParms | ISO 32000-2:2020 | §7.4.4.4 |
| I valori dei metadati decodificano le forme di stringa letterale ed esadecimale | ISO 32000-2:2020 | §7.3.4.2, §7.3.4.3 |
| Output PDF visivo affiancato con revisioni evidenziate | — | Non 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.
Note di sviluppo
Sezione intitolata “Note di sviluppo”- Disponibilità all’interno del pacchetto Pro:
PdfDiffer,DiffEngine,TextExtractore i relativi value object dalla 1.8.0;StructuredDiffer,DiffFormatter,ImageDiffer,MetadataDiffere i relativi dalla 2.2.0. Tutti sono attuali innextpdf/pro3.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
OverflowExceptionquando 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
StructuredDiffercon differ stub nei test per isolare il percorso del testo dalla scansione di immagini e metadati.
Confine di pubblicazione
Sezione intitolata “Confine di pubblicazione”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.
Vedere anche
Sezione intitolata “Vedere anche”- Diff (funzionalità) — installazione, avvio rapido ed esempi di produzione.
- Converter — Riferimento approfondito
- Filter — Riferimento approfondito