Skip to content

[WebParagraph] Line metrics diverge from SkParagraph: kTextHeightNone, leadingDistribution default, StrutStyle, TextHeightBehavior, placeholders #193731

Open

Description

@Rusino

WebParagraph's line metrics diverge from SkParagraph (CanvasKit / Skwasm) in several independent ways. All of them are visible through Paragraph.height, Paragraph.alphabeticBaseline, computeLineMetrics(), getBoxesForRange() and getBoxesForPlaceholders(); the reference behavior is Run::calculateMetrics, InternalLineMetrics, TextWrapper::breakTextIntoLines, ParagraphImpl::computeEmptyMetrics and TextLine::getRectsForRange in modules/skparagraph.

1. kTextHeightNone is treated as a height multiplier of 0.0

  • ChildStyleNode._height maps TextStyle(height: kTextHeightNone) to null and then falls back to the parent's height, so a child span cannot opt out of a ParagraphStyle(height: 3.0).
  • LineBlock and the empty-paragraph branch of TextLayout.wrapText only check height != null, so ParagraphStyle(height: kTextHeightNone) multiplies the line metrics by 0.0.

Expected: kTextHeightNone disables the multiplier and the line is laid out at the font's own ascent + descent.

2. The default TextLeadingDistribution is even, and TextHeightBehavior.leadingDistribution is ignored

  • LineBlock treats leadingDistribution == null as even (half-leading). SkParagraph's default is proportional (fHalfLeading == false), i.e. ascent and descent are scaled by the same factor. The total line height is the same, but the baseline position differs for every paragraph that sets height without an explicit leadingDistribution.
  • WebParagraphStyle doesn't pass textHeightBehavior.leadingDistribution to its root text style and RootStyleNode._leadingDistribution always returns null, so TextHeightBehavior(leadingDistribution: even) has no effect.

3. StrutStyle does not affect line metrics

  • StrutStyle has no effect on line metrics: WebStrutStyle.calculateMetrics computes strutAscent / strutDescent / strutLeading, but their only consumer is getBoxesForRange(boxHeightStyle: BoxHeightStyle.strut). TextLine starts from 0 / 0 and only accumulates block metrics, so a strut never sets a minimum line height, forceStrutHeight is a no-op (for text and placeholders alike) and leading is lost. E.g. StrutStyle(fontFamily: 'Arial', fontSize: 20, height: 1.0, leading: 1.5, forceStrutHeight: true) + 'Hello' should give a 50px line (20 + 30 of leading); WebParagraph gives the font's natural height.
  • WebStrutStyle.calculateMetrics also bails out when fontSize is null (SkParagraph falls back to the default font size, 14), ignores the paragraph's TextHeightBehavior.leadingDistribution as a fallback (dart:ui resolves strutStyle.leadingDistribution ?? textHeightBehavior.leadingDistribution), and accepts a negative leading.
  • An empty paragraph ignores height, StrutStyle and TextHeightBehavior entirely: paragraph.height is just the height of the empty cluster, and both baselines are set to that height.

4. TextHeightBehavior.applyHeightToFirstAscent / applyHeightToLastDescent are ignored

Both flags are no-ops: the first line keeps its scaled ascent and the last line keeps its scaled descent. SkParagraph reverts them to the unscaled font metrics (and, after a trailing \n, it is the synthetic empty last line that loses its descent, not the text line before it).

5. Placeholder positioning and BoxHeightStyle.tight

  • In TextLayout.addLine a placeholder's advance.top = line.fontBoundingBoxAscent - block.ascent is computed in the same pass that grows the line ascent, so a taller placeholder later on the same line leaves the earlier one positioned against a stale ascent. getBoxesForRange and getBoxesForPlaceholders then disagree for the same placeholder.
  • getBoxesForRange(..., boxHeightStyle: tight): for a placeholder, line.fontBoundingBoxAscent - block.multipliedFontBoundingBoxAscent is added a second time on top of block.advance.top; for text, the box uses the scaled (multiplied*) metrics instead of the unscaled glyph bounds that TextLine::getRectsForRange returns.

Part of #172561.

Metadata

Metadata

Assignees

Labels

a: typographyText rendering, possibly libtxtengineflutter/engine related. See also e: labels.platform-webWeb applications specificallyteam-webOwned by Web platform team

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions