Przejdź do głównej zawartości
getnextpdf.com

Pro edycja

Diff — pełna dokumentacja referencyjna

Ta strona jest referencją na poziomie kontraktu dla modułu różnic NextPDF Pro, NextPDF\Pro\Diff. Moduł porównuje dwa dokumenty PDF i raportuje zmiany tekstu, obrazów oraz metadanych. PdfDiffer tworzy różnicę wierszową Myersa wyrównaną do stron. StructuredDiffer dodaje grupowanie akapitów, porównanie obrazów i porównanie metadanych. DiffFormatter serializuje wynik strukturalny do JSON lub do fragmentu HTML. Ta strona określa publiczne API, obserwowalny kontrakt zachowania, limity zasobów oraz tryby awarii. Konfiguracja zorientowana na zadania i przykłady znajdują się na stronie możliwości Diff.

Ta możliwość jest dostarczana w NextPDF Pro (nextpdf/pro) i aktywuje się dzięki kopercie licencyjnej poziomu Pro. Wdrożenie bez tego uprawnienia nie ładuje klas tej możliwości. Porównaj edycje i uzyskaj licencję.

Żadna flaga możliwości czasu wykonania nie bramkuje tego modułu. Klasy różnic są dostępne zawsze, gdy nextpdf/pro jest zainstalowany i licencjonowany.

