Contratos de capas del motor HTML (ADR-010)
De un vistazo
Sección titulada «De un vistazo»El subsistema HTML separa el análisis de CSS, el estado de estilo, el layout y el paint en cuatro capas, y el contrato entre ellas impone un flujo en una sola dirección. El ADR-010 fija los límites y las reglas de extensión.
Instalación
Sección titulada «Instalación»composer require nextpdf/core:^3Resumen conceptual
Sección titulada «Resumen conceptual»El ADR-010 («Engine Layer Contracts, Hot Path Ownership, and Extension Rules», aceptado el 2026-04-12) formaliza la estructura por capas del subsistema HTML. El contrato de renderizado central tiene cuatro capas: análisis de CSS y aplicadores, estado de estilo, layout y formato, y paint. El ADR-010 también documenta dos capas complementarias —medios paginados y el arnés de medición— que envuelven el núcleo de cuatro capas, pero no cambian su flujo de datos. El término canónico del glosario para el núcleo es “pipeline HTML”, un pipeline de cuatro capas.
Los datos fluyen en una sola dirección. El texto CSS se convierte en valores tipados en la capa 1. La capa 1 escribe esos valores en los campos de HtmlStyleState en la capa 2. La capa 3 lee los campos del estado de estilo y calcula la geometría. La capa 4 lee una instantánea inmutable de ComputedStyle más la geometría y emite operadores PDF. Ninguna capa lee datos de una capa posterior.
La separación en cuatro capas no es meramente documental. El ADR-010 registra dos refactorizaciones acotadas aplicadas en v1.2.0 que trasladaron código a la capa correcta. PageBorderPainter se extrajo de HtmlParser para que los operadores de paint ya no residan en el orquestador. Ahora, el docblock de la clase HtmlStyleState contiene el contrato formal de capa, que establece qué campos puede escribir o leer cada capa.
Un límite queda explícito en lugar de oculto. FormattingContextFactory::startTable() todavía lee directamente cinco claves CSS sin procesar. El ADR-010 lo registra como deuda técnica conocida y diferida para un futuro TableApplicator, no como el contrato previsto. Documentar la excepción es parte del contrato.
Las cuatro capas centrales
Sección titulada «Las cuatro capas centrales»| Capa | Archivos (representativos) | Escribe | Lee | No debe |
|---|---|---|---|---|
| 1 — Análisis de CSS y aplicadores | CssValueParser, CssResolver, HtmlCssApplicator, src/Html/Applicator/* | HtmlStyleState campos CSS | Texto CSS sin procesar | Calcular la geometría; emitir operadores |
| 2 — Estado de estilo | HtmlStyleState, State/ComputedStyle, State/LayoutState | — (bolsa de valores pasiva) | — | Analizar CSS; decidir el layout; emitir operadores |
| 3 — Layout y formato | FormattingContextFactory, HtmlBlockHandler, FlexLayoutEngine, TableParser, FloatContext | Geometría del cursor | HtmlStyleState campos | Leer $css[...] sin procesar; emitir operadores de paint |
| 4 — Paint y renderizado | BorderRenderer, BackgroundImageRenderer, src/Html/Paint/*, src/Html/Gradient/* | Flujo de operadores PDF | ComputedStyle (inmutable) + geometría | Calcular la geometría; analizar CSS; decidir los saltos de página |
Las dos capas complementarias
Sección titulada «Las dos capas complementarias»| Capa | Archivos (representativos) | Rol |
|---|---|---|
| 5 — Medios paginados | PageBreakController, PageBorderPainter, PageRule, PageRuleParser, ParserConfigurator | Resolver las reglas @page; evaluar las restricciones de salto y de orphan/widow; delegar en el paint la decoración de página. |
| 6 — Medición y arnés | Scripts del clasificador de WPT, tests/Support/* | Clasificar los resultados de las pruebas; producir instantáneas de regresión; proporcionar ayudantes de aserción. No lleva lógica de renderizado. |
Superficie de la API
Sección titulada «Superficie de la API»El contrato se aplica mediante la ubicación de las clases y el docblock de HtmlStyleState. Puede verificarse en src/Html/.
| Símbolo | Capa | Rol del contrato |
|---|---|---|
PropertyApplicatorInterface | 1 | Interfaz de estrategia; el único punto que escribe los campos CSS computados. |
ParserConfigurator::buildCssApplicator() | 1 (cableado) | Registra cada aplicador; toda nueva propiedad CSS se registra aquí. |
HtmlStyleState | 2 | Bolsa con dos grupos; el docblock de la clase indica la capa propietaria de cada campo. |
HtmlStyleState::toComputedStyle() | 2 | Produce el ComputedStyle inmutable para la capa de paint. |
FormattingContextFactory::dispatchOpenTag() | 3 | Punto único de enrutamiento para el nuevo comportamiento de layout. |
PageBorderPainter::buildStream() | 4 | Decoración de página; se llama desde la capa 5, no se incorpora en línea en HtmlParser. |
Ejemplo de código — Inicio rápido
Sección titulada «Ejemplo de código — Inicio rápido»El código cliente nunca toca las capas. El flujo de cuatro capas se ejecuta dentro de una sola llamada.
<?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');Ejemplo de código — Producción
Sección titulada «Ejemplo de código — Producción»El contrato importa a quienes contribuyen, no a quienes consumen la API. Para agregar una propiedad CSS, se debe seguir el punto de extensión de la capa 1: crear un aplicador, agregar un campo tipado de HtmlStyleState con un docblock de capa, y registrar el aplicador en ParserConfigurator. La ilustración siguiente muestra la forma del contrato del aplicador. Se puede consultar src/Html/Applicator/ para encontrar una clase concreta que copiar.
<?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.Casos límite y problemas frecuentes
Sección titulada «Casos límite y problemas frecuentes»FormattingContextFactory::startTable()lee CSS sin procesar. Esta es la única excepción documentada del contrato, diferida a un futuroTableApplicator. No copiar el patrón.- Seis capas, núcleo de cuatro capas. El ADR-010 numera seis capas. El contrato de flujo de datos corresponde al núcleo de cuatro capas; los medios paginados y la medición son complementos.
HtmlStyleStatees de doble grupo. Contiene campos CSS computados y campos de seguimiento del layout. Solo los aplicadores escriben el grupo CSS. El paint leeComputedStyle, nunca los campos de seguimiento del layout.HtmlParserno tiene capa. Es el orquestador. El análisis de CSS, los cálculos de geometría y la emisión de paint no deben vivir en él.
Rendimiento
Sección titulada «Rendimiento»El contrato de capa es estructural y no añade ningún costo de ejecución. HtmlStyleState::toComputedStyle() produce una instantánea inmutable por cada elemento que requiere paint. La instantánea permite que el código de paint evite leer la bolsa de estado mutable. El costo de renderizado se rige por el modelo de streaming, no por las capas. El performance_budget por página (wall_ms: 1500, peak_mb: 64) es el límite operativo.
Notas de seguridad
Sección titulada «Notas de seguridad»La separación en capas respalda el modelo de seguridad. La capa 1 analiza y filtra conforme a la política los valores CSS antes de que cualquier código de layout o paint los vea, así que DefaultHtmlSecurityPolicy::isCssPropertyAllowed() es el único punto de control. El paint nunca lee CSS sin procesar controlado por un atacante. Consulta el modelo de seguridad del módulo HTML.
Conformidad
Sección titulada «Conformidad»Esta página no cita ningún estándar externo. Los límites de capa se derivan del ADR-010 y del docblock de la clase HtmlStyleState, que codifica el contrato en el código fuente. La conformidad del comportamiento CSS se documenta en css-resolver.
Contexto comercial
Sección titulada «Contexto comercial»Capacidad Enterprise. Las funciones CSS Premium extienden estas mismas cuatro capas mediante los puntos de extensión documentados. No hay un pipeline Premium aparte. Véase la matriz de compatibilidad de CSS.