Salta ai contenuti

Il flusso di elaborazione HTML

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

NextPDF esegue il rendering di HTML e CSS in PDF all’interno del processo PHP — senza browser e, per impostazione predefinita, senza sottoprocesso. Questa pagina illustra le fasi a livelli della conversione, cosa copre davvero il motore CSS e quando delegare a un vero renderer di browser è la scelta corretta e trasparente.

«Da HTML a PDF» sembra una singola operazione. In realtà è una cascata, un box model, una passata di layout e una passata di paint. Ognuno di questi passaggi è un problema ben specificato, con le proprie modalità di errore. Un motore che li fonde in un’unica procedura è fragile. Una modifica all’analisi dei colori può spostare un box, e l’unico modo per verificarlo è eseguire il rendering e osservare il risultato.

Il modello in-process offre un vantaggio concreto: nessun browser da installare, nessuna sandbox da gestire e nessun confine di processo da attraversare con il marshalling. Ma conviene solo se la conversione è scomposta in modo abbastanza pulito da poter testare ogni aspetto in modo isolato. È l’architettura a rendere «il rendering di HTML in PHP» affidabile, non semplicemente possibile.

  • La conversione HTML/CSS viene eseguita in-process tramite writeHtml(). Il risultato è contenuto PDF nativo, non l’immagine di una pagina.
  • È a passata singola e in streaming. Il tokenizer produce un elenco ordinato di token. Il parser lo consuma da sinistra a destra e non viene conservato alcun albero DOM completo (ADR-001). Limiti rigidi vincolano il numero di elementi e la profondità di annidamento.
  • Il motore è organizzato in livelli espliciti: analisi CSS e applicatori, stato dello stile, layout e formattazione, paint e supporti paginati — con regole rigorose su ciò che ogni livello può fare (ADR-010).
  • Il motore CSS copre la cascata, il box model e i layout comuni (block, inline, tabelle, float e altro) — in modo sostanziale, ma come sottoinsieme definito di ciò che implementa un browser moderno.
  • Quando serve fedeltà esatta al browser per CSS moderni arbitrari, NextPDF può delegare a un renderer di browser headless tramite un’estensione opzionale — un punto di giunzione deliberato, isolato dalla rete, non il percorso predefinito.

La conversione è una sequenza di fasi, ognuna delle quali consuma l’output tipizzato della fase precedente.

  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.
Il flusso di elaborazione HTML in-process: una singola passata da sinistra a destra su un flusso di token, con risoluzione CSS, stato dello stile, layout e paint come livelli separati, e le interruzioni di supporto paginato applicate man mano che il cursore avanza.

Due regole architetturali rendono tutto questo qualcosa di più di un semplice flusso.

I livelli hanno contratti. Il testo CSS viene letto solo all’interno delle classi applicatore. Il codice di layout calcola la geometria ma non emette operatori di paint. Il codice di paint legge un’istantanea immutabile dello stile calcolato, mai lo stato mutabile usato per tracciare il layout. Il codice dei supporti paginati attiva le interruzioni ma delega la decorazione della pagina al livello di paint. Questi confini sono imposti (ADR-010). Ecco perché una nuova proprietà CSS si traduce in un nuovo applicatore, non in una modifica che si propaga contemporaneamente nel parser, nel dispatch del layout e nel painter.

Non esiste un DOM. Il flusso di elaborazione è a passata singola e in streaming per scelta progettuale (ADR-001): al massimo mantiene uno stato dello stile per livello di annidamento più il cursore attivo, non un oggetto per elemento. Alcune operazioni richiedono davvero il look-ahead — il dimensionamento delle colonne delle tabelle, :has(), :last-child. Sono gestite tramite strutture di indice di pre-scansione limitate sull’elenco piatto di token, invece di conservare un albero. Il numero di elementi e la profondità di annidamento hanno limiti rigidi, così un input patologico fallisce rapidamente anziché esaurire la memoria.

Il motore CSS risolve la semantica CSS effettiva, non un’imitazione. Le dichiarazioni in competizione vengono ridotte a un valore per proprietà in base a origine, importanza, layer, specificità e ordine — la cascata effettiva. Il layout segue il box model. Il tipo di un box e il contesto di formattazione che stabilisce determinano come vengono posizionati il box e i suoi fratelli in-flow. Il codice sorgente del motore è organizzato proprio attorno a questi aspetti (cascata, box/display, flex, float, tabelle, frammentazione). Ecco perché è possibile ragionare sul suo comportamento rispetto alle specifiche, anziché scoprirlo empiricamente.

