Przejdź do głównej zawartości

Renderowanie HTML na stronę PDF

Użyj writeHtml(), aby wyrenderować fragment Hypertext Markup Language (HTML) i Cascading Style Sheets (CSS) jako zawartość strony Portable Document Format (PDF). Przekaż znaczniki, a NextPDF wyrenderuje sformatowaną stronę. Pełną, gotową do uruchomienia wersję tego kodu znajdziesz w pliku examples/08-html-basic.php. Wykonaj poniższe kroki albo skopiuj przykład bezpośrednio.

NextPDF odczytuje HTML jednokrotnie i przesyła wynik strumieniowo bezpośrednio na stronę. To jednoprzebiegowy potok strumieniowy. Aby skorzystać z tego przepisu, nie musisz rozumieć tego modelu. Warto jednak mieć go na uwadze, bo wyjaśnia kilka reguł w dalszej części tej strony.

Okno terminala
composer require nextpdf/core:^3

To polecenie instaluje pakiet nextpdf/core. Przykłady na tej stronie działają w PHP 8.4, a obsługiwane środowisko uruchomieniowe to >=8.4 <9.0.

writeHtml() przyjmuje ciąg HTML i rysuje go na bieżącej stronie, zaczynając od bieżącej pozycji kursora. W silniku NextPDF HTML jest skanowany jednokrotnie i dzielony na tokeny (HtmlTokenizer). Następnie parser przetwarza tę listę od lewej do prawej (HtmlParser). Dla każdego elementu zapisuje w buforze odpowiadające mu instrukcje rysowania PDF, zwane operatorami strumienia treści. Silnik nigdy nie buduje ani nie przechowuje drzewa elementów w pamięci między wywołaniami. To zamierzone rozwiązanie tworzy jednoprzebiegowy model strumieniowy opisany w ADR-001.

Każdy obsługiwany element blokowy staje się polem układu, a każdy ciąg tekstu operatorem wyświetlania tekstu. Style pochodzące z wbudowanych atrybutów style i bloku <style> rozstrzyga kaskada CSS, czyli zestaw reguł decydujących o tym, który styl wygrywa, gdy ma zastosowanie więcej niż jeden. Zawijanie tekstu, wyrównanie i odstępy są zgodne z modelem CSS Text, który określa, jak tekst źródłowy staje się sformatowanym tekstem z zawijaniem wierszy (W3C CSS Text Level 3).

Jeśli nie wybierzesz czcionki, tekst główny używa domyślnego kroju. Jest nim standardowa czcionka Type 1, jedna z 14 standardowych czcionek wymienionych w ISO 32000-2. Domyślny krój zmienia się tylko wtedy, gdy zarejestrujesz i wybierzesz własną czcionkę albo gdy profil zgodności wymaga, aby NextPDF osadził zamiennik.

Warto od razu doprecyzować zakres: NextPDF obsługuje podzbiór HTML i CSS, a nie pełny zakres któregokolwiek z nich. Ten przepis obejmuje obsługiwany podzbiór. Nie deklaruje pełnej obsługi HTML ani pełnej obsługi CSS. Dokładny, zweryfikowany status każdego modułu znajdziesz w macierzy obsługi CSS.

Sygnatura metody to writeHtml(string $html): static. Jest zadeklarowana w interfejsie NextPDF\Contracts\PdfDocumentInterface i zaimplementowana w NextPDF\Core\Concerns\HasTextOutput. Metoda renderuje na bieżącą stronę, a jeśli żadna strona jeszcze nie istnieje, tworzy ją automatycznie. Pełna tabela PHPDoc dla tej metody jest generowana z kodu źródłowego.

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->setTitle('HTML Basic');
$doc->addPage();
$doc->writeHtml('<h1>HTML Rendering in NextPDF</h1><p>Rendered with <strong>writeHtml()</strong>.</p>');
$doc->save(__DIR__ . '/out.pdf');

