Skip to content

HTML to PDF

Render HTML and CSS to PDF — full browser print-stylesheet parity, no JavaScript.

Terminal window
composer require phpdftk/html-to-pdf

HTML WPT in-scope pass: 95.72% (559 / 584). Full audit at HTML WPT substrate. MathML inline content reached 100% in-scope pass via the inline-MathML adapter (detail).

Phase 1 (MVP) is complete and Phase 2 is most of the way through. The renderer ships a working HTML/CSS-to-PDF pipeline with the following surface in place:

CSS basics — tokenizer, value parsing, selector matching, full cascade with !important, custom properties / var(), calc(), shorthand expansion, @media (with feature queries: (min-width: N), (max-width: N), (orientation: ...), plus MQ5 range syntax (width > 400px) / (100px < width <= 500px)), @supports (full condition evaluation), @container [name?] (<size-query>) evaluated against the viewport as a Phase-1 proxy (full per-element container sizing lands later) with inline-size / block-size aliasing to width / height, @import, @font-face, @page with margin boxes and named pages, cqw / cqh / cqi / cqb / cqmin / cqmax container-relative length units (CSS Containment 3 §6) resolving against ancestors with container-type: inline-size or size.

Layout — block formatting context, inline / line boxes with shaping, floats + clear, position: relative / absolute / fixed / sticky, min/max sizing, aspect-ratio in both directions, box-sizing: border-box, multi-column with column-span: all, paged media with break-before / break-after / break-inside + orphans / widows / box-decoration-break, tables (with border-collapse: collapse running full conflict resolution, caption-side, rowspan / colspan, <col> / <colgroup> widths, auto-width algorithm scaling columns to cell max-content), flexbox (row + column with reverse, wrap, all justify-content / align-items / align-content / flex-grow / flex-shrink / flex-basis / order, column-gap / row-gap including percentage gaps, flex-basis: auto + auto-width items measured at max-content per §9.2, min-{width,height}: auto floored at min-content per §4.5, abspos children removed from the flex flow and positioned against the container as positioned ancestor, item percentage heights resolved against the flex container), grid (grid-template-{columns,rows} accepting <length>+, fr units, repeat(N, ...), and auto / min-content / max-content content-sized tracks; grid-template-areas string-grid with grid-area: <name> placement; grid-column / grid-row shorthand with span N and negative line indices; grid-auto-flow row / column / dense with implicit-track growth via grid-auto-{rows,columns}; column-gap / row-gap; justify-self / align-self with start / end / center / stretch), writing modes (CSS WM 4 §3 — writing-mode: vertical-rl / vertical-lr / sideways-rl / sideways-lr stack block children along the physical x-axis: rightward for *-lr / leftward for *-rl; auto-width shrinks to children’s outer-width sum (block-axis shrink-to-content per Sizing 4 §6.4); auto-height falls back to max child outer-height (orthogonal max-content approximation); adjacent sibling margins collapse along the block axis per §8.3.1 with the axes transposed; the resolver also maps the full logical-property set — margin-block-*, inset-inline-*, border-*-{start,end}, inline-size / block-size — to physical longhands per the active mode + direction).

Painter — backgrounds (including N-stop linear + radial gradients via PDF Type-3 stitching functions, with length-positioned stops), background-clip + background-origin + background-size + background-position + background-repeat, borders (all 8 styles), box-shadow + inset box-shadow, filter: drop-shadow(), outline, text decorations (underline / overline / line-through with text-decoration-style: wavy), 2D + 3D CSS transforms (rotateX / rotateY / rotateZ / rotate3d / matrix3d / perspective, with backface-visibility: hidden suppressing back-facing rotations).

Inputs — full WHATWG HTML5 parser (100% pass on html5lib-tests tree-construction: 1586 / 1586 — full quirks-mode DOCTYPE table, MathML/SVG integration points as scope boundaries, foreign-content </br> and </p> breakout, <applet> / <marquee> / <object> AFE-marker handlers, InTable <form> pointer, stray-html / stray-body attribute merge, adoption-agency + foster parenting (cloned formatting elements lift out of tables, into template content), customizable-<select> (<div>/<button>/<datalist>/<keygen>/<menuitem>/<plaintext>/<svg>/<math> allowed, <selectedcontent> mirrors the selected option), scripted <noscript> RAWTEXT, full named-character-reference table), <picture> / <source> with media-attribute selection and srcset DPR picking, <select> + <select multiple> + <optgroup>, <details> / <summary>, <input type=button|submit|reset|checkbox|radio> static rendering.

Remaining Phase 2 work (Grid last-mile — auto-fill / auto-fit, minmax() / fit-content(), row-axis intrinsic sizing, named lines, subgrid; Writing Modes 4; Universal Shaping Engine for Arabic / Indic; remote url() fetching with SSRF gates; AcroForm widget annotations; raster filter primitives; full auto-width table algorithm; true perspective for 3D transforms) lives in docs/plans/html-and-svg.md. The cross-package contracts document the locked public surface.