Zum Inhalt springen
getnextpdf.com

Pro Edition

Diff — Tiefenreferenz

Diese Seite ist die Referenz auf Vertragsebene für das NextPDF-Pro-Diff-Modul, NextPDF\Pro\Diff. Das Modul vergleicht zwei PDF-Dokumente und meldet Änderungen an Text, Bildern und Metadaten. PdfDiffer erzeugt ein seitenausgerichtetes Myers-Zeilendiff. StructuredDiffer ergänzt Absatzgruppierung, Bildvergleich und Metadatenvergleich. DiffFormatter serialisiert das strukturierte Ergebnis nach JSON oder in ein HTML-Fragment. Diese Seite legt die öffentliche API, den beobachtbaren Verhaltensvertrag, die Ressourcengrenzen und die Fehlermodi dar. Aufgabenorientierte Einrichtung und Beispiele finden Sie auf der Diff-Fähigkeitsseite.

Diese Fähigkeit wird mit NextPDF Pro (nextpdf/pro) ausgeliefert und aktiviert sich mit einer Lizenzhülle der Pro-Stufe. Eine Bereitstellung ohne diese Berechtigung lädt die Klassen der Fähigkeit nicht. Editionen vergleichen und eine Lizenz erwerben.

Kein Laufzeit-Fähigkeitsflag sperrt dieses Modul. Die Diff-Klassen sind nutzbar, sobald nextpdf/pro installiert und lizenziert ist.

