Zum Inhalt springen

HTML: Subsystem für HTML+CSS-zu-PDF-Rendering

Das HTML-Subsystem wandelt HTML+CSS in einem einzigen Vorwärtsdurchlauf in PDF-Content-Streams um. Mit 324 Dateien unter src/Html/ ist es das größte und risikoreichste Subsystem der Engine.

Terminal-Fenster
composer require nextpdf/core:^3

Das HTML-Subsystem ist ein Single-Pass-Streaming-Renderer für HTML+CSS-zu-PDF. Seine öffentliche Schnittstelle besteht aus einer einzigen Methode, Document::writeHtml(). Intern tokenisiert HtmlParser die Eingabe, löst Styles auf, berechnet das Layout und gibt PDF-Operatoren in einem einzigen Vorwärtsdurchlauf aus — ohne einen Dokumentbaum vorzuhalten.

Machen Sie sich den Umfang bewusst. Dieses Subsystem ist kein Retained-Document-Renderer. Es hält keinen Elementgraphen vor, legt bereits geschriebenen Inhalt nicht neu aus und lässt Änderungen an der Eingabe nach Beginn des Parsens nicht mehr zu. Es implementiert eine kuratierte Teilmenge von CSS mit festgelegten Spezifikations-Pins. Zwei Architekturentscheidungen prägen es. ADR-001 legt das Single-Pass-Streaming-Modell und seine Obergrenzen fest. ADR-010 legt den Vier-Layer-Vertrag (CSS-Parsing, Style-State, Layout, Paint) sowie die Paged-Media- und Mess-Zusätze fest.

HtmlParser ist im Modul-Manifest als kritisch eingestuft. Fünf Dateien tragen dokumentierte Danger-Zone-Annotationen: der Orchestrator HtmlParser (Streaming-Tokenizer, 1000+ LOC), HtmlStyleState (100+ CSS-Property-Felder mit Stack-Vererbungsmodell), HtmlBlockHandler (Block-Dispatch, eng an den Style-State gekoppelt), FlexLayoutEngine (vollständige Flex-Vermessung und vollständiges Flex-Layout) und TableParser (Colspan-/Rowspan-Paginierung über Seitenumbrüche hinweg). Behandeln Sie Änderungen hier als Plan-Mode-Arbeit.

Diese Seite ist der Einstiegspunkt. Die Detailseiten behandeln: pipeline die Stage-Reihenfolge, css-resolver Cascade und Spezifität, layer-contracts-adr010 die Layer-Grenzen und streaming-constraints-adr001 das No-Retained-Tree-Modell und seine Obergrenzen.

