Skip to content

Conversation

@dpvc
Copy link
Member

@dpvc dpvc commented Jan 26, 2026

This PR adds support for fonts and font extensions to define delimiters that are specific to a mathvariant. This is needed for the mhchem extension to handle some of its stretchy arrows without needing to use the PUA for them, which causes problems for speech generation.

Resolves part of issue mathjax/MathJax#3486.


Details

This PR touches a large number of files, but many are because the VariantData type now is dependent on the DelimiterData type, so has an additional template variable. That means all the Wrapper definitions need to have that template variable added, and that is a large number of files.

The mhchem extension now uses the standard arrow positions (e.g., U+2192) rather than PUA positions, so the test file for mhchem needed to be updated.

The xArrow method in AmsMethods.ts file is modified to accept a mathvariant in which the arrow is to be found.

That is then used in the mhchem extension to specify that the arrows are to be taken from the -mhchem variant. The arrows are now in the usual Unicode positions rather than the PUA. The left-right harpoons use the newly proposed U+1F8D2 and U+1F8D3 positions.

The CHTML font data now stores the variant as part of the delimiter usage data (so that it can create the proper CSS), and uses the variant in the calls that get delimiters and stretchy variants. The mo wrapper records the variant when a stretchy delimiter is used. Similarly, the SVG mo wrapper passes the variant when it stretches a delimiter.

The main changes are on the common/FontData.ts file. Here, the VariantData now can include delimiters specific to the variant, and the DelimiterData can include a variant specification. A new VDelimiterMap type is used to specify delimiters for variants, and the FontExtensionData is extended to include that, which is now processed by the static and instance addExtension() functions.

The FontData object itself gets a new defaultVariantDelimiters object to hold any variant-specific delimiters, and these are now processed in the constructor() function. The createVariant() method now created a variant.delims object similar to the variant.chars one that has a prototype chain that links delimiters in the same way the characters are links to fallback variants (so, for example, bold has normal as a fallback). The normal delimiters use the original this.delimiters so that object is still the final fallback for all variants. The variant.linked value is now an array consisting of the linked chars and delims objects rather than just he chars. The defineChars() method now takes this array into account.

The defineDelimiters() method now takes a variant name, and sets the delim.v value for variants over than normal. It now assigns the delimiters to the variants delimiter list (which will be this.delimiters for normal variant), and processes linked variants in the same way that variant.chars are handled.

The getDelimiter() method now takes an optional variant name as an argument and looks up the delimiter from the variant. Again, note that the normal variant will have this.delimiters as its delimiters, and all variants have the normal variants as the final fallback. So in most cases, the variant-specific list will be empty and will fall back on the normal list (this.delimiters), and this will operate exactly as before. The only change will be when there are variant-specific delimiters that will be found first.

The getSizeVariant(), getStretchVariant(), and getStretchVariants() methods all now take an optional variant name and look up the delimiter from that variant.

@dpvc dpvc requested a review from zorkow January 26, 2026 14:00
@dpvc
Copy link
Member Author

dpvc commented Jan 26, 2026

This may want to wait for v4.2?

Also, to test it, you will need to get new versions of the fonts and font-tools repositories and rebuild the mathjax-mhchem font using the variant-delims branches of each. I am just putting this in place now.

@codecov
Copy link

codecov bot commented Jan 26, 2026

Codecov Report

❌ Patch coverage is 81.88406% with 50 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.48%. Comparing base (95faa0c) to head (e4a8e28).

Files with missing lines Patch % Lines
ts/output/common/FontData.ts 63.30% 40 Missing ⚠️
ts/output/chtml/FontData.ts 55.55% 4 Missing ⚠️
ts/output/common/Wrappers/mo.ts 50.00% 3 Missing ⚠️
ts/output/chtml/Wrappers/mo.ts 0.00% 2 Missing ⚠️
ts/output/svg/Wrappers/mo.ts 50.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #1424      +/-   ##
===========================================
- Coverage    86.51%   86.48%   -0.03%     
===========================================
  Files          340      340              
  Lines        85993    86088      +95     
  Branches      4825     4832       +7     
===========================================
+ Hits         74397    74457      +60     
- Misses       11596    11608      +12     
- Partials         0       23      +23     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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.

2 participants