SymbolParameterStandardverhaltenRückgabeWirft oder scheitert mitHinweise
PdfDiffer::compare()string $sourcePdf, string $targetPdfExtrahiert Text je Seite und vergleicht dann Seite i der Quelle mit Seite i des ZielsDiffResultInvalidArgumentException, wenn einem Puffer der %PDF-Header fehlt oder der optionale Reader nicht parsen kann; OverflowException bei einer RessourcengrenzeStatischer Einstiegspunkt
PdfDiffer::compareTexts()array $sourcePages, array $targetPages (jeweils list<string>)Vergleicht vorab extrahierte Seitentexte und umgeht die ExtraktionDiffResultOverflowException bei einer RessourcengrenzeStatisch; verwenden, wenn der Text bereits vorliegt
PdfDiffer::extractText()string $contentStreamParst textausgebende Operatoren aus einem rohen Content-Streamstring— (fehlertolerant; nicht parsbare Eingabe ergibt eine leere Zeichenkette)Statisch
StructuredDiffer::__construct()?ImageDiffer $imageDiffer = null, ?MetadataDiffer $metadataDiffer = nullnull-Argumente konstruieren die Standard-DifferKonstruktorinjektion zum Testen
StructuredDiffer::compare()string $sourcePdf, string $targetPdfFührt Text-, Absatz-, Bild- und Metadatenvergleich aus und erstellt dann eine ZusammenfassungStructuredDiffResultPropagiert InvalidArgumentException und OverflowException aus dem TextpfadOrchestrator über das gesamte Modul
DiffFormatter::toJson()StructuredDiffResult $resultFormatiertes JSON-DokumentstringJsonException, wenn die Kodierung scheitert
DiffFormatter::toHtml()StructuredDiffResult $resultHTML-Fragment mit Abschnitten für Zusammenfassung, Absatz und Metadaten; Textwerte werden entitätsmaskiertstringNur Fragment, kein vollständiges Dokument
DiffFormatter::toArray()StructuredDiffResult $resultSerialisierungsarray, das toJson() zugrunde liegtarray<string, mixed>Stabile snake_case-Schlüssel
ImageDiffer::diff()string $sourcePdf, string $targetPdfHasht Bild-XObjects und meldet hinzugefügte, entfernte und geänderte Bilderlist<ImageDiff>— (nicht dekodierbare Strukturen werden fail-closed übersprungen)Identität ist Seiten-Bucket plus Objektnummer
MetadataDiffer::diff()string $sourcePdf, string $targetPdfVergleicht acht /Info-Felder (Title, Author, Subject, Keywords, Creator, Producer, CreationDate, ModDate)list<MetadataChange>— (wirft bei nicht konformer Eingabe niemals)Werte werden als dekodierte Zeichenketten verglichen
DiffEngine::diff()array $sourceLines, array $targetLines, int $pageIndex = 0, int $maxLines = 10000Myers-Zeilendiff über zwei Zeilenlistenlist<DiffRegion>OverflowException, wenn die kombinierten Zeilen $maxLines überschreiten oder die Editierdistanz die speichergebundene Obergrenze überschreitetStatisch; der Regionenerzeuger für alle Textpfade
TextExtractor::fromContentStream()string $contentStreamTokenisiert den Stream und führt die Textzustandsmaschine auslist<TextBlock>Statisch
TextExtractor::fromOperations()array $operations (list<ContentStreamOp>)Führt die Textzustandsmaschine über vorab geparste Operationen auslist<TextBlock>Statisch
ContentStreamParser::parse()Konstruktor nimmt string $dataTokenisiert Operatoren und Operanden; überspringt Dictionaries und Kommentare; fehlertolerantlist<ContentStreamOp>Nicht erkannte Bytes werden übersprungen, niemals fatal
ContentStreamOpstring $operator, list<mixed> $operandsSchreibgeschütztes Operationswertobjekt; isTextOp() klassifiziert textbezogene Operatoren
DiffResultlist<DiffRegion> $regions, int $sourcePagesCount, int $targetPagesCountOrdnet Regionen in $added, $removed, $modified ein; stellt isIdentical(), hasDifferences(), totalChanges() bereitSchreibgeschützt; Unchanged-Regionen verbleiben nur in $regions
StructuredDiffResultTextdiff, Absätze, Bilder, Metadatenänderungen, ZusammenfassungAggregiertes Ergebnis; hasDifferences(), isIdentical() delegieren an die ZusammenfassungSchreibgeschützt
DiffSummaryZählungen je Kategorie plus SeitenzählungenhasDifferences() und totalChanges() über Text-, Bild- und MetadatenzählungenSchreibgeschützt
DiffRegionDiffType $type, string $text, int $pageIndex, int $lineIndex, ?string $counterpartText = nullEine Änderung auf Zeilenebene$counterpartText bleibt in der ausgelieferten Engine null
ParagraphDiffTyp, Text, Seitenindex, Start-/Endzeile, RegionenAufeinanderfolgende gleichtypige Regionen auf einer Seite; lineCount()Schreibgeschützt
ImageDiffTyp, Seitenindex, Quell-Hash, Ziel-Hash, Objekt-IDEin Eintrag einer BildänderungHashes sind auf der fehlenden Seite leere Zeichenketten
MetadataChangestring $field, ?string $sourceValue, ?string $targetValueEine Feldänderung; isAdded(), isRemoved(), isModified()null bedeutet, dass das Feld fehlt
TextBlockText, x, y, Schriftname, Schriftgröße, ZeilenindexEin extrahierter Textlauf mit ungefährer PositionSchreibgeschützt
DiffTypeEnum: Added, Removed, Modified, UnchangedZeichenkettengestützte Änderungsklassifikation für TextSiehe den Hinweis zu Modified im Verhaltensvertrag
ImageDiffTypeEnum: Added, Removed, Modified, UnchangedZeichenkettengestützte Änderungsklassifikation für Bilder
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() extrahiert Text je Seite und vergleicht dann Seite i der Quelle mit Seite i des Ziels. Weichen die Seitenzahlen ab, wird die fehlende Seite für die überzähligen Seiten als leerer Text behandelt. Innerhalb jedes Seitenpaars wird Text an Zeilenumbrüchen getrennt und je Seite ein Myers-Zeilendiff ausgeführt. Die Engine gibt Added-, Removed- und Unchanged-Regionen aus. Eine geänderte Zeile erscheint als eine Removed- plus eine Added-Region; die ausgelieferte Engine gibt niemals Modified-Textregionen aus. Der Modified-Fall und der Bucket DiffResult::$modified dienen aufruferseitig konstruierten Ergebnissen, da der DiffResult-Konstruktor öffentlich ist. totalChanges() zählt hinzugefügte, entfernte und geänderte Regionen; unveränderte Regionen werden ausgeschlossen.

Die Extraktion hat zwei Pfade:

  • Optionaler Artisan-Reader vorhanden. Wenn die optionale Klasse NextPDF\Parser\PdfReader installiert ist, werden Seiten-Content-Streams über sie gelesen, um seitengenauen Text zu erhalten. Die Seitenzahl aus dem Trailer steuert die Schleife. Eine Seite, die nicht gelesen werden kann, trägt leeren Text bei, statt den Vergleich abzubrechen.
  • Fallback. Ein begrenzter Scanner auf Byte-Ebene lokalisiert stream/endstream-Paare per strpos, dekomprimiert FlateDecode-Daten mit einer harten Ausgabeobergrenze von 50 MB und kehrt einen PNG-Prädiktor um, wenn das Stream-Dictionary über /DecodeParms gemäß ISO 32000-2:2020 §7.4.4.4 einen anfordert. Ein fehlerhafter oder nicht unterstützter Prädiktor lässt die dekodierten Bytes unverändert. Der Fallback verkettet den gesamten wiederhergestellten Text in einen einzigen Seiten-Bucket, sodass die Ausrichtung auf Seitenebene nur auf dem Reader-Pfad seitengenau ist.

