Skip to content

feat(math): implement m:nary n-ary operator converter#2752

Merged
caio-pizzol merged 5 commits intosuperdoc-dev:mainfrom
Abdeltoto:feat/math-nary-operator-converter
Apr 13, 2026
Merged

feat(math): implement m:nary n-ary operator converter#2752
caio-pizzol merged 5 commits intosuperdoc-dev:mainfrom
Abdeltoto:feat/math-nary-operator-converter

Conversation

@Abdeltoto
Copy link
Copy Markdown
Contributor

Closes #2602

Summary

  • Implements the m:nary OMML-to-MathML converter for n-ary operators (integral, summation, product, etc.)
  • Supports both subSup (default) and undOvr limit locations, mapping to <msubsup>/<munderover> respectively
  • Handles m:subHide and m:supHide flags, falling back to <msub>/<msup> or bare <mo> as needed
  • Defaults to integral sign (U+222B) when m:chr is absent
  • Registers the converter in MATH_OBJECT_REGISTRY
  • Adds 4 unit tests covering integral, summation with undOvr, hidden limits, and sub-only

Spec reference

ECMA-376 Section 22.1.2.70

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: b02a1238f6

ℹ️ 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".

Comment thread packages/layout-engine/painters/dom/src/features/math/converters/nary.ts Outdated
@caio-pizzol caio-pizzol self-assigned this Apr 9, 2026
Abdeltoto and others added 5 commits April 13, 2026 14:58
… (SD-2381)

- m:limLoc (§22.1.2.53): absent element now uses operator-character
  heuristic — integrals default to subSup, non-integrals to undOvr.
  <m:limLoc/> with no val attribute defaults to undOvr per spec.
- m:sub/m:sup (§22.1.2.70): treat as actually absent when the element
  is missing, not just when a hide flag is set — indefinite integrals
  now render as bare <mo> instead of <msubsup> with empty <mrow/> slots.
- m:chr (§22.1.2.20): <m:chr/> with no val renders an empty operator
  instead of silently defaulting to the integral glyph.
- m:grow (§22.1.2.72): when explicitly OFF, emit largeop="false"
  stretchy="false" on the <mo> so the renderer doesn't enlarge.
- Helper type is OmmlJsonNode (no more typeof subHide). JSDoc documents
  all 6 output shapes.

Tests:
- 10 new unit tests in omml-to-mathml.test.ts covering every ECMA-376
  spec path for m:nary (subHide true/bare/OFF, limLoc no-val, chr no-val,
  operator heuristic, m:grow suppression, etc).
- New behavior fixture math-nary-tests.docx (13 scenarios) + 9 Playwright
  tests in math-equations.spec.ts. Fixture uploaded to the shared R2
  corpus as rendering/sd-2381-nary-scenarios.docx for layout/visual.
…(SD-2381)

Per ECMA-376 §22.1.2.72, the subHide/supHide flags control whether EMPTY
m:sub/m:sup limits are rendered as a placeholder character or hidden.
When the limit has content, it must always be rendered regardless of
the flag — matching Word's actual behavior.

Previous code hid the entire slot whenever the hide flag was ON, which
silently dropped content. For example, an integral with sub="0", sup="1",
and subHide=true was rendering as ∫¹ instead of ∫₀¹.

- hasSub/hasSup now check meaningful content (ignoring m:ctrlPr, the
  formatting-hint child Word emits inside empty limit elements).
- The hide flag only suppresses the slot when the limit is empty/absent.
- Updated 3 unit tests that encoded the incorrect semantics; added a new
  regression test for "content overrides hide" and a test confirming
  m:ctrlPr-only limits are treated as empty.
- Updated the behavior test for the same scenarios.
…ord)

Follow-up to the previous subHide/supHide fix. When m:subHide is ON and
m:sub has content, Word doesn't simply suppress the slot — it promotes
the sub content into the sup slot, prepended to any existing sup content
(symmetric for supHide: sup content appended to sub). This preserves any
author-entered content even when a file is hand-crafted with conflicting
hide+content OMML.

