add a fallback font system to render CJK text in the \text command

This commit is contained in:
Nicolas Guillot
2025-10-01 14:11:00 +02:00
parent b67cc8fd38
commit 11f57f7c6e
6 changed files with 347 additions and 13 deletions

10
Sources/SwiftMath/MathRender/MTFont.swift Executable file → Normal file
View File

@@ -11,12 +11,18 @@ import CoreText
//
public class MTFont {
var defaultCGFont: CGFont!
var ctFont: CTFont!
var mathTable: MTFontMathTable?
var rawMathTable: NSDictionary?
/// Fallback font for characters not supported by the main math font.
/// Defaults to the system font at the same size. This is particularly useful
/// for rendering text in \text{} commands with characters outside the math font's coverage
/// (e.g., Chinese, Japanese, Korean, emoji, etc.)
public var fallbackFont: CTFont?
init() {}
/// `MTFont(fontWithName:)` does not load the complete math font, it only has about half the glyphs of the full math font.

View File

@@ -438,8 +438,15 @@ public struct MTMathListBuilder {
} else {
atom = MTMathAtomFactory.atom(forCharacter: char)
if atom == nil {
// Not a recognized character
continue
// Not a recognized character in standard math mode
// In text mode (spacesAllowed && roman style), accept any Unicode character for fallback font support
// This enables Chinese, Japanese, Korean, emoji, etc. in \text{} commands
if spacesAllowed && currentFontStyle == .roman {
atom = MTMathAtom(type: .ordinary, value: String(char))
} else {
// In math mode or non-text commands, skip unrecognized characters
continue
}
}
}

View File

@@ -1276,11 +1276,18 @@ class MTTypesetter {
func findGlyphForCharacterAtIndex(_ index:String.Index, inString str:String) -> CGGlyph {
// Get the character at index taking into account UTF-32 characters
var chars = Array(str[index].utf16)
// Get the glyph from the font
var glyph = [CGGlyph](repeating: CGGlyph.zero, count: chars.count)
let found = CTFontGetGlyphsForCharacters(styleFont.ctFont, &chars, &glyph, chars.count)
if !found {
// Try fallback font if available
if let fallbackFont = styleFont.fallbackFont {
let fallbackFound = CTFontGetGlyphsForCharacters(fallbackFont, &chars, &glyph, chars.count)
if fallbackFound {
return glyph[0]
}
}
// the font did not contain a glyph for our character, so we just return 0 (notdef)
return 0
}