Fix inline mode rendering for large operators and fractions

This commit addresses three issues with math rendering:

  1. Large operator limits positioning (continued from previous commit)
  Modified makeLargeOp() and addLimitsToDisplay() to show limits above/below
  in both display and text (inline) modes:
    - Changed: op.limits && style == .display
    - To: op.limits && (style == .display || style == .text)

  This enables operators like \lim, \sum, and \prod to show subscripts/
  superscripts above and below even in inline mode \(...\), not just in
  display mode \[...\].

  2. Fraction font size issue
  Fixed fractions appearing too small in inline mode. Previously, fractions
  used one style level smaller than their parent (standard LaTeX behavior):
    - Display mode → fractions use text style (acceptable)
    - Text mode →

  Root cause:
  Inline delimiters \(...\) insert \textstyle, forcing text mode. In text
  mode, fractionStyle() returned style.inc(), making numerator/denominator
  use script style (two levels smaller than display). This made fraction
  numbers tiny compared to surrounding text in expressions like:
    \(\frac{a}{b} = c\) - a, b were script-sized while c was text-sized

  Solution:
  Modified fractionStyle() to return the SAME style instead of incrementing:
    func fractionStyle() -> MTLineStyle {
        return style  // Was: return style.inc()
    }

  This keeps fraction numerators/denominators at the same font size as
  regular text, preventing them from becoming too small. Spacing and
  positioning (numeratorShiftUp, etc.) still vary by parent style.

  3. Non-regression fixes
  Updated test expectations to match new fraction sizing behavior
This commit is contained in:
Nicolas Guillot
2025-11-17 10:12:45 +01:00
parent cb890fb787
commit cc1a7b8023
3 changed files with 187 additions and 18 deletions

View File

@@ -271,7 +271,11 @@ public struct MTMathListBuilder {
return nil
}
// Optionally: Add style hint for inline mode
// Note: For inline mode, we insert \textstyle to match LaTeX behavior.
// However, fractionStyle() has been modified to keep fractions at the
// same font size in both display and text modes (not one level smaller).
// Large operators show limits above/below in text style due to the updated
// condition in makeLargeOp() that checks both .display and .text styles.
if mode == .inline && list != nil && !list!.atoms.isEmpty {
// Prepend \textstyle to force inline rendering
let styleAtom = MTMathStyle(style: .text)

View File

@@ -1726,10 +1726,11 @@ class MTTypesetter {
}
func fractionStyle() -> MTLineStyle {
if style == .scriptOfScript {
return .scriptOfScript
}
return style.inc()
// Keep fractions at the same style level instead of incrementing.
// This ensures that fraction numerators/denominators have the same
// font size as regular text, preventing them from appearing too small
// in inline mode or when nested.
return style
}
func makeFraction(_ frac:MTFraction?) -> MTDisplay? {
@@ -2041,7 +2042,9 @@ class MTTypesetter {
// MARK: - Large Operators
func makeLargeOp(_ op:MTLargeOperator!) -> MTDisplay? {
let limits = op.limits && style == .display
// Show limits above/below in both display and text (inline) modes
// Only show limits to the side in script modes to keep them compact
let limits = op.limits && (style == .display || style == .text)
var delta = CGFloat(0)
if op.nucleus.count == 1 {
var glyph = self.findGlyphForCharacterAtIndex(op.nucleus.startIndex, inString:op.nucleus)
@@ -2086,7 +2089,8 @@ class MTTypesetter {
currentPosition.x += display!.width
return display;
}
if op.limits && style == .display {
// Show limits above/below in both display and text (inline) modes
if op.limits && (style == .display || style == .text) {
// make limits
var superScript:MTMathListDisplay? = nil, subScript:MTMathListDisplay? = nil
if op.superScript != nil {