Before: an integral with m:sub="0", m:sup="1", m:subHide="true" rendered
as <msubsup><mo>∫</mo><mrow>0</mrow><mrow>1</mrow></msubsup> (∫ with 0
below and 1 above — stacked).

After: renders as <msup><mo>∫</mo><mrow>01</mrow></msup>, matching
Word's ∫^{01} where 0 appears to the left of 1 in the superscript.

- Compute renderSubChildren / renderSupChildren with promotion, then pass
  those arrays to convertChildren.
- Strip m:ctrlPr before the check so Word's "empty with formatting hint"
  pattern still resolves to bare <mo>.
- Replaced the "content always wins over hide" unit test with two tests
  anchoring the promotion (sub→sup, sup→sub).
- Updated the behavior test for scenarios 8/9 to assert msup with "01".
@caio-pizzol caio-pizzol force-pushed the feat/math-nary-operator-converter branch from ea6f827 to 7e02eca Compare April 13, 2026 22:04
@caio-pizzol caio-pizzol enabled auto-merge April 13, 2026 22:05
@caio-pizzol caio-pizzol added this pull request to the merge queue Apr 13, 2026
Merged via the queue into superdoc-dev:main with commit 02543d6 Apr 13, 2026
47 of 50 checks passed
@superdoc-bot
Copy link
Copy Markdown
Contributor

superdoc-bot Bot commented Apr 13, 2026

🎉 This PR is included in @superdoc-dev/react v1.2.0-next.7

The release is available on GitHub release

@superdoc-bot
Copy link
Copy Markdown
Contributor

superdoc-bot Bot commented Apr 13, 2026

🎉 This PR is included in vscode-ext v2.3.0-next.10

@superdoc-bot
Copy link
Copy Markdown
Contributor

superdoc-bot Bot commented Apr 13, 2026

🎉 This PR is included in esign v2.3.0-next.10

The release is available on GitHub release

@superdoc-bot
Copy link
Copy Markdown
Contributor

superdoc-bot Bot commented Apr 13, 2026

🎉 This PR is included in template-builder v1.5.0-next.10

The release is available on GitHub release

@superdoc-bot
Copy link
Copy Markdown
Contributor

superdoc-bot Bot commented Apr 13, 2026

🎉 This PR is included in superdoc-cli v0.7.0-next.11

The release is available on GitHub release

@superdoc-bot
Copy link
Copy Markdown
Contributor

superdoc-bot Bot commented Apr 13, 2026

🎉 This PR is included in superdoc v1.26.0-next.10

The release is available on GitHub release

@superdoc-bot
Copy link
Copy Markdown
Contributor

superdoc-bot Bot commented Apr 13, 2026

🎉 This PR is included in superdoc-cli v0.7.0-next.11

The release is available on GitHub release

@superdoc-bot
Copy link
Copy Markdown
Contributor

superdoc-bot Bot commented Apr 13, 2026

🎉 This PR is included in superdoc-sdk v1.6.0-next.8

@superdoc-bot
Copy link
Copy Markdown
Contributor

superdoc-bot Bot commented Apr 17, 2026

🎉 This PR is included in superdoc-cli v0.7.0

The release is available on GitHub release

@superdoc-bot
Copy link
Copy Markdown
Contributor

superdoc-bot Bot commented Apr 17, 2026

🎉 This PR is included in superdoc v1.27.0

The release is available on GitHub release

@superdoc-bot
Copy link
Copy Markdown
Contributor

superdoc-bot Bot commented Apr 21, 2026

🎉 This PR is included in @superdoc-dev/react v1.2.0

The release is available on GitHub release

@superdoc-bot
Copy link
Copy Markdown
Contributor

superdoc-bot Bot commented Apr 22, 2026

🎉 This PR is included in superdoc-sdk v1.6.0

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:nary n-ary operator converter (community)

3 participants