SymbolParametryZachowanie domyślneZwracaZgłasza lub kończy się błędemUwagi
PdfDiffer::compare()string $sourcePdf, string $targetPdfWyodrębnia tekst dla każdej strony, następnie porównuje różnicę strony i źródła ze stroną i celuDiffResultInvalidArgumentException, gdy bufor nie zawiera nagłówka %PDF lub opcjonalny czytnik nie sparsuje danych; OverflowException przy przekroczeniu limitu zasobówStatyczny punkt wejścia
PdfDiffer::compareTexts()array $sourcePages, array $targetPages (każdy list<string>)Porównuje wcześniej wyodrębnione teksty stron, pomijając ekstrakcjęDiffResultOverflowException przy przekroczeniu limitu zasobówStatyczny; użyj, gdy tekst jest już dostępny
PdfDiffer::extractText()string $contentStreamParsuje operatory pokazywania tekstu z jednego surowego strumienia zawartościstring— (odporny na błędy; niesparsowalne wejście daje pusty ciąg)Statyczny
StructuredDiffer::__construct()?ImageDiffer $imageDiffer = null, ?MetadataDiffer $metadataDiffer = nullArgumenty null tworzą domyślne komparatoryWstrzykiwanie przez konstruktor na potrzeby testów
StructuredDiffer::compare()string $sourcePdf, string $targetPdfUruchamia porównanie tekstu, akapitów, obrazów i metadanych, następnie buduje podsumowanieStructuredDiffResultPropaguje InvalidArgumentException i OverflowException ze ścieżki tekstowejOrkiestrator całego modułu
DiffFormatter::toJson()StructuredDiffResult $resultSformatowany dokument JSONstringJsonException, gdy kodowanie się nie powiedzie
DiffFormatter::toHtml()StructuredDiffResult $resultFragment HTML z sekcjami podsumowania, akapitów i metadanych; wartości tekstowe są escapowane jako encjestringTylko fragment, nie pełny dokument
DiffFormatter::toArray()StructuredDiffResult $resultTablica serializacji stojąca za toJson()array<string, mixed>Stabilne klucze snake_case
ImageDiffer::diff()string $sourcePdf, string $targetPdfHaszuje obiekty XObject obrazów i raportuje dodane, usunięte oraz zmodyfikowane obrazylist<ImageDiff>— (niedekodowalne struktury są pomijane fail-closed)Tożsamość to kubełek strony plus numer obiektu
MetadataDiffer::diff()string $sourcePdf, string $targetPdfPorównuje osiem pól /Info (Title, Author, Subject, Keywords, Creator, Producer, CreationDate, ModDate)list<MetadataChange>— (nigdy nie zgłasza wyjątku na niezgodnym wejściu)Wartości porównywane jako zdekodowane ciągi
DiffEngine::diff()array $sourceLines, array $targetLines, int $pageIndex = 0, int $maxLines = 10000Różnica wierszowa Myersa na dwóch listach wierszylist<DiffRegion>OverflowException, gdy łączna liczba wierszy przekracza $maxLines lub odległość edycyjna przekracza limit narzucony przez pamięćStatyczny; producent regionów dla wszystkich ścieżek tekstowych
TextExtractor::fromContentStream()string $contentStreamTokenizuje strumień i uruchamia maszynę stanu tekstulist<TextBlock>Statyczny
TextExtractor::fromOperations()array $operations (list<ContentStreamOp>)Uruchamia maszynę stanu tekstu na wcześniej sparsowanych operacjachlist<TextBlock>Statyczny
ContentStreamParser::parse()konstruktor przyjmuje string $dataTokenizuje operatory i operandy; pomija słowniki i komentarze; odporny na błędylist<ContentStreamOp>Nierozpoznane bajty są pomijane, nigdy nie są krytyczne
ContentStreamOpstring $operator, list<mixed> $operandsTylko-do-odczytu obiekt wartości operacji; isTextOp() klasyfikuje operatory związane z tekstem
DiffResultlist<DiffRegion> $regions, int $sourcePagesCount, int $targetPagesCountRozdziela regiony do $added, $removed, $modified; udostępnia isIdentical(), hasDifferences(), totalChanges()Tylko-do-odczytu; regiony Unchanged pozostają wyłącznie w $regions
StructuredDiffResultróżnica tekstu, akapity, obrazy, zmiany metadanych, podsumowanieWynik zbiorczy; hasDifferences(), isIdentical() delegują do podsumowaniaTylko-do-odczytu
DiffSummaryliczniki dla każdej kategorii plus liczby stronhasDifferences() i totalChanges() na licznikach tekstu, obrazów i metadanychTylko-do-odczytu
DiffRegionDiffType $type, string $text, int $pageIndex, int $lineIndex, ?string $counterpartText = nullJedna zmiana na poziomie wiersza$counterpartText pozostaje null w dostarczanym silniku
ParagraphDifftyp, tekst, indeks strony, wiersz początkowy/końcowy, regionyKolejne regiony tego samego typu na jednej stronie; lineCount()Tylko-do-odczytu
ImageDifftyp, indeks strony, hasz źródła, hasz celu, identyfikator obiektuJeden wpis zmiany obrazuHasze są pustymi ciągami po nieobecnej stronie
MetadataChangestring $field, ?string $sourceValue, ?string $targetValueJedna zmiana pola; isAdded(), isRemoved(), isModified()null oznacza, że pole jest nieobecne
TextBlocktekst, x, y, nazwa czcionki, rozmiar czcionki, indeks wierszaJeden wyodrębniony fragment tekstu z przybliżoną pozycjąTylko-do-odczytu
DiffTypeenum: Added, Removed, Modified, UnchangedKlasyfikacja zmian dla tekstu oparta na łańcuchach znakówZobacz uwagę o Modified w kontrakcie zachowania
ImageDiffTypeenum: Added, Removed, Modified, UnchangedKlasyfikacja zmian dla obrazów oparta na łańcuchach znaków
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() wyodrębnia tekst dla każdej strony, następnie porównuje różnicę strony i źródła ze stroną i celu. Gdy liczby stron się różnią, brakująca strona jest traktowana jako pusty tekst dla nadliczbowych stron. W obrębie każdej pary stron tekst jest dzielony na znakach nowej linii, a różnica wierszowa Myersa działa dla każdej strony. Silnik emituje regiony Added, Removed i Unchanged. Zmieniony wiersz pojawia się jako region Removed plus region Added; dostarczany silnik nigdy nie emituje regionów tekstowych Modified. Przypadek Modified oraz kubełek DiffResult::$modified służą wynikom konstruowanym przez wywołującego, ponieważ konstruktor DiffResult jest publiczny. totalChanges() liczy regiony dodane, usunięte i zmodyfikowane; regiony niezmienione są wykluczone.

