Repository navigation
Read the text-decoration longhands and text-align start, end and match-parent (#607) - #722
Open
gravitypdf-bot wants to merge 2 commits into
Open
gravitypdf-bot wants to merge 2 commits into
gravitypdf-bot wants to merge 2 commits into
Conversation
…h-parent (#607) mPDF read only the lines of text-decoration and matched them by substring, so the style, colour and thickness of the shorthand, the longhands and text-underline-offset/-position were ignored, and every decoration was a solid line in the text colour. text-align: start and end matched none of the keywords mPDF knows, so end left ltr text at the left. Under the standard cssMode the shorthand now expands into its longhands (TEXT-DECORATION keeps the lines, so the propagation from #654 reads them as before), each decoration keeps its own style, colour, thickness and underline position, and the line is stroked solid, double, dotted, dashed or wavy. start and end are taken against the block's or cell's direction, and match-parent computes to the parent's alignment. Legacy mode is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…yle through one path Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 11, 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.
Part of #607
Part of #552, part of #520
Problem
mPDF read only the lines of
text-decoration, matchingunderline,overlineandline-throughinside it by substring. So the longhands, the style, colour and thickness parts of the shorthand,text-underline-offsetandtext-underline-positionwere all ignored, and every decoration was drawn as a solid line in the text colour.text-align: startandend, which Bootstrap 5 uses for.text-startand.text-end, andmatch-parentmatched none of the keywordssetCSS()orTag::ALIGNknew.p { text-decoration-line: underline }p { text-decoration: underline wavy #f00 }text-decoration: underline double/dotted/dashedtext-decoration-thickness: 2pttext-underline-offset: 6pttext-underline-position: underdiv { width: 100mm; text-align: end }div { width: 100mm; direction: rtl; text-align: end }td { text-align: end }<div style="text-align: end"><p dir=rtl style="text-align: match-parent">Fix
Everything here is gated on
CssMode::STANDARD.Decorations
NormalizePropertiesexpands thetext-decorationshorthand: the line keywords stay inTEXT-DECORATION, so the propagation from Draw an ancestor's text decorations over its descendants instead of letting them inherit or undo them #654 and everything else that reads it is unchanged. The style, colour (currentcolorby default) and thickness go to their longhands, each reset to its initial value where the shorthand leaves it out.text-decoration-lineis read asTEXT-DECORATION. A CSS-wide keyword on the shorthand reaches each longhand.CssWideKeywordsknows the new initial values.setCSS()adds the element's style, decoration colour, thickness, underline offset andunderposition to the text parameter of each line the element sets itself (Mpdf::decorationDrawing()). Because Draw an ancestor's text decorations over its descendants instead of letting them inherit or undo them #654 copies those parameters to descendants, a propagated line keeps its own style and colour. An element's own decoration drops the keys of the one it replaces, so<span style="text-decoration: underline">inside a wavy paragraph draws a solid line.under.TextDecorations::stroke()drawssolid,double(two lines a thickness apart, the second away from the text; either side of the line forline-through),dotted(round dots),dashed, andwavy(whole half waves of a sine, each one cubic Bézier, fitted to the text width).text-underline-offsetandtext-underline-positionare inherited (InheritedProperties::DECORATION) and read where the underline is set, sop { text-underline-offset: 2mm }moves the underline of a link inside it. A length inemis taken of the element that declares it, like other inherited lengths.Alignment
startandendmap to left or right against the block's direction aftersetCSS()has read it, and against the cell's direction inTd.match-parentis resolved inComputedValues: it computes to the parent's alignment, with the parent'sstartorendtaken against the parent's direction, so its children inherit the resolved value.Decisions
text-underline-offset: 0puts the line at the baseline.autokeeps the font's position.autoandfrom-fontboth take the font'spostunderline thickness, which is what mPDF already drew. A percentage is of 1em.text-underline-position: left/rightare for vertical text, which mPDF does not set, so they are read asauto.Legacy mode
Unchanged. The shorthand is not expanded, the longhands are not read, and
start,endandmatch-parentare ignored, as before. Solid lines are drawn through the same operators as before, and no existing snapshot fixture changed. Nothing here only fixes a crash, so nothing applies in both modes.Not in this PR
word-break,overflow-wrapandword-wrap. They touchWriteFlowingBlock()and the table column minimum widths, which other PRs are working on at the same time (Align inline text withvertical-alignkeywords, and indent cells and hanging lines withtext-indent#608). They are the third part of Read thetext-decorationlonghands,text-align: startandend, andword-breakandoverflow-wrap#607 and can follow as their own PR.startandendin thetext-alignof form fields (input,textarea) and ofhr. Those readTag::getAlign(), which still does not know them.Tests
tests/Mpdf/TextDecorationLonghandsTest.php: the line longhand; the shorthand's parts in any order; legacy drawing a plain line in the text colour; the shorthand and longhands cascading both ways; the colour on a nested span and on a propagated line; an own decoration not keeping the style it replaces; the operators each of the five styles writes; double-line spacing for underline and line-through; a wavy line spanning the text and alternating; thickness as a length, a percentage and on line-through/overline, withauto/from-fontunchanged; the offset from the baseline in pt and em;underbelow the descenders with an offset from there, using a font with descenders; inheritance of the offset to an inline element, a child block and a cell; CSS-wide keywords on the longhands; legacy ignoring all of it byte for byte.tests/Mpdf/TextAlignLogicalTest.php:start/endin ltr and rtl blocks and cells against their physical equivalents,endinherited into an rtl child,match-parentagainst the parent'sendand rtlstart, a physical parent value, its resolved value handed on, a cell's match-parent from its row, a cell inheritingendfrom its table, and legacy ignoring them.text-decoration-align: each style and colour, thickness, the font's underline position besidetext-underline-offset: 0,6ptandtext-underline-position: under, thenstart/endin ltr and rtl blocks,match-parent, andendin ltr and rtl cells, each under a caption saying what it should show. I rasterised it and checked each case against its caption. Against the pre-fixsrc/it fails (page 1 RMSE 0.039).Checks
composer test: OK, 12771 tests, 31 skipped (none of them snapshots)composer cs: clean--memory-limit=2G): 23 errors, all pre-existing ongravitypdf(Regenerate the PHPStan baseline for PHPStan 2.3, so the Lint job passes again #716 regenerates the baseline); none in the changed files🤖 Generated with Claude Code