From 32ba7c128ade8d309a104aabd346f8379f31d656 Mon Sep 17 00:00:00 2001 From: Peter Tang Date: Tue, 12 Sep 2023 07:18:44 +0800 Subject: [PATCH 01/10] added MTMathImage - draw latex image offscreen. --- Sources/SwiftMath/MathRender/MTConfig.swift | 2 + .../SwiftMath/MathRender/MTFontManager.swift | 11 +- .../SwiftMath/MathRender/MTMathImage.swift | 111 ++++++++++++++++++ 3 files changed, 118 insertions(+), 6 deletions(-) create mode 100644 Sources/SwiftMath/MathRender/MTMathImage.swift diff --git a/Sources/SwiftMath/MathRender/MTConfig.swift b/Sources/SwiftMath/MathRender/MTConfig.swift index 1ef5c70..f936d9d 100644 --- a/Sources/SwiftMath/MathRender/MTConfig.swift +++ b/Sources/SwiftMath/MathRender/MTConfig.swift @@ -18,6 +18,7 @@ public typealias MTBezierPath = UIBezierPath public typealias MTLabel = UILabel public typealias MTEdgeInsets = UIEdgeInsets public typealias MTRect = CGRect +public typealias MTImage = UIImage let MTEdgeInsetsZero = UIEdgeInsets.zero func MTGraphicsGetCurrentContext() -> CGContext? { UIGraphicsGetCurrentContext() } @@ -31,6 +32,7 @@ public typealias MTColor = NSColor public typealias MTBezierPath = NSBezierPath public typealias MTEdgeInsets = NSEdgeInsets public typealias MTRect = NSRect +public typealias MTImage = NSImage let MTEdgeInsetsZero = NSEdgeInsets.init(top: 0, left: 0, bottom: 0, right: 0) func MTGraphicsGetCurrentContext() -> CGContext? { NSGraphicsContext.current?.cgContext } diff --git a/Sources/SwiftMath/MathRender/MTFontManager.swift b/Sources/SwiftMath/MathRender/MTFontManager.swift index 0ebb015..a2e0538 100644 --- a/Sources/SwiftMath/MathRender/MTFontManager.swift +++ b/Sources/SwiftMath/MathRender/MTFontManager.swift @@ -11,17 +11,16 @@ import Foundation public class MTFontManager { - static public private(set) var manager:MTFontManager! = nil + static public private(set) var manager: MTFontManager = { + MTFontManager() + }() let kDefaultFontSize = CGFloat(20) static var fontManager : MTFontManager { - if manager == nil { - manager = MTFontManager() - } - return manager! + return manager } - + public init() { } var nameToFontMap = [String: MTFont]() diff --git a/Sources/SwiftMath/MathRender/MTMathImage.swift b/Sources/SwiftMath/MathRender/MTMathImage.swift new file mode 100644 index 0000000..577cca7 --- /dev/null +++ b/Sources/SwiftMath/MathRender/MTMathImage.swift @@ -0,0 +1,111 @@ +// +// File.swift +// +// +// Created by Peter Tang on 12/9/2023. +// + +import Foundation + +#if os(iOS) + import UIKit +#endif + +#if os(macOS) + import AppKit +#endif + +public class MTMathImage { + public var font: MTFont? = nil + public let fontSize: CGFloat + public let textColor: MTColor + + public let labelMode: MTMathUILabelMode + public let textAlignment: MTTextAlignment + + public var contentInsets: MTEdgeInsets = MTEdgeInsetsZero + + public let latex: String + private(set) var intrinsicContentSize = CGSize.zero + + public init(latex: String, fontSize: CGFloat, textColor: MTColor, labelMode: MTMathUILabelMode = .display, textAlignment: MTTextAlignment = .center) { + self.latex = latex + self.fontSize = fontSize + self.textColor = textColor + self.labelMode = labelMode + self.textAlignment = textAlignment + } +} +extension MTMathImage { + public var currentStyle: MTLineStyle { + switch labelMode { + case .display: return .display + case .text: return .text + } + } + private func intrinsicContentSize(_ displayList: MTMathListDisplay) -> CGSize { + CGSize(width: displayList.width + contentInsets.left + contentInsets.right, + height: displayList.ascent + displayList.descent + contentInsets.top + contentInsets.bottom) + } + public func asImage() -> (NSError?, MTImage?) { + func layoutImage(size: CGSize, displayList: MTMathListDisplay) { + var textX = CGFloat(0) + switch self.textAlignment { + case .left: textX = contentInsets.left + case .center: textX = (size.width - contentInsets.left - contentInsets.right - displayList.width) / 2 + contentInsets.left + case .right: textX = size.width - displayList.width - contentInsets.right + } + let availableHeight = size.height - contentInsets.bottom - contentInsets.top + + // center things vertically + var height = displayList.ascent + displayList.descent + if height < fontSize/2 { + height = fontSize/2 // set height to half the font size + } + let textY = (availableHeight - height) / 2 + displayList.descent + contentInsets.bottom + displayList.position = CGPoint(x: textX, y: textY) + } + if font == nil { + self.font = MTFontManager.fontManager.defaultFont + } + var error: NSError? + guard let mathList = MTMathListBuilder.build(fromString: latex, error: &error), error == nil, + let displayList = MTTypesetter.createLineForMathList(mathList, font: font, style: currentStyle) else { + return (error, nil) + } + + intrinsicContentSize = intrinsicContentSize(displayList) + displayList.textColor = textColor + + let size = intrinsicContentSize + layoutImage(size: size, displayList: displayList) + + #if os(iOS) + let renderer = UIGraphicsImageRenderer(size: size) + let image = renderer.image { rendererContext in + rendererContext.cgContext.saveGState() + rendererContext.cgContext.concatenate(.flippedVertically(size.height)) + displayList.draw(rendererContext.cgContext) + rendererContext.cgContext.restoreGState() + } + return (nil, image) + #endif + #if os(macOS) + let image = NSImage(size: size, flipped: true) { bounds in + guard let context = NSGraphicsContext.current?.cgContext else { return false } + context.saveGState() + displayList.draw(context) + context.restoreGState() + return true + } + return (nil, image) + #endif + } +} +private extension CGAffineTransform { + static func flippedVertically(_ height: CGFloat) -> CGAffineTransform { + var transform = CGAffineTransform(scaleX: 1, y: -1) + transform = transform.translatedBy(x: 0, y: -height) + return transform + } +} From 397d4ece6d098095dc8bc366e0c49c1957db4299 Mon Sep 17 00:00:00 2001 From: Peter Tang Date: Tue, 12 Sep 2023 07:32:39 +0800 Subject: [PATCH 02/10] nsImage flipped --- Sources/SwiftMath/MathRender/MTMathImage.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/SwiftMath/MathRender/MTMathImage.swift b/Sources/SwiftMath/MathRender/MTMathImage.swift index 577cca7..49bc043 100644 --- a/Sources/SwiftMath/MathRender/MTMathImage.swift +++ b/Sources/SwiftMath/MathRender/MTMathImage.swift @@ -91,7 +91,7 @@ extension MTMathImage { return (nil, image) #endif #if os(macOS) - let image = NSImage(size: size, flipped: true) { bounds in + let image = NSImage(size: size, flipped: false) { bounds in guard let context = NSGraphicsContext.current?.cgContext else { return false } context.saveGState() displayList.draw(context) From 018ce0cd085e7d11ff16c91e59726b50e422af3e Mon Sep 17 00:00:00 2001 From: Peter Tang Date: Tue, 12 Sep 2023 07:55:46 +0800 Subject: [PATCH 03/10] MathFont and MathTable added, both do not rely on existing SwifthMath code. --- Sources/SwiftMath/MathBundle/MathFont.swift | 145 +++++++++ Sources/SwiftMath/MathBundle/MathTable.swift | 308 +++++++++++++++++++ Tests/SwiftMathTests/MathFontTests.swift | 26 ++ Tests/SwiftMathTests/MathTableTests.swift | 36 +++ 4 files changed, 515 insertions(+) create mode 100644 Sources/SwiftMath/MathBundle/MathFont.swift create mode 100644 Sources/SwiftMath/MathBundle/MathTable.swift create mode 100644 Tests/SwiftMathTests/MathFontTests.swift create mode 100644 Tests/SwiftMathTests/MathTableTests.swift diff --git a/Sources/SwiftMath/MathBundle/MathFont.swift b/Sources/SwiftMath/MathBundle/MathFont.swift new file mode 100644 index 0000000..66d4c01 --- /dev/null +++ b/Sources/SwiftMath/MathBundle/MathFont.swift @@ -0,0 +1,145 @@ +// +// File.swift +// +// +// Created by Peter Tang on 10/9/2023. +// + +import Foundation +import CoreText + +public enum MathFont: String, CaseIterable { + + case latinModernFont = "latinmodern-math" + case kpMathLightFont = "KpMath-Light" + case kpMathSansFont = "KpMath-Sans" + case xitsFont = "xits-math" + case termesFont = "texgyretermes-math" + + public func cgFont() -> CGFont? { + BundleManager.manager.obtainCGFont(font: self) + } + public func ctFont(withSize size: CGFloat) -> CTFont? { + BundleManager.manager.obtainCTFont(font: self, withSize: size) + } + internal func mathTable() -> NSDictionary? { + BundleManager.manager.obtainMathTable(font: self) + } + internal func get(nameForGlyph glyph: CGGlyph) -> String { + let name = cgFont()?.name(for: glyph) as? String + return name ?? "" + } + internal func get(glyphWithName name: String) -> CGGlyph? { + cgFont()?.getGlyphWithGlyphName(name: name as CFString) + } +} +internal extension CTFont { + /** The size of this font in points. */ + var fontSize: CGFloat { + CTFontGetSize(self) + } + var unitsPerEm: UInt { + return UInt(CTFontGetUnitsPerEm(self)) + } +} +private class BundleManager { + static fileprivate(set) var manager: BundleManager = { + return BundleManager() + }() + + private var cgFonts = [MathFont: CGFont]() + private var ctFonts = [CTFontPair: CTFont]() + private var mathTables = [MathFont: NSDictionary]() + + private var initializedOnceAlready: Bool = false + + private func registerCGFont(mathFont: MathFont) throws { + guard let frameworkBundleURL = Bundle.module.url(forResource: "mathFonts", withExtension: "bundle"), + let resourceBundleURL = Bundle(url: frameworkBundleURL)?.path(forResource: mathFont.rawValue, ofType: "otf") else { + throw FontError.fontPathNotFound + } + guard let fontData = NSData(contentsOfFile: resourceBundleURL), let dataProvider = CGDataProvider(data: fontData) else { + throw FontError.invalidFontFile + } + guard let defaultCGFont = CGFont(dataProvider) else { + throw FontError.initFontError + } + + cgFonts[mathFont] = defaultCGFont + + var errorRef: Unmanaged? = nil + guard CTFontManagerRegisterGraphicsFont(defaultCGFont, &errorRef) else { + throw FontError.registerFailed + } + print("mathFonts bundle: \(mathFont.rawValue) registered.") + } + + private func registerMathTable(mathFont: MathFont) throws { + guard let frameworkBundleURL = Bundle.module.url(forResource: "mathFonts", withExtension: "bundle"), + let mathTablePlist = Bundle(url: frameworkBundleURL)?.url(forResource: mathFont.rawValue, withExtension:"plist") else { + throw FontError.fontPathNotFound + } + guard let rawMathTable = NSDictionary(contentsOf: mathTablePlist), + let version = rawMathTable["version"] as? String, + version == "1.3" else { + throw FontError.invalidMathTable + } + //FIXME: mathTable = MTFontMathTable(withFont:self, mathTable:rawMathTable) + mathTables[mathFont] = rawMathTable + } + + private func registerAllBundleResources() { + guard !initializedOnceAlready else { return } + MathFont.allCases.forEach { font in + do { + try BundleManager.manager.registerCGFont(mathFont: font) + try BundleManager.manager.registerMathTable(mathFont: font) + } catch { + fatalError("MTMathFonts:\(#function) Couldn't load math fonts \(font.rawValue), reason \(error)") + } + } + initializedOnceAlready.toggle() + } + + fileprivate func obtainCGFont(font: MathFont) -> CGFont? { + if !initializedOnceAlready { registerAllBundleResources() } + return cgFonts[font] + } + + fileprivate func obtainCTFont(font: MathFont, withSize size: CGFloat) -> CTFont? { + if !initializedOnceAlready { registerAllBundleResources() } + let fontPair = CTFontPair(font: font, size: size) + guard let ctFont = ctFonts[fontPair] else { + if let cgFont = cgFonts[font] { + let ctFont = CTFontCreateWithGraphicsFont(cgFont, size, nil, nil) + ctFonts[fontPair] = ctFont + return ctFont + } + return nil + } + return ctFont + } + fileprivate func obtainMathTable(font: MathFont) -> NSDictionary? { + if !initializedOnceAlready { registerAllBundleResources() } + return mathTables[font] + } + deinit { + ctFonts.removeAll() + var errorRef: Unmanaged? = nil + cgFonts.values.forEach { cgFont in + CTFontManagerUnregisterGraphicsFont(cgFont, &errorRef) + } + } + public enum FontError: Error { + case invalidFontFile + case fontPathNotFound + case initFontError + case registerFailed + case invalidMathTable + } + + private struct CTFontPair: Hashable { + let font: MathFont + let size: CGFloat + } +} diff --git a/Sources/SwiftMath/MathBundle/MathTable.swift b/Sources/SwiftMath/MathBundle/MathTable.swift new file mode 100644 index 0000000..0f8a604 --- /dev/null +++ b/Sources/SwiftMath/MathBundle/MathTable.swift @@ -0,0 +1,308 @@ +// +// MathTable.swift +// +// +// Created by Peter Tang on 11/9/2023. +// + +import Foundation +import CoreText + +// struct GlyphPart { +// /// The glyph that represents this part +// var glyph: CGGlyph! +// +// /// Full advance width/height for this part, in the direction of the extension in points. +// var fullAdvance: CGFloat = 0 +// +// /// Advance width/ height of the straight bar connector material at the beginning of the glyph in points. +// var startConnectorLength: CGFloat = 0 +// +// /// Advance width/ height of the straight bar connector material at the end of the glyph in points. +// var endConnectorLength: CGFloat = 0 +// +// /// If this part is an extender. If set, the part can be skipped or repeated. +// var isExtender: Bool = false +// } + +/** This class represents the Math table of an open type font. + + The math table is documented here: https://www.microsoft.com/typography/otspec/math.htm + + How the constants in this class affect the display is documented here: + http://www.tug.org/TUGboat/tb30-1/tb94vieth.pdf + + Note: We don't parse the math table from the open type font. Rather we parse it + in python and convert it to a .plist file which is easily consumed by this class. + This approach is preferable to spending an inordinate amount of time figuring out + how to parse the returned NSData object using the open type rules. + + Remark: This class is not meant to be used outside of this library. + */ +internal struct MathTable { + let kConstants = "constants" + + let font: MathFont + private let unitsPerEm: UInt + private let fontSize: CGFloat + + init(withFont font: MathFont, fontSize: CGFloat, unitsPerEm: UInt) { + self.font = font + self.unitsPerEm = unitsPerEm + self.fontSize = fontSize + } + func fontUnitsToPt(_ fontUnits: Int) -> CGFloat { + CGFloat(fontUnits) * fontSize / CGFloat(unitsPerEm) + } + func constantFromTable(_ constName: String) -> CGFloat { + guard let consts = font.mathTable()?[kConstants] as? NSDictionary, let val = consts[constName] as? NSNumber else { + fatalError("\(#function) unable to extract \(constName) from plist") + } + return fontUnitsToPt(val.intValue) + } + func percentFromTable(_ percentName: String) -> CGFloat { + guard let consts = font.mathTable()?[kConstants] as? NSDictionary, let val = consts[percentName] as? NSNumber else { + fatalError("\(#function) unable to extract \(percentName) from plist") + } + return CGFloat(val.floatValue) / 100 + } + /// Math Font Metrics from the opentype specification + // MARK: - Fractions + var fractionNumeratorDisplayStyleShiftUp:CGFloat { constantFromTable("FractionNumeratorDisplayStyleShiftUp") } // \sigma_8 in TeX + var fractionNumeratorShiftUp:CGFloat { constantFromTable("FractionNumeratorShiftUp") } // \sigma_9 in TeX + var fractionDenominatorDisplayStyleShiftDown:CGFloat { constantFromTable("FractionDenominatorDisplayStyleShiftDown") } // \sigma_11 in TeX + var fractionDenominatorShiftDown:CGFloat { constantFromTable("FractionDenominatorShiftDown") } // \sigma_12 in TeX + var fractionNumeratorDisplayStyleGapMin:CGFloat { constantFromTable("FractionNumDisplayStyleGapMin") } // 3 * \xi_8 in TeX + var fractionNumeratorGapMin:CGFloat { constantFromTable("FractionNumeratorGapMin") } // \xi_8 in TeX + var fractionDenominatorDisplayStyleGapMin:CGFloat { constantFromTable("FractionDenomDisplayStyleGapMin") } // 3 * \xi_8 in TeX + var fractionDenominatorGapMin:CGFloat { constantFromTable("FractionDenominatorGapMin") } // \xi_8 in TeX + var fractionRuleThickness:CGFloat { constantFromTable("FractionRuleThickness") } // \xi_8 in TeX + var skewedFractionHorizonalGap:CGFloat { constantFromTable("SkewedFractionHorizontalGap") } // \sigma_20 in TeX + var skewedFractionVerticalGap:CGFloat { constantFromTable("SkewedFractionVerticalGap") } // \sigma_21 in TeX + + // MARK: - Non-standard + /// FractionDelimiterSize and FractionDelimiterDisplayStyleSize are not constants + /// specified in the OpenType Math specification. Rather these are proposed LuaTeX extensions + /// for the TeX parameters \sigma_20 (delim1) and \sigma_21 (delim2). Since these do not + /// exist in the fonts that we have, we use the same approach as LuaTeX and use the fontSize + /// to determine these values. The constants used are the same as LuaTeX and KaTeX and match the + /// metrics values of the original TeX fonts. + /// Note: An alternative approach is to use DelimitedSubFormulaMinHeight for \sigma21 and use a factor + /// of 2 to get \sigma 20 as proposed in Vieth paper. + /// The XeTeX implementation sets \sigma21 = fontSize and \sigma20 = DelimitedSubFormulaMinHeight which + /// will produce smaller delimiters. + /// Of all the approaches we've implemented LuaTeX's approach since it mimics LaTeX most accurately. + var fractionDelimiterSize: CGFloat { 1.01 * fontSize } + + /// Modified constant from 2.4 to 2.39, it matches KaTeX and looks better. + var fractionDelimiterDisplayStyleSize: CGFloat { 2.39 * fontSize } + + // MARK: - Stacks + var stackTopDisplayStyleShiftUp:CGFloat { constantFromTable("StackTopDisplayStyleShiftUp") } // \sigma_8 in TeX + var stackTopShiftUp:CGFloat { constantFromTable("StackTopShiftUp") } // \sigma_10 in TeX + var stackDisplayStyleGapMin:CGFloat { constantFromTable("StackDisplayStyleGapMin") } // 7 \xi_8 in TeX + var stackGapMin:CGFloat { constantFromTable("StackGapMin") } // 3 \xi_8 in TeX + var stackBottomDisplayStyleShiftDown:CGFloat { constantFromTable("StackBottomDisplayStyleShiftDown") } // \sigma_11 in TeX + var stackBottomShiftDown:CGFloat { constantFromTable("StackBottomShiftDown") } // \sigma_12 in TeX + + var stretchStackBottomShiftDown:CGFloat { constantFromTable("StretchStackBottomShiftDown") } + var stretchStackGapAboveMin:CGFloat { constantFromTable("StretchStackGapAboveMin") } + var stretchStackGapBelowMin:CGFloat { constantFromTable("StretchStackGapBelowMin") } + var stretchStackTopShiftUp:CGFloat { constantFromTable("StretchStackTopShiftUp") } + + // MARK: - super/sub scripts + + var superscriptShiftUp:CGFloat { constantFromTable("SuperscriptShiftUp") } // \sigma_13, \sigma_14 in TeX + var superscriptShiftUpCramped:CGFloat { constantFromTable("SuperscriptShiftUpCramped") } // \sigma_15 in TeX + var subscriptShiftDown:CGFloat { constantFromTable("SubscriptShiftDown") } // \sigma_16, \sigma_17 in TeX + var superscriptBaselineDropMax:CGFloat { constantFromTable("SuperscriptBaselineDropMax") } // \sigma_18 in TeX + var subscriptBaselineDropMin:CGFloat { constantFromTable("SubscriptBaselineDropMin") } // \sigma_19 in TeX + var superscriptBottomMin:CGFloat { constantFromTable("SuperscriptBottomMin") } // 1/4 \sigma_5 in TeX + var subscriptTopMax:CGFloat { constantFromTable("SubscriptTopMax") } // 4/5 \sigma_5 in TeX + var subSuperscriptGapMin:CGFloat { constantFromTable("SubSuperscriptGapMin") } // 4 \xi_8 in TeX + var superscriptBottomMaxWithSubscript:CGFloat { constantFromTable("SuperscriptBottomMaxWithSubscript") } // 4/5 \sigma_5 in TeX + + var spaceAfterScript:CGFloat { constantFromTable("SpaceAfterScript") } + + // MARK: - radicals + var radicalExtraAscender:CGFloat { constantFromTable("RadicalExtraAscender") } // \xi_8 in Tex + var radicalRuleThickness:CGFloat { constantFromTable("RadicalRuleThickness") } // \xi_8 in Tex + var radicalDisplayStyleVerticalGap:CGFloat { constantFromTable("RadicalDisplayStyleVerticalGap") } // \xi_8 + 1/4 \sigma_5 in Tex + var radicalVerticalGap:CGFloat { constantFromTable("RadicalVerticalGap") } // 5/4 \xi_8 in Tex + var radicalKernBeforeDegree:CGFloat { constantFromTable("RadicalKernBeforeDegree") } // 5 mu in Tex + var radicalKernAfterDegree:CGFloat { constantFromTable("RadicalKernAfterDegree") } // -10 mu in Tex + var radicalDegreeBottomRaisePercent:CGFloat { percentFromTable("RadicalDegreeBottomRaisePercent") } // 60% in Tex + + // MARK: - Limits + var upperLimitBaselineRiseMin:CGFloat { constantFromTable("UpperLimitBaselineRiseMin") } // \xi_11 in TeX + var upperLimitGapMin:CGFloat { constantFromTable("UpperLimitGapMin") } // \xi_9 in TeX + var lowerLimitGapMin:CGFloat { constantFromTable("LowerLimitGapMin") } // \xi_10 in TeX + var lowerLimitBaselineDropMin:CGFloat { constantFromTable("LowerLimitBaselineDropMin") } // \xi_12 in TeX + var limitExtraAscenderDescender:CGFloat { 0 } // \xi_13 in TeX, not present in OpenType so we always set it to 0. + + // MARK: - Underline + var underbarVerticalGap:CGFloat { constantFromTable("UnderbarVerticalGap") } // 3 \xi_8 in TeX + var underbarRuleThickness:CGFloat { constantFromTable("UnderbarRuleThickness") } // \xi_8 in TeX + var underbarExtraDescender:CGFloat { constantFromTable("UnderbarExtraDescender") } // \xi_8 in TeX + + // MARK: - Overline + var overbarVerticalGap:CGFloat { constantFromTable("OverbarVerticalGap") } // 3 \xi_8 in TeX + var overbarRuleThickness:CGFloat { constantFromTable("OverbarRuleThickness") } // \xi_8 in TeX + var overbarExtraAscender:CGFloat { constantFromTable("OverbarExtraAscender") } // \xi_8 in TeX + + // MARK: - Constants + + var axisHeight:CGFloat { constantFromTable("AxisHeight") } // \sigma_22 in TeX + var scriptScaleDown:CGFloat { percentFromTable("ScriptPercentScaleDown") } + var scriptScriptScaleDown:CGFloat { percentFromTable("ScriptScriptPercentScaleDown") } + var mathLeading:CGFloat { constantFromTable("MathLeading") } + var delimitedSubFormulaMinHeight:CGFloat { constantFromTable("DelimitedSubFormulaMinHeight") } + + // MARK: - Accent + + var accentBaseHeight:CGFloat { constantFromTable("AccentBaseHeight") } // \fontdimen5 in TeX (x-height) + var flattenedAccentBaseHeight:CGFloat { constantFromTable("FlattenedAccentBaseHeight") } + + + // MARK: - Variants + + let kVertVariants = "v_variants" + let kHorizVariants = "h_variants" + + /** Returns an Array of all the vertical variants of the glyph if any. If + there are no variants for the glyph, the array contains the given glyph. */ + func getVerticalVariantsForGlyph( _ glyph: CGGlyph) -> [NSNumber?] { + guard let variants = font.mathTable()?[kVertVariants] as? NSDictionary else { + fatalError("\(#function) unable to extract \(glyph) from plist") + } + return self.getVariantsForGlyph(glyph, inDictionary: variants) + } + + /** Returns an Array of all the horizontal variants of the glyph if any. If + there are no variants for the glyph, the array contains the given glyph. */ + func getHorizontalVariantsForGlyph( _ glyph: CGGlyph) -> [NSNumber?] { + guard let variants = font.mathTable()?[kHorizVariants] as? NSDictionary else { + fatalError("\(#function) unable to extract \(glyph) from plist") + } + return self.getVariantsForGlyph(glyph, inDictionary:variants) + } + + func getVariantsForGlyph(_ glyph: CGGlyph, inDictionary variants: NSDictionary) -> [NSNumber?] { + let glyphName = font.get(nameForGlyph: glyph) + let variantGlyphs = variants[glyphName] as? NSArray + var glyphArray = [NSNumber]() + if variantGlyphs == nil || variantGlyphs?.count == 0, let glyph = font.get(glyphWithName: glyphName) { + // There are no extra variants, so just add the current glyph to it. + glyphArray.append(NSNumber(value:glyph)) + return glyphArray + } else if let variantGlyphs = variantGlyphs { + for gvn in variantGlyphs { + if let glyphVariantName = gvn as? String, let variantGlyph = font.get(glyphWithName: glyphVariantName) { + glyphArray.append(NSNumber(value:variantGlyph)) + } + } + } + return glyphArray + } + + /** Returns a larger vertical variant of the given glyph if any. + If there is no larger version, this returns the current glyph. + */ + func getLargerGlyph(_ glyph:CGGlyph) -> CGGlyph { + let variants = font.mathTable()?[kVertVariants] as? NSDictionary + let glyphName = font.get(nameForGlyph: glyph) + let variantGlyphs = variants![glyphName] as? NSArray + if variantGlyphs == nil || variantGlyphs?.count == 0 { + // There are no extra variants, so just returnt the current glyph. + return glyph + } + // Find the first variant with a different name. + for gvn in variantGlyphs! { + if let glyphVariantName = gvn as? String, + glyphVariantName != glyphName, + let variantGlyph = font.get(glyphWithName: glyphVariantName) { + return variantGlyph + } + } + // We did not find any variants of this glyph so return it. + return glyph; + } + + // MARK: - Italic Correction + + let kItalic = "italic" + + /** Returns the italic correction for the given glyph if any. If there + isn't any this returns 0. */ + func getItalicCorrection(_ glyph: CGGlyph) -> CGFloat { + let italics = font.mathTable()?[kItalic] as? NSDictionary + let glyphName = font.get(nameForGlyph: glyph) + let val = italics![glyphName] as? NSNumber + // if val is nil, this returns 0. + return fontUnitsToPt(val?.intValue ?? 0) + } + + // MARK: - Accents + + let kAccents = "accents" + + /** Returns the adjustment to the top accent for the given glyph if any. + If there isn't any this returns -1. */ + func getTopAccentAdjustment(_ glyph: CGGlyph) -> CGFloat { + var glyph = glyph + let accents = font.mathTable()?[kAccents] as? NSDictionary + let glyphName = font.get(nameForGlyph: glyph) + let val = accents![glyphName] as? NSNumber + if let val = val { + return self.fontUnitsToPt(val.intValue) + } else { + // If no top accent is defined then it is the center of the advance width. + var advances = CGSize.zero + guard let ctFont = font.ctFont(withSize: fontSize) else { + fatalError("\(#function) unable to obtain ctFont \(font.rawValue) with size \(fontSize)") + } + CTFontGetAdvancesForGlyphs(ctFont, .horizontal, &glyph, &advances, 1) + return advances.width/2 + } + } + + // MARK: - Glyph Construction + + /** Minimum overlap of connecting glyphs during glyph construction */ + var minConnectorOverlap:CGFloat { constantFromTable("MinConnectorOverlap") } + + let kVertAssembly = "v_assembly" + let kAssemblyParts = "parts" + + /** Returns an array of the glyph parts to be used for constructing vertical variants + of this glyph. If there is no glyph assembly defined, returns an empty array. */ + func getVerticalGlyphAssembly(forGlyph glyph:CGGlyph) -> [GlyphPart] { + let assemblyTable = font.mathTable()?[kVertAssembly] as? NSDictionary + let glyphName = font.get(nameForGlyph: glyph) + guard let assemblyInfo = assemblyTable?[glyphName] as? NSDictionary, + let parts = assemblyInfo[kAssemblyParts] as? NSArray else { + // No vertical assembly defined for glyph + // parts should always have been defined, but if it isn't return nil + return [] + } + var rv = [GlyphPart]() + for part in parts { + let partInfo = part as? NSDictionary + var part = GlyphPart() + if let adv = partInfo?["advance"] as? NSNumber, + let end = partInfo?["endConnector"] as? NSNumber, + let start = partInfo?["startConnector"] as? NSNumber, + let ext = partInfo?["extender"] as? NSNumber, + let glyphName = partInfo?["glyph"] as? String { + part.fullAdvance = fontUnitsToPt(adv.intValue) + part.endConnectorLength = fontUnitsToPt(end.intValue) + part.startConnectorLength = fontUnitsToPt(start.intValue) + part.isExtender = ext.boolValue + part.glyph = font.get(glyphWithName: glyphName) + rv.append(part) + } + } + return rv + } + +} diff --git a/Tests/SwiftMathTests/MathFontTests.swift b/Tests/SwiftMathTests/MathFontTests.swift new file mode 100644 index 0000000..65ddeac --- /dev/null +++ b/Tests/SwiftMathTests/MathFontTests.swift @@ -0,0 +1,26 @@ +import XCTest +@testable import SwiftMath + +// +// MathFontTests.swift +// +// +// Created by Peter Tang on 12/9/2023. +// + +final class MathFontTests: XCTestCase { + func testMathFontScript() throws { + // for family in UIFont.familyNames.sorted() { + // let names = UIFont.fontNames(forFamilyName: family) + // print("Family: \(family) Font names: \(names)") + // } + let size = Int.random(in: 20 ... 40) + MathFont.allCases.forEach { + // print("\(#function) cgfont \($0.cgFont())") + // print("\(#function) ctfont \($0.ctFont(withSize: CGFloat(size)))") + XCTAssertNotNil($0.cgFont()) + XCTAssertNotNil($0.ctFont(withSize: CGFloat(size))) + XCTAssertEqual($0.ctFont(withSize: CGFloat(size))?.fontSize, CGFloat(size), "ctFont fontSize test") + } + } +} diff --git a/Tests/SwiftMathTests/MathTableTests.swift b/Tests/SwiftMathTests/MathTableTests.swift new file mode 100644 index 0000000..21798b8 --- /dev/null +++ b/Tests/SwiftMathTests/MathTableTests.swift @@ -0,0 +1,36 @@ +import XCTest +@testable import SwiftMath + +// +// MathTableTests.swift +// +// +// Created by Peter Tang on 12/9/2023. +// + +final class MathTableTests: XCTestCase { + func testMathFontScript() throws { + let size = Int.random(in: 20 ... 40) + MathFont.allCases.forEach { + // print("\(#function) cgfont \($0.cgFont())") + // print("\(#function) ctfont \($0.ctFont(withSize: CGFloat(size)))") + // XCTAssertNotNil($0.cgFont()) + // XCTAssertNotNil($0.ctFont(withSize: CGFloat(size))) + // XCTAssertEqual($0.ctFont(withSize: CGFloat(size))?.fontSize, CGFloat(size), "ctFont fontSize test") + let ctFont = $0.ctFont(withSize: CGFloat(size)) + if let unitsPerEm = ctFont?.unitsPerEm { + let mathTable = MathTable(withFont: $0, fontSize: CGFloat(size), unitsPerEm: unitsPerEm) + + let values = [ + mathTable.fractionNumeratorDisplayStyleShiftUp, + mathTable.fractionNumeratorShiftUp, + mathTable.fractionDenominatorDisplayStyleShiftDown, + mathTable.fractionDenominatorShiftDown, + mathTable.fractionNumeratorDisplayStyleGapMin, + mathTable.fractionNumeratorGapMin, + ] + print("\(ctFont) -> \(values)") + } + } + } +} From d1c4ac77a600ef9a4aac80102ac2abd02bc327ea Mon Sep 17 00:00:00 2001 From: Peter Tang Date: Tue, 12 Sep 2023 08:28:24 +0800 Subject: [PATCH 04/10] embedded GlyphPart within MathTable, MTTypesetter is not yet using this GlyphPart. --- Sources/SwiftMath/MathBundle/MathTable.swift | 36 +++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/Sources/SwiftMath/MathBundle/MathTable.swift b/Sources/SwiftMath/MathBundle/MathTable.swift index 0f8a604..1c2028f 100644 --- a/Sources/SwiftMath/MathBundle/MathTable.swift +++ b/Sources/SwiftMath/MathBundle/MathTable.swift @@ -8,23 +8,6 @@ import Foundation import CoreText -// struct GlyphPart { -// /// The glyph that represents this part -// var glyph: CGGlyph! -// -// /// Full advance width/height for this part, in the direction of the extension in points. -// var fullAdvance: CGFloat = 0 -// -// /// Advance width/ height of the straight bar connector material at the beginning of the glyph in points. -// var startConnectorLength: CGFloat = 0 -// -// /// Advance width/ height of the straight bar connector material at the end of the glyph in points. -// var endConnectorLength: CGFloat = 0 -// -// /// If this part is an extender. If set, the part can be skipped or repeated. -// var isExtender: Bool = false -// } - /** This class represents the Math table of an open type font. The math table is documented here: https://www.microsoft.com/typography/otspec/math.htm @@ -306,3 +289,22 @@ internal struct MathTable { } } +extension MathTable { + + struct GlyphPart { + /// The glyph that represents this part + var glyph: CGGlyph! + + /// Full advance width/height for this part, in the direction of the extension in points. + var fullAdvance: CGFloat = 0 + + /// Advance width/ height of the straight bar connector material at the beginning of the glyph in points. + var startConnectorLength: CGFloat = 0 + + /// Advance width/ height of the straight bar connector material at the end of the glyph in points. + var endConnectorLength: CGFloat = 0 + + /// If this part is an extender. If set, the part can be skipped or repeated. + var isExtender: Bool = false + } +} From 2d579c11920396be6c13f689e028659cb8a00350 Mon Sep 17 00:00:00 2001 From: Peter Tang Date: Tue, 12 Sep 2023 08:57:39 +0800 Subject: [PATCH 05/10] modified declaration glyph in GlyphPart within MathTable. --- Sources/SwiftMath/MathBundle/MathTable.swift | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/Sources/SwiftMath/MathBundle/MathTable.swift b/Sources/SwiftMath/MathBundle/MathTable.swift index 1c2028f..944200a 100644 --- a/Sources/SwiftMath/MathBundle/MathTable.swift +++ b/Sources/SwiftMath/MathBundle/MathTable.swift @@ -270,18 +270,17 @@ internal struct MathTable { } var rv = [GlyphPart]() for part in parts { - let partInfo = part as? NSDictionary - var part = GlyphPart() - if let adv = partInfo?["advance"] as? NSNumber, - let end = partInfo?["endConnector"] as? NSNumber, - let start = partInfo?["startConnector"] as? NSNumber, - let ext = partInfo?["extender"] as? NSNumber, - let glyphName = partInfo?["glyph"] as? String { + guard let partInfo = part as? NSDictionary, let glyph = font.get(glyphWithName: glyphName) else { continue } + var part = GlyphPart(glyph: glyph) + if let adv = partInfo["advance"] as? NSNumber, + let end = partInfo["endConnector"] as? NSNumber, + let start = partInfo["startConnector"] as? NSNumber, + let ext = partInfo["extender"] as? NSNumber, + let glyphName = partInfo["glyph"] as? String { part.fullAdvance = fontUnitsToPt(adv.intValue) part.endConnectorLength = fontUnitsToPt(end.intValue) part.startConnectorLength = fontUnitsToPt(start.intValue) part.isExtender = ext.boolValue - part.glyph = font.get(glyphWithName: glyphName) rv.append(part) } } @@ -290,10 +289,9 @@ internal struct MathTable { } extension MathTable { - struct GlyphPart { /// The glyph that represents this part - var glyph: CGGlyph! + var glyph: CGGlyph /// Full advance width/height for this part, in the direction of the extension in points. var fullAdvance: CGFloat = 0 From d1e9a39e11709a6e4fec48543f4df8363522aa78 Mon Sep 17 00:00:00 2001 From: Peter Tang Date: Tue, 12 Sep 2023 09:27:11 +0800 Subject: [PATCH 06/10] cache fontMathTable within MathTable to optimize. --- Sources/SwiftMath/MathBundle/MathTable.swift | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/Sources/SwiftMath/MathBundle/MathTable.swift b/Sources/SwiftMath/MathBundle/MathTable.swift index 944200a..8740206 100644 --- a/Sources/SwiftMath/MathBundle/MathTable.swift +++ b/Sources/SwiftMath/MathBundle/MathTable.swift @@ -28,23 +28,25 @@ internal struct MathTable { let font: MathFont private let unitsPerEm: UInt private let fontSize: CGFloat + weak var fontMathTable: NSDictionary? init(withFont font: MathFont, fontSize: CGFloat, unitsPerEm: UInt) { self.font = font self.unitsPerEm = unitsPerEm self.fontSize = fontSize + self.fontMathTable = font.mathTable() } func fontUnitsToPt(_ fontUnits: Int) -> CGFloat { CGFloat(fontUnits) * fontSize / CGFloat(unitsPerEm) } func constantFromTable(_ constName: String) -> CGFloat { - guard let consts = font.mathTable()?[kConstants] as? NSDictionary, let val = consts[constName] as? NSNumber else { + guard let consts = fontMathTable?[kConstants] as? NSDictionary, let val = consts[constName] as? NSNumber else { fatalError("\(#function) unable to extract \(constName) from plist") } return fontUnitsToPt(val.intValue) } func percentFromTable(_ percentName: String) -> CGFloat { - guard let consts = font.mathTable()?[kConstants] as? NSDictionary, let val = consts[percentName] as? NSNumber else { + guard let consts = fontMathTable?[kConstants] as? NSDictionary, let val = consts[percentName] as? NSNumber else { fatalError("\(#function) unable to extract \(percentName) from plist") } return CGFloat(val.floatValue) / 100 @@ -155,7 +157,7 @@ internal struct MathTable { /** Returns an Array of all the vertical variants of the glyph if any. If there are no variants for the glyph, the array contains the given glyph. */ func getVerticalVariantsForGlyph( _ glyph: CGGlyph) -> [NSNumber?] { - guard let variants = font.mathTable()?[kVertVariants] as? NSDictionary else { + guard let variants = fontMathTable?[kVertVariants] as? NSDictionary else { fatalError("\(#function) unable to extract \(glyph) from plist") } return self.getVariantsForGlyph(glyph, inDictionary: variants) @@ -164,7 +166,7 @@ internal struct MathTable { /** Returns an Array of all the horizontal variants of the glyph if any. If there are no variants for the glyph, the array contains the given glyph. */ func getHorizontalVariantsForGlyph( _ glyph: CGGlyph) -> [NSNumber?] { - guard let variants = font.mathTable()?[kHorizVariants] as? NSDictionary else { + guard let variants = fontMathTable?[kHorizVariants] as? NSDictionary else { fatalError("\(#function) unable to extract \(glyph) from plist") } return self.getVariantsForGlyph(glyph, inDictionary:variants) @@ -192,7 +194,7 @@ internal struct MathTable { If there is no larger version, this returns the current glyph. */ func getLargerGlyph(_ glyph:CGGlyph) -> CGGlyph { - let variants = font.mathTable()?[kVertVariants] as? NSDictionary + let variants = fontMathTable?[kVertVariants] as? NSDictionary let glyphName = font.get(nameForGlyph: glyph) let variantGlyphs = variants![glyphName] as? NSArray if variantGlyphs == nil || variantGlyphs?.count == 0 { @@ -218,7 +220,7 @@ internal struct MathTable { /** Returns the italic correction for the given glyph if any. If there isn't any this returns 0. */ func getItalicCorrection(_ glyph: CGGlyph) -> CGFloat { - let italics = font.mathTable()?[kItalic] as? NSDictionary + let italics = fontMathTable?[kItalic] as? NSDictionary let glyphName = font.get(nameForGlyph: glyph) let val = italics![glyphName] as? NSNumber // if val is nil, this returns 0. @@ -233,7 +235,7 @@ internal struct MathTable { If there isn't any this returns -1. */ func getTopAccentAdjustment(_ glyph: CGGlyph) -> CGFloat { var glyph = glyph - let accents = font.mathTable()?[kAccents] as? NSDictionary + let accents = fontMathTable?[kAccents] as? NSDictionary let glyphName = font.get(nameForGlyph: glyph) let val = accents![glyphName] as? NSNumber if let val = val { @@ -260,7 +262,7 @@ internal struct MathTable { /** Returns an array of the glyph parts to be used for constructing vertical variants of this glyph. If there is no glyph assembly defined, returns an empty array. */ func getVerticalGlyphAssembly(forGlyph glyph:CGGlyph) -> [GlyphPart] { - let assemblyTable = font.mathTable()?[kVertAssembly] as? NSDictionary + let assemblyTable = fontMathTable?[kVertAssembly] as? NSDictionary let glyphName = font.get(nameForGlyph: glyph) guard let assemblyInfo = assemblyTable?[glyphName] as? NSDictionary, let parts = assemblyInfo[kAssemblyParts] as? NSArray else { @@ -276,7 +278,7 @@ internal struct MathTable { let end = partInfo["endConnector"] as? NSNumber, let start = partInfo["startConnector"] as? NSNumber, let ext = partInfo["extender"] as? NSNumber, - let glyphName = partInfo["glyph"] as? String { + let partInfoGlyphName = partInfo["glyph"] as? String, partInfoGlyphName == glyphName { part.fullAdvance = fontUnitsToPt(adv.intValue) part.endConnectorLength = fontUnitsToPt(end.intValue) part.startConnectorLength = fontUnitsToPt(start.intValue) From 85d25cf17786ac722890839b01eda6ea91b9f99a Mon Sep 17 00:00:00 2001 From: Peter Tang Date: Tue, 12 Sep 2023 09:38:11 +0800 Subject: [PATCH 07/10] clean up array contents within BundleManager. --- Sources/SwiftMath/MathBundle/MathFont.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/SwiftMath/MathBundle/MathFont.swift b/Sources/SwiftMath/MathBundle/MathFont.swift index 66d4c01..5e6d443 100644 --- a/Sources/SwiftMath/MathBundle/MathFont.swift +++ b/Sources/SwiftMath/MathBundle/MathFont.swift @@ -129,6 +129,7 @@ private class BundleManager { cgFonts.values.forEach { cgFont in CTFontManagerUnregisterGraphicsFont(cgFont, &errorRef) } + cgFonts.removeAll() } public enum FontError: Error { case invalidFontFile From 490790c06f3cb25a8f3addd5731706bc521b1086 Mon Sep 17 00:00:00 2001 From: Peter Tang Date: Tue, 12 Sep 2023 18:10:05 +0800 Subject: [PATCH 08/10] fixed UIFont and NSFont issue, their names are different from the otf file names --- Sources/SwiftMath/MathBundle/MathFont.swift | 37 +++++++++++++++++++-- Tests/SwiftMathTests/MathFontTests.swift | 26 ++++++++++++--- 2 files changed, 57 insertions(+), 6 deletions(-) diff --git a/Sources/SwiftMath/MathBundle/MathFont.swift b/Sources/SwiftMath/MathBundle/MathFont.swift index 5e6d443..caba500 100644 --- a/Sources/SwiftMath/MathBundle/MathFont.swift +++ b/Sources/SwiftMath/MathBundle/MathFont.swift @@ -5,8 +5,13 @@ // Created by Peter Tang on 10/9/2023. // -import Foundation -import CoreText +#if os(iOS) +import UIKit +#endif + +#if os(macOS) +import AppKit +#endif public enum MathFont: String, CaseIterable { @@ -16,12 +21,40 @@ public enum MathFont: String, CaseIterable { case xitsFont = "xits-math" case termesFont = "texgyretermes-math" + var fontFamilyName: String { + switch self { + case .latinModernFont: return "Latin Modern Math" + case .kpMathLightFont: return "KpMath" + case .kpMathSansFont: return "KpMath" + case .xitsFont: return "XITS Math" + case .termesFont: return "TeX Gyre Termes Math" + } + } + var fontName: String { + switch self { + case .latinModernFont: return "LatinModernMath-Regular" + case .kpMathLightFont: return "KpMath-Light" + case .kpMathSansFont: return "KpMath-Sans" + case .xitsFont: return "XITSMath" + case .termesFont: return "TeXGyreTermesMath-Regular" + } + } public func cgFont() -> CGFont? { BundleManager.manager.obtainCGFont(font: self) } public func ctFont(withSize size: CGFloat) -> CTFont? { BundleManager.manager.obtainCTFont(font: self, withSize: size) } + #if os(iOS) + public func uiFont(withSize size: CGFloat) -> UIFont? { + UIFont(name: fontName, size: size) + } + #endif + #if os(macOS) + public func nsFont(withSize size: CGFloat) -> NSFont? { + NSFont(name: fontName, size: size) + } + #endif internal func mathTable() -> NSDictionary? { BundleManager.manager.obtainMathTable(font: self) } diff --git a/Tests/SwiftMathTests/MathFontTests.swift b/Tests/SwiftMathTests/MathFontTests.swift index 65ddeac..c417cfa 100644 --- a/Tests/SwiftMathTests/MathFontTests.swift +++ b/Tests/SwiftMathTests/MathFontTests.swift @@ -10,10 +10,6 @@ import XCTest final class MathFontTests: XCTestCase { func testMathFontScript() throws { - // for family in UIFont.familyNames.sorted() { - // let names = UIFont.fontNames(forFamilyName: family) - // print("Family: \(family) Font names: \(names)") - // } let size = Int.random(in: 20 ... 40) MathFont.allCases.forEach { // print("\(#function) cgfont \($0.cgFont())") @@ -22,5 +18,27 @@ final class MathFontTests: XCTestCase { XCTAssertNotNil($0.ctFont(withSize: CGFloat(size))) XCTAssertEqual($0.ctFont(withSize: CGFloat(size))?.fontSize, CGFloat(size), "ctFont fontSize test") } + #if os(iOS) + // for family in UIFont.familyNames.sorted() { + // let names = UIFont.fontNames(forFamilyName: family) + // print("Family: \(family) Font names: \(names)") + // } + fontNames.forEach { name in + let font = UIFont(name: name, size: CGFloat(size)) + XCTAssertNotNil(font) + } + #endif + #if os(macOS) + fontNames.forEach { name in + let font = NSFont(name: name, size: CGFloat(size)) + XCTAssertNotNil(font) + } + #endif + } + var fontNames: [String] { + MathFont.allCases.map { $0.fontName } + } + var fontFamilyNames: [String] { + MathFont.allCases.map { $0.fontFamilyName } } } From 6049e93c07b9ffc3ddb27477662bb03308871dea Mon Sep 17 00:00:00 2001 From: Peter Tang Date: Tue, 12 Sep 2023 18:22:32 +0800 Subject: [PATCH 09/10] added test case around UIFont family names. --- Tests/SwiftMathTests/MathFontTests.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Tests/SwiftMathTests/MathFontTests.swift b/Tests/SwiftMathTests/MathFontTests.swift index c417cfa..dff78fb 100644 --- a/Tests/SwiftMathTests/MathFontTests.swift +++ b/Tests/SwiftMathTests/MathFontTests.swift @@ -24,8 +24,10 @@ final class MathFontTests: XCTestCase { // print("Family: \(family) Font names: \(names)") // } fontNames.forEach { name in - let font = UIFont(name: name, size: CGFloat(size)) - XCTAssertNotNil(font) + XCTAssertNotNil(UIFont(name: name, size: CGFloat(size))) + } + fontFamilyNames.forEach { name in + XCTAssertNotNil(UIFont.fontNames(forFamilyName: name)) } #endif #if os(macOS) From 4d922a5638cf789cc1b575f1b71e8d9b36bc4cb1 Mon Sep 17 00:00:00 2001 From: Peter Tang Date: Tue, 12 Sep 2023 18:40:01 +0800 Subject: [PATCH 10/10] updated all resource names separate from fontName, fontFamilyName. --- Sources/SwiftMath/MathBundle/MathFont.swift | 5 +++-- Sources/SwiftMath/MathBundle/MathTable.swift | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Sources/SwiftMath/MathBundle/MathFont.swift b/Sources/SwiftMath/MathBundle/MathFont.swift index caba500..d7abd7c 100644 --- a/Sources/SwiftMath/MathBundle/MathFont.swift +++ b/Sources/SwiftMath/MathBundle/MathFont.swift @@ -104,7 +104,7 @@ private class BundleManager { guard CTFontManagerRegisterGraphicsFont(defaultCGFont, &errorRef) else { throw FontError.registerFailed } - print("mathFonts bundle: \(mathFont.rawValue) registered.") + print("mathFonts bundle resource: \(mathFont.rawValue), font: \(defaultCGFont.fullName) registered.") } private func registerMathTable(mathFont: MathFont) throws { @@ -119,6 +119,7 @@ private class BundleManager { } //FIXME: mathTable = MTFontMathTable(withFont:self, mathTable:rawMathTable) mathTables[mathFont] = rawMathTable + print("mathFonts bundle resource: \(mathFont.rawValue).plist registered.") } private func registerAllBundleResources() { @@ -128,7 +129,7 @@ private class BundleManager { try BundleManager.manager.registerCGFont(mathFont: font) try BundleManager.manager.registerMathTable(mathFont: font) } catch { - fatalError("MTMathFonts:\(#function) Couldn't load math fonts \(font.rawValue), reason \(error)") + fatalError("MTMathFonts:\(#function) Couldn't load mathFont resource \(font.rawValue), reason \(error)") } } initializedOnceAlready.toggle() diff --git a/Sources/SwiftMath/MathBundle/MathTable.swift b/Sources/SwiftMath/MathBundle/MathTable.swift index 8740206..e86886c 100644 --- a/Sources/SwiftMath/MathBundle/MathTable.swift +++ b/Sources/SwiftMath/MathBundle/MathTable.swift @@ -244,7 +244,7 @@ internal struct MathTable { // If no top accent is defined then it is the center of the advance width. var advances = CGSize.zero guard let ctFont = font.ctFont(withSize: fontSize) else { - fatalError("\(#function) unable to obtain ctFont \(font.rawValue) with size \(fontSize)") + fatalError("\(#function) unable to obtain ctFont resource name: \(font.rawValue) with size \(fontSize)") } CTFontGetAdvancesForGlyphs(ctFont, .horizontal, &glyph, &advances, 1) return advances.width/2