MostlyPDF

Wat werkelijk HTML naar PDF overleeft, gemeten

Twaalf print-CSS-functies weergegeven via headless Chromium en vervolgens uit de PDF gelezen — pagina-einden, @page-groottes, herhalende tabelkopteksten, koppelingen en lettertypen. Twee ervan werken niet, en één veelvoorkomend probleem is de schuld van de API, niet de CSS.

Max Čelar··HTML naar PDF

Wat is hier nieuw: Een ondersteuningsmatrix per functie voor HTML-naar-PDF-print-CSS, gemeten op Chromium 148 door elk geval weer te geven en de resulterende PDF's paginaantal, paginaafmetingen, koppelingsopmerkingen en tekst per pagina te inspecteren — niet door documentatie te lezen. Elk oordeel is reproduceerbaar op basis van de gepubliceerde testopstelling en dataset, en twee veel aanbevolen technieken werken niet.

Elke HTML-to-PDF-service zegt dat de uitvoer getrouw is. Geen van hen publiceert wat dat betekent. We hebben het gemeten: twaalf print-CSS-functies, elk gerenderd via headless Chromium — de engine achter MostlyRender en achter de meeste HTML-to-PDF-services — en vervolgens uit de resulterende PDF gelezen.

Hier wordt niets uit documentatie geciteerd. Elk oordeel komt voort uit inspectie van het bestand: hoeveel pagina’s het heeft, welke grootte ze zijn, welke tekst op welke pagina terechtkwam, of een linkannotatie bestaat, of een lettertype is ingebed.

# De matrix

Gemeten op Chromium 148.0.7778.96, eerst op 1 september 2026 en opnieuw op 27 september 2026. Beide runs produceerden dezelfde twaalf uitkomsten.

Functie Werkt Wat we hebben waargenomen
break-after: page Ja 2 pagina’s, zoals gevraagd
page-break-after: always (verouderd) Ja 2 pagina’s — de oude spelling wordt nog steeds gerespecteerd
break-inside: avoid Ja Het blok verplaatste zich geheel naar pagina 2 in plaats van te splitsen
@page { size: A5 } Ja 420×595pt — precies A5
@page { size: A4 landscape } Ja 842×595pt — liggend formaat
Herhalende <thead> over pagina’s Ja 3 pagina’s, header aanwezig op pagina 2
position: fixed als lopende koptekst Ja Aanwezig op 2 van 2 pagina’s
@page margeblokken + counter(page) Nee Geen paginanummer ergens in de uitvoer
<a href> als een klikbare link Ja Linkannotatie aanwezig
Interne #anchor-links Ja Bestemming aanwezig
Lettertypeinbedding Ja DejaVuSansMono ingebed en subset
CSS-raster over een pagina-einde Nee Alle 8 cellen op pagina 1; pagina 2 leeg

Tien van twaalf werken. De twee die niet werken zijn het waard om te weten voordat je er omheen ontwerpt.

# De twee die niet werken

CSS Paged Media margeblokken worden niet weergegeven. De spec geeft je @page { @bottom-center { content: "Page " counter(page) } }, het zit in elke tutorial over afdrukken vanaf het web, en Chromium negeert het. We vonden geen paginanummer in de uitvoer. Als je paginanummers nodig hebt, moeten ze worden toegevoegd door wat de PDF produceert — wat is waarom tools die paginanummering aanbieden dit als functie doen in plaats van je te vertellen CSS te schrijven.

CSS-raster fragmenteert niet. We legden acht items in een twee-koloms raster uit dat hoog genoeg was om twee pagina’s nodig te hebben. Alle acht werden op pagina één weergegeven, en pagina twee was leeg. Raster is prima voor inhoud die op een enkele pagina past; voor alles wat pagineert, gebruik je blokstroom of een tabel.

# De valkuil die helemaal geen CSS is