Ekstrakcja ma dwie ścieżki:

  • Obecny opcjonalny czytnik Artisan. Gdy zainstalowana jest opcjonalna klasa NextPDF\Parser\PdfReader, strumienie zawartości stron są odczytywane przez nią, aby uzyskać dokładny stronowo tekst. Liczba stron z trailera steruje pętlą. Strona, której nie uda się odczytać, wnosi pusty tekst zamiast przerywać porównanie.
  • Rozwiązanie awaryjne. Ograniczony skaner bajtowy lokalizuje pary stream/endstream za pomocą strpos, dekompresuje dane FlateDecode ze sztywnym pułapem wyjścia 50 MB oraz odwraca filtr predyktora PNG, gdy słownik strumienia żąda go przez /DecodeParms zgodnie z ISO 32000-2:2020 §7.4.4.4. Zniekształcony lub nieobsługiwany predyktor pozostawia zdekodowane bajty bez zmian. Rozwiązanie awaryjne łączy cały odzyskany tekst w pojedynczy kubełek strony, więc wyrównanie na poziomie stron jest dokładne stronowo tylko na ścieżce czytnika.

Obie ścieżki parsują operatory pokazywania tekstu z §9.4: Tj, TJ i '. Maszyna stanu śledzi BT/ET, Tm (tylko punkt początkowy), Td/TD, T* oraz Tf.

StructuredDiffer::compare() uruchamia różnicę tekstu, grupuje kolejne regiony tego samego typu na tej samej stronie w akapity (włącznie z ciągami niezmienionymi), następnie uruchamia porównanie obrazów i metadanych oraz składa DiffSummary. Liczniki akapitów w podsumowaniu obejmują wyłącznie akapity dodane, usunięte i zmodyfikowane.

Porównanie obrazów wylicza obiekty PDF strukturalnie. Zasięg ciała strumienia jest wyznaczany przez jego wpis /Length zgodnie z §7.3.8.2, więc bajty binarne, które jedynie przypominają składnię obiektu, nigdy nie rejestrują się jako fantomowe obiekty. Skompresowane strumienie obiektów (/Type /ObjStm) są dekodowane zgodnie z §7.5.7, aby zagnieżdżone w nich obiekty XObject obrazów były widoczne. Każdy wykryty obraz jest haszowany po zawartości niekryptograficzną funkcją xxh128; tożsamością jest para kubełka strony i numeru obiektu. Obrazy bez strony właścicielskiej w kolejności strumienia są przypisywane do strony 0.

Porównanie metadanych rozwiązuje prawdziwy słownik /Info przez trailer, gdy to możliwe, więc zwodniczy token pola wewnątrz strumienia zawartości nie jest mylony z metadanymi dokumentu. Wartości pól są dekodowane jako ciągi PDF: forma literalna zgodnie z §7.3.4.2 oraz forma szesnastkowa zgodnie z §7.3.4.3. Bez możliwego do rozwiązania trailera wyszukiwanie sięga do całego wejścia. Daty są porównywane jako zdekodowane ciągi, a nie sparsowane znaczniki czasu.

DiffFormatter::toJson() zwraca sformatowany JSON i koduje z JSON_THROW_ON_ERROR, więc niepowodzenie kodowania zgłasza JsonException zamiast zwracać false. toHtml() zwraca fragment <div class="nextpdf-diff">; tekst akapitów i wartości metadanych przechodzą przez escapowanie encji HTML. Nie ma wizualnego wyniku PDF z korektą zestawioną obok siebie. Dla identycznych danych wejściowych regiony i sformatowany wynik są deterministyczne.

  • Wyrównanie stron jest pozycyjne. Pojedyncza wstawiona lub usunięta strona przesuwa wyrównanie dla wszystkich kolejnych stron i zawyża liczbę zmian w dalszej części.
  • Na awaryjnej ścieżce ekstrakcji cały tekst trafia na stronę o indeksie 0. Porównanie dokumentu wyodrębnionego przez czytnik z oczekiwaniami ze ścieżki awaryjnej daje inne przypisanie stron.
  • Bufor źródła lub celu, który nie zaczyna się od %PDF, kończy się błędem InvalidArgumentException przed jakimkolwiek porównaniem.
  • Ponad 10,000 łącznych wierszy w jednej parze stron kończy się błędem OverflowException (limit liczby wierszy).
  • Dwa teksty stron dzielące zbyt mało wspólnych wierszy kończą się błędem OverflowException, gdy odległość edycyjna Myersa przekroczy limit narzucony przez pamięć. Legalne wersje dzielą większość wierszy i pozostają nienaruszone; wrogie wejścia o niskiej wspólności naruszają ten limit.
  • Zdekompresowany wynik strumienia awaryjnego większy niż 50 MB kończy się błędem OverflowException (limit bomby dekompresyjnej). Skaner używa strpos, a nie nieograniczonego wyrażenia regularnego, więc spreparowane wejście nie może wywołać katastroficznego nawracania.
  • Operator pokazywania tekstu " jest tokenizowany, ale nie tworzy bloku tekstu w 3.1.0; tekst pokazany wyłącznie przez " nie uczestniczy w różnicy.
  • Zeskanowane pliki PDF zawierające tylko obrazy dają niewielką różnicę tekstu lub jej brak. Nie działa żaden OCR.
  • Wykrywanie zmian obrazów jest strukturalne, nie percepcyjne. Nie rasteryzuje stron, a obraz ponownie zakodowany z identycznymi pikselami jest raportowany jako zmodyfikowany, gdy jego bajty się różnią.
  • Obraz, którego kubełek strony lub numer obiektu zmienia się między wersjami, jest raportowany jako para usunięcie-plus-dodanie, a nie jako zmodyfikowany.
  • Strumienie obiektów skompresowane filtrami innymi niż FlateDecode są pomijane fail-closed; ich obrazy członkowskie nie są porównywane.
  • W tym module nie zachodzi żadna operacja kryptograficzna, więc nie istnieje zachowanie specyficzne dla trybu FIPS. Hasz obrazu służy wyłącznie do wykrywania zmian i nie ma żadnej wagi integralności ani dowodowej.