Beide Pfade parsen die textausgebenden §9.4-Operatoren Tj, TJ und '. Die Zustandsmaschine verfolgt BT/ET, Tm (nur Ursprung), Td/TD, T* und Tf.

StructuredDiffer::compare() führt das Textdiff aus, gruppiert aufeinanderfolgende gleichtypige Regionen auf derselben Seite zu Absätzen (unveränderte Läufe eingeschlossen), führt dann den Bild- und Metadatenvergleich aus und setzt eine DiffSummary zusammen. Die Absatzzählungen der Zusammenfassung erfassen nur hinzugefügte, entfernte und geänderte Absätze.

Der Bildvergleich zählt PDF-Objekte strukturell auf. Die Ausdehnung eines Stream-Körpers wird durch seinen /Length-Eintrag gemäß §7.3.8.2 bestimmt, sodass Binärbytes, die lediglich Objektsyntax ähneln, niemals als Phantomobjekte erfasst werden. Komprimierte Objekt-Streams (/Type /ObjStm) werden gemäß §7.5.7 dekodiert, damit darin verschachtelte Bild-XObjects sichtbar sind. Jedes erkannte Bild wird mit der nicht kryptografischen Funktion xxh128 inhaltsgehasht; die Identität ist das Paar aus Seiten-Bucket und Objektnummer. Bilder ohne zugehörige Seite in Stream-Reihenfolge werden Seite 0 zugeordnet.

Der Metadatenvergleich löst das reale /Info-Dictionary nach Möglichkeit über den Trailer auf, sodass ein Köderfeld-Token innerhalb eines Content-Streams nicht mit Dokumentmetadaten verwechselt wird. Feldwerte werden als PDF-Zeichenketten dekodiert: die literale Form gemäß §7.3.4.2 und die hexadezimale Form gemäß §7.3.4.3. Ohne einen auflösbaren Trailer greift die Suche auf die gesamte Eingabe zurück. Daten werden als dekodierte Zeichenketten verglichen, nicht als geparste Zeitstempel.

DiffFormatter::toJson() gibt formatiertes JSON zurück und kodiert mit JSON_THROW_ON_ERROR, sodass ein Kodierungsfehler eine JsonException auslöst, statt false zurückzugeben. toHtml() gibt ein <div class="nextpdf-diff">-Fragment zurück; Absatztext und Metadatenwerte durchlaufen die HTML-Entitätsmaskierung. Es gibt keine visuelle Nebeneinander-Redline-PDF-Ausgabe. Für identische Eingaben sind Regionen und formatierte Ausgabe deterministisch.

  • Die Seitenausrichtung ist positionsbasiert. Eine einzelne eingefügte oder gelöschte Seite verschiebt die Ausrichtung für alle nachfolgenden Seiten und bläht die nachgelagerten Änderungszählungen auf.
  • Auf dem Fallback-Extraktionspfad landet der gesamte Text auf Seitenindex 0. Der Vergleich eines reader-extrahierten Dokuments mit Erwartungen aus dem Fallback-Pfad ergibt eine abweichende Seitenzuordnung.
  • Ein Quell- oder Zielpuffer, der nicht mit %PDF beginnt, scheitert vor jedem Vergleich mit InvalidArgumentException.
  • Mehr als 10.000 kombinierte Zeilen in einem Seitenpaar scheitern mit OverflowException (Zeilenanzahlgrenze).
  • Zwei Seitentexte, die zu wenige Zeilen teilen, scheitern mit OverflowException, sobald die Myers-Editierdistanz die speichergebundene Obergrenze überschreitet. Legitime Revisionen teilen die meisten Zeilen und bleiben unberührt; adversarielle Eingaben mit geringer Gemeinsamkeit lösen die Grenze aus.
  • Eine dekomprimierte Fallback-Stream-Ausgabe größer als 50 MB scheitert mit OverflowException (Dekompressionsbombengrenze). Der Scanner verwendet strpos, keine unbegrenzten regulären Ausdrücke, sodass präparierte Eingaben kein katastrophales Backtracking auslösen können.
  • Der textausgebende Operator " wird tokenisiert, erzeugt in 3.1.0 aber keinen Textblock; Text, der nur über " gezeigt wird, nimmt am Diff nicht teil.
  • Gescannte, reine Bild-PDFs erzeugen wenig oder kein Textdiff. Es läuft keine OCR.
  • Die Erkennung von Bildänderungen ist strukturell, nicht perzeptuell. Sie rastert keine Seiten und meldet ein mit identischen Pixeln neu kodiertes Bild als geändert, wenn sich seine Bytes unterscheiden.
  • Ein Bild, dessen Seiten-Bucket oder Objektnummer sich zwischen Revisionen ändert, wird als Paar aus entfernt und hinzugefügt gemeldet, nicht als geändert.
  • Objekt-Streams, die mit anderen Filtern als FlateDecode komprimiert sind, werden fail-closed übersprungen; ihre Mitgliedsbilder werden nicht verglichen.
  • In diesem Modul findet keine kryptografische Operation statt, daher existiert kein FIPS-modus-spezifisches Verhalten. Der Bild-Hash dient ausschließlich der Änderungserkennung und trägt kein Integritäts- oder Beweisgewicht.