Bij het bouwen van de testomgeving registreerden we @page { size: A5 } als niet ondersteund — en het was onze eigen fout. De renderer werd via de API-aanroep gevraagd A4 te gebruiken, en een paperformaat dat door de aanroeper wordt doorgegeven overschrijft de @page-grootte van het document stil. Met het formaat weggelaten en CSS-paginagrootte geprefereerd, kwamen A5 en liggend formaat beide precies goed uit.

Dit is het waard om duidelijk te stellen omdat het bijna zeker de meest voorkomende reden is waarom mensen concluderen dat @page kapot is: de CSS is prima, en iets upstream rangschikt het stilzwijgend. We hebben het alleen opgemerkt omdat een resultaat dat het niet eens was met de spec ons ertoe zette de testomgeving opnieuw te lezen in plaats van het getal uit te geven.

# Dit reproduceren

De testomgeving en de dataset achter de tabel zijn beide gepubliceerd:

  • bench-htmltopdf.js start Chromium, rendert elk geval en inspecteert de uitvoer met pdf-lib en pdf.js.
  • html-to-pdf.json is de uitvoer waar we deze tabel van hebben geschreven, voorzien van de Chromium-versie die het heeft gemeten.

Om het zelf uit te voeren, sla je de testomgeving op in een lege map en voer je uit:

text
npm i playwright pdf-lib pdfjs-dist@3
npx playwright install chromium
node bench-htmltopdf.js

Een beperking die het waard is om te vermelden: lettertypedetectie scant de raw PDF-bytes naar lettertypenamen, dus een positief resultaat is solide maar een lege alleen betekent “niet zichtbaar voor deze methode”. De testomgeving rapporteert dat geval als inconlusief in plaats van als een fout, omdat publiceren “lettertypen zijn niet ingebed” op basis van een scan die ze niet kan zien precies het soort claim zou zijn waar dit artikel voor bestaat om te voorkomen.

# Waarom we het hebben gemeten

We bouwen een HTML naar PDF-tool en API op deze engine. Publiceren wat het wel en niet doet is nuttiger dan stellen dat het getrouw is — en als een getal hier fout is, is de testomgeving die het heeft geproduceerd hierboven gelinkt om te worden uitgevoerd.

Veelgestelde vragen

Werkt page-break-after nog steeds, of heb ik break-after nodig?

Beide werken. We hebben de moderne break-after: page en de verouderde page-break-after: always apart getest en beide produceerden het verwachte document van twee pagina's op Chromium 148. Als je oudere sjablonen hebt die de verouderde spelling gebruiken, zijn zij niet de reden dat je paginering fout is.

Waarom wordt mijn @page-grootte genegeerd?

Bijna altijd omdat de aanroeper het heeft overschreven. Als de API of bibliotheek die de weergave aanstuurt een eigen papierformat doorgeeft, wint dat en wordt je @page { size: … } stilzwijgend genegeerd. Met het format weggelaten en CSS-paginagrootte als voorkeur, kwamen A5 en landscape beide precies uit zoals gevraagd. Dit is het meest voorkomende valse rapport van "@page size werkt niet" — het trof ons bij het bouwen van de harnas voor dit artikel.

Kan ik paginanummers in de marge plaatsen met CSS?

Niet in Chromium. @page { @bottom-center { content: counter(page) } } is onderdeel van CSS Paged Media en is veel gedocumenteerd, maar de marge-box wordt gewoon niet weergegeven — we hebben het gemeten en nergens een paginanummer in de uitvoer gevonden. Paginanummers moeten afkomstig zijn van de tool die de conversie doet, niet van je stylesheet.

Breken CSS-rasterindelingen correct over pagina's?

Nee. In onze test werden alle acht rasteritems op pagina één weergegeven en de tweede pagina was leeg — het raster fragmenteerde niet. Als uw document lang genoeg is om in te pagineren, leg dan het herhalende gedeelte uit met normale blokflow of een tabel en behoud raster voor dingen die op één pagina passen.

Blijven hyperlinks in de PDF behouden?

Ja. Zowel een externe <a href> als een interne #anchor produceerden echte linkaantekeningen in het uitvoerbestand, dus links blijven klikbaar en een gegenereerde inhoudsopgave blijft navigeerbaar.