diff --git a/CHANGELOG.md b/CHANGELOG.md
index 496a863..293120d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,30 @@
## Changelog
+### v2.3.0 (unreleased) — Breaking API change
+
+**Breaking:** Removed `latinModernFontWithSize:`, `xitsFontWithSize:`, and
+`termesFontWithSize:` from `MTFontManager`. Migrate to the generic accessor
+with the new public name constants:
+
+```objc
+// Before
+label.font = [[MTFontManager fontManager] termesFontWithSize:20];
+
+// After
+label.font = [[MTFontManager fontManager] fontWithName:MTFontNameTermes size:20];
+```
+
+`defaultFont` is unchanged and returns Latin Modern Math at 20pt.
+
+**New fonts:** Added five new OpenType MATH fonts — New Computer Modern Math,
+TeX Gyre Pagella Math, STIX Two Math, Fira Math, and Noto Sans Math. XITS Math
+updated to v1.302 (final upstream release).
+
+**New public constants** (`MTFontManager.h`): `MTFontNameLatinModern`,
+`MTFontNameXITS`, `MTFontNameTermes`, `MTFontNameNewComputerModern`,
+`MTFontNamePagella`, `MTFontNameSTIXTwo`, `MTFontNameFiraMath`,
+`MTFontNameNotoSansMath`.
+
### v2.2.0 (2026-05-16)
* Add `\text{}`, `\textrm{}`, `\textbf{}`, `\textit{}`, `\textsf{}`, `\texttt{}` for rendering non-Latin text alongside math — supports CJK, Devanagari, Arabic, Hebrew, Cyrillic, and any other script handled by CoreText system-font cascade.
* Add prime shorthand: `f'` parses as `f^{\prime}`, `f''` as `f^{\prime\prime}`, etc.
diff --git a/README.md b/README.md
index f96da90..8c49b19 100755
--- a/README.md
+++ b/README.md
@@ -200,22 +200,41 @@ label.fontSize = 30
### Font
-The default font is *Latin Modern Math*. Three fonts are bundled; you can
-also use any OTF math font:
+The default font is *Latin Modern Math*. Eight fonts are bundled; you can
+also use any OTF math font. Select a font using `MTFontName*` constants and
+`font(withName:size:)`:
```swift
-label.font = MTFontManager().termesFont(withSize: 20)
+label.font = MTFontManager.fontManager.font(withName: MTFontNameTermes, size: 20)
```
Objective-C
```objective-c
-label.font = [[MTFontManager fontManager] termesFontWithSize:20];
+label.font = [MTFontManager.fontManager fontWithName:MTFontNameTermes size:20];
```
+The three per-font convenience methods (`latinModernFontWithSize:`,
+`xitsFontWithSize:`, `termesFontWithSize:`) were removed. Use
+`fontWithName:size:` with one of the bundled constants instead.
+`defaultFont` is unchanged and returns Latin Modern Math at 20pt.
+
+Available `MTFontName*` constants:
+
+| Constant | Font |
+|---|---|
+| `MTFontNameLatinModern` | Latin Modern Math |
+| `MTFontNameXITS` | XITS Math |
+| `MTFontNameTermes` | TeX Gyre Termes Math |
+| `MTFontNameNewComputerModern` | New Computer Modern Math |
+| `MTFontNamePagella` | TeX Gyre Pagella Math |
+| `MTFontNameSTIXTwo` | STIX Two Math |
+| `MTFontNameFiraMath` | Fira Math |
+| `MTFontNameNotoSansMath` | Noto Sans Math |
+
### Color
```swift
diff --git a/SwiftMathExample/MathLabel.swift b/SwiftMathExample/MathLabel.swift
index d16213c..f5629f6 100644
--- a/SwiftMathExample/MathLabel.swift
+++ b/SwiftMathExample/MathLabel.swift
@@ -38,7 +38,7 @@ struct MathLabel: View {
}
}
-/// The three math fonts bundled with iosMath, exposed for the font switcher.
+/// The math fonts bundled with iosMath, exposed for the font switcher.
enum MathFont: String, CaseIterable, Identifiable {
case latinModern = "Latin Modern"
case termes = "TeX Gyre Termes"
@@ -46,14 +46,17 @@ enum MathFont: String, CaseIterable, Identifiable {
var id: String { rawValue }
- func font(size: CGFloat) -> MTFont? {
- let manager = MTFontManager.fontManager()
+ var fontName: String {
switch self {
- case .latinModern: return manager.latinModernFont(withSize: size)
- case .termes: return manager.termesFont(withSize: size)
- case .xits: return manager.xitsFont(withSize: size)
+ case .latinModern: return MTFontNameLatinModern
+ case .termes: return MTFontNameTermes
+ case .xits: return MTFontNameXITS
}
}
+
+ func font(size: CGFloat) -> MTFont? {
+ MTFontManager.fontManager.font(withName: fontName, size: size)
+ }
}
// MARK: - Platform representables
diff --git a/iosMath/render/MTFontManager.h b/iosMath/render/MTFontManager.h
index ee2e131..1292e1f 100644
--- a/iosMath/render/MTFontManager.h
+++ b/iosMath/render/MTFontManager.h
@@ -17,12 +17,31 @@
NS_ASSUME_NONNULL_BEGIN
+extern NSString *const MTFontNameLatinModern;
+extern NSString *const MTFontNameXITS;
+extern NSString *const MTFontNameTermes;
+extern NSString *const MTFontNameNewComputerModern;
+extern NSString *const MTFontNamePagella;
+extern NSString *const MTFontNameSTIXTwo;
+extern NSString *const MTFontNameFiraMath;
+extern NSString *const MTFontNameNotoSansMath;
+
/** A manager to load font files from disc and keep them
in memory. */
@interface MTFontManager : NSObject
-/** Get the singleton instance of MTFontManager. */
-+ (instancetype) fontManager;
+/** The shared font manager.
+
+ Declared as a class property (not a `+fontManager` factory method) so that
+ Swift imports it as `MTFontManager.fontManager` instead of collapsing it into
+ `init()`. In Objective-C it is still reached via `[MTFontManager fontManager]`
+ or `MTFontManager.fontManager`. */
+@property (class, readonly, strong) MTFontManager *fontManager;
+
+/** MTFontManager is a singleton; use +fontManager. Constructing your own
+ instance bypasses the shared font cache, so init/new are unavailable. */
++ (instancetype) new NS_UNAVAILABLE;
+- (instancetype) init NS_UNAVAILABLE;
/** Returns the default font, which is Latin Modern Math with 20pt */
- (MTFont *) defaultFont;
@@ -36,15 +55,6 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (MTFont *) fontWithName:(NSString *)name size:(CGFloat)size;
-/** Helper function to return the Xits Math font. */
-- (MTFont *) xitsFontWithSize:(CGFloat)size;
-
-/** Helper function to return the Tex Gyre Termes Math font. */
-- (MTFont *) termesFontWithSize:(CGFloat)size;
-
-/** Helper function to return the Latin Modern Math font. */
-- (MTFont *) latinModernFontWithSize:(CGFloat)size;
-
/**
Returns a CoreText font suitable for `\text*` rendering. The caller owns
the returned reference (CF_RETAINED) and must `CFRelease` it.
diff --git a/iosMath/render/MTFontManager.m b/iosMath/render/MTFontManager.m
index a6877ba..5c832de 100644
--- a/iosMath/render/MTFontManager.m
+++ b/iosMath/render/MTFontManager.m
@@ -14,6 +14,15 @@
const int kDefaultFontSize = 20;
+NSString *const MTFontNameLatinModern = @"latinmodern-math";
+NSString *const MTFontNameXITS = @"xits-math";
+NSString *const MTFontNameTermes = @"texgyretermes-math";
+NSString *const MTFontNameNewComputerModern = @"newcm-math";
+NSString *const MTFontNamePagella = @"texgyrepagella-math";
+NSString *const MTFontNameSTIXTwo = @"stixtwo-math";
+NSString *const MTFontNameFiraMath = @"firamath";
+NSString *const MTFontNameNotoSansMath = @"notosansmath";
+
@interface MTFontManager ()
@property (nonatomic, nonnull) NSMutableDictionary* nameToFontMap;
@@ -22,16 +31,19 @@ @interface MTFontManager ()
@implementation MTFontManager
-+ (instancetype) fontManager
++ (MTFontManager *) fontManager
{
static MTFontManager* manager = nil;
- if (manager == nil) {
- manager = [MTFontManager new];
- }
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ manager = [[self alloc] initPrivate];
+ });
return manager;
}
-- (instancetype)init
+// init/new are NS_UNAVAILABLE so callers can't bypass the singleton; the
+// shared instance is built through this private initializer instead.
+- (instancetype)initPrivate
{
self = [super init];
if (self) {
@@ -54,24 +66,9 @@ - (MTFont *)fontWithName:(NSString *)name size:(CGFloat)size
}
}
-- (MTFont *)latinModernFontWithSize:(CGFloat)size
-{
- return [self fontWithName:@"latinmodern-math" size:size];
-}
-
-- (MTFont *)xitsFontWithSize:(CGFloat)size
-{
- return [self fontWithName:@"xits-math" size:size];
-}
-
-- (MTFont *)termesFontWithSize:(CGFloat)size
-{
- return [self fontWithName:@"texgyretermes-math" size:size];
-}
-
- (MTFont *)defaultFont
{
- return [self latinModernFontWithSize:kDefaultFontSize];
+ return [self fontWithName:MTFontNameLatinModern size:kDefaultFontSize];
}
+ (CTFontRef) textCTFontForStyle:(MTTextStyle) style
diff --git a/iosMathExample/example/ViewController.m b/iosMathExample/example/ViewController.m
index e3a9ef4..df139b9 100644
--- a/iosMathExample/example/ViewController.m
+++ b/iosMathExample/example/ViewController.m
@@ -39,6 +39,8 @@ @interface ViewController ()
@property (weak, nonatomic) IBOutlet MTMathUILabel *mathLabel;
@property (weak, nonatomic) IBOutlet UITextField *latexField;
+- (void)applyFontWithName:(NSString *)name;
+
@end
@implementation ViewController
@@ -253,33 +255,13 @@ - (void) setVerticalGap:(CGFloat) gap between:(UIView*) view1 and:(UIView*) view
}
#pragma mark Buttons
-- (void)latinButtonPressed:(id)sender
-{
- for (MTMathUILabel* label in self.demoLabels) {
- label.font = [[MTFontManager fontManager] latinModernFontWithSize:label.font.fontSize];
- }
- for (MTMathUILabel* label in self.labels) {
- label.font = [[MTFontManager fontManager] latinModernFontWithSize:label.font.fontSize];
- }
-}
-
-- (void)termesButtonPressed:(id)sender
-{
- for (MTMathUILabel* label in self.demoLabels) {
- label.font = [[MTFontManager fontManager] termesFontWithSize:label.font.fontSize];
- }
- for (MTMathUILabel* label in self.labels) {
- label.font = [[MTFontManager fontManager] termesFontWithSize:label.font.fontSize];
- }
-}
-
-- (void)xitsButtonPressed:(id)sender
+- (void)applyFontWithName:(NSString *)name
{
for (MTMathUILabel* label in self.demoLabels) {
- label.font = [[MTFontManager fontManager] xitsFontWithSize:label.font.fontSize];
+ label.font = [[MTFontManager fontManager] fontWithName:name size:label.font.fontSize];
}
for (MTMathUILabel* label in self.labels) {
- label.font = [[MTFontManager fontManager] xitsFontWithSize:label.font.fontSize];
+ label.font = [[MTFontManager fontManager] fontWithName:name size:label.font.fontSize];
}
}
@@ -341,24 +323,14 @@ - (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row f
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
+ // Display names (self.fontNames) map 1:1 to these loader keys.
+ // Not static: extern const NSString* values aren't compile-time constants.
+ NSString *const kFontKeys[] = {
+ MTFontNameLatinModern, MTFontNameTermes, MTFontNameXITS,
+ };
self.controller.fontField.text = self.fontNames[row];
[self.controller.fontField resignFirstResponder];
- switch (row) {
- case 0:
- [self.controller latinButtonPressed:nil];
- break;
-
- case 1:
- [self.controller termesButtonPressed:nil];
- break;
-
- case 2:
- [self.controller xitsButtonPressed:nil];
- break;
-
- default:
- break;
- }
+ [self.controller applyFontWithName:kFontKeys[row]];
}
@end
diff --git a/iosMathSwiftTests/iosMathSwiftAPITests.swift b/iosMathSwiftTests/iosMathSwiftAPITests.swift
index 6c702d3..16d14d3 100644
--- a/iosMathSwiftTests/iosMathSwiftAPITests.swift
+++ b/iosMathSwiftTests/iosMathSwiftAPITests.swift
@@ -39,29 +39,27 @@ final class MTMathUILabelTests: XCTestCase {
}
final class MTFontManagerTests: XCTestCase {
- func testFontManagerInit() {
- let mgr = MTFontManager()
- XCTAssertNotNil(mgr)
+ func testFontManagerSingleton() {
+ // fontManager is a class property, so repeated access returns the same
+ // shared instance (and there is no MTFontManager() to bypass it).
+ XCTAssertTrue(MTFontManager.fontManager === MTFontManager.fontManager)
}
func testDefaultFont() {
- let font = MTFontManager().defaultFont()
+ let font = MTFontManager.fontManager.defaultFont()
XCTAssertNotNil(font)
}
- func testLatinModernFont() {
- let font = MTFontManager().latinModernFont(withSize: 18)
- XCTAssertNotNil(font)
- }
-
- func testXitsFont() {
- let font = MTFontManager().xitsFont(withSize: 16)
- XCTAssertNotNil(font)
- }
-
- func testTermesFont() {
- let font = MTFontManager().termesFont(withSize: 16)
- XCTAssertNotNil(font)
+ func testFontByName() {
+ let manager = MTFontManager.fontManager
+ let names = [
+ MTFontNameLatinModern, MTFontNameXITS, MTFontNameTermes,
+ MTFontNameNewComputerModern, MTFontNamePagella, MTFontNameSTIXTwo,
+ MTFontNameFiraMath, MTFontNameNotoSansMath,
+ ]
+ for name in names {
+ XCTAssertNotNil(manager.font(withName: name, size: 18), "Font \(name) failed to load")
+ }
}
}
diff --git a/iosMathTests/MTTypesetterTest.m b/iosMathTests/MTTypesetterTest.m
index 5642368..0557841 100644
--- a/iosMathTests/MTTypesetterTest.m
+++ b/iosMathTests/MTTypesetterTest.m
@@ -2027,7 +2027,7 @@ - (void)testOverrightarrowNarrow
// horizontal glyph assembly.
- (void)testStretchyArrowAssemblyOnlyFont
{
- MTFont* xits = [MTFontManager.fontManager xitsFontWithSize:20];
+ MTFont* xits = [MTFontManager.fontManager fontWithName:MTFontNameXITS size:20];
XCTAssertNotNil(xits);
for (NSString* latex in @[@"\\overrightarrow{x}", @"\\overrightarrow{ABCD}",
@@ -2057,7 +2057,7 @@ - (void)testStretchyArrowAssemblyOnlyFont
// vertical glyph assembly instead.
- (void)testStretchyVerticalArrowAssemblyOnlyFont
{
- MTFont* xits = [MTFontManager.fontManager xitsFontWithSize:20];
+ MTFont* xits = [MTFontManager.fontManager fontWithName:MTFontNameXITS size:20];
XCTAssertNotNil(xits);
// Tall content (a fraction) forces the boundary delimiter to stretch, exercising