Contratti dei layer del motore HTML (ADR-010)
In breve
Sezione intitolata “In breve”Il sottosistema HTML separa parsing CSS, stato di stile, impaginazione e paint in quattro layer, con un contratto tra layer che procede in una sola direzione. ADR-010 ne definisce i confini e le regole di estensione.
Installazione
Sezione intitolata “Installazione”composer require nextpdf/core:^3Panoramica concettuale
Sezione intitolata “Panoramica concettuale”ADR-010 («Engine Layer Contracts, Hot Path Ownership, and Extension Rules», accettato il 2026-04-12) formalizza la suddivisione in layer del sottosistema HTML. Il contratto di rendering principale comprende quattro layer: parsing CSS e applicator, stato di stile, impaginazione e formattazione, e paint. ADR-010 documenta inoltre due layer accessori — paged media e harness di misurazione — che racchiudono il nucleo a quattro layer senza alterarne il flusso di dati. Il termine canonico del glossario per il nucleo è «HTML pipeline», una pipeline a quattro layer.
I dati scorrono in una sola direzione. Il testo CSS diventa valori tipizzati nel Layer 1. Il Layer 1 scrive tali valori nei campi di HtmlStyleState nel Layer 2. Il Layer 3 legge i campi dello stato di stile e calcola la geometria. Il Layer 4 legge uno snapshot immutabile di ComputedStyle insieme alla geometria ed emette operatori PDF. Nessun layer legge da un layer successivo.
La separazione in quattro layer non è solo documentazione. ADR-010 registra due refactor circoscritti applicati nella v1.2.0, che hanno spostato il codice nel layer corretto. PageBorderPainter è stato estratto da HtmlParser affinché gli operatori di paint non risiedano più nell’orchestratore. Il docblock della classe HtmlStyleState riporta ora il contratto formale dei layer, indicando quali campi ciascun layer può scrivere o leggere.
Un confine è dichiarato esplicitamente, non nascosto. FormattingContextFactory::startTable() legge ancora direttamente cinque chiavi CSS grezze. ADR-010 lo registra come debito tecnico noto, rinviato in attesa di un futuro TableApplicator, e non come contratto previsto. Documentare l’eccezione fa parte del contratto.
I quattro layer principali
Sezione intitolata “I quattro layer principali”| Layer | File (rappresentativi) | Scrive | Legge | Non deve |
|---|---|---|---|---|
| 1 — Parsing CSS e applicator | CssValueParser, CssResolver, HtmlCssApplicator, src/Html/Applicator/* | Campi CSS di HtmlStyleState | Testo CSS grezzo | Calcolare la geometria; emettere operatori |
| 2 — Stato di stile | HtmlStyleState, State/ComputedStyle, State/LayoutState | — (contenitore passivo di valori) | — | Effettuare il parsing del CSS; decidere l’impaginazione; emettere operatori |
| 3 — Impaginazione e formattazione | FormattingContextFactory, HtmlBlockHandler, FlexLayoutEngine, TableParser, FloatContext | Geometria del cursore | Campi di HtmlStyleState | Leggere $css[...] grezzo; emettere operatori di paint |
| 4 — Paint e rendering | BorderRenderer, BackgroundImageRenderer, src/Html/Paint/*, src/Html/Gradient/* | Flusso di operatori PDF | ComputedStyle (immutabile) + geometria | Calcolare la geometria; effettuare il parsing del CSS; decidere le interruzioni di pagina |
I due layer accessori
Sezione intitolata “I due layer accessori”| Layer | File (rappresentativi) | Ruolo |
|---|---|---|
| 5 — Paged media | PageBreakController, PageBorderPainter, PageRule, PageRuleParser, ParserConfigurator | Risolvere le regole @page; valutare i vincoli di interruzione e orphan/widow; delegare al paint la decorazione della pagina. |
| 6 — Misurazione e harness | Script classificatori WPT, tests/Support/* | Classificare gli esiti dei test; produrre snapshot di regressione; fornire helper di asserzione. Non contiene logica di rendering. |
Superficie API
Sezione intitolata “Superficie API”Il contratto è imposto dalla collocazione delle classi e dal docblock di HtmlStyleState. Verificare rispetto a src/Html/.
| Simbolo | Layer | Ruolo nel contratto |
|---|---|---|
PropertyApplicatorInterface | 1 | Interfaccia strategy; unico punto in cui vengono scritti i campi CSS calcolati. |
ParserConfigurator::buildCssApplicator() | 1 (wiring) | Registra ogni applicator. Le nuove proprietà CSS vanno registrate qui. |
HtmlStyleState | 2 | Contenitore a doppio gruppo; il docblock della classe indica il layer proprietario di ogni campo. |
HtmlStyleState::toComputedStyle() | 2 | Produce il ComputedStyle immutabile per il layer di paint. |
FormattingContextFactory::dispatchOpenTag() | 3 | Punto unico di instradamento per i nuovi comportamenti di impaginazione. |
PageBorderPainter::buildStream() | 4 | Decorazione della pagina; richiamata dal Layer 5, non inserita inline in HtmlParser. |
Esempio di codice — Avvio rapido
Sezione intitolata “Esempio di codice — Avvio rapido”I chiamanti non interagiscono mai con i layer. Il flusso a quattro layer avviene all’interno di una singola chiamata.
<?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');Esempio di codice — Produzione
Sezione intitolata “Esempio di codice — Produzione”Il contratto riguarda i contributor, non i chiamanti. Per aggiungere una proprietà CSS, seguire il punto di estensione del Layer 1: creare un applicator, aggiungere un campo HtmlStyleState tipizzato con un docblock di layer e registrare l’applicator in ParserConfigurator. L’esempio seguente mostra la forma del contratto dell’applicator. Consultare src/Html/Applicator/ per una classe concreta da copiare.
<?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.Casi limite e insidie
Sezione intitolata “Casi limite e insidie”FormattingContextFactory::startTable()legge CSS grezzo. È l’unica eccezione documentata al contratto, rinviata a un futuroTableApplicator. Non replicare questo pattern.- Sei layer, nucleo a quattro layer. ADR-010 numera sei layer. Il contratto del flusso di dati coincide con il nucleo a quattro layer; paged media e misurazione sono accessori.
HtmlStyleStateè a doppio gruppo. Contiene campi calcolati CSS e campi di tracciamento dell’impaginazione. Solo gli applicator scrivono il gruppo CSS. Il paint leggeComputedStyle, mai i campi di tracciamento dell’impaginazione.HtmlParsernon ha alcun layer. È l’orchestratore. Il parsing CSS, i calcoli geometrici e l’emissione del paint non devono risiedere al suo interno.
Prestazioni
Sezione intitolata “Prestazioni”Il contratto dei layer è strutturale e non aggiunge alcun costo a runtime. HtmlStyleState::toComputedStyle() produce uno snapshot immutabile per ogni elemento che richiede il paint. Lo snapshot evita al codice di paint di leggere il contenitore di stato mutabile. Il costo del rendering è governato dal modello di streaming, non dalla suddivisione in layer. Il performance_budget per pagina (wall_ms: 1500, peak_mb: 64) è il limite operativo.
Note sulla sicurezza
Sezione intitolata “Note sulla sicurezza”La separazione in layer sostiene il modello di sicurezza. Il Layer 1 effettua il parsing e applica i filtri di policy ai valori CSS prima che qualunque codice di impaginazione o paint li veda; di conseguenza, DefaultHtmlSecurityPolicy::isCssPropertyAllowed() è l’unico gate. Il paint non legge mai CSS grezzo controllato da un utente malintenzionato. Vedere il modello di sicurezza del modulo HTML.
Conformità
Sezione intitolata “Conformità”Questa pagina non cita alcuno standard esterno. I confini tra i layer derivano da ADR-010 e dal docblock della classe HtmlStyleState, che codifica il contratto nel sorgente. La conformità comportamentale CSS è documentata in css-resolver.
Contesto commerciale
Sezione intitolata “Contesto commerciale”Funzionalità Enterprise. Le funzionalità CSS Premium estendono questi stessi quattro layer attraverso i punti di estensione documentati. Non esiste una pipeline Premium separata. Vedere la matrice di supporto CSS.