HTML mit dem Artisan-Chrome-Renderer als PDF rendern
Auf einen Blick
Abschnitt betitelt „Auf einen Blick“Die Artisan-Brücke rendert HTML über einen Headless-Chrome-Prozess und importiert das Ergebnis als Vektor-Form-XObject in ein NextPDF-Dokument. Der Text bleibt auswählbar und durchsuchbar, statt gerastert zu werden. Sie hängen eine ChromeRendererConfig an, rufen writeHtmlChrome() auf einem Dokument auf (oder verwenden ChromeHtmlRenderer direkt), und Chrome übernimmt das Layout. Diese Anleitung behandelt den Render-Aufruf, die Netzwerkisolationsrichtlinie, das Seitengrößen- und Inhaltshöhenmodell sowie den Renderer-Lebenszyklus in einem langlebigen Worker.
Vorab gelten diese Voraussetzungen:
- NextPDF Core und
nextpdf/artisansind installiert. - Eine Chrome- oder Chromium-Binary ist installiert, und der Worker-Benutzer kann sie headless ausführen. Prüfen Sie das vor dem Start mit
chromium --headless --dump-dom about:blank. Hinweise zur Bereitstellung der Binary und zur Entscheidung über die Container-Sandbox finden Sie auf der unter „Siehe auch“ verlinkten Seite zur Chrome-Renderer-Einrichtung.
Dies ist eine Schritt-für-Schritt-Anleitung. Sie setzt voraus, dass Sie einen Chrome-Prozess nahe bei der Anwendung ausführen können. Für ein erstes lauffähiges Beispiel lesen Sie den Artisan-Schnellstart.
Installation
Abschnitt betitelt „Installation“Installieren Sie die Brücke zusammen mit Core.
composer require nextpdf/artisanInstallieren Sie einen Chrome- oder Chromium-Build, den der Worker-Benutzer ausführen kann. Unter Debian oder Ubuntu verwenden Sie das Distributionspaket.
apt-get install -y chromiumStellen Sie sicher, dass die Binary als Worker-Benutzer headless läuft.
chromium --headless --dump-dom about:blankExit-Code 0 mit leerem DOM bedeutet, dass die Binary und ihre Shared Libraries vorhanden sind. Ein Exit-Code ungleich null entspricht dem Fehler, den die Brücke als ChromeRenderException meldet. Beheben Sie ihn zuerst an dieser Stelle.
Konzeptioneller Überblick
Abschnitt betitelt „Konzeptioneller Überblick“writeHtmlChrome() ist eine Methode des NextPDF-Core-Document. Sie validiert die Eingabe, löst den Artisan-Renderer auf, sendet das HTML über das Chrome DevTools Protocol (CDP) an Chrome, parst das zurückgegebene PDF und bettet Seite 0 als Form-XObject an der aktuellen Cursorposition ein. Chrome läuft als Kindprozess des PHP-Workers. Die Brücke steuert ihn über CDP, statt sich über einen Debugging-Port mit einem separat laufenden Chrome zu verbinden, sodass es keinen Netzwerkendpunkt gibt, den Sie freigeben oder authentifizieren müssten.
Die Brücke rendert mit einer Netzwerkhaltung, die standardmäßig alles verweigert. Jeder Rendervorgang wird in eine Content-Security-Policy gehüllt, die alle Ressourcenursprünge verweigert (default-src 'none') und nur Inline-Bilder erlaubt (img-src data:). Die Brücke blockiert außerdem jede Subressourcen-URL auf der CDP-Transportschicht mit Network.setBlockedURLs(['*']). Dadurch lädt ein entferntes Bild, Stylesheet, eine entfernte Schrift, ein entferntes Skript oder ein iframe in Ihrem HTML nicht. Binden Sie jedes Asset als data:-URI inline ein. Das ist die Antwort der Brücke auf das Risiko durch Server-Side Request Forgery (SSRF) beim Rendern von potenziell nicht vertrauenswürdigem HTML, und sie gilt unabhängig von der Konfiguration.
Das Seitengrößenmodell hat zwei Modi. Wenn Sie sowohl Breite als auch Höhe angeben (in PDF-Punkt), druckt Chrome genau auf dieses Papierformat. Wird die Höhe weggelassen oder ist sie null, misst die Brücke die gerenderte Inhaltshöhe in Chrome, rechnet sie in Punkt um und fügt einen kleinen Sicherheitspuffer für den Reflow hinzu (etwa 14,4 Punkt), damit printToPDF nicht auf eine zweite Seite überläuft, die der Importer (er übernimmt nur Seite 0) abschneiden würde.
API-Oberfläche
Abschnitt betitelt „API-Oberfläche“// 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 ist die einzige Konfigurationsoberfläche, und sie ist unveränderlich – erstellen Sie also eine neue Instanz, um einen Wert zu ändern. ChromeRenderResult::getPdfData() gibt die PDF-Bytes zurück. Die vollständige Optionsreferenz und die festen Chrome-Start-Flags finden Sie auf der unter „Siehe auch“ verlinkten Artisan-Konfigurationsseite.
Codebeispiel — Schnellstart
Abschnitt betitelt „Codebeispiel — Schnellstart“Hängen Sie die Konfiguration an ein Dokument an, rendern Sie vertrauenswürdiges HTML und speichern Sie.
<?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 übernimmt das Flex-Layout; die Zahlen bleiben in der Ausgabe auswählbar, weil die Seite als Vektor-Form-XObject eingebettet wird und nicht als Rasterbild. Um auf eine feste A4-Seite zu passen, übergeben Sie Breite und Höhe in Punkt.
$document->writeHtmlChrome($html, width: 595.28, height: 841.89);Codebeispiel — Produktion
Abschnitt betitelt „Codebeispiel — Produktion“In der Produktion erstellen Sie einen Renderer pro Worker, injizieren einen PSR-3-Logger, fangen die beiden unterschiedlichen Exception-Typen getrennt ab und geben den Chrome-Prozess beim Herunterfahren deterministisch frei.
<?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(); }}Der Renderer wird einmal erstellt und wiederverwendet. Der zugrunde liegende Browser-Pool hält einen Chrome-Prozess am Leben und startet ihn alle 100 Render-Vorgänge neu, um das Speicherwachstum zu begrenzen. Die beiden catch-Zweige trennen einen Deployment-Fehler (fehlende Runtime) von einem Renderzeitfehler (wiederholbar), und keiner der catch-Blöcke ist leer. Rufen Sie beim Herunterfahren des Workers shutdown() auf, um den Chrome-Prozess freizugeben, statt auf den Destruktor zu warten.
Erstellen Sie die Konfiguration aus einem Framework-Konfigurationsarray, um snake_case-Schlüssel zu erhalten, und fixieren Sie chromeBinaryPath in der Produktion auf eine deterministische Binary.
Randfälle & Stolperfallen
Abschnitt betitelt „Randfälle & Stolperfallen“- Leeres HTML ist ein No-op.
writeHtmlChrome('')gibt das Dokument unverändert zurück. - Noch keine Seite. Hat das Dokument noch keine Seite, fügt
writeHtmlChrome()vor dem Rendern eine hinzu. - Entfernte Assets laden nicht — das ist beabsichtigt.
<img src="https://...">rendert leer. Binden Sie jedes Asset alsdata:-URI inline ein. Das ist die Netzwerkisolationshaltung, kein Defekt. - Nur Seite 0 wird importiert. Die automatisch angepasste Höhe enthält den Reflow-Puffer, sodass eine einzige Seite entsteht. Bei einer expliziten Höhe wird kein Puffer hinzugefügt, und die Ausgabe entspricht exakt dem angeforderten Papierformat – bemessen Sie die Höhe also so, dass sie zu Ihrem Inhalt passt.
- Brücke fehlt. Ist
nextpdf/artisannicht installiert, wirft Core eine Layout-Exception statt eines fatalen Fehlers. Fehlt die Bibliothekchrome-php/chrome, wirft die BrückeChromeNotAvailableExceptionmit dem Installationsbefehl. defaultCssund</style>. Jede</style>-Sequenz indefaultCsswird vor der Injektion entfernt, um Style-Break-out zu verhindern. Berücksichtigen Sie das, wenn Sie CSS über Templates erzeugen.
Performance
Abschnitt betitelt „Performance“Der erste Rendervorgang umfasst den Chrome-Start plus Layout. Spätere Rendervorgänge verwenden den laufenden Chrome-Prozess wieder, sodass die Startkosten nur selten anfallen. Erstellen Sie einen Renderer pro Worker und verwenden Sie ihn wieder. Erstellen Sie nicht für jede Anfrage einen eigenen. Rechnen Sie bei jedem 100. Render-Vorgang mit einer Latenzspitze, wenn die Brücke den Chrome-Prozess neu startet, um das Speicherwachstum zu begrenzen. Berücksichtigen Sie das in Ihren Latenz-Zielen, statt es als Incident zu behandeln. Kombinieren Sie renderTimeout auf jedem Pfad, der über nicht vertrauenswürdige Eingaben erreichbar ist, mit einem vorgelagerten Anfragebudget.
Sicherheitshinweise
Abschnitt betitelt „Sicherheitshinweise“- Die Netzwerkisolation ist die primäre Kontrolle. Die Brücke erlaubt überhaupt keinen ausgehenden Subressourcen-Abruf — CSP
default-src 'none'plus eine Sperre jeder URL auf CDP-Transportebene. Sie implementiert keine Domain-Allowlist, weil sie keine benötigt. Binden Sie Assets alsdata:-URIs inline ein. - Die Eingabe wird begrenzt, bevor Chrome kontaktiert wird. Die Brücke weist HTML über
maxHtmlSize(Standard 5 MB) zurück, ebenso eine zu große base64-Data-URI (ein Schutz gegen Dekompressions-Bomben) und jedes<meta http-equiv="refresh">-Tag (das eine Navigation zu einem internen Endpunkt auslösen könnte). Belassen SiemaxHtmlSizebeim Standard, es sei denn, eine bekannte Last erfordert mehr. Eine Erhöhung vergrößert die Angriffsfläche für Ressourcenerschöpfung. - Die Chrome-Sandbox ist eine separate Kontrolle. Wenn Sie
noSandbox: truesetzen, startet Chrome mit--no-sandbox, was die Chrome-Prozessisolation entfernt — eine echte Verringerung der Eindämmung, kein kosmetisches Flag. Belassen Sie es außerhalb von Containern auffalse. Kann die Container-Sandbox nicht initialisiert werden, führen Sie Chrome als Nicht-Root-Benutzer in einem eingeschränkten Container aus und behandeln Sie das Deployment als Anforderung mit höherem Vertrauen an die Eingabe. - Logs enthalten nur Metadaten. Injizieren Sie einen PSR-3-Logger. Die Brücke protokolliert Byte-Längen, Abmessungen und Lebenszyklus-Ereignisse, niemals HTML, PDF-Bytes oder extrahierten Text.
- Geben Sie niemals einen Chrome-Remote-Debugging-Port frei. Die Brücke nutzt keinen, und ein offener CDP-Port ist ein nicht authentifizierter Steuerkanal.
Das vollständige Bedrohungsmodell — die SSRF-Abwehr, die explizit benannte Sandbox-Grenze und der Katalog der Fehlermodi — finden Sie auf der unter „Siehe auch“ verlinkten Artisan-Seite zu Sicherheit und Betrieb, die die relevanten OWASP-, CWE- und NIST-Klauseln festhält.
Konformität
Abschnitt betitelt „Konformität“Diese Anleitung erhebt selbst keinen normativen Standardanspruch. Die Netzwerk-, Isolations- und Ressourcenerschöpfungskontrollen der Brücke sind auf der vorgelagerten Artisan-Seite zu Sicherheit und Betrieb OWASP ASVS, den CWE Top 25 (SSRF / unkontrollierter Ressourcenverbrauch) und NIST SP 800-53 SC-7 zugeordnet. Diese Cookbook-Seite gibt die Verwendung wieder und überlässt die normativen Zitate jener Seite. Die Brücke führt keine kryptografische Operation aus — Signieren und Verschlüsseln sind Aufgaben von Core oder der kommerziellen Edition und bleiben von Artisan unberührt.
Siehe auch
Abschnitt betitelt „Siehe auch“- Am Edge mit Cloudflare rendern — HTML am Edge rendern, mit lokalem Fallback.
- Artisan-Schnellstart — der minimale erste Render-Vorgang.
- Chrome-Renderer-Einrichtung — die Binary bereitstellen, über die Container-Sandbox entscheiden und einen Health-Probe einrichten.
- Artisan – Sicherheit und Betrieb — das Netzwerkisolationsmodell, die Sandbox-Grenze und die Fehlermodi.