writeHtml() rendert Rechts-nach-links-Inhalt (RTL). Setzen Sie die CSS-Property direction: rtl auf den Body, eine Tabelle oder ein beliebiges Element. Die Engine löst die visuelle Reihenfolge mit dem Unicode-Bidirectional-Algorithmus (UAX #9) über die bidirektionale Engine der Typografie-Schicht auf — Einzelheiten zu BidiEngine finden Sie unter Typografie. Gemischter lateinischer, arabischer und numerischer Inhalt wird korrekt angeordnet, und eine Zahl nach arabischem Text behält ihre Ziffern von links nach rechts.

Arabisch erhält außerdem eine kontextuelle Formung: Die Engine wählt die initiale, mediale, finale oder isolierte Form jedes Buchstabens und wendet die Lam-Alef-Ligatur an. Die Formung benötigt einen registrierten Font, dessen Character-Map den Block Arabic Presentation Forms-B abdeckt; ein rein lateinischer Schnitt, einschließlich der standard-14-Schriften, kann kein Arabisch darstellen. In Tabellen wird jede Zelle für sich umgeordnet und geformt und richtet sich unter direction: rtl am Start-Rand (rechts) aus. RTL gilt für Arabisch, Hebräisch, Persisch und Urdu; Hebräisch wird umgeordnet, aber nicht geformt.

Setzen Sie die Richtung mit der CSS-Property direction — das HTML-Attribut dir wird nicht darauf abgebildet. Die horizontale Ausrichtung von Block- und Inline-Text außerhalb von Tabellen sowie text-align: justify werden noch nicht angewendet. Für eine lauffähige arabische Rechnung und die vollständige Liste der aktuellen Beschränkungen siehe Rechts-nach-links-Arabisch-HTML rendern.

SymbolSpeicherortRolle
Document::writeHtml(string $html): staticsrc/Core/Concerns/HasTextOutput.phpÖffentlicher Einstiegspunkt. Rendert HTML an der aktuellen Cursor-Position.
Document::createStandalone(): selfsrc/Core/Document.phpErzeugt ein eigenständiges Dokument.
HtmlParser::parse(string $html): HtmlRenderResultsrc/Html/HtmlParser.phpInterner Orchestrator.
HtmlRenderResultsrc/Html/HtmlRenderResult.phpUnveränderliches Ergebnis: Stream, End-Cursor, verwendete Schriften.
DefaultHtmlSecurityPolicysrc/Html/DefaultHtmlSecurityPolicy.phpStandardrichtlinie für Tags, Attribute, CSS und URLs.
HtmlSecurityPolicyInterfacesrc/Contracts/HtmlSecurityPolicyInterface.phpRichtlinienvertrag für eigene Policies.

Entnommen aus examples/08-html-basic.php.

<?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 style="color:#1E3A8A;">HTML Rendering</h1><p>Direct to PDF.</p>');
$doc->save(__DIR__ . '/output/08-html-basic.pdf');

Ein Tabellenbericht mit eingebettetem Style-Block, angelehnt an examples/09-html-table.php.

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
use NextPDF\Exception\HtmlParsingException;
function renderInventory(string $rowsHtml, string $out): void
{
$doc = Document::createStandalone();
$doc->setTitle('Inventory');
$doc->addPage();
$html = '<style>table { width: 100%; } '
. 'th { background-color: #1E3A8A; color: #FFFFFF; }</style>'
. '<table border="1" cellpadding="5">' . $rowsHtml . '</table>';
try {
$doc->writeHtml($html);
} catch (HtmlParsingException $e) {
// Input cap, element cap (50,000), or nesting cap (100). Do not retry.
throw $e;
}
$doc->save($out);
}
  • Kuratierte CSS-Teilmenge. Die Unterstützung ist pro Modul angegeben und gepinnt. Prüfen Sie die CSS-Support-Matrix, bevor Sie sich auf eine Property verlassen.
  • Harte Obergrenzen werfen Ausnahmen. 10 MB Eingabe, 50,000 Elemente, 100 Verschachtelungsebenen — jede davon führt zu einer HtmlParsingException. Siehe Streaming-Beschränkungen.
  • Kein Re-Layout. Die Ausgabe wird einmal in Dokumentreihenfolge geschrieben; nachfolgende Styles können bereits erzeugte Ausgabe nicht mehr ändern.
  • :has() ist gegated durch das experimentelle Feature css.has.
  • Subsystem mit kritischem Risiko. Fünf Danger-Zone-Dateien. Nutzen Sie den Plan-Mode für Änderungen unter src/Html/.

Der Renderer hält keinen Dokumentbaum vor und arbeitet in einem einzigen Vorwärtsdurchlauf. Die Obergrenzen für Elemente, Verschachtelung und Eingabe sind harte Limits. Alle Details sowie den Worker-Safety-Vertrag finden Sie unter Streaming-Beschränkungen (ADR-001).

CSS-Parsing, Style-State, Layout und Paint sind in vier Layer mit gerichteten Verträgen gegliedert, ergänzt um Paged-Media- und Mess-Zusätze. Alle Details finden Sie unter Layer-Verträge (ADR-010).

Der Speicherbedarf für Style-State und Cursor liegt bei O(Verschachtelungstiefe), nicht bei O(Elementanzahl). Das performance_budget pro Seite beträgt peak_mb: 64. Die Obergrenze von 50,000 Elementen ist die harte Grenze; teilen Sie größere Eingaben auf mehrere writeHtml()-Aufrufe auf. Details finden Sie unter Streaming-Beschränkungen.

Die Traversierung erfolgt in O(Token-Anzahl). Die Spaltendimensionierung von Tabellen fügt pro Tabelle einen begrenzten Zeilen-Scan hinzu. Der optionale :has()-Vorscan ergänzt einen begrenzten Durchlauf über die Token-Liste. Der Performance-Benchmark der HTML-Render-Pipeline setzt ein Regression-Gate von 5 % durch (gemergte Arbeit, PR #564). Das performance_budget pro Seite (wall_ms: 1500, peak_mb: 64) ist die operative Obergrenze.

DefaultHtmlSecurityPolicy erzwingt eine Allowlist für Tags, Attribute, CSS-Properties und URL-Schemata sowie eine Eingabe-Obergrenze von 10 MB und eine Verschachtelungs-Obergrenze von 100 Ebenen — unabhängig vom Parser. Die CSS-Property-Allowlist ist die Sicherheitsobergrenze. Die Runtime-Support-Tabelle ist eine separate Capability-Obergrenze. Implementieren Sie HtmlSecurityPolicyInterface, um eine strengere Richtlinie bereitzustellen. Das Abrufen externer Ressourcen wird separat durch DefaultExternalResourcePolicy geregelt.

In href- und Bild-src-Werten weist die URL-Allowlist auch Werte zurück, die mit Backslash beginnen (\…), sowie UNC-Pfade (\\host\share); zusätzlich weist sie wie bisher protokollrelative (//) Pfade zurück und nutzt eine Allowlist, die nur http(s) oder relative Pfade zulässt. Backslashes werden vor der Prüfung zu Forward-Slashes normalisiert, sodass eine lokale Dateieinbindung über einen Windows-Absolutpfad oder ein Abruf über eine SMB-Freigabe — beide ohne URI-Schema — nicht durch den Zweig „kein Schema, also relativ“ fallen kann.

Auszug aus der CSS-Support-Matrix (nur verifizierte Zeilen)

Abschnitt betitelt „Auszug aus der CSS-Support-Matrix (nur verifizierte Zeilen)“

Diese Seite wiederholt die Unterstützung pro Property nicht. Die CSS-Support-Matrix ist die alleinige Autorität für den verifizierten Status pro W3C-Modul, einschließlich der Unterscheidung, welche Module „Verified“ und welche „Claimed“ sind.

Das Subsystem implementiert eine kuratierte CSS-Teilmenge mit festgelegten Spezifikations-Pins. Die Verhaltens-Spec-Mappings für die Cascade sind auf css-resolver mit Clause- und Chunk-Identifiern dokumentiert. Der Konformitätsstatus pro Modul steht in der CSS-Support-Matrix.

Enterprise-Funktion. Premium erweitert die CSS-Abdeckung (erweiterter Druck und zusätzliche Module) auf derselben Single-Pass-Pipeline. Architektur, Obergrenzen und Layer-Verträge sind über alle Editionen hinweg gleich. Siehe die CSS-Support-Matrix.