From d0b1ff94dfd7785b9b6681ebc87377e49f68a30c Mon Sep 17 00:00:00 2001 From: Michael Griebling Date: Tue, 17 Dec 2024 10:21:27 -0500 Subject: [PATCH] =?UTF-8?q?Added=20support=20for=20accented=20and=20specia?= =?UTF-8?q?l=20characters=20in=20the=20input=20text=20stream.=20(e.g.,=20?= =?UTF-8?q?=C3=A1=C3=A9=C3=AD=C3=B3=C3=BA=C3=BD=C3=A0=C3=A8=C3=AC=C3=B2?= =?UTF-8?q?=C3=B9=C3=A2=C3=AA=C3=AE=C3=B4=C3=BB=C3=A4=C3=AB=C3=AF=C3=B6?= =?UTF-8?q?=C3=BC=C3=BF=C3=A3=C3=B1=C3=B5=C3=A7=C3=B8=C3=A5=C3=A6=C5=93?= =?UTF-8?q?=C3=9F'=C3=81=C3=89=C3=8D=C3=93=C3=9A=C3=9D=C3=80=C3=88=C3=8C?= =?UTF-8?q?=C3=92=C3=99=C3=82=C3=8A=C3=8E=C3=94=C3=9B=C3=84=C3=8B=C3=8F?= =?UTF-8?q?=C3=96=C3=9C=C3=83=C3=91=C3=95=C3=87=C3=98=C3=85=C3=86=C5=92)?= =?UTF-8?q?=20Also=20added=20some=20new=20commands=20to=20support=20specia?= =?UTF-8?q?l=20characters=20like=20\aa=20(=C3=A5),=20etc.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/SwiftMath/MathBundle/MathFont.swift | 19 +---- .../MathRender/MTMathAtomFactory.swift | 71 ++++++++++++++++++- .../MTMathListBuilderTests.swift | 23 ++++++ 3 files changed, 96 insertions(+), 17 deletions(-) diff --git a/Sources/SwiftMath/MathBundle/MathFont.swift b/Sources/SwiftMath/MathBundle/MathFont.swift index 95010ce..0224611 100755 --- a/Sources/SwiftMath/MathBundle/MathFont.swift +++ b/Sources/SwiftMath/MathBundle/MathFont.swift @@ -45,22 +45,9 @@ public enum MathFont: String, CaseIterable, Identifiable { case .leteSansFont: "Lete Sans Math" } } - var fontName: String { - switch self { - case .latinModernFont: "LatinModernMath-Regular" - case .kpMathLightFont: "KpMath-Light" - case .kpMathSansFont: "KpMath-Sans" - case .xitsFont: "XITSMath" - case .termesFont: "TeXGyreTermesMath-Regular" - case .asanaFont: "Asana Math" - case .eulerFont: "Euler Math" - case .firaFont: "Fira Math" - case .notoSansFont: "Noto Sans Math" - case .libertinusFont: "Libertinus Math" - case .garamondFont: "Garamond Math" - case .leteSansFont: "Lete Sans Math" - } - } + + var fontName: String { self.rawValue } + public func cgFont() -> CGFont { BundleManager.manager.obtainCGFont(font: self) } diff --git a/Sources/SwiftMath/MathRender/MTMathAtomFactory.swift b/Sources/SwiftMath/MathRender/MTMathAtomFactory.swift index f9d9517..a0fc56c 100755 --- a/Sources/SwiftMath/MathRender/MTMathAtomFactory.swift +++ b/Sources/SwiftMath/MathRender/MTMathAtomFactory.swift @@ -402,6 +402,52 @@ public class MTMathAtomFactory { "scriptstyle" : MTMathStyle(style: .script), "scriptscriptstyle" : MTMathStyle(style: .scriptOfScript), ] + + static var supportedAccentedCharacters: [Character: (String, String)] = [ + // Acute accents + "á": ("acute", "a"), "é": ("acute", "e"), "í": ("acute", "i"), + "ó": ("acute", "o"), "ú": ("acute", "u"), "ý": ("acute", "y"), + + // Grave accents + "à": ("grave", "a"), "è": ("grave", "e"), "ì": ("grave", "i"), + "ò": ("grave", "o"), "ù": ("grave", "u"), + + // Circumflex + "â": ("hat", "a"), "ê": ("hat", "e"), "î": ("hat", "i"), + "ô": ("hat", "o"), "û": ("hat", "u"), + + // Umlaut/dieresis + "ä": ("ddot", "a"), "ë": ("ddot", "e"), "ï": ("ddot", "i"), + "ö": ("ddot", "o"), "ü": ("ddot", "u"), "ÿ": ("ddot", "y"), + + // Tilde + "ã": ("tilde", "a"), "ñ": ("tilde", "n"), "õ": ("tilde", "o"), + + // Special characters + "ç": ("c", "c"), + "ø": ("o", ""), + "å": ("aa", ""), + "æ": ("ae", ""), + "œ": ("oe", ""), + "ß": ("ss", ""), + "'": ("upquote", ""), + + // Upper case variants + "Á": ("acute", "A"), "É": ("acute", "E"), "Í": ("acute", "I"), + "Ó": ("acute", "O"), "Ú": ("acute", "U"), "Ý": ("acute", "Y"), + "À": ("grave", "A"), "È": ("grave", "E"), "Ì": ("grave", "I"), + "Ò": ("grave", "O"), "Ù": ("grave", "U"), + "Â": ("hat", "A"), "Ê": ("hat", "E"), "Î": ("hat", "I"), + "Ô": ("hat", "O"), "Û": ("hat", "U"), + "Ä": ("ddot", "A"), "Ë": ("ddot", "E"), "Ï": ("ddot", "I"), + "Ö": ("ddot", "O"), "Ü": ("ddot", "U"), + "Ã": ("tilde", "A"), "Ñ": ("tilde", "N"), "Õ": ("tilde", "O"), + "Ç": ("c", "C"), + "Ø": ("O", ""), + "Å": ("AA", ""), + "Æ": ("AE", ""), + "Œ": ("OE", ""), + ] private static let textToLatexLock = NSLock() static var _textToLatexSymbolName: [String: String]? = nil @@ -531,6 +577,25 @@ public class MTMathAtomFactory { rad.degree?.add(placeholder()) return rad } + + public static func atom(fromAccentedCharacter ch: Character) -> MTMathAtom? { + if let symbol = supportedAccentedCharacters[ch] { + // first handle any special characters + if let atom = atom(forLatexSymbol: symbol.0) { + return atom + } + + if let accent = MTMathAtomFactory.accent(withName: symbol.0) { + // The command is an accent + let list = MTMathList() + let ch = Array(symbol.1)[0] + list.add(atom(forCharacter: ch)) + accent.innerList = list + return accent + } + } + return nil + } // MARK: - /** Gets the atom with the right type for the given character. If an atom @@ -541,13 +606,17 @@ public class MTMathAtomFactory { - Any control character or spaces (< 0x21) - Latex control chars: $ % # & ~ ' - Chars with special meaning in latex: ^ _ { } \ - All other characters will have a non-nil atom returned. + All other characters, including those with accents, will have a non-nil atom returned. */ public static func atom(forCharacter ch: Character) -> MTMathAtom? { let chStr = String(ch) switch chStr { case "\u{0410}"..."\u{044F}": + // Cyrillic alphabet return MTMathAtom(type: .ordinary, value: chStr) + case _ where supportedAccentedCharacters.keys.contains(ch): + // support for áéíóúýàèìòùâêîôûäëïöüÿãñõçøåæœß'ÁÉÍÓÚÝÀÈÌÒÙÂÊÎÔÛÄËÏÖÜÃÑÕÇØÅÆŒ + return atom(fromAccentedCharacter: ch) case _ where ch.utf32Char < 0x0021 || ch.utf32Char > 0x007E: return nil case "$", "%", "#", "&", "~", "\'", "^", "_", "{", "}", "\\": diff --git a/Tests/SwiftMathTests/MTMathListBuilderTests.swift b/Tests/SwiftMathTests/MTMathListBuilderTests.swift index 0a969fc..5a9e3fb 100755 --- a/Tests/SwiftMathTests/MTMathListBuilderTests.swift +++ b/Tests/SwiftMathTests/MTMathListBuilderTests.swift @@ -922,6 +922,29 @@ final class MTMathListBuilderTests: XCTestCase { let latex = MTMathListBuilder.mathListToString(list) XCTAssertEqual(latex, "\\bar{x}", desc); } + + func testAccentedCharacter() throws { + let str = "á" + let list = MTMathListBuilder.build(fromString: str)! + let desc = "Error for string:\(str)" + + XCTAssertNotNil(list, desc) + XCTAssertEqual((list.atoms.count), 1, desc) + let accent = list.atoms[0] as! MTAccent + XCTAssertEqual(accent.type, .accent, desc) + XCTAssertEqual(accent.nucleus, "\u{0301}", desc) + + let subList = accent.innerList! + XCTAssertNotNil(subList, desc) + XCTAssertEqual((subList.atoms.count), 1, desc) + let atom = subList.atoms[0] + XCTAssertEqual(atom.type, .variable, desc) + XCTAssertEqual(atom.nucleus, "a", desc) + + // convert it back to latex + let latex = MTMathListBuilder.mathListToString(list) + XCTAssertEqual(latex, "\\acute{a}", desc) + } func testMathSpace() throws { let str = "\\!";