Ten pełny, samodzielny przykład jest uruchamiany przez mechanizm testowy. Odpowiada on plikowi examples/08-html-basic.php. Zamiast mieć ścieżkę wyjściową wpisaną na stałe, zapisuje plik w ścieżce dostarczonej przez mechanizm testowy. Dzięki temu mechanizm weryfikujący powtarzalność może uruchomić skrypt dwukrotnie i porównać wyniki.

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->setTitle('HTML Basic');
$doc->addPage();
$html = <<<'HTML'
<h1 style="color: #1E3A8A;">HTML Rendering in NextPDF</h1>
<p>NextPDF renders <strong>HTML content</strong> directly into PDF pages.
This is the recommended approach for <em>mixed formatting</em>.</p>
<h2>Supported elements</h2>
<ul>
<li>Headings (h1-h6)</li>
<li>Paragraphs with <strong>bold</strong> and <em>italic</em></li>
<li>Ordered and unordered lists</li>
<li>Tables with borders and alignment</li>
<li>Inline styles (color, font-size, margin)</li>
</ul>
<h2>Ordered list</h2>
<ol>
<li>Create a Document instance</li>
<li>Add pages and content</li>
<li>Call save() or output()</li>
</ol>
HTML;
$doc->writeHtml($html);
// The harness sets NEXTPDF_COOKBOOK_OUTPUT and runs this script twice.
// Honour it: do not hard-code a path, do not echo the PDF to STDOUT.
$out = getenv('NEXTPDF_COOKBOOK_OUTPUT');
$doc->save($out !== false ? $out : __DIR__ . '/render-html-to-pdf.pdf');
echo "Wrote render-html-to-pdf.pdf\n";

Oczekiwane STDOUT:

Wrote render-html-to-pdf.pdf
  • Przesunięcie kursora. writeHtml() przesuwa kursor na koniec wyrenderowanej treści. Kolejne wywołanie cell() lub drugie writeHtml() zacznie od tego miejsca, a nie od góry strony.
  • Brak strony. Jeśli nie istnieje żadna strona, writeHtml() dodaje ją przed renderowaniem. Najpierw wywołaj addPage(), gdy chcesz ustawić konkretny rozmiar strony.
  • Limity elementów i zagnieżdżenia. Silnik strumieniowy wymusza limit 50,000 elementów oraz limit 100 poziomów zagnieżdżenia (ADR-001). Dokument, który przekracza którykolwiek z tych limitów, jest odrzucany, a nie obcinany po cichu.
  • Nieobsługiwane znaczniki. Elementy i właściwości spoza obsługiwanego podzbioru są ignorowane albo otrzymują wartości zastępcze; nie powodują błędów. Sprawdź pokrycie w macierzy obsługi CSS, zanim zaczniesz polegać na danej właściwości.
  • Zasoby zewnętrzne. Zdalne obrazy i arkusze stylów podlegają zasadom dotyczącym zasobów zewnętrznych; domyślna zasada nie pobiera dowolnych zdalnych adresów URL.

Tokenizacja i renderowanie wykonują jeden przebieg po danych wejściowych, więc koszt rośnie liniowo wraz z liczbą tokenów, O(n). Domyślny budżet dla tego przepisu to wall_ms: 1500, peak_mb: 96. Ponieważ silnik przesyła wynik strumieniowo i nie utrzymuje w pamięci modelu Document Object Model (DOM), szczytowe zużycie pamięci zależy od bufora strumienia treści i aktywnego stosu stylów, a nie od całkowitego rozmiaru dokumentu.

Fragment macierzy obsługi CSS (tylko wiersze zweryfikowane)

Dział zatytułowany „Fragment macierzy obsługi CSS (tylko wiersze zweryfikowane)”

Ten fragment obejmuje tylko wiersze ocenione jako Verified w macierzy obsługi CSS zweryfikowanej pod kątem zgodności z rzeczywistym stanem. „Verified” oznacza implementację w src/Html/ oraz obszerny, dedykowany zestaw fixtur, który przechodzi deterministycznie w profilu strukturalnym.

