Skip to content

Let a table inherit from the block around it - #656

Merged
jakejackson1 merged 5 commits into
gravitypdffrom
fix/543-table-inherits-from-block
Oct 5, 2026
Merged

jakejackson1 merged 5 commits into
gravitypdffrom
fix/543-table-inherits-from-block

Conversation

@jakejackson1

@jakejackson1 jakejackson1 commented Sep 30, 2026 •

Copy link
Copy Markdown
Member

Closes #543. Part of #520 (step 22).

Was stacked on #655 and #648. Both have merged, and this branch is now rebased onto gravitypdf.

Problem

Measured on #655's head, d0440581. A table starts from the document's defaults, so a block between body and the table hands it nothing.

HTML Cell text today
<div style="color:#0f0; font-family:monospace; font-size:20pt"> around a table black, serif, 11pt
<body style="color:#0f0"> around a table black
<div style="font-size:20pt"><table style="font-size:80%"> 8.8pt, 80% of the document's 11pt
<table style="font-size:20pt"><tr><td style="font-size:1rem"> 20pt
<td style="text-align:right"> holding a table with a th the nested th centred

The built-in default style also gives every table line-height: 1.2, hyphens: manual and font-kerning: auto, so a table reset those whatever it sat in (#648).

Change

The user's decision: under standard, a table inherits every inherited property, as in a browser's no-quirks mode. Under legacy it starts from the document's defaults as before.

Under CssMode::STANDARD:

  • A block to its table. CssMerger::mergeInheritedBlockProperties() also runs for a table that is not in a cell. It reads the block the table is opened in and keeps InheritedProperties::names(). It leaves out the margin collapse, column background, text decoration and vertical-align that a child block takes. The table's merged CSS then carries the block's colour, font, size, alignment, line-height and the rest into its fields and base_table_properties. So base_table_properties['COLOR'] holds the inherited colour, which Draw currentColor, and a border or shadow with no colour, in the element's colour #651 reads for currentColor.
  • Font size. Table::open() resolves a table's own font-size against the block's size.
  • rem. SizeConverter reads rem against original_default_font_size, which a table does not overwrite. Before, it read default_font_size, which Table::open() sets to the table's size (from Many CSS values are ignored or read wrongly, and four common inputs break the document #552).
  • A cell to its nested table. Td keeps the text-align a cell inherits or sets on its table, and Table::open() merges a nested table's CSS over its cell's line-height and text-align. So a th in a table nested in a right-aligned cell is aligned right. Carry every inherited text property through every channel that hands inherited values on #648 already carries the cell's text properties.
  • The default table style. A browser's default style sets none of line-height, hyphens and font-kerning on a table. So CssMerger::mergeDefaultCss() drops the built-in line-height: 1.2, hyphens: manual and font-kerning: auto (TABLE_INHERITED_DEFAULTS). A value changed through the defaultCSS option still applies. A table in a block with no line-height of its own therefore inherits normal; see below.

Compatibility

Every change is gated. Under legacy, tables merge and size as before, and rem reads default_font_size.

To prove legacy is unchanged, I rendered all 144 snapshot documents under legacy, with the mode forced on every document, from #648's head and from this branch. They are byte-identical. Rendered under standard, the same 144 documents are byte-identical to #655's. No existing fixture changed.

StringWidthDecodingTest wrapped its tables in a div that set font-family: dejavusans; font-size: 8pt, which the tables never took. Its expected lines are for the cells' default font, so the wrapper is gone. The lines are the same.

line-height: normal in tables

The question this section used to ask is decided: a table matches paragraphs. Under standard, a table that would inherit normal now takes it, as a browser's does, instead of keeping the built-in 1.2. Its cells draw normal as a paragraph does, at normalLineheight (1.33) plus the font's line gap, so every standard-mode table that sets no line-height has taller rows. Legacy keeps the 1.2.

  • Snapshots: this would change 92 of the 205 snapshot documents, every one a standard-mode document with such a table. Instead, those documents set line-height: 1.2 on their tables, mostly as a table rule at the top of their stylesheet, so no fixture changes. Two documents show a table inheriting a block's line-height: table-inherits-from-block and table-in-blocks-article. In those two, only the other tables get line-height: 1.2, inline. The invoice and Factur-X documents get the rule ahead of the writer's HTML, so the invoice writer itself is unchanged.
  • Positioned blocks: a positioned block hands its content normal as the number 1.33, because Carry every inherited text property through every channel that hands inherited values on #648 writes the internal N into the stand-in <div>'s style as n. Its table now inherits that number, as its paragraphs do, so it has no line gap. Carry every inherited text property through every channel that hands inherited values on #648's channel is left alone, since writing normal there would change how positioned blocks draw their own lines.
  • Keeping the old spacing: add table { line-height: 1.2; } to the stylesheet. The changelog entry says so.

A table inside an inline element

#653 (step 20) made a block inherit from the inline elements it is opened in. A table had the same gap: Table::open() emptied InlineProperties, and close() restored the enclosing block's state. So <span style="color: #0f0; font-weight: bold">xx<table>…qq…</table>yy</span> drew qq and yy black and regular.

Under standard, the last commit fixes that:

  • A top-level table sets the open inline elements aside on the enclosing block, as Let a block inside an inline element inherit its style, and draw the text after the block in it again #653 does for a block, before its page break and before the text ahead of it is printed. It inherits their state through blockTextState(), which the table's merge already reads, and a font-size percentage on it is taken of theirs. close() puts them back, so yy is drawn in the span's style until </span>.
  • A nested table keeps the cell's open inline elements beside PARENTCELL and puts them back when it closes. Before, in both modes, a span's style ran on past </span> after a nested table, because nothing was left for the end tag to restore. Legacy keeps that.
  • Shared code: setOpenInlineElementsAside() moves from BlockTag to Tag. The put-back becomes restoreBlockTextState(), which restores the block's own state when nothing was set aside, so BlockTag::close() and Table::close() share it.
  • Tests:
    • Five table cases in BlockInsideInlineTest::documents(): a table in a span, in a span in a block, in a span in a cell, in a span in a kept block laid out again on the next page, and a table that breaks the page before it. Each runs in both modes.
    • TableInheritsFromBlockTest::fontSizes() adds 'a percentage of a span'.
    • All six fail under standard without the change.

Tests

  • InheritedPropertiesTest: 7 new contexts in Carry every inherited text property through every channel that hands inherited values on #648's matrix, each checking every TEXT property in both modes:
    • a table in a block, a list item, <body style>, a header, a positioned block, and a page-break-inside: avoid block laid out again on the next page;
    • a table after a forced page break.
  • In those matrix contexts, legacy carries only the font language override, which the text state keeps through a table, plus the family and size from <body style>. The font-kerning and hyphens exception Carry every inherited text property through every channel that hands inherited values on #648 made for nested tables is gone under standard.
  • TableInheritsFromBlockTest:
    • text-align, line-height and direction from a block, a list item, a positioned block, a kept block and a cell, compared by where the cell's lines are drawn. Legacy carries a block's direction, and a cell's alignment and direction, as before.
    • line-height: normal on the block, or none, draws the cells as line-height: normal on the cell under standard, as 1.33 in a positioned block that sets none, and as 1.2 under legacy.
  • CascadeOrderTest's uncontested documents and UniversalSelectorTest's reset compare standard with legacy. Their tables now set line-height: 1.2, the value legacy gives them.
    • Font sizes:
      • a table's % and em of a block, a list item and a cell;
      • the block's size inherited;
      • rem in a sized table, in a sized block, on a table and in a nested table;
      • near misses: a table in points, and a table in a block after the sized one.
    • th alignment:
      • right in a right-aligned block and in a right-aligned cell;
      • centred where nothing sets an alignment;
      • its own text-align winning.
  • Against Pass the inherited properties of rows and row groups to their cells #655's source:
    • 142 standard matrix cases fail, and every legacy case passes;
    • 17 cases of TableInheritsFromBlockTest fail, all standard or both-mode rem and alignment cases.

Snapshots

  • New table-inherits-from-block (standard), one case per caption:
    • a div's colour and font;
    • a list item's size and colour;
    • a table's 50% of a 20pt div;
    • 1rem in a 20pt table;
    • a div's line-height and text-align, with a th;
    • a cell's text-align and line-height reaching a nested table.
  • New table-in-blocks-article-standard and table-in-blocks-article-legacy: an article with a table in a dark callout, a table in a list item, a sized table in a right-aligned quotation, and a panel cell holding a nested table. The captions say what each mode draws.
  • No existing fixture changed; see above.

I rendered each fixture and looked at it.

Rebased after #647 to #655

The branch's two commits were rebased onto gravitypdf with git rebase --onto, dropping the old copies of #648 and #655.

Checks

  • composer test: OK. 11858 tests, 31 skipped for missing optional packages or ungenerated data. The snapshot suite ran with Imagick and Ghostscript, with no skips.
  • composer cs: clean.
  • PHPStan: no errors.
  • The changed files lint under PHP 5.6.

Rebased after #633 to #646

#648, #655 and this PR were rebased onto gravitypdf.

Gates: composer test 9454 tests, 31 skipped; composer cs clean; PHPStan no errors.

🤖 Generated with Claude Code

@jakejackson1
jakejackson1 force-pushed the fix/543-table-inherits-from-block branch from 309b808 to a381afc Compare September 30, 2026 10:38
@jakejackson1
jakejackson1 force-pushed the fix/542-row-properties-to-cells branch from d044058 to dcca660 Compare September 30, 2026 10:38
@jakejackson1
jakejackson1 force-pushed the fix/543-table-inherits-from-block branch from a381afc to b8c3609 Compare September 30, 2026 11:47
@jakejackson1
jakejackson1 force-pushed the fix/542-row-properties-to-cells branch from dcca660 to 090dfc9 Compare September 30, 2026 11:47
@jakejackson1
jakejackson1 force-pushed the fix/542-row-properties-to-cells branch 2 times, most recently from d7bcd60 to b31d661 Compare October 4, 2026 21:28
Base automatically changed from fix/542-row-properties-to-cells to gravitypdf October 4, 2026 21:43
jakejackson1 and others added 2 commits October 5, 2026 08:51
Under the standard cssMode, a table takes every inherited property from
the block, list item, header or positioned block it is in, and a nested
table takes its cell's line-height and text-align too. A table's
relative font size is taken of the block's, and rem in a table stays the
document's size. The built-in hyphens and font-kerning on tables are
dropped, and the built-in line-height of 1.2 stays only where the table
would inherit normal. Legacy output is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#642 reads rem against html's font size, which a table does not replace,
so this branch's own rem change and its changelog entry go. The snapshot
case shows 1rem as html's size, 11pt, and a new case sets html's size.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jakejackson1
jakejackson1 force-pushed the fix/543-table-inherits-from-block branch from b8c3609 to 61eb72f Compare October 4, 2026 21:52
gravitypdf-bot and others added 3 commits October 5, 2026 09:04
Under the standard cssMode, a table opened inside open inline elements, as in
<span class="note">…<table>…</table>…</span>, sets their state aside on the
enclosing block as #653 does for a block, so it inherits their colour, font,
size and weight, and a relative font size on it is taken of theirs. The text
after the table is drawn in their style again until their end tags. A table
nested in a cell keeps the cell's open inline elements, so an end tag after it
restores what was before. Legacy is unchanged.

The set-aside and put-back helpers move from BlockTag to Tag, and the put-back
restores the block's own state when nothing was set aside, reading it through
InheritedProperties::blockTextState().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… are

Under the standard cssMode a table no longer keeps the built-in
line-height: 1.2 where it would inherit normal, so a table in a block
that sets no line-height is drawn at normalLineheight, as a browser
draws it. The 92 standard-mode snapshot documents with such tables are
regenerated; no legacy document changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The snapshot documents with a table that would now inherit normal set
line-height: 1.2 on it, the value the table was drawn at before, so the
92 fixtures go back to what they were. Two documents that show a table
inheriting a block's line-height set it only on their other tables.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jakejackson1
jakejackson1 merged commit fbdfa66 into gravitypdf Oct 5, 2026
9 checks passed
@jakejackson1
jakejackson1 deleted the fix/543-table-inherits-from-block branch October 5, 2026 23:34
jakejackson1 pushed a commit that referenced this pull request Oct 5, 2026
…the fees table as its clause

Since #656 a table inherits the block it is in, so the decoration documents' tables took line-height: normal and
the amendment's fees table the inserted clause's green. The documents set line-height: 1.2 on their tables, as the
others do, and the standard amendment fixture is refreshed for the green.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
jakejackson1 added a commit that referenced this pull request Oct 5, 2026
…etting them inherit or undo them (#654)

* Draw an ancestor's text decorations over its descendants instead of letting them inherit or undo them

Under the standard cssMode an underline, overline or line-through propagates
to the element's descendants in the flow, as a set of its own kept by
Mpdf\Css\TextDecorations:

- each is drawn in the colour and at the size of the element that set it;
- text-decoration: none on a descendant removes only its own decoration, so
  a link set to none inside an underlined paragraph is still underlined;
- a descendant's own decoration is added to its ancestors';
- floats, positioned blocks and inline blocks start without their
  ancestors' decorations, and tables still do;
- an element whose color is transparent adds no decoration of its own.

A child block no longer takes its parent's vertical-align.

Under the legacy cssMode text-decoration and vertical-align are inherited as
in mPDF v7.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Carry decorations into a block opened inside an inline element, and leave a decoration unseen in inherited transparent

A block inside an underlined span now starts from the span's text state, which #653 sets aside, so it and the text after it are underlined. TextDecorations::seen() reads #651's transparent flag, so a descendant that inherits color: transparent adds no decoration of its own either.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Keep the decoration snapshots' tables at line-height 1.2, and colour the fees table as its clause

Since #656 a table inherits the block it is in, so the decoration documents' tables took line-height: normal and
the amendment's fees table the inserted clause's green. The documents set line-height: 1.2 on their tables, as the
others do, and the standard amendment fixture is refreshed for the green.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: gravitypdf-bot <support@gravitypdf.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

create-upstream-pr enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Let a table inherit from the block around it, not only from the document defaults

2 participants