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.
Disponibilité et licence
Section intitulée « Disponibilité et licence »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é.
Installation
Section intitulée « Installation »composer require nextpdf/pro:^3Aperçu conceptuel
Section intitulée « Aperçu conceptuel »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.
Pourquoi ça fonctionne ainsi
Section intitulée « Pourquoi ça fonctionne ainsi »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.
Contrat de comportement
Section intitulée « Contrat de comportement »- Entrée. Octets PDF bruts pour la source et la cible. Un tampon ne
commençant pas par
%PDFlève uneInvalidArgumentException. - Sortie (chemin rapide). Un
DiffResultavec les listes de régionsadded,removed,modified, plusisIdentical(),hasDifferences(),totalChanges(). - Sortie (chemin structuré). Un
StructuredDiffResultavec les diffs de paragraphes, les diffs d’images, les changements de métadonnées et unDiffSummary. - 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.
Surface d’API publique
Section intitulée « Surface d’API publique »| Type | Genre | Membres clés |
|---|---|---|
NextPDF\Pro\Diff\PdfDiffer | final class | static compare(string $sourcePdf, string $targetPdf): DiffResult, static compareTexts(array $sourcePages, array $targetPages): DiffResult, static extractText(string $contentStream): string |
NextPDF\Pro\Diff\StructuredDiffer | final class | __construct(?ImageDiffer $imageDiffer = null, ?MetadataDiffer $metadataDiffer = null), compare(string $sourcePdf, string $targetPdf): StructuredDiffResult |
NextPDF\Pro\Diff\DiffFormatter | final class | toJson(StructuredDiffResult $result): string, toHtml(StructuredDiffResult $result): string |
NextPDF\Pro\Diff\DiffResult | final readonly class | array $added, array $removed, array $modified, isIdentical(): bool, hasDifferences(): bool, totalChanges(): int |
NextPDF\Pro\Diff\StructuredDiffResult | final readonly class | diff de texte, paragraphes, images, changements de métadonnées, résumé |
NextPDF\Pro\Diff\DiffType | enum | Added, Removed, Modified, Unchanged |
Exemple de code — Démarrage rapide
Section intitulée « Exemple de code — Démarrage rapide »<?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";}Exemple de code — Production
Section intitulée « Exemple de code — Production »<?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);}Cas limites et pièges
Section intitulée « Cas limites et pièges »- 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.
Performance
Section intitulée « Performance »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.
Notes de sécurité
Section intitulée « Notes de sécurité »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.
Conformité
Section intitulée « Conformité »| Affirmation | Clause de spécification | Statut |
|---|---|---|
Opérateur de texte Tj analysé pour l’extraction | ISO 32000-2:2020 §9.4 | Vérifié (suite unitaire) |
Opérateur de tableau de texte TJ analysé pour l’extraction | ISO 32000-2:2020 §9.4 | Vérifié (suite unitaire) |
| Sortie PDF de relecture côte à côte avec annotations visuelles | — | Non pris en charge (JSON/HTML uniquement) |
Repli / alternative Core
Section intitulée « Repli / alternative Core »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.
Note de frontière Enterprise
Section intitulée « Note de frontière Enterprise »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.
Frontière de publication
Section intitulée « Frontière de publication »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.