Przejdź do głównej zawartości

Migracja z Dompdf do NextPDF

Ten przewodnik pomaga przenieść kod oparty na Dompdf, który generuje pliki w formacie Portable Document Format (PDF) z języka Hypertext Markup Language (HTML), na potok Html silnika NextPDF. Większość miejsc wywołań przekłada się mechanicznie, ponieważ obie biblioteki realizują ten sam ogólny przepływ: wczytanie HTML, renderowanie i wyemitowanie pliku PDF. Najważniejsza praca dotyczy mapy opcji oraz różnic w obsłudze Cascading Style Sheets (CSS). NextPDF i Dompdf to niezależne silniki, więc układ utworzony przez Dompdf jest zgodny z wynikiem silnika NextPDF, a nie identyczny z nim bajt po bajcie. Ten przewodnik obejmuje mapowanie czasowników, mapowanie kluczy opcji, różnice w zachowaniu oraz bezpieczną sekwencję migracji.

Obsługa danej funkcji HTML/CSS przez NextPDF nie gwarantuje, że dokument Dompdf zostanie odtworzony piksel po pikselu. Źródłem rozstrzygającym dla funkcji zweryfikowanych jest Macierz obsługi CSS. Ten przewodnik opisuje zachowanie, ale nie deklaruje równoważności wizualnej.

Okno terminala
composer require nextpdf/core:^3

Zachowaj zainstalowany pakiet dompdf/dompdf na czas przejścia. Opisana niżej bezpieczna sekwencja migracji uruchamia oba silniki obok siebie, dopóki każde miejsce wywołania nie zostanie przełączone. Usuń pakiet po zakończeniu przełączenia.

Obiekt Dompdf w Dompdf to pojedyncza fasada, która zarządza modelem Document Object Model (DOM), arkuszem stylów, drzewem ramek oraz kanwą. NextPDF rozdziela te zadania: NextPDF\Core\Document zarządza modelem strony i wyjściem, a writeHtml() steruje potokiem HTML. Nie ma osobnej, dwuetapowej fazy „renderowanie, potem wyjście”. writeHtml() rozkłada treść w miarę jej zapisywania, a dokument emituje się za pomocą save(), output() lub getPdfData().

