Schichtverträge für die HTML-Engine (ADR-010)
Auf einen Blick
Abschnitt betitelt „Auf einen Blick“Das HTML-Subsystem trennt CSS-Parsing, Style-State, Layout und Paint in vier Schichten. Der Vertrag zwischen ihnen verläuft in eine Richtung. ADR-010 legt die Grenzen und Erweiterungsregeln fest.
Installation
Abschnitt betitelt „Installation“composer require nextpdf/core:^3Konzeptioneller Überblick
Abschnitt betitelt „Konzeptioneller Überblick“ADR-010 („Engine Layer Contracts, Hot Path Ownership, and Extension Rules“, angenommen am 2026-04-12) formalisiert die Schichtung des HTML-Subsystems. Der zentrale Rendering-Vertrag umfasst vier Schichten: CSS-Parsing und Applicators, Style-State, Layout und Formatierung sowie Paint. ADR-010 dokumentiert außerdem zwei Zusatzschichten — Paged Media und das Mess-Harness —, die den vierschichtigen Kern ergänzen, ohne dessen Datenfluss zu verändern. Der kanonische Glossarbegriff für den Kern ist „HTML-Pipeline“, eine vierschichtige Pipeline.
Daten fließen in eine Richtung. In Schicht 1 wird CSS-Text in typisierte Werte umgewandelt. Schicht 1 schreibt diese Werte in Schicht 2 in HtmlStyleState-Felder. Schicht 3 liest Style-State-Felder und berechnet die Geometrie. Schicht 4 liest einen unveränderlichen ComputedStyle-Snapshot plus Geometrie und gibt PDF-Operatoren aus. Keine Schicht liest aus einer nachgelagerten Schicht.
Die Trennung in vier Schichten ist mehr als Dokumentation. ADR-010 hält zwei eng begrenzte Refactorings fest, die in v1.2.0 Code in die richtige Schicht verschoben haben. PageBorderPainter wurde aus HtmlParser herausgelöst, damit Paint-Operatoren nicht länger im Orchestrator liegen. Der Klassen-Docblock von HtmlStyleState enthält jetzt den formalen Schichtvertrag, der festlegt, welche Felder jede Schicht schreiben oder lesen darf.
Eine Grenze wird bewusst offengelegt, statt versteckt zu werden. FormattingContextFactory::startTable() liest weiterhin fünf rohe CSS-Schlüssel direkt. ADR-010 hält dies als bekannte, aufgeschobene technische Schuld für einen künftigen TableApplicator fest, nicht als beabsichtigten Vertrag. Die Ausnahme zu dokumentieren, ist Teil des Vertrags.
Die vier Kernschichten
Abschnitt betitelt „Die vier Kernschichten“| Schicht | Dateien (repräsentativ) | Schreibt | Liest | Darf nicht |
|---|---|---|---|---|
| 1 — CSS-Parsing & Applicators | CssValueParser, CssResolver, HtmlCssApplicator, src/Html/Applicator/* | HtmlStyleState-CSS-Felder | Roher CSS-Text | Geometrie berechnen; Operatoren ausgeben |
| 2 — Style-State | HtmlStyleState, State/ComputedStyle, State/LayoutState | — (passive Sammlung von Werten) | — | CSS parsen; über Layout entscheiden; Operatoren ausgeben |
| 3 — Layout & Formatierung | FormattingContextFactory, HtmlBlockHandler, FlexLayoutEngine, TableParser, FloatContext | Cursor-Geometrie | HtmlStyleState-Felder | Rohes $css[...] lesen; Paint-Operatoren ausgeben |
| 4 — Paint & Rendering | BorderRenderer, BackgroundImageRenderer, src/Html/Paint/*, src/Html/Gradient/* | PDF-Operator-Stream | ComputedStyle (unveränderlich) + Geometrie | Geometrie berechnen; CSS parsen; über Seitenumbrüche entscheiden |
Die zwei Zusatzschichten
Abschnitt betitelt „Die zwei Zusatzschichten“| Schicht | Dateien (repräsentativ) | Rolle |
|---|---|---|
| 5 — Paged Media | PageBreakController, PageBorderPainter, PageRule, PageRuleParser, ParserConfigurator | @page-Regeln auflösen; Umbruch- und orphan/widow-Constraints auswerten; Seitendekoration an Paint delegieren. |
| 6 — Messung & Harness | WPT-Klassifizierer-Skripte, tests/Support/* | Testergebnisse klassifizieren; Regressions-Snapshots erzeugen; Assertion-Hilfen bereitstellen. Trägt keine Rendering-Logik. |
API-Oberfläche
Abschnitt betitelt „API-Oberfläche“Der Vertrag wird durch die Platzierung der Klassen und durch den Docblock von HtmlStyleState erzwungen. Prüfen Sie dies anhand von src/Html/.
| Symbol | Schicht | Vertragsrolle |
|---|---|---|
PropertyApplicatorInterface | 1 | Strategieinterface; die einzige Stelle, die CSS-Computed-Felder schreibt. |
ParserConfigurator::buildCssApplicator() | 1 (Verdrahtung) | Registriert jeden Applicator. Eine neue CSS-Eigenschaft wird hier registriert. |
HtmlStyleState | 2 | Sammlung mit zwei Gruppen; der Klassen-Docblock nennt die pro Feld zuständige Schicht. |
HtmlStyleState::toComputedStyle() | 2 | Erzeugt das unveränderliche ComputedStyle für die Paint-Schicht. |
FormattingContextFactory::dispatchOpenTag() | 3 | Einziger Routingpunkt für neues Layout-Verhalten. |
PageBorderPainter::buildStream() | 4 | Seitendekoration; wird aus Schicht 5 aufgerufen, nicht in HtmlParser eingebettet. |
Code-Beispiel — Schnellstart
Abschnitt betitelt „Code-Beispiel — Schnellstart“Aufrufender Code berührt die Schichten nie. Der vierschichtige Ablauf läuft innerhalb eines einzigen Aufrufs.
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();$doc->addPage();$doc->writeHtml('<p style="color:#1E3A8A;border:1px solid #999;">Layered render.</p>');$doc->save(__DIR__ . '/output/layers.pdf');Code-Beispiel — Produktion
Abschnitt betitelt „Code-Beispiel — Produktion“Der Vertrag ist für Mitwirkende wichtig, nicht für aufrufenden Code. Um eine CSS-Eigenschaft hinzuzufügen, folgen Sie dem Erweiterungspunkt von Schicht 1: Legen Sie einen Applicator an, ergänzen Sie ein typisiertes HtmlStyleState-Feld mit einem Schicht-Docblock und registrieren Sie den Applicator in ParserConfigurator. Die folgende Darstellung zeigt die Form des Applicator-Vertrags. Sehen Sie sich src/Html/Applicator/ an, um eine konkrete Klasse zum Kopieren zu finden.
<?php
declare(strict_types=1);
// Layer 1 extension contract (see ADR-010 §C "New CSS property").// A new property group ships as a PropertyApplicatorInterface// implementation registered in ParserConfigurator::buildCssApplicator().// It writes a typed HtmlStyleState field and never computes geometry// or emits PDF operators — those belong to Layers 3 and 4.Sonderfälle & Stolperfallen
Abschnitt betitelt „Sonderfälle & Stolperfallen“FormattingContextFactory::startTable()liest rohes CSS. Dies ist die einzige dokumentierte Vertragsausnahme, aufgeschoben auf einen künftigenTableApplicator. Kopieren Sie dieses Muster nicht.- Sechs Schichten, vierschichtiger Kern. ADR-010 nummeriert sechs Schichten. Der Datenflussvertrag ist der vierschichtige Kern; Paged Media und Messung sind Zusatzschichten.
HtmlStyleStatehat zwei Gruppen. Es trägt CSS-Computed-Felder und Layout-Tracking-Felder. Nur Applicators schreiben in die CSS-Gruppe. Paint liestComputedStyle, nie die Layout-Tracking-Felder.HtmlParserhat keine Schicht. Er ist der Orchestrator. CSS-Parsing, Geometrie-Mathematik und Paint-Ausgabe gehören nicht in ihn.
Performance
Abschnitt betitelt „Performance“Der Schichtvertrag ist strukturell und verursacht keine Laufzeitkosten. HtmlStyleState::toComputedStyle() erzeugt einen unveränderlichen Snapshot für jedes Element, das Paint benötigt. Durch den Snapshot muss Paint-Code nicht aus der veränderlichen State-Sammlung lesen. Die Rendering-Kosten werden vom Streaming-Modell bestimmt, nicht von der Schichtung. Das performance_budget pro Seite (wall_ms: 1500, peak_mb: 64) ist die operative Obergrenze.
Sicherheitshinweise
Abschnitt betitelt „Sicherheitshinweise“Die Schichttrennung stützt das Sicherheitsmodell. Schicht 1 parst und filtert CSS-Werte per Policy, bevor sie Layout- oder Paint-Code erreichen, sodass DefaultHtmlSecurityPolicy::isCssPropertyAllowed() der einzige Entscheidungspunkt ist. Paint liest nie angreiferkontrolliertes rohes CSS. Siehe das Sicherheitsmodell des HTML-Moduls.
Konformität
Abschnitt betitelt „Konformität“Diese Seite zitiert keinen externen Standard. Die Schichtgrenzen leiten sich aus ADR-010 und aus dem Klassen-Docblock von HtmlStyleState ab, der den Vertrag im Quellcode abbildet. Die CSS-Verhaltenskonformität ist auf css-resolver dokumentiert.
Kommerzieller Kontext
Abschnitt betitelt „Kommerzieller Kontext“Enterprise-Funktion. Premium-CSS-Funktionen erweitern dieselben vier Schichten über die dokumentierten Erweiterungspunkte. Es gibt keine separate Premium-Pipeline. Siehe die CSS-Support-Matrix.