Skip to content

feat(math): implement m:groupChr group-character converter#2751

Open
Abdeltoto wants to merge 2 commits intosuperdoc-dev:mainfrom
Abdeltoto:feat/math-group-character-converter
Open

feat(math): implement m:groupChr group-character converter#2751
Abdeltoto wants to merge 2 commits intosuperdoc-dev:mainfrom
Abdeltoto:feat/math-group-character-converter

Conversation

@Abdeltoto
Copy link
Copy Markdown

Closes #2606

Summary

  • Implements the m:groupChr OMML-to-MathML converter (overbrace, underbrace, and other stretchy group characters)
  • Maps to <munder> (default, bottom position) or <mover> (top position) with a stretchy <mo> character
  • Defaults to U+23DF (bottom curly bracket) when m:chr is absent, matching Word behavior
  • Registers the converter in MATH_OBJECT_REGISTRY
  • Adds 2 unit tests covering default underbrace and custom top overbrace

Spec reference

ECMA-376 Section 22.1.2.41

Test plan

  • vitest run passes for omml-to-mathml.test.ts
  • Linter and formatter pass (lefthook pre-commit)

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0be6b2d3d6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

'm:d': null, // Delimiter (parentheses, brackets, braces)
'm:eqArr': null, // Equation array (vertical array of equations)
'm:groupChr': null, // Group character (overbrace, underbrace)
'm:groupChr': convertGroupCharacter, // Group character (overbrace, underbrace)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Update math height estimation for m:groupChr runs

Enabling m:groupChr conversion here changes output from a flat fallback to real <munder>/<mover> layout, but run sizing still comes from estimateMathDimensions() (packages/layout-engine/pm-adapter/src/converters/math-constants.ts), where VERTICAL_ELEMENTS does not include m:groupChr. The DOM measurer then uses run.height for line height budgeting (packages/layout-engine/measuring/dom/src/index.ts, math run branch via maxImageHeight), so under/over-brace formulas can render taller than allocated space and visually collide with neighboring lines. This registration should be paired with a m:groupChr height multiplier update (and a sizing test) to avoid layout regressions.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

@caio-pizzol caio-pizzol left a comment

Choose a reason for hiding this comment

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

@Abdeltoto solid contribution, tested all the common cases against Word and they match. one small edge case on the character fallback — left an inline comment.

const chr = groupChrPr?.elements?.find((e) => e.name === 'm:chr');
const pos = groupChrPr?.elements?.find((e) => e.name === 'm:pos');

const groupChar = chr?.attributes?.['m:val'] ?? DEFAULT_GROUP_CHAR;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

when <m:chr/> exists but has no value, the spec says the character should be hidden — Word shows nothing. right now this falls back to the default curly bracket instead. doesn't happen with Word-generated files (Word always writes an explicit value), but worth handling correctly.

Suggested change
const groupChar = chr?.attributes?.['m:val'] ?? DEFAULT_GROUP_CHAR;
const groupChar = chr ? (chr.attributes?.['m:val'] ?? '') : DEFAULT_GROUP_CHAR;

* Convert m:groupChr (group character) to MathML <munder> or <mover>.
*
* OMML structure:
* m:groupChr → m:groupChrPr (optional: m:chr@m:val, m:pos@m:val, m:vertJc@m:val), m:e
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

the spec also has a m:vertJc property that controls where the baseline sits — Word renders it differently for each combination. not blocking, just something to keep in mind for later.

@caio-pizzol caio-pizzol self-assigned this Apr 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Math: implement m:groupChr group character converter (community)

2 participants