Aller au contenu
getnextpdf.com

Pro édition

Comparaison

NextPDF\Pro\Diff compare deux documents PDF et rend compte de ce qui a changé. Le chemin rapide produit un diff de texte aligné par page ; le chemin structuré ajoute la détection des changements d’image et de métadonnées et formate le résultat en JSON ou HTML.

Cette fonctionnalité est fournie dans NextPDF Pro (nextpdf/pro) et s’active avec une enveloppe de licence de niveau Pro. Un déploiement sans ce droit ne charge pas les classes de la fonctionnalité. Compare les éditions et obtiens une licence.

Aucun indicateur de capacité à l’exécution ne restreint les classes Diff ; elles sont présentes dès que le paquet Pro est installé.

Fenêtre de terminal
composer require nextpdf/pro:^3

PdfDiffer::compare() extrait le texte page par page de chaque document, le découpe en lignes et exécute un diff de lignes de Myers par paire de pages, ce qui produit des régions ajoutées, supprimées et modifiées. L’extraction de texte analyse les opérateurs d’affichage de texte d’ISO 32000-2:2020 §9.4 (Tj, TJ, ').

StructuredDiffer s’appuie là-dessus : il regroupe les régions de texte en changements de niveau paragraphe, compare les images embarquées, compare les métadonnées et produit un StructuredDiffResult avec un résumé agrégé. DiffFormatter sérialise ce résultat en chaîne JSON ou en fragment de rapport HTML.

Lorsque le lecteur PDF Artisan optionnel est installé, l’extraction de texte l’utilise pour un contenu exact à la page ; sinon, un repli borné au niveau des octets balaye directement les flux de contenu.

Le différenciateur compare le texte et la structure extraits, pas des pixels rendus. Une comparaison structurelle est déterministe, peu coûteuse et correspond aux changements éditoriaux qui importent à un relecteur. Un diff de pixels signalerait au contraire l’anticrénelage et le bruit de hinting de police comme du contenu. Parce qu’un PDF stocke des glyphes et du positionnement, pas des caractères prêts à lire, chaque comparaison reconstruit d’abord le texte à partir du flux de contenu. Cette étape d’extraction explique pourquoi le lecteur Artisan affine la précision, pourquoi le repli borné FlateDecode échange de la couverture contre de la sûreté, et pourquoi les pages scannées se différencient à peine. L’alignement des pages reste fondé sur l’indice pour la prévisibilité, si bien qu’une page insérée se lit comme un décalage net en aval.

Contexte de conception : Pourquoi le texte d’un PDF n’est pas vraiment du texte.

  • Entrée. Octets PDF bruts pour la source et la cible. Un tampon ne commençant pas par %PDF lève une InvalidArgumentException.
  • Sortie (chemin rapide). Un DiffResult avec les listes de régions added, removed, modified, plus isIdentical(), hasDifferences(), totalChanges().
  • Sortie (chemin structuré). Un StructuredDiffResult avec les diffs de paragraphes, les diffs d’images, les changements de métadonnées et un DiffSummary.
  • Sortie de rapport. DiffFormatter émet une chaîne JSON ou un fragment HTML. Il ne produit pas de PDF de relecture côte à côte avec annotations visuelles.
  • Bornes de ressources. La taille décompressée des flux de contenu est plafonnée pour se prémunir contre les bombes de décompression ; le balayeur au niveau des octets évite le retour arrière (backtracking) catastrophique des expressions régulières sur des entrées forgées.
  • Déterminisme. Pour des entrées identiques, les régions du diff et la sortie formatée sont stables.
TypeGenreMembres clés
NextPDF\Pro\Diff\PdfDifferfinal classstatic compare(string $sourcePdf, string $targetPdf): DiffResult, static compareTexts(array $sourcePages, array $targetPages): DiffResult, static extractText(string $contentStream): string
NextPDF\Pro\Diff\StructuredDifferfinal class__construct(?ImageDiffer $imageDiffer = null, ?MetadataDiffer $metadataDiffer = null), compare(string $sourcePdf, string $targetPdf): StructuredDiffResult
NextPDF\Pro\Diff\DiffFormatterfinal classtoJson(StructuredDiffResult $result): string, toHtml(StructuredDiffResult $result): string
NextPDF\Pro\Diff\DiffResultfinal readonly classarray $added, array $removed, array $modified, isIdentical(): bool, hasDifferences(): bool, totalChanges(): int
NextPDF\Pro\Diff\StructuredDiffResultfinal readonly classdiff de texte, paragraphes, images, changements de métadonnées, résumé
NextPDF\Pro\Diff\DiffTypeenumAdded, Removed, Modified, Unchanged
<?php
declare(strict_types=1);
use NextPDF\Pro\Diff\PdfDiffer;
$diff = PdfDiffer::compare(
file_get_contents('v1.pdf'),
file_get_contents('v2.pdf'),
);
if ($diff->hasDifferences()) {
echo $diff->totalChanges(), " text changes detected\n";
}
<?php
declare(strict_types=1);
use NextPDF\Pro\Diff\DiffFormatter;
use NextPDF\Pro\Diff\StructuredDiffer;
function reviewReport(string $oldPdf, string $newPdf): string
{
$result = (new StructuredDiffer())->compare($oldPdf, $newPdf);
// JSON for machine consumption; toHtml() for a review UI fragment.
return (new DiffFormatter())->toJson($result);
}
  • Le diff est aligné par page selon l’indice. Insérer une page tôt décale toutes les pages suivantes et signale de larges changements en aval — c’est attendu pour une comparaison alignée par indice.
  • La comparaison d’images détecte les images embarquées ajoutées, supprimées et modifiées ; ce n’est pas un diff visuel perceptuel et elle ne fait pas de rendu pixel des pages.
  • Les PDF scannés uniquement composés d’images produisent peu ou pas de diff de texte, car aucun OCR n’est effectué.
  • Sans le lecteur Artisan optionnel, l’extraction utilise le repli borné ; les documents fortement compressés peuvent produire une couverture de texte réduite.

L’extraction de texte est linéaire par rapport aux octets du document ; le diff de Myers est quasi linéaire pour des documents similaires et quadratique dans le pire des cas par paire de pages. Le plafond de décompression borne la mémoire. Voir performance_budget.

Le repli au niveau des octets utilise un balayage fondé sur strpos plutôt qu’une expression régulière non bornée pour éviter le retour arrière catastrophique sur des PDF forgés, et borne la sortie de décompression. Le diff n’exécute aucun script embarqué. Voir le modèle de sécurité de Core.

AffirmationClause de spécificationStatut
Opérateur de texte Tj analysé pour l’extractionISO 32000-2:2020 §9.4Vérifié (suite unitaire)
Opérateur de tableau de texte TJ analysé pour l’extractionISO 32000-2:2020 §9.4Vérifié (suite unitaire)
Sortie PDF de relecture côte à côte avec annotations visuellesNon pris en charge (JSON/HTML uniquement)

Aucun équivalent Core n’existe pour la comparaison de documents. Le lecteur Artisan optionnel améliore la précision de l’extraction lorsqu’il est installé, mais n’est pas requis.

C’est un détecteur de changements de contenu. Ce n’est pas un analyseur de différences forensique et il ne produit pas de rapports probatoires ou d’attribution d’altération ; ces préoccupations sont hors du périmètre de ce module.

Cette page ne documente que le comportement observable de l’extérieur et la surface d’API publique prise en charge. Les chemins d’espaces de noms internes, les classes d’assistance, les tables de mécanismes, les noms de fichiers de runbook et les préfixes de tickets sont hors du périmètre.