Skip to content

release: 1.13.0 — character-range highlight geometry (#26) + line-breaking fixes - #27

Merged
vietnguyentuan2019 merged 15 commits into
mainfrom
release/1.13.0
Oct 10, 2026
Merged

vietnguyentuan2019 merged 15 commits into
mainfrom
release/1.13.0

Conversation

@vietnguyentuan2019

@vietnguyentuan2019 vietnguyentuan2019 commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Release 1.13.0 of hyper_render_core and hyper_render. Not published yet.

Contents

#26 by @DrkXo (already merged into main)

  • New RenderHyperBox.getBoxesForCharRange(charStart, charEnd) and RenderHyperBox.debugLineFragments().
  • text-overflow: ellipsis: the truncated line fragment records ellipsisVisibleLength, so the … glyph is no longer counted as a source character.

#29 by @DrkXo (merged into this branch; fixes #28)

  • Eliminates the O(N²) line-breaking bottleneck on massive unspaced text: a 14,000-character CJK paragraph no longer takes ~13 s. On my machine 16,000 unspaced characters went from 4.7 s (CJK) / 1.9 s (Latin) to ~0.07 s / ~0.05 s.
  • Also fixes text-overflow: ellipsis on the first block of a document (it wrapped instead of truncating).
  • Follow-up on this branch: a floated block's unmeasured text was being measured, adding a phantom 8px line height (flagship_execution_audit_test). Narrowed the re-measure condition and added regression tests.
  • Behavior change: CJK text now fills the line after a space between runs, as a browser does. cjk_kinsoku.png golden regenerated.

Line-breaking fixes (found while reviewing #29; rendering changes)

  • Wrapped lines no longer overflow the box: the break point came from the nearest caret, so a line could run up to half a glyph past the edge. Every full CJK line did.
  • RTL paragraphs wrap. The breaker read an RTL painter from its logical end, so an Arabic paragraph put most of its text on line one and then one glyph per line.
  • word-break / overflow-wrap are inherited, so word-break: break-all on a <p> now reaches its text. It had no effect before.
  • A word wider than the line breaks after as many characters as fit, instead of after its first letter.
  • A word that doesn't fit beside a float moves below it, instead of being split as "c" / "onsectetur".
  • Spaces at the start and end of a line collapse (CSS Text 3 §4.1.2). Lines after <br> or an indented tag no longer start one space in, and centered / right-aligned lines ignore trailing spaces.

Goldens

15 goldens were regenerated on CI (Update Goldens (manual), Flutter 3.41.5). I reviewed every one side by side with its previous version. All changes come from the fixes above: RTL lines are flush right instead of one glyph per line, the stray one-space indents are gone, and lines next to floats and in mixed inline text fill to the edge. The previous rtl_arabic.png / rtl_hebrew.png had captured the RTL bug.

Tests

Versions

  • hyper_render_core 1.13.0
  • hyper_render 1.13.0 (requires hyper_render_core: ^1.13.0)
  • Other packages unchanged.

Character-range highlight geometry from #26 (by @DrkXo):
RenderHyperBox.getBoxesForCharRange and debugLineFragments, plus the
ellipsisVisibleLength fix on truncated line fragments. hyper_render requires
hyper_render_core ^1.13.0. The other packages are unchanged and need no
release. CHANGELOG headings, README, MIGRATION_GUIDE, CSS matrix and roadmaps
updated.
@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

⚠️ Layout Regression — Budget exceeded (informational)

Fixture Budget (ms) Median (ms) P95 (ms)
❌ simple_paragraph 8 17 19
❌ mixed_inline 10 15 16
❌ float_layout 12 16 35
❌ table_20_rows 14 45 73
✅ cjk_ruby 14 12 13
❌ large_article 16 40 53

One or more fixtures exceeded the 16 ms budget.

Flutter 3.41.5 · ubuntu-22.04

Informational, does not block merge: CI runners use software
rendering and are 2-3x slower than the devices the budgets are
calibrated for. Compare against the base branch; if a fixture
regressed relative to it, profile with:

flutter test benchmark/layout_regression.dart --reporter expanded

and check _performLineLayout / _buildCharacterMapping for
any new O(N²) or O(N log N) paths introduced in this PR.

Both properties inherit in CSS, but the resolver never copied them to child
nodes. Line breaking reads them from the text node's own style, so
`<p style="word-break:break-all">` had no effect at all.
…er one letter

Found while reviewing #29 by dumping debugLineFragments() on a 300px box and
checking each line's real width.

- Lines ran up to half a glyph past the box. getPositionForOffset returns the
  nearest caret, not the last one that fits; every full CJK line was 304px in
  a 300px box. The new _fitPrefixLength helper corrects the probe against
  real prefix widths.
- RTL paragraphs did not wrap. The x = maxWidth probe read an RTL painter
  from its logical end, so an Arabic paragraph put 72 chars (1,152px) on line
  one, then one glyph per line. The probe is mirrored for RTL.
- A word wider than the line was split after its first letter, leaving a
  column of one-letter lines. It now breaks after as many characters as fit,
  as Flutter's Text does.
- A word that does not fit beside a float was split the same way ("c" /
  "onsectetur"). The line now moves below the float, as CSS line boxes do.
- Force-split lines kept their trailing space, so centered and right-aligned
  lines were off by a space. Trailing collapsible spaces are now trimmed, as
  _splitTextFragment already did. Copying is unaffected; it reads the
  unsplit fragments.
- word-break: break-all fills the line by character instead of first looking
  for a space.

_prefixWidth reads the caret offset: summing getBoxesForSelection(0, k) is
O(k) per call and slowed long paragraphs.
@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

⚠️ Layout Regression — Budget exceeded (informational)

Fixture Budget (ms) Median (ms) P95 (ms)
❌ simple_paragraph 8 17 26
❌ mixed_inline 10 16 17
❌ float_layout 12 16 31
❌ table_20_rows 14 44 64
✅ cjk_ruby 14 10 12
❌ large_article 16 42 61

One or more fixtures exceeded the 16 ms budget.

Flutter 3.41.5 · ubuntu-22.04

Informational, does not block merge: CI runners use software
rendering and are 2-3x slower than the devices the budgets are
calibrated for. Compare against the base branch; if a fixture
regressed relative to it, profile with:

flutter test benchmark/layout_regression.dart --reporter expanded

and check _performLineLayout / _buildCharacterMapping for
any new O(N²) or O(N log N) paths introduced in this PR.

@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

❌ Visual Regression Detected

6 golden test(s) failed on Flutter 3.41.5 / ubuntu-22.04.

The rendered output no longer matches the reference images.

If the change is intentional, regenerate the goldens on the
same platform via the Update Goldens workflow dispatch, or run
locally in Docker:

docker run --rm \
  -v $(pwd):/workspace -w /workspace \
  ghcr.io/cirruslabs/flutter:3.41.5 \
  bash -c "apt-get update -qq && \
    apt-get install -y fonts-noto fonts-noto-cjk fonts-roboto && \
    flutter pub get && \
    flutter test test/golden/ --update-goldens"
git add test/golden/goldens/
git commit -m "chore: update golden references (Flutter 3.41.5)"

⚠️ Always regenerate goldens on ubuntu-22.04 with Flutter
3.41.5
to keep references pixel-stable across machines.

CSS Text 3 removes collapsible spaces at the start of a line and hangs them
at the end. HyperRenderWidget kept both: a line after `<p>` + newline, after
`<br>`, or after an indented `<dt>` started one space in, and the trailing
space of a line's last fragment counted toward the width that
`text-align: center/right` positions, so right-aligned text (all the RTL
goldens) ended one space short of the edge.
@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

⚠️ Layout Regression — Budget exceeded (informational)

Fixture Budget (ms) Median (ms) P95 (ms)
❌ simple_paragraph 8 16 30
❌ mixed_inline 10 16 18
❌ float_layout 12 16 31
❌ table_20_rows 14 34 57
✅ cjk_ruby 14 11 14
❌ large_article 16 36 60

One or more fixtures exceeded the 16 ms budget.

Flutter 3.41.5 · ubuntu-22.04

Informational, does not block merge: CI runners use software
rendering and are 2-3x slower than the devices the budgets are
calibrated for. Compare against the base branch; if a fixture
regressed relative to it, profile with:

flutter test benchmark/layout_regression.dart --reporter expanded

and check _performLineLayout / _buildCharacterMapping for
any new O(N²) or O(N log N) paths introduced in this PR.

@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

❌ Visual Regression Detected

10 golden test(s) failed on Flutter 3.41.5 / ubuntu-22.04.

The rendered output no longer matches the reference images.

If the change is intentional, regenerate the goldens on the
same platform via the Update Goldens workflow dispatch, or run
locally in Docker:

docker run --rm \
  -v $(pwd):/workspace -w /workspace \
  ghcr.io/cirruslabs/flutter:3.41.5 \
  bash -c "apt-get update -qq && \
    apt-get install -y fonts-noto fonts-noto-cjk fonts-roboto && \
    flutter pub get && \
    flutter test test/golden/ --update-goldens"
git add test/golden/goldens/
git commit -m "chore: update golden references (Flutter 3.41.5)"

⚠️ Always regenerate goldens on ubuntu-22.04 with Flutter
3.41.5
to keep references pixel-stable across machines.

github-actions Bot and others added 2 commits October 8, 2026 13:58
When a whole fragment did not fit and was moved to the next line, it was
placed there without going through the line-start rules, so " inside" after a
wrapped `<b>` started one space in. The fragment is now processed again on the
fresh line.
@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

⚠️ Layout Regression — Budget exceeded (informational)

Fixture Budget (ms) Median (ms) P95 (ms)
❌ simple_paragraph 8 10 21
✅ mixed_inline 10 8 10
✅ float_layout 12 10 17
❌ table_20_rows 14 23 40
✅ cjk_ruby 14 6 7
❌ large_article 16 28 62

One or more fixtures exceeded the 16 ms budget.

Flutter 3.41.5 · ubuntu-22.04

Informational, does not block merge: CI runners use software
rendering and are 2-3x slower than the devices the budgets are
calibrated for. Compare against the base branch; if a fixture
regressed relative to it, profile with:

flutter test benchmark/layout_regression.dart --reporter expanded

and check _performLineLayout / _buildCharacterMapping for
any new O(N²) or O(N log N) paths introduced in this PR.

@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

❌ Visual Regression Detected

3 golden test(s) failed on Flutter 3.41.5 / ubuntu-22.04.

The rendered output no longer matches the reference images.

If the change is intentional, regenerate the goldens on the
same platform via the Update Goldens workflow dispatch, or run
locally in Docker:

docker run --rm \
  -v $(pwd):/workspace -w /workspace \
  ghcr.io/cirruslabs/flutter:3.41.5 \
  bash -c "apt-get update -qq && \
    apt-get install -y fonts-noto fonts-noto-cjk fonts-roboto && \
    flutter pub get && \
    flutter test test/golden/ --update-goldens"
git add test/golden/goldens/
git commit -m "chore: update golden references (Flutter 3.41.5)"

⚠️ Always regenerate goldens on ubuntu-22.04 with Flutter
3.41.5
to keep references pixel-stable across machines.

@vietnguyentuan2019 vietnguyentuan2019 changed the title release: 1.13.0 — character-range highlight geometry (#26) release: 1.13.0 — character-range highlight geometry (#26) + line-breaking fixes Oct 8, 2026
@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

⚠️ Layout Regression — Budget exceeded (informational)

Fixture Budget (ms) Median (ms) P95 (ms)
❌ simple_paragraph 8 19 26
❌ mixed_inline 10 15 16
❌ float_layout 12 16 28
❌ table_20_rows 14 52 69
✅ cjk_ruby 14 12 16
❌ large_article 16 47 69

One or more fixtures exceeded the 16 ms budget.

Flutter 3.41.5 · ubuntu-22.04

Informational, does not block merge: CI runners use software
rendering and are 2-3x slower than the devices the budgets are
calibrated for. Compare against the base branch; if a fixture
regressed relative to it, profile with:

flutter test benchmark/layout_regression.dart --reporter expanded

and check _performLineLayout / _buildCharacterMapping for
any new O(N²) or O(N log N) paths introduced in this PR.

DrkXo and others added 3 commits October 9, 2026 00:58
…ed CJK text

Resolves severe UI freezes and layout slowness when rendering documents containing long paragraphs without whitespace (e.g. CJK EPUBs).

- Add single-pass native multi-line layout fast path via ICU LineMetrics when no floats encroach on available width.
- Bound candidate prefix measurement in fallback line-breaker loop to eliminate quadratic HarfBuzz text shaping overhead.
- Ensure loop termination on non-positive line widths by clearing floats or flooring to 1.0.
- Exclude trailing whitespace in caret measurement to match native LineMetrics.
- Add deterministic parity, intrinsic width, and call-counting tests in test/line_breaking_perf_and_parity_test.dart.
… to #29)

#29 re-measured every fragment with `measuredSize == null` right before it is
placed. A floated block's own text arrives unmeasured (a known gap), so it got
a real height and the first line grew from 16px to 24px; this broke
flagship_execution_audit_test. Only an oversized tail (estimated infinite
width) needs measuring there.

Also adds regression tests for the first-block `text-overflow: ellipsis` fix
from #29 (parsed HTML, no preceding block) and for the float case, and notes
the CJK line-filling behavior change in the core changelog.
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Layout Regression — Budget exceeded (informational)

Fixture Budget (ms) Median (ms) P95 (ms)
❌ simple_paragraph 8 11 18
❌ mixed_inline 10 12 14
❌ float_layout 12 13 17
❌ table_20_rows 14 31 40
✅ cjk_ruby 14 8 11
❌ large_article 16 39 73

One or more fixtures exceeded the 16 ms budget.

Flutter 3.41.5 · ubuntu-22.04

Informational, does not block merge: CI runners use software
rendering and are 2-3x slower than the devices the budgets are
calibrated for. Compare against the base branch; if a fixture
regressed relative to it, profile with:

flutter test benchmark/layout_regression.dart --reporter expanded

and check _performLineLayout / _buildCharacterMapping for
any new O(N²) or O(N log N) paths introduced in this PR.

@github-actions

Copy link
Copy Markdown
Contributor

❌ Visual Regression Detected

1 golden test(s) failed on Flutter 3.41.5 / ubuntu-22.04.

The rendered output no longer matches the reference images.

If the change is intentional, regenerate the goldens on the
same platform via the Update Goldens workflow dispatch, or run
locally in Docker:

docker run --rm \
  -v $(pwd):/workspace -w /workspace \
  ghcr.io/cirruslabs/flutter:3.41.5 \
  bash -c "apt-get update -qq && \
    apt-get install -y fonts-noto fonts-noto-cjk fonts-roboto && \
    flutter pub get && \
    flutter test test/golden/ --update-goldens"
git add test/golden/goldens/
git commit -m "chore: update golden references (Flutter 3.41.5)"

⚠️ Always regenerate goldens on ubuntu-22.04 with Flutter
3.41.5
to keep references pixel-stable across machines.

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Layout Regression — Budget exceeded (informational)

Fixture Budget (ms) Median (ms) P95 (ms)
❌ simple_paragraph 8 18 25
❌ mixed_inline 10 17 20
❌ float_layout 12 21 28
❌ table_20_rows 14 52 62
✅ cjk_ruby 14 12 14
❌ large_article 16 55 89

One or more fixtures exceeded the 16 ms budget.

Flutter 3.41.5 · ubuntu-22.04

Informational, does not block merge: CI runners use software
rendering and are 2-3x slower than the devices the budgets are
calibrated for. Compare against the base branch; if a fixture
regressed relative to it, profile with:

flutter test benchmark/layout_regression.dart --reporter expanded

and check _performLineLayout / _buildCharacterMapping for
any new O(N²) or O(N log N) paths introduced in this PR.

@vietnguyentuan2019
vietnguyentuan2019 merged commit ef062d9 into main Oct 10, 2026
9 checks passed
@vietnguyentuan2019
vietnguyentuan2019 deleted the release/1.13.0 branch October 10, 2026 09:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug / Perf]: Line-breaking bottleneck on massive unspaced text blocks (CJK / Gutenberg EPUBs)

2 participants