Contrats des couches du moteur HTML (ADR-010)
Le sous-système HTML sépare l’analyse CSS, l’état de style, la mise en page et la peinture en quatre couches. Le flux défini par leur contrat ne circule que dans un seul sens. L’ADR-010 fixe leurs frontières et leurs règles d’extension.
Installation
Section intitulée « Installation »composer require nextpdf/core:^3Vue d’ensemble conceptuelle
Section intitulée « Vue d’ensemble conceptuelle »L’ADR-010 (« Contrats des couches du moteur, propriété du chemin critique et règles d’extension », acceptée le 2026-04-12) formalise le découpage en couches du sous-système HTML. Le contrat de rendu du cœur s’articule autour de quatre couches : analyse CSS et applicateurs, état de style, mise en page et formatage, puis peinture. L’ADR-010 documente aussi deux couches adjointes — le média paginé et le harnais de mesure — qui enveloppent ce cœur à quatre couches sans modifier son flux de données. Dans le glossaire, le terme canonique pour ce cœur est « HTML pipeline », un pipeline à quatre couches.
Les données ne circulent que dans un seul sens. Le texte CSS devient des valeurs typées dans la couche 1. La couche 1 écrit ces valeurs dans les champs de HtmlStyleState, dans la couche 2. La couche 3 lit les champs d’état de style et calcule la géométrie. La couche 4 lit un instantané ComputedStyle immuable, accompagné de la géométrie, puis émet des opérateurs PDF. Aucune couche ne lit de données issues d’une couche située après elle.
La séparation en quatre couches n’est pas seulement documentaire. L’ADR-010 consigne deux refactorisations bornées, appliquées dans v1.2.0, qui ont replacé du code dans la bonne couche. PageBorderPainter a été extrait de HtmlParser afin que les opérateurs de peinture ne résident plus dans l’orchestrateur. Le docbloc de la classe HtmlStyleState porte désormais le contrat de couche formel, qui précise quels champs chaque couche peut écrire ou lire.
Une frontière est explicitée plutôt que dissimulée. FormattingContextFactory::startTable() lit encore directement cinq clés CSS brutes. L’ADR-010 consigne cette exception comme une dette technique connue, reportée à un futur TableApplicator, et non comme le contrat prévu. Documenter l’exception fait partie du contrat.
Les quatre couches du cœur
Section intitulée « Les quatre couches du cœur »| Couche | Fichiers (représentatifs) | Écrit | Lit | Ne doit pas |
|---|---|---|---|---|
| 1 — analyse CSS et applicateurs | CssValueParser, CssResolver, HtmlCssApplicator, src/Html/Applicator/* | HtmlStyleState (champs CSS) | Texte CSS brut | Calculer la géométrie ; émettre des opérateurs |
| 2 — état de style | HtmlStyleState, State/ComputedStyle, State/LayoutState | — (sac de valeurs passif) | — | Analyser le CSS ; décider de la mise en page ; émettre des opérateurs |
| 3 — mise en page et formatage | FormattingContextFactory, HtmlBlockHandler, FlexLayoutEngine, TableParser, FloatContext | Géométrie du curseur | HtmlStyleState (champs) | Lire le $css[...] brut ; émettre des opérateurs de peinture |
| 4 — peinture et rendu | BorderRenderer, BackgroundImageRenderer, src/Html/Paint/*, src/Html/Gradient/* | Flux d’opérateurs PDF | ComputedStyle (immuable) + géométrie | Calculer la géométrie ; analyser le CSS ; décider des sauts de page |
Les deux couches adjointes
Section intitulée « Les deux couches adjointes »| Couche | Fichiers (représentatifs) | Rôle |
|---|---|---|
| 5 — média paginé | PageBreakController, PageBorderPainter, PageRule, PageRuleParser, ParserConfigurator | Résoudre les règles @page ; évaluer les contraintes de saut et orphan/widow ; déléguer la décoration de page à la peinture. |
| 6 — mesure et harnais | Scripts de classification WPT, tests/Support/* | Classer les résultats de test ; produire des instantanés de régression ; fournir des assistants d’assertion. Ne contient aucune logique de rendu. |
Surface d’API
Section intitulée « Surface d’API »Le contrat est imposé à la fois par l’emplacement des classes et par le docbloc de HtmlStyleState. Vérifie-le dans src/Html/.
| Symbole | Couche | Rôle dans le contrat |
|---|---|---|
PropertyApplicatorInterface | 1 | Interface de stratégie ; le seul endroit qui écrit les champs CSS calculés. |
ParserConfigurator::buildCssApplicator() | 1 (câblage) | Enregistre chaque applicateur. Toute nouvelle propriété CSS y est enregistrée. |
HtmlStyleState | 2 | Sac de valeurs à double groupe ; le docbloc de la classe indique la couche propriétaire de chaque champ. |
HtmlStyleState::toComputedStyle() | 2 | Produit le ComputedStyle immuable pour la couche de peinture. |
FormattingContextFactory::dispatchOpenTag() | 3 | Point de routage unique pour tout nouveau comportement de mise en page. |
PageBorderPainter::buildStream() | 4 | Décoration de page appelée depuis la couche 5, sans intégration en ligne dans HtmlParser. |
Exemple de code — démarrage rapide
Section intitulée « Exemple de code — démarrage rapide »Les appelants n’ont jamais à manipuler les couches. Le flux à quatre couches s’exécute dans un seul appel.
<?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');Exemple de code — production
Section intitulée « Exemple de code — production »Ce contrat concerne les contributeurs, pas les appelants. Pour ajouter une propriété CSS, suis le point d’extension de la couche 1 : crée un applicateur, ajoute un champ HtmlStyleState typé avec un docbloc de couche, et enregistre l’applicateur dans ParserConfigurator. L’extrait ci-dessous illustre la forme du contrat d’applicateur. Consulte src/Html/Applicator/ pour une classe concrète à copier.
<?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.Cas limites et pièges
Section intitulée « Cas limites et pièges »FormattingContextFactory::startTable()lit du CSS brut. C’est la seule exception documentée au contrat ; elle est reportée à un futurTableApplicator. Ne copie pas ce modèle.- Six couches, un cœur à quatre couches. L’ADR-010 numérote six couches. Le contrat de flux de données correspond au cœur à quatre couches ; le média paginé et la mesure sont des adjoints.
HtmlStyleStateest à double groupe. Il contient des champs CSS calculés et des champs de suivi de mise en page. Seuls les applicateurs écrivent le groupe CSS. La peinture litComputedStyle, jamais les champs de suivi de mise en page.HtmlParsern’a pas de couche. C’est l’orchestrateur. L’analyse CSS, le calcul de géométrie et l’émission de peinture ne doivent pas y résider.
Performance
Section intitulée « Performance »Le contrat de couche est structurel et n’ajoute aucun coût à l’exécution. HtmlStyleState::toComputedStyle() produit un seul instantané immuable par élément qui doit être peint. L’instantané évite au code de peinture d’avoir à lire le sac d’état mutable. Le coût de rendu est régi par le modèle de streaming, pas par le découpage en couches. Le performance_budget par page (wall_ms: 1500, peak_mb: 64) est le plafond opérationnel.
Notes de sécurité
Section intitulée « Notes de sécurité »La séparation en couches soutient le modèle de sécurité. La couche 1 analyse et filtre les valeurs CSS selon la politique avant qu’elles ne soient visibles par le moindre code de mise en page ou de peinture, si bien que DefaultHtmlSecurityPolicy::isCssPropertyAllowed() est l’unique point de contrôle. La peinture ne lit jamais de CSS brut contrôlé par un attaquant. Consulte le modèle de sécurité du module HTML.
Conformité
Section intitulée « Conformité »Cette page ne cite aucune norme externe. Les frontières des couches dérivent de l’ADR-010 et du docbloc de la classe HtmlStyleState, qui encode le contrat dans le code source. La conformité comportementale du CSS est documentée dans css-resolver.
Contexte commercial
Section intitulée « Contexte commercial »Capacité Enterprise. Les fonctionnalités CSS Premium étendent ces mêmes quatre couches au moyen des points d’extension documentés. Il n’existe pas de pipeline Premium distinct. Consulte la matrice de prise en charge CSS.