NextPDF zapisuje treść strony przez malowanie strumienia treści zgodnie z ISO 32000-2 (ISO 32000-2 §8, iso32000_2_sec8#x1.x3.p14). Geometria strony sterowana opcją rozmiaru papieru odwzorowuje się na obiekt strony MediaBox (ISO 32000-2 §7, iso32000_2_sec7#x1.x104.p10). Są to podstawy działania silnika, wspólne dla każdego zgodnego ze specyfikacją modułu zapisującego. Algorytm układu, który przekształca CSS w tę treść, jest własnym algorytmem silnika NextPDF i różni się od algorytmu Dompdf; zobacz Różnice w zachowaniu.

Interfejs programowania aplikacji (API) potoku Html silnika NextPDF jest udokumentowany w dokumentacji referencyjnej modułu Html, generowanej automatycznie z PHPDoc. Punkty wejścia używane poniżej to Document::createStandalone(), Document::writeHtml(string $html): static, Document::writeHtmlCell(...), Document::output(?string, OutputDestination), Document::save(string $path): void, Document::getPdfData(): string oraz obiekt wartości NextPDF\Core\Config (pageSize, margins, fontsDirectory).

Nazwy publicznego API Dompdf podane poniżej zostały potwierdzone w publicznym repozytorium upstream (dompdf/dompdf, master); zobacz dołączony do repozytorium plik pochodzenia _source-sidecar-upstream-api.md. Nie przytoczono żadnego tekstu dokumentacji upstream.

DompdfNextPDFUwagi
new Dompdf($options)Document::createStandalone($config)Dompdf przyjmuje obiekt Options; NextPDF przyjmuje obiekt NextPDF\Core\Config. W długo działających procesach roboczych użyj DocumentFactory zamiast createStandalone().
$dompdf->loadHtml($html, $encoding)$doc->writeHtml($html)NextPDF traktuje dane wejściowe jako UTF-8. Przekonwertuj dane wejściowe spoza UTF-8 przed wywołaniem, zamiast przekazywać argument kodowania.
$dompdf->loadHtmlFile($file)$doc->writeHtml(file_get_contents($file))NextPDF nie ma wariantu wczytującego plik. Odczytaj plik samodzielnie, aby polityka zasobów pozostała w Twoim kodzie.
$dompdf->setPaper($size, $orientation)ConfigpageSize (obiekt wartości PageSize)Zobacz mapę opcji.
$dompdf->render()(niejawnie)NextPDF rozkłada układ podczas writeHtml(); nie ma osobnej fazy renderowania. Usuń wywołanie render().
$dompdf->output()$doc->getPdfData()Zwraca bajty pliku PDF.
$dompdf->stream($name, $opts)$doc->output($name, OutputDestination::Download)NextPDF wybiera miejsce docelowe za pomocą wyliczenia OutputDestination.
$dompdf->setBasePath($p) / setProtocol() / setBaseHost()(rozstrzyganie zasobów różni się)NextPDF rozstrzyga zasoby względne względem zestawu roboczego dokumentu, a nie bazowej trójki path/protocol; zobacz Różnice w zachowaniu.
$dompdf->addInfo($label, $value)$doc->setTitle() / setAuthor() / API metadanychDowolne pary informacji z Dompdf odwzorowują się na typowane settery metadanych (ISO 32000-2 §14 informacje o dokumencie, iso32000_2_sec14#x1.x5.p5).
$dompdf->setHttpContext($ctx)(brak odpowiednika)NextPDF nie pobiera zasobów zdalnych przez kontekst strumienia; zobacz Nieobsługiwane / brak bezpośredniego odpowiednika.
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
// Dompdf:
// $dompdf = new Dompdf();
// $dompdf->loadHtml('<h1>Invoice</h1>');
// $dompdf->setPaper('A4', 'portrait');
// $dompdf->render();
// file_put_contents('out.pdf', $dompdf->output());
// NextPDF — the createStandalone() default page size is A4 portrait:
$doc = Document::createStandalone();
$doc->setTitle('Invoice');
$doc->addPage();
$doc->writeHtml('<h1>Invoice</h1>');
$doc->save(__DIR__ . '/out.pdf');
echo "Wrote out.pdf\n";

Poniższy przykład odzwierciedla examples/08-html-basic.php, uruchamialne zaplecze tego przewodnika, z jawnie ustawionym rozmiarem papieru innym niż domyślny oraz marginesami. Odpowiada to wywołaniu Dompdf setPaper() wraz z konfiguracją marginesów Options.

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Contracts\OutputDestination;
use NextPDF\Core\Config;
use NextPDF\Core\Document;
use NextPDF\ValueObjects\Margin;
use NextPDF\ValueObjects\PageSize;
// Equivalent of: $dompdf->setPaper('letter','portrait') + margin options.
// US Letter portrait = 612 x 792 pt.
// Margin constructor order is (top, right, bottom, left) — all 0.5in here.
$config = new Config(
pageSize: new PageSize(612.0, 792.0, 'Letter'),
margins: new Margin(36.0, 36.0, 36.0, 36.0), // top,right,bottom,left; 0.5in in points
);
$doc = Document::createStandalone($config);
$doc->setTitle('Quarterly Report');
$doc->setAuthor('Finance');
$doc->addPage();
$html = <<<'HTML'
<h1 style="color:#1E3A8A;">Quarterly Report</h1>
<p>This report renders through the NextPDF Html pipeline. The CSS subset that
is <strong>Verified</strong> for production is the support-matrix authority,
not this page.</p>
<table border="1">
<tr><th>Region</th><th>Total</th></tr>
<tr><td>EMEA</td><td>1,204</td></tr>
</table>
HTML;
$doc->writeHtml($html);
// Equivalent of $dompdf->stream('report.pdf'):
$doc->output('report.pdf', OutputDestination::Download);
  • Brak dwufazowego renderowania. Kod Dompdf, który sprawdza stan między render() a output(), taki jak odczyt liczby stron, nie ma bezpośredniego odpowiednika w NextPDF na tej samej granicy. Zamiast tego odpytaj dokument po writeHtml().
  • Kodowanie. NextPDF pomija parametr $encoding z Dompdf. Przekonwertuj dane wejściowe na UTF-8 przed writeHtml(). Przekazanie bajtów Latin-1 powoduje mojibake, a nie błąd.
  • Pozostawione render(). Pozostawione wywołanie w stylu $dompdf->render() nie odpowiada żadnej metodzie w NextPDF i kończy się krytycznym błędem „undefined method”. Usuń je podczas przełączenia; nie zastępuj go zaślepką.
  • Wbudowany PHP. enable_php w Dompdf wykonuje <script type="text/php">. NextPDF z założenia nie wykonuje PHP wewnątrz dokumentu, ponieważ byłaby to powierzchnia ataku przez wstrzyknięcie. Przenieś tę logikę do swojego kodu PHP przed writeHtml().
  • Rozstrzyganie zasobów względnych. Dompdf rozstrzyga <img src> względem trójki bazowej path/protocol/host. NextPDF rozstrzyga względem zestawu roboczego dokumentu. Podczas migracji przekazuj ścieżki bezwzględne lub wstępnie rozstrzygnięte identyfikatory data URI, aby wyeliminować tę zmienną.

writeHtml() rozkłada układ w jednym przebiegu strumieniowym, jak opisano w rekordzie decyzji architektonicznej ADR-001. Po ułożeniu dokumentu nie jest zachowywany żaden pośredni obiekt drzewa ramek, więc szczytowe zużycie pamięci zależy od rozmiaru dokumentu, a nie od liczby węzłów DOM. Budżet wydajności przykładu w tym przewodniku to wall_ms: 2000, peak_mb: 128. W przypadku dużych dokumentów dziel HTML na granicach addPage(), zamiast budować jeden wielomegabajtowy ciąg znaków.

  • Brak pobierania zdalnego przez kontekst strumienia. NextPDF nie implementuje ścieżki zdalnego pobierania setHttpContext() / enable_remote z Dompdf. Rozstrzygnij i zweryfikuj zasoby zdalne w swojej aplikacji, a następnie przekaż bajty lub identyfikatory data URI. Eliminuje to powierzchnię ataku server-side request forgery (SSRF), którą wprowadza enable_remote.
  • Brak wykonywania kodu wewnątrz dokumentu. Brak odpowiednika enable_php to celowe utwardzenie, a nie luka.
  • Metadane dokumentu ustawiane przez typowane settery są zapisywane do słownika informacji ISO 32000-2 §14 / Extensible Metadata Platform (XMP) (iso32000_2_sec14#x1.x5.p5). Nie umieszczaj tam danych poufnych.
StwierdzenieSpecyfikacjaKlauzulareference_id
Treść strony jest malowaniem strumienia treści w modelu opaque/transparent.ISO 32000-2§8
Rozmiar papieru odwzorowuje się na prostokąt graniczny obiektu strony.ISO 32000-2§7
Czcionki HTML są zapisywane jako programy czcionek embedded/subset.ISO 32000-2§9
Przetwarzanie białych znaków i łamania wierszy jest specyficzne dla silnika.CSS Text 3§6.5

NextPDF tworzy treść zgodną z ISO 32000-2. Nie deklaruje, że dokument zmigrowany z Dompdf jest identyczny wizualnie. Ponownie sprawdzaj wynik za każdym razem, gdy zmieniasz silnik renderujący.

Nie dotyczy. Rdzeń obejmuje tę ścieżkę migracji z HTML na PDF.


Skorzystaj z niego, jeśli Twój zespół używa dompdf/dompdf do konwersji HTML na PDF po stronie serwera i chce przejść na silnik NextPDF. Jeśli wywołujesz tylko loadHtml / setPaper / render / output, mapowanie czasowników obejmuje całą używaną powierzchnię API.

Zakres obejmuje: czasowniki fasady Dompdf, klucze Options, oczekiwania dotyczące zgodności funkcji CSS, rozstrzyganie zasobów oraz metadane. Poza zakresem są wewnętrzne obiekty FrameTree/Canvas/Stylesheet w Dompdf. NextPDF nie ma publicznych odpowiedników, więc nie migruj kodu, który do nich sięga; zastąp go publicznym API.

Pokrycie oznacza zgodność behawioralną, a nie nakładkę typu drop-in. NextPDF nie ma nakładki klasy Dompdf (inaczej niż ścieżka TCPDF; zobacz /migration/tcpdf-compat/). Przepisz każde miejsce wywołania przy użyciu mapowania czasowników. Zweryfikowane wiersze macierzy obsługi CSS w pełni określają oczekiwania dotyczące funkcji CSS. Ten przewodnik nie powtarza statusu poszczególnych właściwości.

Opcja Dompdf (klucz / setter)NextPDFUwagi
default_paper_size / setDefaultPaperSize() ; setPaper($size,...)Config->pageSize (obiekt wartości PageSize)Nazwane rozmiary stają się jawnymi wymiarami w punktach. new PageSize(595.276, 841.890, 'A4') to domyślna wartość createStandalone().
default_paper_orientation / setDefaultPaperOrientation()zamień width/height w PageSizeNextPDF nie ma flagi orientacji. Strona w orientacji poziomej to PageSize z szerokością > wysokością.
dpi / setDpi()(nie jest globalnym parametrem)NextPDF działa w punktach PDF (1/72 cala). Skalowanie obrazów odbywa się dla każdego obrazu z osobna, a nie jako mnożnik dokumentu dots per inch (DPI). Przelicz stałe rozmiary w pikselach na punkty.
enable_remote / setIsRemoteEnabled()(brak odpowiednika — celowo)Rozstrzygnij zasoby zdalne w swoim kodzie; zobacz Uwagi dotyczące bezpieczeństwa.
enable_html5_parser / setIsHtml5ParserEnabled()(zawsze parsuje HTML)Brak przełącznika; parser jest częścią potoku.
enable_php / setIsPhpEnabled()(brak odpowiednika — celowo)PHP wewnątrz dokumentu nie jest obsługiwany. Przenieś logikę poza szablon.
font_dir / setFontDir()Config->fontsDirectoryPojedynczy ciąg znaków katalogu czcionek.
chroot(rozstrzygnij w aplikacji)NextPDF nie przyjmuje opcji izolacji systemu plików. Zweryfikuj ścieżki przed przekazaniem bajtów.
default_font / setDefaultFont()CSS font-family / zarejestrowana czcionkaUstaw wartość domyślną w bazowym arkuszu stylów lub przy rejestracji czcionki, a nie w opcji globalnej.
enable_font_subsetting / setIsFontSubsettingEnabled()(zawsze tworzy podzbiory)NextPDF zawsze tworzy podzbiory osadzonych czcionek (ISO 32000-2 §9, iso32000_2_sec9#x1.x45.p7). Nie ma opcji „off”; ścieżka Dompdf z wyłączoną flagą nie ma odpowiednika i nie jest potrzebna.
  • Silnik układu. Dompdf i NextPDF używają niezależnych implementacji układu CSS. Zwijanie białych znaków oraz łamanie wierszy są określone specyfikacją, ale pozostają zależne od silnika (CSS Text 3 §6.5, css_text_3#x1.x6.x5.p20). Przy gęstym tekście spodziewaj się różnic w zawijaniu wierszy i podziale na strony. Po migracji ponownie ustal punkt odniesienia dla różnic wizualnych.
  • Granica renderowania. Brak dwufazowej granicy render()/output() (zobacz Przypadki brzegowe).
  • Rozstrzyganie zasobów. Obsługa ścieżki bazowej/protokołu/hosta różni się od zestawu roboczego dokumentu.
  • Model DPI. Punkty PDF różnią się od mnożnika DPI w Dompdf.
  • Metadane. Dowolne pary addInfo() różnią się od typowanych setterów (ISO 32000-2 §14, iso32000_2_sec14#x1.x5.p5).

To udokumentowane różnice w zachowaniu, a nie wady któregokolwiek z silników.

  • enable_php (PHP wewnątrz dokumentu) — celowo nieobecne.
  • setHttpContext() / enable_remote pobieranie zdalne — celowo nieobecne.
  • Publiczny dostęp do FrameTree / Canvas / Stylesheet — brak publicznego odpowiednika.
  • dpi jako globalny mnożnik dokumentu — nie jest modelowane.

Kod, który zależy od tych elementów, nie podlega „migracji”. Usuń go albo zapisz ponownie w kodzie aplikacji, korzystając z powyższych wierszy.

  1. Dodaj nextpdf/core obok dompdf/dompdf. Nie usuwaj jeszcze Dompdf.
  2. Wybierz jeden dokument o niskim ryzyku. Przepisz jego miejsce wywołania przy użyciu mapowania czasowników i usuń wywołanie render().
  3. Wygeneruj oba pliki PDF z tych samych danych wejściowych i porównaj je wizualnie. Traktuj różnice jako oczekiwane, ponieważ silniki są niezależne, a akceptację oceniaj dla każdego dokumentu z osobna.
  4. Przerób użycie opcji przy użyciu mapy opcji; przelicz rozmiary pochodzące z DPI na punkty.
  5. Wstępnie rozstrzygnij zasoby zdalne i względne na ścieżki bezwzględne lub identyfikatory data URI, aby wyeliminować zmienną rozstrzygania.
  6. Powtarzaj dla każdego dokumentu, od najniższego do najwyższego ryzyka. Zachowaj oba silniki zainstalowane, dopóki ostatnie miejsce wywołania nie zostanie przełączone.
  7. Usuń dompdf/dompdf z composer.json dopiero po ostatnim przełączeniu.
  • Wykonaj zrzut wyjścia Dompdf dla reprezentatywnych dokumentów przed zmianą kodu. Używaj referencyjnych danych wejściowych, a nie referencyjnych bajtów, ponieważ bajty będą się różnić.
  • Dla każdego zmigrowanego dokumentu przepuść wynik NextPDF przez własną procedurę akceptacji, taką jak różnica wizualna lub asercje wyodrębniania tekstu. Własne zachowanie potoku HTML silnika NextPDF w examples/08-html-basic.php jest objęte przez tests/ oraz pakiet testów Html w rdzeniu. Akceptacja migracji zależy od dokumentu i to Ty odpowiadasz za jej potwierdzenie.
  • Dodaj test regresji dla każdego zmigrowanego dokumentu, aby wychwycić zmiany po przyszłej aktualizacji silnika.

Każde stwierdzenie behawioralne dotyczące NextPDF na tej stronie jest poparte testem w repozytorium, przykładem, sygnaturą źródła lub rekordem decyzji architektonicznej (ADR), a w przypadku właściwości formatu PDF — klauzulami ISO 32000-2 / CSS zakotwiczonymi przez Retrieval-Augmented Generation (RAG) w polu citations: frontmatteru oraz tabelą Zgodność. Zachowanie Dompdf jest stwierdzane wyłącznie jako „niezależny silnik — spodziewaj się udokumentowanych różnic”. Ta strona nie deklaruje żadnej równoważności, o ile nie dowodzi tego artefakt w repozytorium.

NextPDF stwierdzenie behawioralneDowód w repozytorium (ścieżka)
Domyślna strona createStandalone() to A4 w orientacji pionowej (595.276 × 841.890 pt).src/Core/Config.php (domyślnie PageSize(595.276, 841.890, 'A4')); tests/Unit/Core/DocumentCreateStandaloneAndConfigWithersEdgeCaseTest.php (createStandaloneWithNullConfigBuildsDocumentWithA4Defaults).
writeHtml() rozkłada układ w jednym przebiegu strumieniowym; po wykonaniu układu nie zachowuje DOM.docs/architecture/adr/ADR-001-stream-based-rendering-pipeline.md; src/Core/Concerns/HasTextOutput.php (writeHtml()).
writeHtml() automatycznie tworzy pierwszą stronę, gdy żadna nie istnieje.tests/Unit/Core/Concerns/DocumentTextOutputFontSubsettingAndBorderEdgeCaseTest.php (writeHtmlAutoCreatesFirstPageWhenNoPagesExist).
output() / save() / getPdfData() to czasowniki emisji (brak dwufazowego schematu render/output).src/Core/Concerns/HasOutput.php (output(), save(), getPdfData()); tests/Unit/Core/Concerns/DocumentOutputDestinationDispatchTest.php.
Miejsce docelowe wyjścia to wyliczenie NextPDF\Contracts\OutputDestination (Inline/Download/File/String).src/Contracts/OutputDestination.php; tests/Unit/Core/Concerns/DocumentOutputDestinationDispatchTest.php.
Czcionki HTML są zawsze zapisywane jako programy embedded/subset.tests/Unit/Core/Concerns/DocumentTextOutputFontSubsettingAndBorderEdgeCaseTest.php (recordUsedCharactersAffectsFontSubsetting); ISO 32000-2 §9 (frontmatter citations:).
Typowane settery metadanych (setTitle/setAuthor) zastępują dowolne addInfo().src/Core/Concerns/HasMetadata.php (setTitle(), setAuthor()); tests/Unit/Core/Concerns/DocumentInfoMetadataSetterBaselineTest.php.
Kompleksowy potok HTML (uruchamialne zaplecze tego przewodnika).examples/08-html-basic.php; pakiet testów tests/Unit/Html/ w rdzeniu.
Białe znaki / łamanie wierszy są specyficzne dla silnika (różnica układu).CSS Text 3 §6.5 (frontmater citations: + Zgodność).

Ponieważ oba pakiety pozostają zainstalowane aż do ostatecznego przełączenia, wycofanie nieprzekonwertowanego miejsca wywołania oznacza przywrócenie tego miejsca do ścieżki Dompdf. Po ostatecznym przełączeniu wycofanie oznacza przywrócenie dompdf/dompdf oraz poprzedniego miejsca wywołania z systemu kontroli wersji. Dotyczy to wyłącznie zmian w kodzie, nie migracji danych.

Zobacz Wydajność. Model jednoprzebiegowy oznacza, że migracja nie wprowadza kosztu utrzymywania drzewa ramek. Główna zmiana kosztu dla dokumentu wynika z wyprzedzającego rozstrzygania zasobów z kroku 5, które można buforować.

  • Pozostawienie render() na miejscu, co powoduje krytyczny błąd niezdefiniowanej metody.
  • Przekazanie bajtów spoza UTF-8 po usunięciu $encoding, co powoduje ciche mojibake.
  • Oczekiwanie wyniku identycznego bajt po bajcie lub piksel po pikselu od niezależnych silników. Ten przewodnik nigdy nie deklaruje rozwiązania typu drop-in ani 100% zgodności.
  • Poleganie na szablonach enable_php, które trzeba zrefaktoryzować.
  • Traktowanie macierzy obsługi CSS jako informacji pomocniczej. Jest ona źródłem rozstrzygającym dla funkcji zweryfikowanych i określa, czego się spodziewać.