Repository navigation
Read each HTML presentational attribute only on the elements HTML defines it for, under any stylesheet rule - #641
Merged
Conversation
jakejackson1
force-pushed
the
feature/531-presentational-attributes
branch
from
September 30, 2026 08:10
361f517 to
8874512
Compare
jakejackson1
force-pushed
the
feature/531-presentational-attributes
branch
from
September 30, 2026 10:10
8874512 to
9a8d6df
Compare
27 of 28 tasks
…ines it for, under any stylesheet rule In standard mode, color, width, height, valign, vspace, hspace, align, border, nowrap, size and type are hints of no specificity on the elements the HTML Rendering section gives them to. <a color>, <p color> and <div width> no longer apply. The tag handlers no longer read align, nowrap, a table's border, <caption align> or <hr width> again over the stylesheet. <div align> is inherited, and <hr align>, <hr size>, <img align>, <img border>, <thead align>, <li type> and wider table borders take effect. Legacy mode is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
jakejackson1
force-pushed
the
feature/531-presentational-attributes
branch
from
September 30, 2026 10:42
9a8d6df to
3958f44
Compare
This was referenced Sep 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #531. Part of #520 (step 11).
Stacked on #633 (
feature/535-legacy-css-mode), which merges first.Problem
#631 moved the presentational attributes after the built-in defaults under
standard, as author rules of no specificity. Two gaps were left.1. Every attribute was read on every element. Measured on
feature/535-legacy-css-mode(f1b1c530),standard:<a href color="#f00"><span color="#f00">,<p color="#f00">,<td color="#f00"><div width="50mm">2. Some tag handlers read the attribute again after the merge, over the stylesheet, so a rule could not beat it:
p { text-align: left }<p align="right">td { text-align: left }<td align="right">td { white-space: normal }<td nowrap><table border="1" style="border: none">caption { caption-side: top }<caption align="bottom">hr { width: 80% }<hr width="50%">in a table cellinput { text-align: right }<input align="center">Some attributes HTML defines did nothing, in both modes:
<div align="center"><p>: the paragraph was not centred.<hr align>,<hr size>,<img align>and<img border>were ignored.<table border="2">was ignored.<li type="square">lost to the disc the item inherits.Change
All of this applies under
cssModestandardonly. Underlegacy, output is unchanged, and the snapshot suite shows no legacy fixture changed.Mpdf\Css\PresentationalHintsgives the properties an element's attributes set. Each attribute is read on the elements the Rendering section of the HTML Living Standard defines it for:dir,langdirection,langcolorfont,hrcolorface,sizefontfont-family,font-size(mPDF's size table, unchanged)widthimg,table,td,th,hr, and mPDF'smeter,progresswidthheightimg,table,thead,tbody,tfoot,tr,td,th,meter,progressheightvalignthead,tbody,tfoot,tr,td,thvertical-alignaligndiv(middleascenter),p,h1–h6text-alignalignmiddle/absmiddleascenter;charas mPDF's decimal alignment ontd/th)text-alignaligncaption(bottom)caption-sidealignhrmargin-left/margin-rightalignimgfloat(left, right) orvertical-alignvspace,hspaceimg,input type=imagebordertable(any width; 1px if not a number),img(above 0)nowraptd,thwhite-space: nowrapsizehrheight, in pxtypeol,ul,li(1 a A i Ias written;none disc circle squarein any case)list-style-typemPDF's own tags that run the cascade (
barcode,dottab,textcircle) keep every attribute they read before, through the old code.CssMerger::mergeInCascadeOrder()merges the hints in the same place as before.mergeInlineAttributes()(legacy) sharesofFont()and is otherwise unchanged.<table border>: the table's own border is a hint on the table. Each cell takes a 1px black border of its own, as a browser'stable[border] tdrule gives it, below any rule for the cells. A rule for the table's border no longer removes the cells' borders, and the cells no longer copy a rule's border from the table. In collapsed tables a wider table border wins the outer edges, as in a browser.Tag handlers that read attributes directly
This answers the plan's open question. Gated under
standardthroughTag::appliesStandardCascade(), because they beat the cascade:Td:align(includingchar) andnowrap. Both now come through the properties.BlockTag:block-alignfromalignon every block, and<caption align="bottom">.Hr: theW-PERCENTtaken from thewidthattribute in a table cell.Table:border. Alsotable_border_attr_set, which made cells copy the table's border.Input:align(mPDF's own use, aligning an active field's text). Atext-alignrule now beats it, as it already did fortextarea.Left as they are, because they only fall back to the attribute when no property is set, so the cascade order is already right:
Td:valign,width,height,bgcolor,dir.Tr:bgcolor.Table:width,bgcolor,dir,cellpadding(applied bymergeTableSpecificCss()), andalign(table['a'], which margin rules still override).Hr:width,align,coloroutside tables.Meter:width,height.TextArea:align.BlockTag: thetypeswitches, which setCSS overrides.mPDF-only attributes (
autosize,rotate,text-rotate,columns valign, form attributes, the page and header tags) are unchanged.Left out, for later
<table align="left|right">floats the table in a browser. mPDF only positions it.bgcolorandtextonbody,bgcoloronthead/tbody/tfoot, andbackgroundon tables and cells are not read in either mode.<tr align>sets the row'stext-align, but rows do not pass it on to their cells yet (tr,tbody,theadandtfootpass none of their properties to their cells #542).<td><p align="right">is not right-aligned.<font size>keeps mPDF's size table: 3 issmall, notmediumas in a browser.Tests
PresentationalHintsTest(unit, 105 cases) covers each attribute on each element it applies to and on elements it must be ignored on, plus theborderwidth parsing.PresentationalAttributeTestrenders every case under both modes, each with a must-match, a near miss and a stylesheet rule beating the attribute:font,a,span,p,div,td,h3);hrcolor/width/align/size, including in a table cell;alignonp,h2,div(inherited),address,section,td,thandthead;nowrap,valign,bgcoloron a table, a row and a cell, andcellpadding;table border: 0, 1 and 3, against rules for the table and the cells, and collapsed;caption align;imgwidth/height/hspace/vspace/align/border;<div width>;<input align>in active forms;ol/ul/litype;<barcode color>.testInEveryContextputsfont color,a/span/p color,p alignagainst a rule,div alignandhr color width alignin a block, a table cell, a header, a footer, a positioned block, apage-break-inside: avoidblock that unwinds, and after a forced page break, under both modes.LineRecordingMpdfrecords eachLine()with its colour and width.ListMarkerRecordingMpdfalso records the marker type, andPageStreams::placementsIn()is split out ofimagePlacements().StandardBlockTagsTestruns itsblock-aligncheck under legacy, and adds one for standard: onlydivtakesalign, as itstext-align.CascadeOrderTest: the "as wide as the border attribute" case uses1pxrather than0.264583mm. Under standard the table's border is a CSS border, which collapsed tables compare by width, and 0.264583mm is a hair narrower than 1px.With the source changes reverted, every standard case of
PresentationalAttributeTestthat differs from legacy fails, and no legacy case does.Snapshots
presentational-attributes. One attribute per caption, understandard.email-invoice-template-legacyandemail-invoice-template-standard. An invoice built like an HTML email (nested tables,bgcolor,align,valign,cellpadding,border,font color,nowrap), with a newer stylesheet laid over it that restyles some of those. The captions say what each mode draws.barcode. Its stylesheet setstable.items { border: 3px solid #880000 }on a<table border="1">. The outer border is now the rule's thick dark red, and the cells keep a thin black border, as in a browser. Before, the attribute replaced the rule, and the table and its cells were all 1px black.Checks
composer test: OK. 6410 tests, 31 skipped for missing optional packages or ungenerated data. The snapshot suite ran with Imagick and Ghostscript.composer cs: clean.php -lunder PHP 5.6.🤖 Generated with Claude Code