AussageStandardKlausel
Die textausgebenden Operatoren Tj und TJ werden für die Extraktion geparstISO 32000-2:2020§9.4
Fallback-Stream-Daten beginnen nach dem CRLF oder LF, das dem stream-Schlüsselwort folgtISO 32000-2:2020§7.3.8.1
Die Stream-Ausdehnungen beim Bildscan werden durch den Dictionary-Eintrag /Length bestimmtISO 32000-2:2020§7.3.8.2
Objekt-Stream-Mitglieder werden über die /N-Paartabelle und den /First-Offset lokalisiertISO 32000-2:2020§7.5.7
Die PNG-Prädiktorumkehr folgt dem /DecodeParms-Parameter PredictorISO 32000-2:2020§7.4.4.4
Metadatenwerte dekodieren die literale und die hexadezimale ZeichenkettenformISO 32000-2:2020§7.3.4.2, §7.3.4.3
Visuelle Nebeneinander-Redline-PDF-AusgabeNicht unterstützt (nur JSON/HTML)

Alle Klauseln sind paraphrasiert; NextPDF gibt normativen Text nicht wieder. Dies sind Fähigkeitsaussagen, keine Zertifizierungen; NextPDF hält keine Zertifizierung und erteilt keine. Die Textwiederherstellung rekonstruiert Zeilentext aus textausgebenden Operatoren. Sie führt die vollständige §9.4-Textzustandsmaschine nicht aus, sodass das Diff auf Inhaltsebene liegt, nicht auf Geometrieebene.

  • Verfügbarkeit innerhalb des Pro-Pakets: PdfDiffer, DiffEngine, TextExtractor und ihre Wertobjekte seit 1.8.0; StructuredDiffer, DiffFormatter, ImageDiffer, MetadataDiffer und ihre seit 2.2.0. Alle sind in nextpdf/pro 3.1.0 aktuell.
  • Bevorzugen Sie PdfDiffer::compareTexts(), wenn Seitentext bereits vorliegt; es überspringt die Extraktion und ihre Fehlermodi vollständig.
  • Der optionale Artisan-Reader verbessert die Extraktionsgenauigkeit und die Seitenzuordnung. Er wird zur Laufzeit erkannt und ist niemals erforderlich.
  • Fangen Sie OverflowException beim Diffen nicht vertrauenswürdiger Eingaben; die Grenzen sind bewusste Fail-closed-Zurückweisungen, keine transienten Fehler.
  • DiffFormatter::toHtml() gibt Klassennamen (diff-added, diff-removed, diff-modified, diff-unchanged) aus, aber kein Stylesheet; liefern Sie Ihr eigenes CSS.
  • Konstruieren Sie StructuredDiffer in Tests mit Stub-Differn, um den Textpfad vom Bild- und Metadatenscannen zu isolieren.

Diese Seite dokumentiert ausschließlich extern beobachtbares Verhalten und die unterstützte öffentliche API-Oberfläche. Interne Namensraumpfade, Hilfsklassen, Mechanismustabellen, Runbook-Dateinamen und Ticket-Präfixe liegen außerhalb des Umfangs.