Renderowanie HTML do PDF za pomocą renderera Chrome w Artisan
W skrócie
Dział zatytułowany „W skrócie”Mostek Artisan renderuje HTML w procesie Chrome działającym w trybie bezgłowym, a następnie importuje wynik do dokumentu NextPDF jako wektorowy obiekt Form XObject. Tekst pozostaje zaznaczalny i przeszukiwalny, zamiast zostać zrasteryzowany. Dołączasz obiekt ChromeRendererConfig, wywołujesz metodę writeHtmlChrome() na dokumencie albo używasz bezpośrednio klasy ChromeHtmlRenderer, a za układ odpowiada Chrome. Ten przewodnik omawia wywołanie renderowania, izolację sieciową, ustawianie rozmiaru strony, wysokość treści oraz długotrwały cykl życia renderera w procesie roboczym.
Na początek sprawdź wymagania wstępne:
- Rdzeń NextPDF oraz pakiet
nextpdf/artisansą zainstalowane. - Plik wykonywalny Chrome lub Chromium jest zainstalowany, a użytkownik procesu roboczego może uruchomić go w trybie bezgłowym. Zanim zaczniesz, sprawdź to poleceniem
chromium --headless --dump-dom about:blank. Strona konfiguracji renderera Chrome, do której odnośnik znajduje się w sekcji Zobacz też, omawia udostępnianie pliku wykonywalnego oraz decyzję dotyczącą piaskownicy w kontenerze.
Ten poradnik zakłada, że możesz uruchomić proces Chrome w tym samym środowisku co aplikacja. Pierwszy uruchamialny przykład znajdziesz w przewodniku szybkiego startu Artisan.
Instalacja
Dział zatytułowany „Instalacja”Zainstaluj mostek obok rdzenia.
composer require nextpdf/artisanZainstaluj wersję Chrome lub Chromium, którą może uruchomić użytkownik procesu roboczego. W systemach Debian lub Ubuntu użyj pakietu z dystrybucji.
apt-get install -y chromiumUpewnij się, że plik wykonywalny uruchamia się w trybie bezgłowym jako użytkownik procesu roboczego.
chromium --headless --dump-dom about:blankKod wyjścia 0 z pustym obiektowym modelem dokumentu (DOM) oznacza, że plik wykonywalny i jego biblioteki współdzielone są dostępne. Niezerowy kod wyjścia oznacza tę samą awarię, którą mostek zgłasza jako ChromeRenderException. Najpierw usuń problem na tym etapie.
Przegląd koncepcyjny
Dział zatytułowany „Przegląd koncepcyjny”writeHtmlChrome() to metoda klasy Document w rdzeniu NextPDF. Waliduje dane wejściowe, rozwiązuje renderer Artisan, wysyła HTML do Chrome za pośrednictwem protokołu Chrome DevTools Protocol (CDP), parsuje zwrócony PDF i osadza stronę 0 jako obiekt Form XObject w bieżącej pozycji kursora. Chrome działa jako proces potomny procesu roboczego PHP. Mostek steruje Chrome przez CDP zamiast łączyć się z osobnym procesem Chrome przez port debugowania, więc nie ma żadnego sieciowego punktu końcowego, który trzeba by wystawiać lub uwierzytelniać.
Mostek renderuje z domyślnie odmownym modelem sieci. Każde renderowanie korzysta z reguły Content-Security-Policy, która blokuje dostęp do wszystkich źródeł zasobów (default-src 'none') i zezwala wyłącznie na obrazy osadzone w treści (img-src data:). Mostek blokuje też każdy adres URL podzasobu w warstwie transportowej CDP za pomocą Network.setBlockedURLs(['*']). W rezultacie zdalny obraz, arkusz stylów, czcionka, skrypt lub iframe w kodzie HTML nie jest ładowany. Każdy zasób osadź w treści jako identyfikator URI data:. W ten sposób mostek przeciwdziała ryzyku fałszowania żądań po stronie serwera (SSRF) podczas renderowania kodu HTML, który może pochodzić z niezaufanego źródła. Mechanizm działa niezależnie od konfiguracji.
Model rozmiaru strony ma dwa tryby. Gdy podasz zarówno szerokość, jak i wysokość w punktach PDF, Chrome drukuje dokładnie do tego rozmiaru papieru. Gdy pominiesz wysokość albo ustawisz ją na null, mostek mierzy wysokość wyrenderowanej treści w Chrome, przelicza ją na punkty i dodaje niewielki bufor bezpieczeństwa na przepływ treści o wartości około 14,4 punktu. Dzięki temu wynik printToPDF nie przechodzi na drugą stronę, którą importer obsługujący tylko stronę 0 by przyciął.
Powierzchnia API
Dział zatytułowany „Powierzchnia API”// On a NextPDF core Document (the HasTextOutput concern):writeHtmlChrome(string $html, ?float $width = null, ?float $height = null): static
// The standalone renderer:new ChromeHtmlRenderer(ChromeRendererConfig $config, ?LoggerInterface $logger = null)ChromeHtmlRenderer::render(string $html, float $widthPt, float $heightPt = 0.0): ChromeRenderResultChromeHtmlRenderer::close(): void
// The configuration value object (final readonly):new ChromeRendererConfig( ?string $chromeBinaryPath = null, int $renderTimeout = 30, string $defaultCss = '', int $maxHtmlSize = 5_000_000, bool $noSandbox = false,)ChromeRendererConfig::fromArray(array $config): selfChromeRendererConfig to jedyna powierzchnia konfiguracji. Obiekt jest niemutowalny, więc aby zmienić wartość, utwórz nową instancję. ChromeRenderResult::getPdfData() zwraca bajty PDF. Strona konfiguracji Artisan, do której odnośnik znajduje się w sekcji Zobacz też, zawiera pełne zestawienie opcji oraz stałych flag uruchomieniowych Chrome.
Przykład kodu — szybki start
Dział zatytułowany „Przykład kodu — szybki start”Dołącz konfigurację do dokumentu, wyrenderuj zaufany kod HTML i zapisz wynik.
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Artisan\ChromeRendererConfig;use NextPDF\Core\Document;
$config = new ChromeRendererConfig( chromeBinaryPath: '/usr/bin/chromium',);
$document = Document::createStandalone();$document->setChromeRendererConfig($config);$document->addPage();
$document->writeHtmlChrome(' <div style="display: flex; gap: 20px; font-family: sans-serif;"> <div style="flex: 1; background: #f0f0f0; padding: 24px;"> <h2>Revenue</h2> <p style="font-size: 2em; color: #2563eb;">$124,500</p> </div> <div style="flex: 1; background: #f0f0f0; padding: 24px;"> <h2>Orders</h2> <p style="font-size: 2em; color: #16a34a;">1,847</p> </div> </div>');
$document->save('/tmp/report.pdf');Chrome obsługuje układ flex, a liczby pozostają zaznaczalne w pliku wynikowym, ponieważ strona jest osadzona jako wektorowy obiekt Form XObject, a nie obraz rastrowy. Aby dopasować treść do stałej strony A4, podaj szerokość i wysokość w punktach.
$document->writeHtmlChrome($html, width: 595.28, height: 841.89);Przykład kodu — środowisko produkcyjne
Dział zatytułowany „Przykład kodu — środowisko produkcyjne”W środowisku produkcyjnym utwórz jeden renderer na proces roboczy, wstrzyknij rejestrator zgodny z PSR-3, obsługuj osobno dwa odrębne typy wyjątków i zwalniaj proces Chrome w sposób deterministyczny podczas zamykania.
<?php
declare(strict_types=1);
use NextPDF\Artisan\ChromeHtmlRenderer;use NextPDF\Artisan\ChromeRendererConfig;use NextPDF\Artisan\Exception\ChromeNotAvailableException;use NextPDF\Artisan\Exception\ChromeRenderException;use Psr\Log\LoggerInterface;
final class ReportRenderer{ private ChromeHtmlRenderer $renderer;
public function __construct(LoggerInterface $logger) { $config = ChromeRendererConfig::fromArray([ 'chrome_binary' => getenv('CHROME_BINARY') ?: null, 'render_timeout' => 45, 'max_html_size' => 2_000_000, 'no_sandbox' => (bool) getenv('CHROME_NO_SANDBOX'), ]);
$this->renderer = new ChromeHtmlRenderer($config, $logger); }
public function render(string $html, float $widthPt, float $heightPt = 0.0): string { try { return $this->renderer->render($html, $widthPt, $heightPt)->getPdfData(); } catch (ChromeNotAvailableException $exception) { // Deployment fault: the Chrome runtime is missing. Page on-call. throw $exception; } catch (ChromeRenderException $exception) { // Render-time fault: timeout, crash, or empty output. Retryable once. throw $exception; } }
public function shutdown(): void { $this->renderer->close(); }}Utwórz renderer raz, a następnie używaj go ponownie. Bazowa pula przeglądarek utrzymuje jeden proces Chrome i restartuje go co 100 renderowań, aby ograniczyć wzrost zużycia pamięci. Dwa bloki catch oddzielają awarię wdrożenia, taką jak brakujące środowisko uruchomieniowe, od awarii w trakcie renderowania, którą można jednokrotnie ponowić. Żaden z bloków catch nie jest pusty. Wywołaj shutdown() przy zamykaniu procesu roboczego, aby zwolnić proces Chrome zamiast czekać na destruktor.
Zbuduj konfigurację z tablicy konfiguracyjnej frameworka, aby używać kluczy w stylu snake-case, i ustaw na stałe chromeBinaryPath w środowisku produkcyjnym, aby wybór pliku wykonywalnego był deterministyczny.
Przypadki brzegowe i pułapki
Dział zatytułowany „Przypadki brzegowe i pułapki”- Pusty kod HTML nie wykonuje żadnej operacji.
writeHtmlChrome('')zwraca dokument bez zmian. - Brak strony. Jeśli dokument nie ma żadnej strony,
writeHtmlChrome()dodaje jedną przed renderowaniem. - Zdalne zasoby nie ładują się — celowo.
<img src="https://...">renderuje się jako pusty element. Każdy zasób osadź w treści jako identyfikator URIdata:. To efekt modelu izolacji sieciowej, a nie usterka. - Importowana jest tylko strona 0. Automatyczne dopasowanie wysokości dodaje bufor na przepływ treści, dzięki czemu powstaje pojedyncza strona. Przy jawnie podanej wysokości żaden bufor nie jest dodawany, a wynik odpowiada dokładnie żądanemu rozmiarowi papieru, dlatego dobierz wysokość tak, aby pomieściła treść.
- Brak mostka. Jeśli pakiet
nextpdf/artisannie jest zainstalowany, rdzeń zgłasza wyjątek układu, a nie błąd krytyczny. Jeśli brakuje bibliotekichrome-php/chrome, mostek zgłaszaChromeNotAvailableExceptionwraz z poleceniem instalacji. defaultCssa</style>. Każda sekwencja</style>wdefaultCssjest usuwana przed wstrzyknięciem jako zabezpieczenie przed wyłamaniem się z kontekstu stylu. Uwzględnij to, jeśli tworzysz CSS z szablonów.
Wydajność
Dział zatytułowany „Wydajność”Pierwsze renderowanie wiąże się z kosztem uruchomienia Chrome i wyznaczenia układu. Kolejne renderowania wykorzystują ponownie działający proces Chrome, więc rzadko ponoszą koszt uruchomienia. Utwórz jeden renderer na proces roboczy i używaj go ponownie. Nie twórz go osobno dla każdego żądania. Spodziewaj się skoku opóźnienia przy co setnym renderowaniu, gdy mostek restartuje proces Chrome, aby ograniczyć zużycie pamięci. Uwzględnij to w swoich celach dotyczących opóźnień, zamiast traktować jako incydent. Połącz renderTimeout z budżetem żądania po stronie nadrzędnej w każdej ścieżce dostępnej dla niezaufanych danych wejściowych.
Uwagi dotyczące bezpieczeństwa
Dział zatytułowany „Uwagi dotyczące bezpieczeństwa”- Izolacja sieciowa jest podstawowym mechanizmem kontroli. Mostek w ogóle nie zezwala na żadne wychodzące pobieranie podzasobów: CSP
default-src 'none'oraz blokada każdego adresu URL na poziomie transportu CDP. Nie wdraża listy dozwolonych domen, ponieważ jej nie potrzebuje. Zasoby osadzaj w treści jako identyfikatory URIdata:. - Dane wejściowe są ograniczane, zanim nastąpi kontakt z Chrome. Mostek odrzuca kod HTML przekraczający
maxHtmlSize(domyślnie 5 MB), zbyt duży identyfikator URI typu data w formacie base64 (zabezpieczenie przed bombą dekompresyjną) oraz każdy znacznik<meta http-equiv="refresh">(który mógłby wymusić nawigację do wewnętrznego punktu końcowego). PozostawmaxHtmlSizena wartości domyślnej, chyba że znane obciążenie wymaga więcej. Jego zwiększenie poszerza powierzchnię ataku polegającego na wyczerpaniu zasobów. - Piaskownica Chrome jest osobnym mechanizmem kontroli. Ustawienie
noSandbox: trueuruchamia Chrome z flagą--no-sandbox, co usuwa izolację procesu Chrome. To rzeczywiste obniżenie poziomu izolacji, a nie kosmetyczna flaga. Poza kontenerami pozostaw ją na wartościfalse. Gdy piaskownica kontenera nie może się zainicjować, uruchom Chrome jako użytkownik bez uprawnień roota w ograniczonym kontenerze i traktuj takie wdrożenie jako wymagające wyższego poziomu zaufania do danych wejściowych. - Dzienniki zawierają wyłącznie metadane. Wstrzyknij rejestrator zgodny z PSR-3. Mostek rejestruje długości w bajtach, wymiary i zdarzenia cyklu życia, ale nigdy kodu HTML, bajtów PDF ani wyodrębnionego tekstu.
- Nigdy nie udostępniaj portu zdalnego debugowania Chrome. Mostek go nie używa, a otwarty port CDP to nieuwierzytelniony kanał sterujący.
Pełny model zagrożeń, obejmujący obronę przed SSRF, jawnie wyznaczoną granicę piaskownicy oraz katalog trybów awarii, znajduje się na stronie Artisan dotyczącej bezpieczeństwa i operacji, do której odnośnik znajduje się w sekcji Zobacz też. Ta strona mapuje odpowiednie klauzule OWASP, CWE i NIST.
Zgodność
Dział zatytułowany „Zgodność”Ten przewodnik nie formułuje własnych deklaracji zgodności z normatywnymi standardami. Nadrzędna strona Artisan dotycząca bezpieczeństwa i operacji odwzorowuje mechanizmy kontroli sieci, izolacji i wyczerpania zasobów stosowane przez mostek na OWASP ASVS, CWE Top 25 (SSRF / niekontrolowane zużycie zasobów) oraz NIST SP 800-53 SC-7. Ta strona poradnika ponownie przedstawia sposób użycia, a normatywne cytaty pozostawia tamtej stronie. Mostek nie wykonuje żadnej operacji kryptograficznej; podpisywanie i szyfrowanie należą do rdzenia lub edycji komercyjnej i nie zależą od Artisan.
Zobacz też
Dział zatytułowany „Zobacz też”- Renderowanie na brzegu sieci za pomocą Cloudflare — renderowanie HTML na brzegu sieci z lokalnym mechanizmem awaryjnym.
- Szybki start Artisan — minimalne pierwsze renderowanie.
- Konfiguracja renderera Chrome — udostępnienie pliku wykonywalnego, decyzja dotycząca piaskownicy w kontenerze oraz sonda kondycji.
- Bezpieczeństwo i operacje Artisan — model izolacji sieciowej, granica piaskownicy oraz tryby awarii.