Moduł W3CPoziomStatusDowód
CSS Flexible Box Layout (css_flexbox_1)1Verifiedsrc/Html/Flex/, tests/Unit/Html/Flex/
CSS Grid Layout (css_grid_1)1Verifiedsrc/Html/Grid/, korpus WPT
CSS Cascading and Inheritance (css_cascade_3)3Verifiedsrc/Html/Cascade/, tests/Unit/Html/Cascade/
CSS Table (css_tables_3)3Verifiedsrc/Html/Table/, fixtury tabel + wzorcowe pliki PDF
CSS Fonts (css_fonts_4)4Verifiedsrc/Html/FontFace/, tests/Unit/Html/FontFace/

Właściwości takie jak text-align, text-indent i color mają w macierzy status „Claimed” (zaimplementowane, bez dedykowanej fixtury modułu), dlatego nie są tutaj wymienione jako Verified.

Ograniczenia jednoprzebiegowego strumieniowania (ADR-001)

Dział zatytułowany „Ograniczenia jednoprzebiegowego strumieniowania (ADR-001)”

Silnik HTML nie zachowuje żadnego drzewa DOM. Stan silnika obejmuje skalarny kursor oraz stos stylów typu push/pop; węzły tekstowe zawierające wyłącznie białe znaki są odrzucane podczas tokenizacji. Jedną z konsekwencji jest to, że element występujący później nie może zmienić stylu wcześniejszego, a selektory wymagające kontekstu całego drzewa (na przykład złożone przypadki :has()) są ograniczone zgodnie z ADR-006. Planuj układ tak, aby zależał wyłącznie od kolejności w dokumencie.

Parsowanie, układ i rysowanie to osobne warstwy. Parser nie emituje surowych operatorów rysowania, a dyspozytor układu nie parsuje CSS. Przekraczanie tych granic tworzy dług sprzężenia, którego zabrania ADR-010. Dla autorów przepisów oznacza to, że publicznym punktem wejścia jest writeHtml(). Nie sięgaj do wewnętrznych elementów parsera.

Zgodnie z ADR-020 konteksty formatowania o zasięgu kontenera (flex, tabela) mogą zbudować tymczasowe poddrzewo ograniczone do 5,000 węzłów na kontekst i 20 poziomów głębokości, z pułapem aktywnej pamięci 50 MB w obrębie żywych kontekstów oraz limitem 10 poziomów zagnieżdżenia. Poza tymi kontekstami model strumieniowy nie przechowuje żadnego drzewa. Utrzymuj poszczególne tabele i kontenery flex w granicach limitu węzłów, aby zużycie pamięci było przewidywalne.

Traktuj dane wejściowe HTML jako niezaufane. NextPDF nie wykonuje skryptów, a domyślna zasada dotycząca zasobów zewnętrznych nie pobiera dowolnych zdalnych adresów URL, więc sam silnik działa zachowawczo. Mimo to waliduj lub oczyszczaj każdy HTML składany z danych wprowadzonych przez użytkownika, zanim go wyrenderujesz. Limity elementów i zagnieżdżenia także pełnią funkcję ochronną: ograniczają nakład pracy, którego może wymagać złośliwy lub uszkodzony dokument.

StwierdzenieSpecyfikacjaKlauzulareference_id
CSS Text steruje przekształcaniem tekstu źródłowego w sformatowany tekst z zawijaniem wierszy.W3C CSS Text Level 3css_text_3#x1.x2.p4
Domyślny krój tekstu głównego jest ustalany jako standardowa czcionka Type 1.ISO 32000-2iso32000_2_sec9#x1.x29

Ten przepis pokazuje, jak NextPDF renderuje obsługiwany podzbiór HTML i CSS. Nie deklaruje pełnej obsługi HTML ani pełnej obsługi CSS. Zweryfikowany status każdego modułu znajduje się w macierzy obsługi CSS.

Nie dotyczy.