Ga naar inhoud

De HTML-pijplijn

Spec: CSS Cascade 5, §6.1 Spec: CSS Display 3, §2 Evidence: Code-backed

NextPDF rendert HTML en CSS naar PDF binnen je PHP-proces: standaard zonder browser en zonder subproces. Deze pagina legt uit door welke gelaagde fasen de conversie loopt, wat de CSS-engine daadwerkelijk dekt en wanneer delegatie naar een echte browser-renderer de eerlijke keuze is.

“HTML naar PDF” klinkt als één bewerking. In werkelijkheid bestaat het uit een cascade, een boxmodel, een lay-outfase en een paintfase. Elk daarvan is een goed gespecificeerd probleem met eigen manieren waarop het mis kan gaan. Een engine die ze samensmelt tot één procedure is broos. Een wijziging in het parsen van kleuren kan een box verplaatsen, en de enige manier om dat vast te stellen is renderen en kijken.

Het in-procesmodel heeft een reëel voordeel: geen browser om te installeren, geen sandbox om te beheren en geen procesgrens waarover je data moet marshalen. Maar dat betaalt zich alleen uit als de conversie schoon genoeg is opgedeeld om elke verantwoordelijkheid afzonderlijk te testen. Die architectuur maakt “HTML renderen in PHP” betrouwbaar in plaats van alleen mogelijk.

  • HTML/CSS-conversie verloopt in-process via writeHtml(). Het resultaat is native PDF-inhoud, niet een afbeelding van een pagina.
  • De verwerking gebeurt in één doorgang en streamend. De tokenizer produceert een tokenlijst. De parser verwerkt die van links naar rechts, en er wordt geen volledige DOM-boom bewaard (ADR-001). Harde limieten begrenzen het aantal elementen en de nestingdiepte.
  • De engine is opgebouwd uit expliciete lagen: CSS-parsing en applicators, stijlstatus, lay-out en opmaak, paint en paged media — met strikte regels over welke laag wat mag doen (ADR-010).
  • De CSS-engine dekt de cascade, het boxmodel en gangbare lay-out (block, inline, tabellen, floats en meer) — substantieel, maar een afgebakende subset van wat een moderne browser implementeert.
  • Wanneer je exacte browsergetrouwheid nodig hebt voor willekeurige moderne CSS, kan NextPDF via een optionele extensie delegeren naar een headless browser-renderer — een bewuste, netwerkgeïsoleerde overgang, niet het standaardpad.

De conversie is een reeks fasen, waarbij elke fase de getypeerde uitvoer van de vorige fase verbruikt.

  1. Tokenize HTML becomes an ordered token list — no retained DOM tree.
  2. Resolve CSS Parse styles; the cascade and applicators compute typed values.
  3. Style state A push/pop style stack carries computed values per nesting level.
  4. Layout Block, inline, table, and float geometry computed; no paint here.
  5. Paint Borders, backgrounds, text, and decorations emit PDF operators.
  6. Paged media Page-break and @page rules applied as the cursor crosses page bounds.
De in-process HTML-pijplijn: één doorgang van links naar rechts over een tokenstream, met CSS-resolutie, stijlstatus, lay-out en paint als afzonderlijke lagen, en paged-media-onderbrekingen die worden toegepast naarmate de cursor vordert.

Twee architecturale regels maken hiervan meer dan een stroom.

Lagen hebben contracten. CSS-tekst wordt uitsluitend binnen applicatorklassen gelezen. Lay-outcode berekent geometrie, maar geeft geen paint-operators uit. Paint-code leest een onveranderlijke momentopname van de computed style, nooit de veranderlijke lay-outtrackingstatus. Paged-media-code activeert onderbrekingen, maar delegeert paginadecoratie aan de paintlaag. Deze grenzen worden afgedwongen (ADR-010). Daarom is een nieuwe CSS-eigenschap een nieuwe applicator, in plaats van een wijziging die tegelijk door de parser, de lay-outdispatch en de painter golft.

Er is geen DOM. De pijplijn werkt door een ontwerpkeuze in één doorgang en streamend (ADR-001): hooguit één stijlstatus per nestingniveau plus de actieve cursor, niet één object per element. Enkele bewerkingen hebben echt look-ahead nodig — het bepalen van tabelkolombreedtes, :has(), :last-child. Deze worden afgehandeld door begrensde pre-scan-indexstructuren over de platte tokenlijst, niet door het bewaren van een boom. Het aantal elementen en de nestingdiepte zijn hard begrensd, zodat pathologische invoer snel faalt in plaats van het geheugen uit te putten.

De CSS-engine past echte CSS-semantiek toe, geen imitatie. Concurrerende declaraties worden teruggebracht tot één waarde per eigenschap op basis van origin, importance, layer, specificiteit en volgorde — de werkelijke cascade. De lay-out volgt het boxmodel. Het type van een box en de opmaakcontext die deze creëert, bepalen hoe de box en de naastliggende in-flow-elementen worden geplaatst. De broncode van de engine is precies rond deze verantwoordelijkheden georganiseerd (cascade, box/display, flex, float, tabellen, fragmentatie). Daarom kun je over het gedrag ervan redeneren aan de hand van de specificaties, in plaats van het empirisch te ontdekken.