TwierdzenieStandardKlauzula
Operatory pokazywania tekstu Tj i TJ są parsowane na potrzeby ekstrakcjiISO 32000-2:2020§9.4
Dane strumienia awaryjnego zaczynają się po CRLF lub LF następującym po słowie kluczowym streamISO 32000-2:2020§7.3.8.1
Zasięgi strumieni w skanowaniu obrazów są wyznaczane przez wpis słownika /LengthISO 32000-2:2020§7.3.8.2
Członkowie strumienia obiektów są lokalizowani przez tabelę par /N i przesunięcie /FirstISO 32000-2:2020§7.5.7
Odwrócenie predyktora PNG podąża za parametrem Predictor w /DecodeParmsISO 32000-2:2020§7.4.4.4
Wartości metadanych dekodują literalną i szesnastkową formę ciąguISO 32000-2:2020§7.3.4.2, §7.3.4.3
Wizualny wynik PDF z korektą zestawioną obok siebieNieobsługiwane (tylko JSON/HTML)

Wszystkie klauzule są parafrazowane; NextPDF nie odtwarza tekstu normatywnego. Są to oświadczenia o możliwościach, a nie certyfikaty; NextPDF nie posiada żadnego certyfikatu ani go nie udziela. Odzyskiwanie tekstu rekonstruuje tekst wierszy z operatorów pokazywania tekstu. Nie uruchamia pełnej maszyny stanu tekstu z §9.4, więc różnica jest na poziomie zawartości, a nie geometrii.

  • Dostępność w pakiecie Pro: PdfDiffer, DiffEngine, TextExtractor oraz ich obiekty wartości od 1.8.0; StructuredDiffer, DiffFormatter, ImageDiffer, MetadataDiffer oraz ich obiekty od 2.2.0. Wszystkie są aktualne w nextpdf/pro 3.1.0.
  • Preferuj PdfDiffer::compareTexts(), gdy tekst strony jest już dostępny; całkowicie pomija ekstrakcję i jej tryby awarii.
  • Opcjonalny czytnik Artisan poprawia dokładność ekstrakcji i przypisanie stron. Jest wykrywany w czasie wykonania i nigdy nie jest wymagany.
  • Przechwytuj OverflowException przy porównywaniu niezaufanego wejścia; limity są celowymi odrzuceniami fail-closed, a nie błędami przejściowymi.
  • DiffFormatter::toHtml() emituje nazwy klas (diff-added, diff-removed, diff-modified, diff-unchanged), ale nie arkusz stylów; dostarcz własny CSS.
  • Konstruuj StructuredDiffer z zaślepkami komparatorów w testach, aby odizolować ścieżkę tekstową od skanowania obrazów i metadanych.

Ta strona dokumentuje wyłącznie zachowanie obserwowalne z zewnątrz oraz obsługiwaną powierzchnię publicznego API. Wewnętrzne ścieżki przestrzeni nazw, klasy pomocnicze, tabele mechanizmów, nazwy plików runbooków oraz prefiksy zgłoszeń są poza zakresem.