Questa pagina è Evidence: Code-backed . Le fasi e le regole corrispondono al repository core:

  • Il punto di ingresso in-process è writeHtml(string $html): static in src/Core/Concerns/HasTextOutput.php.
  • Il design a passata singola, senza DOM conservato, con limiti per elementi e annidamento è ADR-001 e il codice tokenizer/parser/style-stack in src/Html/.
  • Il contratto a livelli del motore — parsing/applicators CSS, stato dello stile, layout, paint, supporti paginati — è ADR-010, riflesso nella struttura di src/Html/ (ad esempio Cascade/, Css/, Flex/, Float/, Fragmentation/ e le classi applicatore).
  • Il punto di giunzione per la delega al browser è writeHtmlChrome() nello stesso file, documentato come richiedente l’estensione opzionale del renderer più un binario Chrome/Chromium.

Gli standard fondano in modo trasparente l’affermazione sulla copertura. La cascata riduce le dichiarazioni in competizione a un singolo valore per proprietà — origine, importanza, layer, specificità, ordine — secondo Spec: CSS Cascade 5, §6.1 , e il posizionamento in-flow segue le regole del box e del contesto di formattazione secondo Spec: CSS Display 3, §2 . Altrettanto importante è il confine: una feature query esiste proprio perché non ogni processore supporta tutte le funzionalità secondo Spec: CSS Conditional 5, §2 . Il motore CSS di NextPDF è un sottoinsieme definito e allineato alle specifiche, e affermarlo chiaramente fa parte del contratto.

Il rendering in-process richiede una sola chiamata. L’output è testo PDF selezionabile, non una pagina rasterizzata:

<?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');

Se lo stesso documento richiedesse CSS moderni arbitrari con fedeltà esatta al browser, la chiamata sarebbe invece writeHtmlChrome($html) — stesso documento, percorso di rendering diverso e una dipendenza deliberata dal renderer di browser opzionale.

Un equivoco ricorrente è pensare che un motore da HTML a PDF sia «praticamente un browser». Non lo è, e non pretende di esserlo. Un browser è un’implementazione vasta e aggiornata di continuo dell’intera piattaforma web. Il motore in-process di NextPDF è un sottoinsieme allineato alle specifiche, focalizzato sul layout dei documenti. Il modello mentale corretto è «un motore CSS competente per documenti da stampa», non «Chrome in PHP». Quando serve davvero l’intera piattaforma, è a questo che serve writeHtmlChrome(). È un percorso separato e opt-in con un proprio impatto operativo, non un fallback silenzioso.

Un secondo equivoco è pensare che il percorso del browser significhi semplicemente «eseguire il rendering della pagina sulla rete». Per costruzione, è l’opposto. Il punto di giunzione per la delega esegue il rendering con l’accesso di rete alle sottorisorse bloccato incondizionatamente — nessuna immagine remota, nessun font remoto, nessun foglio di stile remoto e nessun frame remoto — così non può diventare un vettore di richieste in uscita. Fedeltà al pixel, sì; uscita di rete aperta, no.

Questa pagina illustra la forma del flusso di elaborazione e la scelta tra in-process e browser. Non è una matrice di supporto CSS. Le proprietà, i moduli e i selettori esatti coperti dal motore in-process sono definiti dal codice e dai suoi test di conformità, non da questa panoramica. Tale copertura evolve. Il percorso di delega al browser richiede un’estensione opzionale e un binario Chrome/Chromium. La sua configurazione, le caratteristiche operative e la struttura interna dell’estensione sono fuori ambito qui e documentate insieme a quel pacchetto. «In-process» descrive il percorso predefinito writeHtml(). Non afferma che ogni percorso di rendering eviti un sottoprocesso. Le affermazioni architetturali sono accurate alla data di revisione di questa pagina. Le fonti autorevoli sono src/Html/, ADR-001 e ADR-010 nel repository core.

Il motore CSS in-process è una capacità Core. Il punto di giunzione per la delega al browser è un’estensione opzionale, presentata qui solo a livello di capacità:

HTML rendering paths — edition availability
Edition Availability
Core Core fornisce il motore HTML/CSS in-process (writeHtml).
Pro Il percorso di delega al browser è un’estensione opzionale aggiuntiva, indipendente dal livello dell’edizione.
Enterprise Il percorso di delega al browser è un’estensione opzionale aggiuntiva, indipendente dal livello dell’edizione.
  • Rendering in-process — la conversione di HTML/CSS in PDF all’interno del processo PHP, senza browser e senza sottoprocesso per impostazione predefinita (writeHtml()).
  • A passata singola / in streaming — il consumo di un flusso di token da sinistra a destra senza conservare un albero DOM completo (ADR-001).
  • Cascata — il processo CSS che risolve le dichiarazioni in competizione in un valore per proprietà in base a origine, importanza, layer, specificità e ordine.
  • Contesto di formattazione — l’ambiente di layout che un box stabilisce e che governa il posizionamento dei suoi contenuti in-flow.
  • Contratto dei livelli del motore — l’insieme di regole imposte (ADR-010) che definisce cosa può fare ciascun livello di parsing, stile, layout, paint e supporti paginati.
  • Punto di giunzione per la delega al browser — il percorso opzionale writeHtmlChrome() che esegue il rendering tramite un browser headless con accesso di rete alle sottorisorse bloccato.