Repository navigation
A float without a width takes the whole line, and every ancestor of a float grows to contain it #590
Description
Part of #552 (step 28, values read wrongly).
Floats with a width work. Two things differ from CSS:
- A float with no
widthtakes the whole remaining line instead of shrinking to fit its content, so nothing flows beside it. - Every ancestor of a float grows to contain it. In CSS, a float sticks out of a parent in normal flow, and text after the parent flows beside it. Only a block that starts a new block formatting context contains its floats, such as one with
overflow: hidden,display: flow-rootor its ownfloat.
Measured on gravitypdf at 60b82630, PHP 8.5.4, from the filled rectangles and the positions text is drawn at.
| Document | Browser | mPDF |
|---|---|---|
<div style="float:left;background:#0f0">aa</div><p>bb</p> |
a float as wide as aa, with bb beside it |
a 180 mm float; bb below it at y = 25.5 mm |
<div style="background:#0f0"><div style="float:left;width:40mm;height:30mm">aa</div></div><p>zz</p> |
the green parent is 0 mm tall; zz beside the float |
the parent is 30 mm tall; zz below the float at y = 50.4 mm |
the same with overflow: hidden on the parent |
the parent is 30 mm tall; zz below |
the same |
Cause.
- Width. With no
width,BlockTag::open()setscss_set_widthto the container's remaining width. The comment there reads "For now - just set to maximum width left" (src/Tag/BlockTag.php:636-644forright,:680-690forleft). Shrinking to fit needs the content's widest line, which is not known until the content has been laid out. - Containment. When a float closes,
BlockTag::close()records where it ends on every open ancestor (src/Tag/BlockTag.php:1310-1318). Each ancestor then moves its bottom down to that position when it closes (:1203-1222).
Fix.
-
Width. Shrink-to-fit is
min(max(min-content, available), max-content). Table cells already work out the widths they need before anything is drawn. They track the longest word and the longest line as content is added (miw,sandmaxs,src/Tag/Td.php:411-460), and_tableColumnWidth()(src/Mpdf.php:20547) sizes the columns from those. A float with no width can be sized the same way:- buffer its content
- measure it
- set
css_set_width - lay it out
The buffering has to hold nested blocks, which cells do. A measuring pass must leave no state behind, or
StateSnapshotmust put it back. -
Containment. Record a float's end only up to the nearest ancestor that starts a block formatting context: the body, a float, a positioned block, a table cell,
overflowother thanvisible,display: flow-root. Blocks after a parent that does not contain the float then flow beside it, whichGetFloatDivInfo()has to allow across levels.
The containment change alters the output of existing documents. Say so in the PR, and list the snapshot fixtures that change.
Tests. A left and a right float with no width, holding one short word and holding a long paragraph. A float with no width beside a float with one. A parent with and without overflow: hidden, and with display: flow-root. A float crossing a page break.
Activity
Point the changelog entry for #590 at its pull request