From ebb1deac373aad08e14492f97f0d8ebaff830250 Mon Sep 17 00:00:00 2001 From: Kostub D Date: Tue, 4 Aug 2026 04:40:00 +0530 Subject: [PATCH 1/5] [item 5] Keep routable characters plain ASCII under \mathit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit styleCharacter's italic case no longer remaps Latin letters, digits, and capital Greek into the math-italic Unicode block. Those are TeX's class-7 mathchars (LLD 2026-07-27 mathit-text-italic-routing.md §2.1) and now stay plain code points so they can be drawn from a text-italic companion face in the next item; getItalicized still handles lowercase Greek and Greek symbol variants, which the companion face cannot serve (LLD §2.3). --- iosMath/render/internal/MTTypesetter.m | 14 +++++++++++ iosMathTests/MTTypesetterTest.m | 33 ++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/iosMath/render/internal/MTTypesetter.m b/iosMath/render/internal/MTTypesetter.m index 1b3f831..2923f57 100644 --- a/iosMath/render/internal/MTTypesetter.m +++ b/iosMath/render/internal/MTTypesetter.m @@ -382,6 +382,17 @@ UTF32Char getBlackboard(unichar ch) { return getDefaultStyle(ch); } +// \mathit draws these from the text-italic companion face with their plain +// code points, instead of remapping them into the math-italic block. The set +// is TeX's class-7 mathchars: Latin letters, digits, capital Greek. +static BOOL MTIsMathItalicRoutable(unichar ch) +{ + // U+03A2 is unassigned — the Greek capital block holds 24 letters, not 25. + BOOL capitalGreek = (ch >= kMTUnicodeGreekCapitalStart && + ch <= kMTUnicodeGreekCapitalEnd && ch != 0x03A2); + return IS_UPPER_EN(ch) || IS_LOWER_EN(ch) || IS_NUMBER(ch) || capitalGreek; +} + static UTF32Char styleCharacter(unichar ch, MTFontStyle fontStyle) { switch (fontStyle) { @@ -395,6 +406,9 @@ static UTF32Char styleCharacter(unichar ch, MTFontStyle fontStyle) return getBold(ch); case kMTFontStyleItalic: + if (MTIsMathItalicRoutable(ch)) { + return ch; + } return getItalicized(ch); case kMTFontStyleBoldItalic: diff --git a/iosMathTests/MTTypesetterTest.m b/iosMathTests/MTTypesetterTest.m index dc46935..498983f 100644 --- a/iosMathTests/MTTypesetterTest.m +++ b/iosMathTests/MTTypesetterTest.m @@ -3661,4 +3661,37 @@ - (void) testPreprocessUnstyledListUnchanged XCTAssertEqualObjects(atoms[2].nucleus, @"1"); } +// LLD §7 mapping table: routable characters keep plain code points under +// \mathit; everything else is unchanged from today. +- (void) testMathitKeepsRoutableCharactersPlain +{ + NSDictionary* cases = @{ + @"\\mathit{f}": @"f", + @"\\mathit{h}": @"h", // not U+210E: the companion has its own h + @"\\mathit{1}": @"1", + @"\\mathit{\\Gamma}": @"Γ", // routed, not math-italic U+1D6E4 + @"\\mathit{\\alpha}": @"\U0001D6FC", // unrouted, identical to \alpha + @"\\mathit{\\varepsilon}": @"\U0001D700", // Greek symbol variants unrouted + @"\\Gamma": @"Γ", // no \mathit: still upright + // one atom mixing routable and unrouted characters (LLD §3.4) + @"\\mathit{a\\theta b}": @"a\U0001D703b", + }; + for (NSString* latex in cases) { + NSArray* atoms = [MTTypesetter preprocessMathList: + [MTMathListBuilder buildFromString:latex].finalized]; + XCTAssertEqual(atoms.count, 1, @"%@", latex); + XCTAssertEqualObjects(atoms[0].nucleus, cases[latex], @"%@", latex); + } +} + +// setFont: re-typesets the same retained (mutated) list; the mapping must be +// idempotent (LLD §6 re-entrancy). +- (void) testMathitRetypesetIsIdempotent +{ + MTMathList* list = [MTMathListBuilder buildFromString:@"\\mathit{f1\\Gamma}x"].finalized; + MTMathListDisplay* first = [MTTypesetter createLineForMathList:list font:self.font style:kMTLineStyleDisplay]; + MTMathListDisplay* second = [MTTypesetter createLineForMathList:list font:self.font style:kMTLineStyleDisplay]; + XCTAssertEqualWithAccuracy(first.width, second.width, 0.001); +} + @end From d04e90fccd80ee9c2910c0ebec3174199e5afe1d Mon Sep 17 00:00:00 2001 From: Kostub D Date: Tue, 4 Aug 2026 04:41:31 +0530 Subject: [PATCH 2/5] [item 6] Draw \mathit routable runs with the companion face MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every nucleus append now stamps _styleFont.ctFont over its own range, then applyMathitFontToRoutableCharactersInRange:forAtom: overrides maximal runs of routable characters within an Italic atom with _styleFont.mathitCTFont. The whole-line font stamp in addDisplayLine is deleted since every character already carries a font by the time flush runs, so nothing can silently overwrite a \mathit range (LLD §3.3 C-D). --- iosMath/render/internal/MTTypesetter.m | 33 +++++++++- iosMathTests/MTTypesetterTest.m | 83 ++++++++++++++++++++++++++ 2 files changed, 114 insertions(+), 2 deletions(-) diff --git a/iosMath/render/internal/MTTypesetter.m b/iosMath/render/internal/MTTypesetter.m index 2923f57..8703c5e 100644 --- a/iosMath/render/internal/MTTypesetter.m +++ b/iosMath/render/internal/MTTypesetter.m @@ -1010,7 +1010,14 @@ - (void) createDisplayAtoms:(NSArray*) preprocessed } else { current = [[NSAttributedString alloc] initWithString:atom.nucleus]; } + NSRange appendedRange = NSMakeRange(_currentLine.length, current.length); [_currentLine appendAttributedString:current]; + [_currentLine addAttribute:(NSString*) kCTFontAttributeName + value:(__bridge id) (_styleFont.ctFont) + range:appendedRange]; + if (atom.fontStyle == kMTFontStyleItalic) { + [self applyMathitFontToRoutableCharactersInRange:appendedRange forAtom:atom]; + } // add the atom to the current range if (_currentLineIndexRange.location == NSNotFound) { _currentLineIndexRange = atom.indexRange; @@ -1058,10 +1065,32 @@ - (void) createDisplayAtoms:(NSArray*) preprocessed } } +// Gives maximal runs of routable characters the \mathit companion face. +// Evaluated per character, not per atom: fusion merges a whole \mathit group +// into one atom, which can mix routable and non-routable characters +// (\mathit{a\theta b} — theta must stay in the math font, LLD §3.3 C). +- (void) applyMathitFontToRoutableCharactersInRange:(NSRange) range forAtom:(MTMathAtom*) atom +{ + NSString* string = _currentLine.string; + NSUInteger runStart = NSNotFound; + for (NSUInteger i = range.location; i <= NSMaxRange(range); i++) { + BOOL routable = (i < NSMaxRange(range)) && MTIsMathItalicRoutable([string characterAtIndex:i]); + if (routable) { + NSAssert(atom.type == kMTMathAtomOrdinary, @"Routable character in non-Ordinary atom: %@", atom); + if (runStart == NSNotFound) { + runStart = i; + } + } else if (runStart != NSNotFound) { + [_currentLine addAttribute:(NSString*) kCTFontAttributeName + value:(__bridge id) (_styleFont.mathitCTFont) + range:NSMakeRange(runStart, i - runStart)]; + runStart = NSNotFound; + } + } +} + - (MTCTLineDisplay*) addDisplayLine { - // add the font - [_currentLine addAttribute:(NSString *)kCTFontAttributeName value:(__bridge id)(_styleFont.ctFont) range:NSMakeRange(0, _currentLine.length)]; /*NSAssert(_currentLineIndexRange.length == numCodePoints(_currentLine.string), @"The length of the current line: %@ does not match the length of the range (%d, %d)", _currentLine, _currentLineIndexRange.location, _currentLineIndexRange.length);*/ diff --git a/iosMathTests/MTTypesetterTest.m b/iosMathTests/MTTypesetterTest.m index 498983f..9a0ccba 100644 --- a/iosMathTests/MTTypesetterTest.m +++ b/iosMathTests/MTTypesetterTest.m @@ -3694,4 +3694,87 @@ - (void) testMathitRetypesetIsIdempotent XCTAssertEqualWithAccuracy(first.width, second.width, 0.001); } +// Collects (range, PostScript name) for every kCTFontAttributeName run and +// asserts no character is left without a font. +- (NSArray*) fontRunsOfLine:(MTCTLineDisplay*) line +{ + NSMutableArray* runs = [NSMutableArray array]; + NSAttributedString* str = line.attributedString; + [str enumerateAttribute:(NSString*) kCTFontAttributeName + inRange:NSMakeRange(0, str.length) + options:0 + usingBlock:^(id value, NSRange range, BOOL* stop) { + XCTAssertNotNil(value, @"unstamped range %@ in %@", NSStringFromRange(range), str.string); + NSString* ps = CFBridgingRelease(CTFontCopyPostScriptName((__bridge CTFontRef) value)); + [runs addObject:@[ [NSValue valueWithRange:range], ps ]]; + }]; + return runs; +} + +- (void) testMathitStampsCompanionFontPerRange +{ + // \mathit{f}x: one CTLine, two font runs — companion on {0,1}, math font + // on the U+1D465 surrogate pair. This is the test that catches a missed + // addDisplayLine deletion, which a width check cannot (LLD §5). + MTMathListDisplay* display = [self displayForLaTeX:@"\\mathit{f}x"]; + XCTAssertEqual(display.subDisplays.count, 1); + MTCTLineDisplay* line = (MTCTLineDisplay*) display.subDisplays[0]; + XCTAssertTrue([line isKindOfClass:[MTCTLineDisplay class]]); + NSArray* runs = [self fontRunsOfLine:line]; + XCTAssertEqual(runs.count, 2); + XCTAssertTrue(NSEqualRanges([runs[0][0] rangeValue], NSMakeRange(0, 1))); + XCTAssertEqualObjects(runs[0][1], @"LMRoman10-Italic"); + XCTAssertTrue(NSEqualRanges([runs[1][0] rangeValue], NSMakeRange(1, 2))); + XCTAssertEqualObjects(runs[1][1], @"LatinModernMath-Regular"); +} + +- (void) testMathitFlagshipCompanionCoversOnlyAbc +{ + // LLD flagship: \mathit reaches only abc; \sin, delimiters, +, and theta + // stay in the math font. + MTMathListDisplay* display = [self displayForLaTeX:@"\\mathit{\\sin(abc + \\theta)}"]; + NSMutableArray* companionSubstrings = [NSMutableArray array]; + for (MTDisplay* sub in display.subDisplays) { + if (![sub isKindOfClass:[MTCTLineDisplay class]]) continue; + MTCTLineDisplay* line = (MTCTLineDisplay*) sub; + for (NSArray* run in [self fontRunsOfLine:line]) { + if ([run[1] isEqualToString:@"LMRoman10-Italic"]) { + [companionSubstrings addObject:[line.attributedString.string substringWithRange:[run[0] rangeValue]]]; + } + } + } + XCTAssertEqualObjects(companionSubstrings, @[ @"abc" ]); +} + +- (void) testMathitMixedAtomSplitsCompanionRanges +{ + // \mathit{a\theta b} is ONE atom; the companion covers {0,1} and {3,1} + // with the theta surrogate pair between them in the math font. The case + // a per-atom stamp would get wrong (LLD §3.4). + MTMathListDisplay* display = [self displayForLaTeX:@"\\mathit{a\\theta b}"]; + XCTAssertEqual(display.subDisplays.count, 1); + MTCTLineDisplay* line = (MTCTLineDisplay*) display.subDisplays[0]; + NSArray* runs = [self fontRunsOfLine:line]; + XCTAssertEqual(runs.count, 3); + XCTAssertEqualObjects(runs[0][1], @"LMRoman10-Italic"); + XCTAssertTrue(NSEqualRanges([runs[0][0] rangeValue], NSMakeRange(0, 1))); + XCTAssertEqualObjects(runs[1][1], @"LatinModernMath-Regular"); + XCTAssertEqualObjects(runs[2][1], @"LMRoman10-Italic"); + XCTAssertTrue(NSEqualRanges([runs[2][0] rangeValue], NSMakeRange(3, 1))); +} + +- (void) testMathitDelimitersStayInMathFont +{ + // Condition 2 rejects (, ) per character even though the atoms carry the + // italic stamp; matching pdflatex, not MathJax (LLD §3.3 G). + MTMathListDisplay* display = [self displayForLaTeX:@"\\mathit{(a)}"]; + XCTAssertEqual(display.subDisplays.count, 1); + MTCTLineDisplay* line = (MTCTLineDisplay*) display.subDisplays[0]; + NSArray* runs = [self fontRunsOfLine:line]; + XCTAssertEqual(runs.count, 3); + XCTAssertEqualObjects(runs[0][1], @"LatinModernMath-Regular"); + XCTAssertEqualObjects(runs[1][1], @"LMRoman10-Italic"); + XCTAssertEqualObjects(runs[2][1], @"LatinModernMath-Regular"); +} + @end From 3604aacd170ab3e8970ead102f9ac8fc7dd60432 Mon Sep 17 00:00:00 2001 From: Kostub D Date: Tue, 4 Aug 2026 04:42:13 +0530 Subject: [PATCH 3/5] [item 7] Pin \mathit geometry and shaping with integration tests Integration tests for items 5-6: companion advances for f and 1, unrouted categories left byte-identical to plain, companion pair-kerning on AV, the f_i ligature shaping to one glyph, and no ligature crossing a \mathit{f}i style boundary. Written after the implementation per plan; all pass immediately. --- iosMathTests/MTTypesetterTest.m | 73 +++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/iosMathTests/MTTypesetterTest.m b/iosMathTests/MTTypesetterTest.m index 9a0ccba..7da64ec 100644 --- a/iosMathTests/MTTypesetterTest.m +++ b/iosMathTests/MTTypesetterTest.m @@ -3777,4 +3777,77 @@ - (void) testMathitDelimitersStayInMathFont XCTAssertEqualObjects(runs[2][1], @"LatinModernMath-Regular"); } +// The direct assertion that \mathit stopped being a no-op: widths move to +// the companion's advances (LLD §3 contract table; em values from the face). +- (void) testMathitChangesWidthOfRoutableCharacters +{ + CGFloat em = self.font.fontSize; + MTMathListDisplay* mathitF = [self displayForLaTeX:@"\\mathit{f}"]; + MTMathListDisplay* plainF = [self displayForLaTeX:@"f"]; + XCTAssertEqualWithAccuracy(mathitF.width, 0.307 * em, 0.01 * em); + XCTAssertGreaterThan(fabs(plainF.width - mathitF.width), 0.05 * em); + + MTMathListDisplay* mathitOne = [self displayForLaTeX:@"\\mathit{1}"]; + MTMathListDisplay* plainOne = [self displayForLaTeX:@"1"]; + XCTAssertEqualWithAccuracy(mathitOne.width, 0.511 * em, 0.01 * em); + XCTAssertGreaterThan(fabs(plainOne.width - mathitOne.width), 0.005 * em); +} + +// Regression guard on the categories iosMath already gets right (LLD §2.2): +// \mathit must not move anything outside the routing domain. +- (void) testMathitLeavesUnroutedCategoriesUnchanged +{ + NSDictionary* pairs = @{ + @"\\mathit{(}": @"(", + @"\\mathit{+}": @"+", + @"\\mathit{\\alpha}": @"\\alpha", + @"\\mathit{\\aleph}": @"\\aleph", + }; + for (NSString* latex in pairs) { + MTMathListDisplay* styled = [self displayForLaTeX:latex]; + MTMathListDisplay* plain = [self displayForLaTeX:pairs[latex]]; + XCTAssertEqualWithAccuracy(styled.width, plain.width, 0.001, @"%@", latex); + } +} + +// The contiguous companion range lets CoreText apply the face's own kern +// table (cmti10's b->c kern analogue, LLD §3 contract). +- (void) testMathitAppliesCompanionKerning +{ + CGFloat em = self.font.fontSize; + MTMathListDisplay* pair = [self displayForLaTeX:@"\\mathit{AV}"]; + MTMathListDisplay* a = [self displayForLaTeX:@"\\mathit{A}"]; + MTMathListDisplay* v = [self displayForLaTeX:@"\\mathit{V}"]; + // AV pair-kerns -0.102 em in lmroman10-italic; allow half as margin. + XCTAssertLessThan(pair.width, a.width + v.width - 0.05 * em); +} + +- (void) testMathitAppliesCompanionLigature +{ + // One shaped run, one glyph: the f_i ligature. Asserted on glyph count, + // not width, since a ligature can be advance-neutral. + MTMathListDisplay* display = [self displayForLaTeX:@"\\mathit{fi}"]; + MTCTLineDisplay* line = (MTCTLineDisplay*) display.subDisplays[0]; + XCTAssertTrue([line isKindOfClass:[MTCTLineDisplay class]]); + NSArray* runs = (__bridge NSArray*) CTLineGetGlyphRuns(line.line); + XCTAssertEqual(runs.count, 1); + XCTAssertEqual(CTRunGetGlyphCount((__bridge CTRunRef) runs[0]), 1); +} + +- (void) testMathitShapingDoesNotCrossStyleBoundary +{ + // \mathit{f}i: f in the companion, i in the math font — two runs, two + // glyphs, no ligature. The font attribution must partition shaping. + MTMathListDisplay* display = [self displayForLaTeX:@"\\mathit{f}i"]; + MTCTLineDisplay* line = (MTCTLineDisplay*) display.subDisplays[0]; + XCTAssertTrue([line isKindOfClass:[MTCTLineDisplay class]]); + NSArray* runs = (__bridge NSArray*) CTLineGetGlyphRuns(line.line); + XCTAssertEqual(runs.count, 2); + CFIndex totalGlyphs = 0; + for (id run in runs) { + totalGlyphs += CTRunGetGlyphCount((__bridge CTRunRef) run); + } + XCTAssertEqual(totalGlyphs, 2); +} + @end From a33c04bd06b08e39095cc84df907f5c4c72083a3 Mon Sep 17 00:00:00 2001 From: Kostub D Date: Wed, 5 Aug 2026 02:55:11 +0530 Subject: [PATCH 4/5] Route \mathit only for Ordinary atoms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit +addLatexSymbol:value: is public, so a caller can register a non-Ordinary atom whose nucleus is routable — e.g. a BinaryOperator with value "mod". Under \mathit that reached an NSAssert and aborted every Debug build. Make the atom type a third routing condition instead. This is also what TeX gives: \mathit selects a family for class-7 mathchars, so a class-3 relation keeps the math font. changeFont/styleCharacter run only for variables and numbers, so such an atom's nucleus is not remapped either and it renders exactly as it does without \mathit. The forAtom: parameter existed only to feed the assert; drop it. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01TmCWfSMYeLRJmUEvSd5XoT --- iosMath/render/internal/MTTypesetter.m | 10 ++++++---- iosMathTests/MTTypesetterTest.m | 15 +++++++++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/iosMath/render/internal/MTTypesetter.m b/iosMath/render/internal/MTTypesetter.m index 8703c5e..62cfc6c 100644 --- a/iosMath/render/internal/MTTypesetter.m +++ b/iosMath/render/internal/MTTypesetter.m @@ -1015,8 +1015,11 @@ - (void) createDisplayAtoms:(NSArray*) preprocessed [_currentLine addAttribute:(NSString*) kCTFontAttributeName value:(__bridge id) (_styleFont.ctFont) range:appendedRange]; - if (atom.fontStyle == kMTFontStyleItalic) { - [self applyMathitFontToRoutableCharactersInRange:appendedRange forAtom:atom]; + // Ordinary only: \mathit selects a family for class-7 mathchars, so a + // relation or binary operator registered via +addLatexSymbol:value: with + // a routable nucleus keeps the math font. + if (atom.fontStyle == kMTFontStyleItalic && atom.type == kMTMathAtomOrdinary) { + [self applyMathitFontToRoutableCharactersInRange:appendedRange]; } // add the atom to the current range if (_currentLineIndexRange.location == NSNotFound) { @@ -1069,14 +1072,13 @@ - (void) createDisplayAtoms:(NSArray*) preprocessed // Evaluated per character, not per atom: fusion merges a whole \mathit group // into one atom, which can mix routable and non-routable characters // (\mathit{a\theta b} — theta must stay in the math font, LLD §3.3 C). -- (void) applyMathitFontToRoutableCharactersInRange:(NSRange) range forAtom:(MTMathAtom*) atom +- (void) applyMathitFontToRoutableCharactersInRange:(NSRange) range { NSString* string = _currentLine.string; NSUInteger runStart = NSNotFound; for (NSUInteger i = range.location; i <= NSMaxRange(range); i++) { BOOL routable = (i < NSMaxRange(range)) && MTIsMathItalicRoutable([string characterAtIndex:i]); if (routable) { - NSAssert(atom.type == kMTMathAtomOrdinary, @"Routable character in non-Ordinary atom: %@", atom); if (runStart == NSNotFound) { runStart = i; } diff --git a/iosMathTests/MTTypesetterTest.m b/iosMathTests/MTTypesetterTest.m index 7da64ec..442c265 100644 --- a/iosMathTests/MTTypesetterTest.m +++ b/iosMathTests/MTTypesetterTest.m @@ -3777,6 +3777,21 @@ - (void) testMathitDelimitersStayInMathFont XCTAssertEqualObjects(runs[2][1], @"LatinModernMath-Regular"); } +- (void) testMathitLeavesCustomNonOrdinaryAtomInMathFont +{ + // +addLatexSymbol:value: is public, so a caller can register a non-Ordinary + // atom whose nucleus is routable. \mathit selects a family for class-7 + // mathchars only, so such an atom keeps the math font. + [MTMathAtomFactory addLatexSymbol:@"zzmathitrel" + value:[MTMathAtom atomWithType:kMTMathAtomRelation value:@"R"]]; + MTMathListDisplay* display = [self displayForLaTeX:@"\\mathit{\\zzmathitrel}"]; + XCTAssertEqual(display.subDisplays.count, 1); + MTCTLineDisplay* line = (MTCTLineDisplay*) display.subDisplays[0]; + NSArray* runs = [self fontRunsOfLine:line]; + XCTAssertEqual(runs.count, 1); + XCTAssertEqualObjects(runs[0][1], @"LatinModernMath-Regular"); +} + // The direct assertion that \mathit stopped being a no-op: widths move to // the companion's advances (LLD §3 contract table; em values from the face). - (void) testMathitChangesWidthOfRoutableCharacters From 759d7983bd4b1c4434ceeb3497a8220e818181d3 Mon Sep 17 00:00:00 2001 From: Kostub D Date: Thu, 6 Aug 2026 17:31:39 +0530 Subject: [PATCH 5/5] Route U+03A2 like any other capital Greek code point MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MTIsMathItalicRoutable excluded U+03A2, the unassigned code point in the Greek capital block, so it fell through to getItalicized and was mapped to U+1D6F3 — MATHEMATICAL ITALIC CAPITAL THETA SYMBOL, a real glyph every bundled math font has. An unassigned input rendered as a different, real letter. Route it with the rest instead. The companion has no glyph for it, so it draws .notdef, which is a visible failure and matches what the character already gets without \mathit: getDefaultStyle returns capital Greek unchanged and no bundled math font carries U+03A2 either. The exclusion was also guarding an input the parser cannot produce -- atomForCharacter: returns nil for every non-ASCII literal, so U+03A2 reaches a math list only via direct MTMathAtom construction. Lets the function reuse IS_CAPITAL_GREEK instead of open-coding the range. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01TmCWfSMYeLRJmUEvSd5XoT --- iosMath/render/internal/MTTypesetter.m | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/iosMath/render/internal/MTTypesetter.m b/iosMath/render/internal/MTTypesetter.m index 62cfc6c..da0032e 100644 --- a/iosMath/render/internal/MTTypesetter.m +++ b/iosMath/render/internal/MTTypesetter.m @@ -387,10 +387,7 @@ UTF32Char getBlackboard(unichar ch) { // is TeX's class-7 mathchars: Latin letters, digits, capital Greek. static BOOL MTIsMathItalicRoutable(unichar ch) { - // U+03A2 is unassigned — the Greek capital block holds 24 letters, not 25. - BOOL capitalGreek = (ch >= kMTUnicodeGreekCapitalStart && - ch <= kMTUnicodeGreekCapitalEnd && ch != 0x03A2); - return IS_UPPER_EN(ch) || IS_LOWER_EN(ch) || IS_NUMBER(ch) || capitalGreek; + return IS_UPPER_EN(ch) || IS_LOWER_EN(ch) || IS_NUMBER(ch) || IS_CAPITAL_GREEK(ch); } static UTF32Char styleCharacter(unichar ch, MTFontStyle fontStyle)