Deze pagina is Evidence: Code-backed . De fasen en regels zijn terug te voeren op de core-repository:

  • Het in-process-toegangspunt is writeHtml(string $html): static in src/Core/Concerns/HasTextOutput.php.
  • Het ontwerp met één doorgang, zonder bewaarde DOM en met element- en nestinglimieten is ADR-001 en de tokenizer/parser/style-stack-code in src/Html/.
  • Het gelaagde engine-contract — CSS parsing/applicators, stijlstatus, lay-out, paint, paged media — is ADR-010 en wordt weerspiegeld in de indeling van src/Html/ (bijvoorbeeld Cascade/, Css/, Flex/, Float/, Fragmentation/ en de applicatorklassen).
  • De overgang voor browserdelegatie is writeHtmlChrome() in hetzelfde bestand, gedocumenteerd als afhankelijk van de optionele renderer-extensie plus een Chrome/Chromium-binary.

De standaarden verankeren de dekkingsbewering op een eerlijke manier. De cascade brengt concurrerende declaraties terug tot één enkele waarde per eigenschap — origin, importance, layer, specificiteit, volgorde — volgens Spec: CSS Cascade 5, §6.1 , en in-flow-plaatsing volgt de regels voor box en opmaakcontext volgens Spec: CSS Display 3, §2 . Even belangrijk is de grens: een feature query bestaat juist omdat niet elke processor elke feature ondersteunt, volgens Spec: CSS Conditional 5, §2 . De CSS-engine van NextPDF is een afgebakende, op de specificatie afgestemde subset, en dat openlijk benoemen hoort bij het contract.

In-process renderen is één aanroep. De uitvoer is selecteerbare PDF-tekst, geen gerasterde pagina:

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->setTitle('HTML Basic');
$doc->addPage();
$html = <<<'HTML'
<h1 style="color: #1E3A8A;">HTML Rendering in NextPDF</h1>
<p>NextPDF renders <strong>HTML and CSS</strong> directly into PDF pages,
<em>in-process</em>.</p>
<ul>
<li>Headings, paragraphs, bold and italic</li>
<li>Lists, tables, inline styles</li>
</ul>
HTML;
$doc->writeHtml($html);
$doc->save(__DIR__ . '/html-basic.pdf');

Als hetzelfde document willekeurige moderne CSS met exacte browsergetrouwheid vereiste, zou de aanroep in plaats daarvan writeHtmlChrome($html) zijn — hetzelfde document, een ander renderpad en een bewuste afhankelijkheid van de optionele browser-renderer.

Het terugkerende misverstand is dat een HTML-naar-PDF-engine “in wezen een browser is”. Dat is niet zo, en de engine pretendeert dat ook niet. Een browser is een omvangrijke, continu bijgewerkte implementatie van het volledige webplatform. De in-process engine van NextPDF is een op de specificatie afgestemde subset voor documentlay-out. Het eerlijke mentale model is “een degelijke CSS-engine voor printdocumenten”, niet “Chrome in PHP”. Wanneer je echt het volledige platform nodig hebt, is writeHtmlChrome() daarvoor bedoeld. Het is een afzonderlijk pad waarvoor je zelf kiest, met zijn eigen operationele voetafdruk, geen stille terugval.

Een tweede misverstand: aannemen dat het browserpad slechts “de pagina over het netwerk rendert”. De constructie doet juist het tegenovergestelde. De delegatie-overgang rendert altijd met geblokkeerde netwerktoegang voor subresources — geen externe afbeeldingen, lettertypen, stylesheets of frames — zodat deze geen vector voor uitgaande verzoeken kan worden. Pixelgetrouwheid, ja; open netwerk-egress, nee.

Deze pagina legt de vorm van de pijplijn uit en de keuze tussen in-process en browser. Het is geen CSS-ondersteuningsmatrix. Welke exacte eigenschappen, modules en selectors de in-process engine dekt, wordt bepaald door de code en de bijbehorende conformiteitstests, niet door dit overzicht. Die dekking evolueert. Het pad voor browserdelegatie vereist een optionele extensie en een Chrome/Chromium-binary. De configuratie, de operationele kenmerken en de interne indeling van die extensie vallen hier buiten de scope en zijn gedocumenteerd bij dat pakket. “In-process” beschrijft het standaardpad writeHtml(). Het is geen bewering dat elk renderpad een subproces vermijdt. De architecturale beweringen zijn juist op de beoordelingsdatum van deze pagina. De gezaghebbende bronnen zijn src/Html/, ADR-001 en ADR-010 in de core-repository.

De in-process CSS-engine is een Core-mogelijkheid. De overgang voor browserdelegatie is een optionele extensie, die hier alleen op het mogelijkheidsniveau wordt getoond:

HTML rendering paths — edition availability
Edition Availability
Core Core levert de in-process HTML/CSS-engine (writeHtml).
Pro Het pad voor browserdelegatie is een optionele add-on-extensie, onafhankelijk van de editieniveau.
Enterprise Het pad voor browserdelegatie is een optionele add-on-extensie, onafhankelijk van de editieniveau.
  • In-process renderen — HTML/CSS naar PDF omzetten binnen het PHP-proces, zonder browser of standaard subproces (writeHtml()).
  • Eén doorgang / streamend — een tokenstream van links naar rechts verwerken zonder een volledige DOM-boom te bewaren (ADR-001).
  • Cascade — het CSS-proces dat concurrerende declaraties tot één waarde per eigenschap herleidt op basis van origin, importance, layer, specificiteit en volgorde.
  • Opmaakcontext — de lay-outomgeving die een box creëert en die bepaalt hoe de in-flow-inhoud ervan wordt geplaatst.
  • Engine-laagcontract — de afgedwongen regelset (ADR-010) die definieert wat de parsing-, stijl-, lay-out-, paint- en paged-media-lagen elk mogen doen.
  • Overgang voor browserdelegatie — het optionele writeHtmlChrome()-pad dat rendert via een headless browser met geblokkeerde netwerktoegang voor subresources.