Quello che sopravvive effettivamente a HTML in PDF, misurato
Dodici funzioni di print-CSS rese attraverso Chromium headless e poi lette dal PDF — interruzioni di pagina, dimensioni @page, intestazioni di tabella ripetute, link e caratteri. Due di loro non funzionano, e una trappola comune è colpa dell'API, non del CSS.
Novità qui: Una matrice di supporto per-funzione per CSS di stampa HTML-to-PDF, misurata su Chromium 148 renderizzando ogni caso e ispezionando il numero di pagine, le dimensioni della pagina, le annotazioni di link e il testo per pagina del PDF risultante — non dalla documentazione. Ogni risultato è riproducibile dal tool pubblicato e dal dataset, e due tecniche ampiamente consigliate non funzionano.
Ogni servizio HTML-to-PDF afferma che il suo output è fedele. Nessuno di loro pubblica cosa significhi. Quindi l’abbiamo misurato: dodici funzionalità di print-CSS, ognuna renderizzata tramite Chromium headless — il motore dietro MostlyRender e dietro la maggior parte dei servizi HTML-to-PDF — e poi lette dal PDF risultante.
Nulla qui proviene dalla documentazione. Ogni verdetto proviene dall’ispezione del file: quante pagine ha, quali dimensioni hanno, quale testo è finito su quale pagina, se esiste un’annotazione di collegamento, se un font è stato incorporato.
# La matrice
Misurato con Chromium 148.0.7778.96, il 1º settembre 2026 e di nuovo il 27 settembre 2026. Entrambe le esecuzioni hanno prodotto gli stessi dodici verdetti.
| Funzionalità | Funziona | Cosa abbiamo osservato |
|---|---|---|
break-after: page |
Sì | 2 pagine, come richiesto |
page-break-after: always (precedente) |
Sì | 2 pagine — l’ortografia precedente è ancora rispettata |
break-inside: avoid |
Sì | Il blocco si è spostato completamente alla pagina 2 invece di dividersi |
@page { size: A5 } |
Sì | 420×595pt — esattamente A5 |
@page { size: A4 landscape } |
Sì | 842×595pt — orizzontale |
Ripetizione di <thead> attraverso le pagine |
Sì | 3 pagine, intestazione presente sulla pagina 2 |
position: fixed come intestazione ricorrente |
Sì | Presente su 2 delle 2 pagine |
@page box dei margini + counter(page) |
No | Nessun numero di pagina ovunque nell’output |
<a href> come collegamento cliccabile |
Sì | Annotazione di collegamento presente |
Collegamenti #anchor interni |
Sì | Destinazione presente |
| Incorporamento dei font | Sì | DejaVuSansMono incorporato e subset |
| Griglia CSS attraverso un’interruzione di pagina | No | Tutti gli 8 elementi sulla pagina 1; pagina 2 vuota |
Dieci su dodici funzionano. I due che non funzionano vale la pena conoscere prima di progettare intorno ad essi.
# I due che falliscono
I box dei margini di CSS Paged Media non vengono renderizzati. La specifica ti dà @page { @bottom-center { content: "Page " counter(page) } }, si trova in ogni tutorial sulla stampa dal web, e Chromium lo ignora. Non abbiamo trovato alcun numero di pagina ovunque nell’output.
Se hai bisogno di numeri di pagina, devono essere aggiunti da qualunque cosa produca il PDF — ecco perché gli strumenti che offrono la numerazione delle pagine lo fanno come funzionalità piuttosto che dirti di scrivere CSS.
CSS grid non si frammenta. Abbiamo disposto otto elementi in una griglia a due colonne abbastanza alta da richiedere due pagine. Tutti gli otto sono stati renderizzati sulla pagina uno, e la pagina due è uscita vuota. Grid va bene per i contenuti che si adattano a una singola pagina; per qualsiasi cosa che pagini, usa il flusso di blocco o una tabella.
# L’inghippo che non è CSS affatto
Mentre costruivamo l’harness abbiamo registrato @page { size: A5 } come non supportato — ed è stato nostro errore. Al renderer è stato detto di usare A4 dalla chiamata API, e un formato carta passato dal chiamante sostituisce silenziosamente la dimensione @page del documento. Con il formato omesso e la dimensione della pagina CSS preferita, sia A5 che orizzontale sono usciti esattamente bene.
Vale la pena affermarlo chiaramente perché è quasi certamente il motivo più comune per cui le persone concludono che @page è rotto: il CSS è fine, e qualcosa a monte lo sta silenziosamente superando. L’abbiamo colto solo perché un risultato che era in disaccordo con la specifica ci ha fatto rileggere l’harness piuttosto che pubblicare il numero.
# Riprodurre questo
L’harness e il dataset dietro la tabella sono entrambi pubblicati:
- bench-htmltopdf.js lancia Chromium, renderizza ogni caso, e ispeziona l’output con pdf-lib e pdf.js.
- html-to-pdf.json è l’output da cui abbiamo scritto questa tabella, timbrato con la versione di Chromium che ha misurato.
Per eseguirlo tu stesso, salva l’harness in una cartella vuota ed esegui:
npm i playwright pdf-lib pdfjs-dist@3
npx playwright install chromium
node bench-htmltopdf.js
Un limite vale la pena affermarlo: il rilevamento dei font scansiona i byte grezzi del PDF per i nomi dei font, quindi un risultato positivo è solido ma uno vuoto significa solo “non visibile a questo metodo”. L’harness segnala quel caso come inconcludente piuttosto che come un fallimento, perché pubblicare “i font non sono incorporati” sulla base di una scansione che non può vederli sarebbe esattamente il tipo di affermazione che questo articolo esiste per evitare.
# Perché l’abbiamo misurato
Costruiamo uno strumento e un’API HTML to PDF su questo motore. Pubblicare cosa fa e cosa non fa è più utile che affermare che è fedele — e se un numero qui è sbagliato, l’harness che l’ha prodotto è collegato sopra per essere eseguito.
Domande frequenti
page-break-after funziona ancora, o devo usare break-after?
Entrambi funzionano. Abbiamo misurato separatamente il moderno break-after: page e il legacy page-break-after: always, e entrambi hanno prodotto il documento a due pagine previsto su Chromium 148. Se hai modelli più vecchi che emettono la sintassi legacy, non sono la ragione per cui la tua paginazione è sbagliata.
Perché il mio @page size viene ignorato?
Quasi sempre perché il chiamante l'ha sovrascritto. Se l'API o la libreria che gestisce il rendering passa un formato carta proprio, questo prevale e il tuo @page { size: … } viene scartato silenziosamente. Con il formato omesso e la dimensione della pagina CSS preferita, sia A5 che paesaggio sono usciti esattamente come richiesto. Questo è il falso report più comune di "@page size non funziona" — ci ha colto di sorpresa durante la creazione dell'harness per questo articolo.
Posso mettere i numeri di pagina nel margine con CSS?
Non in Chromium. @page { @bottom-center { content: counter(page) } } fa parte di CSS Paged Media ed è ampiamente documentato, ma il margin box semplicemente non viene renderizzato — l'abbiamo misurato e non abbiamo trovato alcun numero di pagina da nessuna parte nell'output. I numeri di pagina devono provenire dallo strumento che esegue la conversione, non dal tuo stylesheet.
I layout della griglia CSS si rompono correttamente tra le pagine?
No. Nel nostro test, tutti gli otto elementi della griglia sono stati renderizzati sulla pagina uno e la seconda pagina è uscita vuota — la griglia non si è frammentata. Se il tuo documento è abbastanza lungo da paginare, disponi la parte ripetuta con il flusso di blocco normale o una tabella e mantieni la griglia per le cose che si adattano su una pagina.
I hyperlink vengono preservati nel PDF?
Sì. Sia un <a href> esterno che un #anchor interno hanno prodotto annotazioni di collegamento reali nel file di output, quindi i link rimangono cliccabili e un indice generato rimane navigabile.