Repository navigation
Many CSS values are ignored or read wrongly, and four common inputs break the document #552
Description
Part of #520 (step 28).
An audit of which CSS properties, values, units, functions and at-rules mPDF reads, on which elements, and where it gets them wrong. The rest of #520 decides which declaration an element gets; this is about what mPDF then does with the value. Measured on gravitypdf at d660a410, PHP 8.5.4, by rendering each case and reading the drawn state, the content streams and rasterised pages. "Browser" means what the CSS specs give.
mPDF handles the core of print CSS well. That covers:
- the block box model, and floats with a width
- solid colours, border longhands and backgrounds
- fonts and spacing, and the common alignments
page-break-*, repeated table headers and@pagemargins
About 100 of 210 common properties are read somewhere; the other 110 never are.
Inputs that break the document
| Input | Result |
|---|---|
color: rgb(255 0 0) (the space-separated syntax) |
a TypeError escapes WriteHTML(), and no PDF is produced |
@page { size: A4 }, letter or A5 landscape, or two lengths with no margin |
63 pages, one character per page (mpdf#1220) |
font: 12pt Roboto, font: 16px "DejaVu Sans Mono", <font face="Segoe UI"> |
every character is drawn as an empty box, in the first registered font |
@charset, @namespace or @import before a rule |
the rule is lost, as with the block at-rules in #524 |
Values read wrongly
These are worse than ignored values, because the output is corrupted.
- Lengths
calc(),min(),max(),clamp()andvar()become 0, which wipes the default margins.vw,vh,vmin,vmax,chandQare read aspx.remin a table uses the table's font size.%heights resolve against the page area.line-height: 0is treated asnormal.
- Colours
#rgbaand#rrggbbaaare misread.- A
%alpha overflows (/ca 1.36), and channels wrap instead of clamping. transparentdraws a black border, with warnings, and black text.- A
var()or invalid colour becomes black. - Shadows with no colour are grey, not
currentColor. rebeccapurpleis missing.
- Shorthands
backgroundloses a colour afterurl(), and any/ size.border: solid #c00 2mmand other orders draw nothing.fontturnssmall-capsinto uppercase and keeps an inheritedline-height.
- Backgrounds and effects
background-sizelengths are 2.83× too small.to top/to bottomgradients are reversed, and angles use the legacy convention.repeating-radial-gradient()draws one solid colour.outline-widthandoutline-colorstroke the text.insetshadows are drawn outside.- A block's
%radius uses the parent's width. border-style: hiddendraws a hairline.
- Text
text-transform: nonedoesn't undo an inherited transform on inline elements.list-style-imageURLs are lowercased.- A
url()with spaces, parentheses or inner quotes fails; Bootstrap's select arrow is one.
- Layout
- A float without a width takes the whole line, and parents always contain their floats.
img { display: block; margin: 0 auto }doesn't centre the image.- A
max-widthcontainer withmargin: autois full width. display: contentsstill paints the box.rotate: 90degrotates on PHP 7 but not on PHP 8.
- At-rules and pages
@mediamatches by substring:not printapplies, andmin-widthnever does.@importignores its media list and any URL without.css.page-break-beforeon tables andalwayson rows are ignored.page-break-after: avoidprotects one line only.
Not supported
- High
displaybeyondnone(flex, grid,inline-block, table values), andnoneon table partspositionon nested blocks,relative,stickybox-sizing;min-/max-width/-heighton blocks and tables- CSS functions and custom properties
white-spaceoutside<pre>;break-*;@font-face- modern colour syntax:
hsl()with spaces,hwb(),lab(),oklch(),color-mix()
- Medium
- a fixed block on every page;
z-indexbetween positioned blocks opacity/transformbeyond images;border-radius/box-shadowon inline elements, cells and tables;outlineobject-fit,aspect-ratio;orphans/widows;overflowclipping in flowtable-layout,<col>widths, row and table heightstext-decoration-*longhands;text-align: start/end;word-break/overflow-wrap;vertical-alignkeywords on text;text-indentin cells- multiple backgrounds;
@pagemargin boxes;@mediafeatures - further list styles, counters,
content,::marker,quotes
- a fixed block on every page;
- Low:
background-attachment,conic-gradient(),filter, blend modes,clip-path,writing-mode,text-overflow,tab-size,font-stretch, variable-font settings.
Already tracked in #520
| Finding | Issue |
|---|---|
| at-rules swallowing the next rule | #524 |
currentColor |
#540 |
numeric weights, larger/smaller |
#545 |
inline line-height |
#548 |
| decorations | #544 |
text-shadow to child blocks |
#539 |
| presentational attributes | #531 |
inline !important |
#523, #532 |
Order of work
Some parts are done earlier in #520, with the steps that share their code:
| Part | Where |
|---|---|
space-syntax rgb()/hsl(), @page size names, the font shorthand's family, dropping declarations mPDF cannot parse (lengths that become 0, colours that become black, unknown units read as px), break-* read as page-break-* |
#520's quick fixes, before the decision steps |
@charset, @namespace and @import losing the next rule |
#524 |
the background, border and font shorthand parsers |
before #532, and so before #546 |
text-transform: none on inline elements |
#539 |
transparent borders and text, border-style: hidden, grey default shadows |
#540 |
rem inside tables |
#543 |
var() and custom properties |
after #547 |
the remaining values read wrongly that are quick to fix: line-height: 0; #rgba/#rrggbbaa, % alpha, channel clamping, rebeccapurple; background-size lengths; gradient directions and angles; outline-* stroking the text; list-style-image URLs; url() with spaces or quotes; rotate: 90deg; @media matching and @import; page-break-before on tables. Most are a few lines in ColorConverter, SizeConverter, Gradient, ShadowParser, MediaQueryProcessor and CssLoader, and change output only where it is wrong today |
#520's quick fixes |
The rest is done here:
- The values read wrongly that are not quick to fix. Each needs layout work, or support that is still missing:
%heights resolved against the page arearepeating-radial-gradient()drawing one colourinsetshadows drawn outside- a block's
%border radius resolved against the parent's width (its height is not known until the block closes) - a float without a width taking the whole line, and a parent always growing to contain its floats
img { display: block; margin: 0 auto }not centring, amax-widthcontainer withmargin: autorendering full width, anddisplay: contentsstill painting its box; these wait fordisplayandmax-widthsupportpage-break-before: alwayson table rows, andpage-break-after: avoidprotecting one line only
- Missing support, by priority. The large items (
display, positioning) depend on Track the stack of open elements while parsing HTML #533 and Inherit from the parent element's computed values under the opt-in cascade #547. Split each into an issue of its own when work on it starts.
Sub-issues
Values read wrongly
- A gradient's first colour stop written at a unitless
0is dropped, sorepeating-radial-gradient(red 0, blue 10px)draws solid blue #584 a first gradient stop at a unitless0is dropped (the audit'srepeating-radial-gradient()finding) -
box-shadow: insetis drawn outside the box #585insetbox shadows drawn outside - A percentage
border-radiuson a block resolves against the parent's width, so50%draws a pill instead of an ellipse #586 a block's%border radius resolved against the parent's width - A percentage
heighton a block resolves against the page area, andheightacts as a minimum thatoverflow: hiddendoes not clip #587%heights resolved against the page area,heightacting as a minimum, andoverflowclipping in normal flow - A block with
max-widthandmargin: autorenders full width, becausemin-widthandmax-widthare not read on blocks #588 amax-widthcontainer withmargin: autorendering full width -
img { display: block; margin: 0 auto }does not centre the image #589img { display: block; margin: 0 auto }not centring - A float without a width takes the whole line, and every ancestor of a float grows to contain it #590 a float without a width taking the whole line, and parents always containing their floats
-
page-break-before: alwaysandpage-break-after: alwayson a table row are ignored #591page-break-before/-after: alwayson table rows -
page-break-after: avoidleaves a heading at the foot of a page when a table or kept block follows it #592page-break-after: avoidprotecting one line only
Follow-ups to the values read wrongly PRs (#689 to #697)
- Draw the outer box-shadow with the ring builder
InsetBoxShadowadded, so one class builds both shadows #698 draw the outer box-shadow with the ring builder Draw an inset box-shadow inside the padding box (#585) #694 added - A
box-shadowon a block that breaks across pages is drawn along the split edges, wherebox-decoration-break: slicewould leave them open #699 box-shadow drawn along the split edges of a block that breaks across pages -
page-break-before: left/righton a table row start the next page, not the next even or odd one, andpage-break-after: alwayson the last row does nothing #700left/rightbreaks on table rows, andpage-break-after: alwayson the last row - A
min-widthwider than the container is capped at the container, andWriteFixedPosHTML()subtracts the left margin where it means the right #701min-widthwider than the container, and the$bbox_mlslip inWriteFixedPosHTML() - With
autoPadding, a percentageborder-radiuson a block no longer adds padding #702autoPaddingwith a percentageborder-radius - A block-level image wider than its line is narrowed instead of overflowing, and
text-indentshifts it on a first line #703 a block-level image wider than its line, andtext-indenton its line - A nested
box-shadowescapes anoverflow: hiddenclip, the clip ignoresborder-radius, and a%height inside a positioned block isauto#704 nested shadows andborder-radiusunderoverflow: hidden,%height in a positioned block - A float with no width is not held to its min-content width, and keeps the full width after a forced page break or with no text to measure #705 the min-content bound and unmeasured cases of a float with no width
-
page-break-after: avoidestimates the following line, measures a kept block three times, and waits for a block after bare text #706page-break-after: avoid: the estimated following line, repeated measuring, bare text - A gradient position whose last word is
0, such as10px 0, is read as the angle 0 #707 a gradient position ending in0read as the angle
Missing support, high priority
- Support
box-sizing, andmin-/max-widths and heights on blocks, tables and cells #593box-sizing, andmin-/max-sizes on blocks, tables and cells - Evaluate
calc(),min(),max()andclamp()in lengths #594calc(),min(),max()andclamp() - Support custom properties and
var()#595 custom properties andvar()(after Inherit from the parent element's computed values under the opt-in cascade #547) - Support
white-spaceon every element #596white-spaceon every element - Load fonts declared with
@font-face#597@font-face - Lay elements out by their
displayvalue:inline-block,table-*,list-item,contents, andnoneon table parts #598display:inline-block,table-*,list-item,contents, andnoneon table parts (after Track the stack of open elements while parsing HTML #533) - Position nested blocks with
absoluteandfixed, offsetrelativeboxes, and readsticky#599 positioning: nestedabsolute/fixed,relative,sticky(after Track the stack of open elements while parsing HTML #533 and Inherit from the parent element's computed values under the opt-in cascade #547) - Flexbox and grid layout: the design constraints in a streaming renderer #600 flex and grid: design constraints only, no implementation planned
Missing support, medium priority
- Draw a
position: fixedblock on every page, and stack positioned blocks byz-index#601 a fixed block on every page, andz-indexbetween positioned blocks - Apply
opacityandtransformto blocks, tables and inline elements, not only images #602opacityandtransformbeyond images - Draw
border-radius,box-shadowand 3D border styles on every element, blurtext-shadow, and drawoutline#603border-radius,box-shadowand 3D border styles on every element,text-shadowblur, andoutline - Support
object-fit,object-positionandaspect-ratio#604object-fit,object-position,aspect-ratio - Honour
orphansandwidows#605orphansandwidows - Size tables by
table-layout,<col>and<colgroup>widths, and row and table heights #606table-layout,<col>/<colgroup>widths, row and table heights,%cell heights - Read the
text-decorationlonghands,text-align: startandend, andword-breakandoverflow-wrap#607text-decoration-*longhands,text-align: start/end,word-break/overflow-wrap - Align inline text with
vertical-alignkeywords, and indent cells and hanging lines withtext-indent#608vertical-alignkeywords on text,text-indentin cells,hanging,each-line - Draw every layer of a multiple background #609 multiple backgrounds
- Draw
@pagemargin boxes such as@top-center, withcounter(page)andcounter(pages)#610@pagemargin boxes - Add list marker styles,
reversed,value,::markerand the list-item counter, and draw quotation marks for<q>#611 list marker styles,reversed,value,::marker, the list-item counter, and quotes on<q> - Lay out an element in CSS columns with
column-count,column-widthandcolumns#612 CSS columns on an element
@media size and orientation features are in #575, and the low-priority list is not filed.
Activity
0is dropped, sorepeating-radial-gradient(red 0, blue 10px)draws solid blue #584box-shadow: insetis drawn outside the box #585border-radiuson a block resolves against the parent's width, so50%draws a pill instead of an ellipse #586heighton a block resolves against the page area, andheightacts as a minimum thatoverflow: hiddendoes not clip #587max-widthandmargin: autorenders full width, becausemin-widthandmax-widthare not read on blocks #588img { display: block; margin: 0 auto }does not centre the image #589page-break-before: alwaysandpage-break-after: alwayson a table row are ignored #591page-break-after: avoidleaves a heading at the foot of a page when a table or kept block follows it #592box-sizing, andmin-/max-widths and heights on blocks, tables and cells #593calc(),min(),max()andclamp()in lengths #594var()#5955 remaining items
position: fixedblock on every page, and stack positioned blocks byz-index#601opacityandtransformto blocks, tables and inline elements, not only images #602border-radius,box-shadowand 3D border styles on every element, blurtext-shadow, and drawoutline#603object-fit,object-positionandaspect-ratio#604orphansandwidows#605table-layout,<col>and<colgroup>widths, and row and table heights #606text-decorationlonghands,text-align: startandend, andword-breakandoverflow-wrap#607vertical-alignkeywords, and indent cells and hanging lines withtext-indent#608@pagemargin boxes such as@top-center, withcounter(page)andcounter(pages)#610reversed,value,::markerand the list-item counter, and draw quotation marks for<q>#611column-count,column-widthandcolumns#612Drop a declaration mPDF cannot read so the value it would replace sti…
Drop a declaration mPDF cannot read so the value it would replace sti…
Drop a declaration mPDF cannot read so the value it would replace sti…