Skip to content

Never fuse atoms across a font-style change - #269

Merged
kostub merged 2 commits into
masterfrom
feature/mathit-routing-pr1
Aug 4, 2026
Merged

Never fuse atoms across a font-style change#269
kostub merged 2 commits into
masterfrom
feature/mathit-routing-pr1

Conversation

@kostub

@kostub kostub commented Aug 3, 2026

Copy link
Copy Markdown
Owner

Plan: docs/plans/2026-08-04-mathit-text-italic-routing.md (PR 1 of 3, items 1-2)
LLD: docs/lld/2026-07-27-mathit-text-italic-routing.md

Goal

Both atom-merge sites decline to fuse atoms whose fontStyle differs, so a style set on part of a run survives to the typesetter.

fuse: keeps the first atom's properties, including its fontStyle, so an unguarded merge either drops a style or spreads it onto an unstyled atom. Neither site checked style, and they cover disjoint inputs: site A merges only Number atoms, site B merges Ordinary atoms after preprocessMathList: has rewritten variables and numbers. 1\mathit{2} is fixed only by A, f\mathit{x} only by B.

This independently fixes a live rendering bug: 1\mathbf{2} renders its 2 non-bold today, because site A's merge discards the bold before getBold can map the digit to U+1D7CE (LLD §2.6). Geometrically neutral for uniform-style input.

Commits

  1. [item 1] Guard digit fusion on fontStyle in MTMathList.finalized
  2. [item 2] Guard Rule-14 fusion on fontStyle in preprocessMathList

Full swift test green at each commit (451 tests).

Summary by CodeRabbit

  • Bug Fixes
    • Fixed math rendering so adjacent numbers and symbols with different font styles remain separate.
    • Prevented italic, bold, and mixed-style content from being incorrectly merged.
    • Continued combining adjacent content when it uses the same font style, preserving existing formatting behavior.
    • Improved consistency of font-style handling during math layout and rendering.

kostub added 2 commits August 4, 2026 03:19
fuse: keeps the first atom's fontStyle, so 1\mathit{2} silently dropped
the italic and \mathit{1}2 spread it onto a plain digit. Never fuse
across a font-style change; uniform-style runs still fuse.
f\mathit{x} fused into one Default atom and lost the italic on x. Rule
14 now merges ordinary atoms only within one font style, matching the
site A guard from item 1.
@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8e846eb6-645f-46bd-8a06-9759e0fbbc18

📥 Commits

Reviewing files that changed from the base of the PR and between 6d0cb6e and fdd18ab.

📒 Files selected for processing (4)
  • iosMath/lib/MTMathList.m
  • iosMath/render/internal/MTTypesetter.m
  • iosMathTests/MTMathListTest.m
  • iosMathTests/MTTypesetterTest.m

📝 Walkthrough

Walkthrough

The change prevents number and ordinary-atom fusion across different font styles. Uniformly styled atoms continue to fuse. Regression tests cover mixed, default, italic, bold, and unstyled inputs.

Changes

Font-style-aware atom fusion

Layer / File(s) Summary
Number finalization preserves font styles
iosMath/lib/MTMathList.m, iosMathTests/MTMathListTest.m
Number fusion now requires matching fontStyle values. Tests verify mixed-style runs remain separate and uniform-style runs still fuse.
Typesetter preprocessing preserves font styles
iosMath/render/internal/MTTypesetter.m, iosMathTests/MTTypesetterTest.m
Ordinary-atom fusion now requires matching fontStyle values. Tests cover style boundaries, bold digit conversion, uniform fusion, and unstyled lists.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

  • kostub/iosMath#267: Both PRs modify atom fusion and preprocessing, but address different behaviors.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: preventing atom fusion across font-style changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/mathit-routing-pr1

Comment @coderabbitai help to get the list of available commands.

@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@kostub kostub left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review — PR 1 of 3 (fontStyle guard at both fusion sites)

No blocking findings. The diff is two extra conjuncts and their tests; I could not find a defect, and there is no machinery here to collapse.

What I verified

  • Both guards are load-bearing; neither is redundant. Site A fuses before changeFont runs, so with only the site B guard 1\mathbf{2} still arrives at preprocessMathList: as a single Default Number and the bold 2 is lost. With only the site A guard, the two resulting Ordinary atoms re-fuse at site B. Reverting either source hunk fails testPreprocessDoesNotFuseAcrossFontStyles.
  • The new tests genuinely fail without the change. Reverted MTMathList.m + MTTypesetter.m to master on top of the PR's test files: testFinalizedDoesNotFuseDigitsAcrossFontStyles and testPreprocessDoesNotFuseAcrossFontStyles fail; the three "still fuses" / "unchanged" methods pass, so they work as regression pins.
  • grep confirms fuse: has exactly two call sites (MTMathList.m:1712, MTTypesetter.m:557) — no third merge path is left unguarded.
  • Geometrically neutral, including for the newly-split mixed-style case. Splitting an Ord run into two Ord atoms adds nothing: getInterElementSpaces()[0][0] is kMTSpaceNone, so createDisplayAtoms adds no kern, and both nuclei are appended to the same _currentLine before a single addDisplayLine, so the CTLine string is byte-identical to the fused one. The superscript path is also unaffected — the italic correction is taken from the last character of the nucleus, which is the same character either way, and !prevNode.subScript && !prevNode.superScript already blocked fusing into a scripted atom.
  • _currentAtoms / index-range bookkeeping is unchanged, because a fused atom already contributed its _fusedAtoms copies rather than itself.
  • The \mathbf digit fix does not hit a font-coverage hole. U+1D7CE/U+1D7D0 are present in all eight bundled .otfs (checked via fontTools cmap), so the newly-preserved bold digit resolves in every bundled font.
  • Full swift test at fdd18ab: 451 tests, 0 failures.

Notes, not requests

  • The test-only @interface MTTypesetter (FusionTesting) category is the right call over adding an MTTypesetter+Internal.h: it exposes nothing outside the test binary and costs three lines.
  • \mathsf digits map to U+1D7E2, which firamath.otf lacks (the only bundled font that does). This PR widens the reach of that pre-existing gap from \mathsf{2} to 1\mathsf{2}, but the gap and its CoreText-substitution behaviour are unchanged by this diff — flagging only so it isn't a surprise later.

Not approving — leaving the merge call to you.

@kostub

kostub commented Aug 4, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@kostub
kostub merged commit 76fd773 into master Aug 4, 2026
2 checks passed
@kostub
kostub deleted the feature/mathit-routing-pr1 branch August 20, 2026 07: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.

1 participant