Ce qui survit réellement à HTML vers PDF, mesuré
Douze fonctionnalités de print-CSS rendues via Chromium headless puis relues à partir du PDF — sauts de page, tailles @page, en-têtes de tableau répétés, liens et polices. Deux d'entre elles ne fonctionnent pas, et une erreur courante est la faute de l'API, pas du CSS.
Quoi de neuf ici : Une matrice de support par fonctionnalité pour le print CSS HTML vers PDF, mesurée sur Chromium 148 en rendant chaque cas et en inspectant le nombre de pages du PDF résultant, les dimensions de la page, les annotations de lien et le texte par page — pas en lisant la documentation. Chaque verdict est reproductible à partir de l'infrastructure publiée et de l'ensemble de données, et deux techniques largement recommandées ne fonctionnent pas.
Tout service HTML-vers-PDF affirme que sa sortie est fidèle. Aucun ne publie ce que cela signifie. Nous l’avons mesuré : douze fonctionnalités CSS pour l’impression, chacune rendue via Chromium sans interface — le moteur derrière MostlyRender et derrière la plupart des services HTML-vers-PDF — puis relues dans le PDF résultant.
Rien ici n’est tiré de la documentation. Chaque conclusion provient d’une inspection du fichier : combien de pages il a, quelle taille elles ont, quel texte a atterri sur quelle page, si une annotation de lien existe, si une police a été intégrée.
# La matrice
Mesuré sur Chromium 148.0.7778.96, d’abord le 1er septembre 2026 et à nouveau le 27 septembre 2026. Les deux exécutions ont produit les mêmes douze conclusions.
| Fonctionnalité | Fonctionne | Observations |
|---|---|---|
break-after: page |
Oui | 2 pages, comme demandé |
page-break-after: always (héritage) |
Oui | 2 pages — l’ancienne syntaxe est toujours respectée |
break-inside: avoid |
Oui | Le bloc a été déplacé en entier à la page 2 au lieu d’être divisé |
@page { size: A5 } |
Oui | 420×595pt — exactement A5 |
@page { size: A4 landscape } |
Oui | 842×595pt — paysage |
Répétition de <thead> sur les pages |
Oui | 3 pages, en-tête présent à la page 2 |
position: fixed comme en-tête persistant |
Oui | Présent sur 2 des 2 pages |
@page boîtes de marge + counter(page) |
Non | Aucun numéro de page dans la sortie |
<a href> comme lien cliquable |
Oui | Annotation de lien présente |
Liens #anchor internes |
Oui | Destination présente |
| Intégration de police | Oui | DejaVuSansMono intégrée et sous-ensemble |
| Grille CSS sur un saut de page | Non | Les 8 cellules à la page 1 ; page 2 vide |
Dix sur douze fonctionnent. Les deux qui ne le font pas méritent d’être connues avant de concevoir autour d’elles.
# Les deux qui échouent
Les boîtes de marge CSS Paged Media ne se rendent pas. La spécification vous propose @page { @bottom-center { content: "Page " counter(page) } }, c’est dans chaque tutoriel sur l’impression à partir du web, et Chromium l’ignore. Nous n’avons trouvé aucun numéro de page dans la sortie du tout.
Si vous avez besoin de numéros de page, ils doivent être ajoutés par tout ce qui produit le PDF — c’est pourquoi les outils qui offrent la numérotation des pages le font en tant que fonctionnalité plutôt que de vous dire d’écrire du CSS.
La grille CSS ne se fragmente pas. Nous avons disposé huit éléments dans une grille à deux colonnes assez haute pour nécessiter deux pages. Les huit se sont rendus à la page un, et la page deux est sortie vide. La grille est correcte pour le contenu qui tient sur une seule page ; pour tout ce qui paginé, utilisez le flux de bloc ou un tableau.
# L’écueil qui n’est pas du CSS du tout
Lors de la construction du harnais, nous avions enregistré @page { size: A5 } comme non supporté — et c’était notre propre erreur. Le moteur de rendu se voyait dire d’utiliser A4 par l’appel API, et un format de papier transmis par l’appelant remplace silencieusement la taille @page du document. Avec le format omis et la taille de page CSS préférée, A5 et paysage se sont tous deux avérés être exactement justes.
Cela vaut la peine de le dire clairement parce que c’est presque certainement la raison la plus courante pour laquelle les gens concluent que @page est cassé : le CSS est correct, et quelque chose en amont le dépasse silencieusement.
Nous ne l’avons découvert que parce qu’un résultat qui était en désaccord avec la spécification nous a fait relire le harnais plutôt que de publier le nombre.
# Reproduire ceci
Le harnais et l’ensemble de données derrière le tableau sont tous deux publiés :
- bench-htmltopdf.js lance Chromium, rend chaque cas et inspecte la sortie avec pdf-lib et pdf.js.
- html-to-pdf.json est la sortie à partir de laquelle nous avons rédigé ce tableau, horodaté avec la version Chromium qu’il mesure.
Pour l’exécuter vous-même, enregistrez le harnais dans un dossier vide et exécutez :
npm i playwright pdf-lib pdfjs-dist@3
npx playwright install chromium
node bench-htmltopdf.js
Une limite qui vaut la peine de noter : la détection de police scanne les octets PDF bruts pour les noms de police, donc un résultat positif est solide mais un résultat vide ne signifie que « non visible pour cette méthode ». Le harnais signale ce cas comme inconclus plutôt que comme un échec, car publier « les polices ne sont pas intégrées » sur la base d’une analyse qui ne peut pas les voir serait exactement le genre de réclamation que cet article existe pour éviter.
# Pourquoi nous l’avons mesuré
Nous construisons un outil HTML vers PDF et une API sur ce moteur. Publier ce qu’il fait et ne fait pas est plus utile qu’affirmer qu’il est fidèle — et si un nombre ici est faux, le harnais qui l’a produit est lié ci-dessus pour être exécuté.
Questions fréquemment posées
Est-ce que page-break-after fonctionne toujours, ou ai-je besoin de break-after ?
Les deux fonctionnent. Nous avons mesuré séparément la syntaxe moderne break-after: page et la syntaxe héritée page-break-after: always, et chacune a produit le document à deux pages attendu sur Chromium 148. Si vos anciens modèles émettent la syntaxe héritée, ce n'est pas la raison pour laquelle votre pagination est incorrecte.
Pourquoi la taille de ma @page est-elle ignorée ?
Presque toujours parce que l'appelant l'a remplacé. Si l'API ou la bibliothèque qui pilote le rendu passe son propre format de papier, celui-ci l'emporte et votre @page { size: … } est silencieusement ignoré. Avec le format omis et la taille de page CSS préférée, A5 et landscape se sont tous deux avérés exactement comme demandé. C'est le faux rapport le plus courant sur « @page size ne fonctionne pas » — cela nous a piégés lors de la construction de l'outil pour cet article.
Puis-je mettre les numéros de page dans la marge avec CSS ?
Pas dans Chromium. @page { @bottom-center { content: counter(page) } } fait partie de CSS Paged Media et est largement documenté, mais la boîte de marge ne s'affiche simplement pas — nous l'avons mesuré et n'avons trouvé aucun numéro de page dans la sortie. Les numéros de page doivent provenir de l'outil qui effectue la conversion, pas de votre feuille de style.
Les mises en page de grille CSS se divisent-elles correctement sur plusieurs pages ?
Non. Dans notre test, les huit éléments de grille se sont tous rendus sur la première page et la deuxième page est sortie vide — la grille ne s'est pas fragmentée. Si votre document est assez long pour paginer, disposez la partie répétée avec un flux de bloc normal ou un tableau et gardez la grille pour les choses qui tiennent sur une page.
Les hyperliens sont-ils conservés dans le PDF ?
Oui. À la fois une <a href> externe et une #anchor interne ont produit de véritables annotations de lien dans le fichier de sortie, donc les liens restent cliquables et une table des matières générée reste navigable.