Updated comments and minor fixes.
This commit is contained in:
31
README.md
31
README.md
@@ -4,20 +4,19 @@
|
|||||||
for displaying beautifully rendered math equations in iOS and MacOS applications. It typesets formulae written
|
for displaying beautifully rendered math equations in iOS and MacOS applications. It typesets formulae written
|
||||||
using LaTeX in a `UILabel` equivalent class. It uses the same typesetting rules as LaTeX and
|
using LaTeX in a `UILabel` equivalent class. It uses the same typesetting rules as LaTeX and
|
||||||
so the equations are rendered exactly as LaTeX would render them.
|
so the equations are rendered exactly as LaTeX would render them.
|
||||||
`
|
|
||||||
SwiftMath` is a Swift translation of the latest `iosMath` v0.9.5 release but includes bug fixes
|
`SwiftMath` is similar to [MathJax](https://www.mathjax.org) or
|
||||||
|
[KaTeX](https://github.com/Khan/KaTeX) for the web but for native iOS or MacOS
|
||||||
|
applications without having to use a `UIWebView` and Javascript. More
|
||||||
|
importantly, it is significantly faster than using a `UIWebView`.
|
||||||
|
|
||||||
|
`SwiftMath` is a Swift translation of the latest `iosMath` v0.9.5 release but includes bug fixes
|
||||||
and enhancements like a new \lbar (lambda bar) character and cyrillic alphabet support.
|
and enhancements like a new \lbar (lambda bar) character and cyrillic alphabet support.
|
||||||
The original `iosMath` test suites have also been translated to Swift and run without errors.
|
The original `iosMath` test suites have also been translated to Swift and run without errors.
|
||||||
Note: Error test conditions are ignored to avoid tagging everything with silly `throw`s.
|
Note: Error test conditions are ignored to avoid tagging everything with silly `throw`s.
|
||||||
Please let me know of any bugs or bug fixes that you find.
|
Please let me know of any bugs or bug fixes that you find.
|
||||||
|
|
||||||
`SwiftMath` prepackages everything needed for direct access via the Swift Package Manager.
|
`SwiftMath` prepackages everything needed for direct access via the Swift Package Manager.
|
||||||
No need for complicated alien pods that never seem to work quite right.
|
|
||||||
|
|
||||||
It is similar to [MathJax](https://www.mathjax.org) or
|
|
||||||
[KaTeX](https://github.com/Khan/KaTeX) for the web but for native iOS or MacOS
|
|
||||||
applications without having to use a `UIWebView` and Javascript. More
|
|
||||||
importantly, it is significantly faster than using a `UIWebView`.
|
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
Here are screenshots of some formulae that were rendered with this library:
|
Here are screenshots of some formulae that were rendered with this library:
|
||||||
@@ -50,7 +49,7 @@ f(x) = \int\limits_{-\infty}^\infty\!\hat f(\xi)\,e^{2 \pi i \xi x}\,\mathrm{d}\
|
|||||||
More examples are included in [EXAMPLES](EXAMPLES.md)
|
More examples are included in [EXAMPLES](EXAMPLES.md)
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
`SwiftMath` works on iOS 6+ or MacOS 10.8+ and requires ARC to build. It depends
|
`SwiftMath` works on iOS 11+ or MacOS 11+. It depends
|
||||||
on the following Apple frameworks:
|
on the following Apple frameworks:
|
||||||
|
|
||||||
* Foundation.framework
|
* Foundation.framework
|
||||||
@@ -97,8 +96,8 @@ import SwiftMath
|
|||||||
|
|
||||||
struct MathView: UIViewRepresentable {
|
struct MathView: UIViewRepresentable {
|
||||||
|
|
||||||
@Binding var equation: String
|
var equation: String
|
||||||
@Binding var fontSize: CGFloat
|
var fontSize: CGFloat
|
||||||
|
|
||||||
func makeUIView(context: Context) -> MTMathUILabel {
|
func makeUIView(context: Context) -> MTMathUILabel {
|
||||||
let view = MTMathUILabel()
|
let view = MTMathUILabel()
|
||||||
@@ -115,7 +114,7 @@ struct MathView: UIViewRepresentable {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
If you need code that works with SwiftUI running natively under MacOS you'll need the following:
|
For code that works with SwiftUI running natively under MacOS use the following:
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
@@ -123,8 +122,8 @@ import SwiftMath
|
|||||||
|
|
||||||
struct MathView: NSViewRepresentable {
|
struct MathView: NSViewRepresentable {
|
||||||
|
|
||||||
@Binding var equation: String
|
var equation: String
|
||||||
@Binding var fontSize: CGFloat
|
var fontSize: CGFloat
|
||||||
|
|
||||||
func makeNSView(context: Context) -> MTMathUILabel {
|
func makeNSView(context: Context) -> MTMathUILabel {
|
||||||
let view = MTMathUILabel()
|
let view = MTMathUILabel()
|
||||||
@@ -168,7 +167,7 @@ This is a list of formula types that the library currently supports:
|
|||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
The [SwiftMathDemo](https://github.com/mgriebling/SwiftMathDemo) is a Swift version
|
The [SwiftMathDemo](https://github.com/mgriebling/SwiftMathDemo) is a SwiftUI version
|
||||||
of the Objective-C demo included in `iosMath` that uses `SwiftMath` as a Swift package dependency.
|
of the Objective-C demo included in `iosMath` that uses `SwiftMath` as a Swift package dependency.
|
||||||
|
|
||||||
### Advanced configuration
|
### Advanced configuration
|
||||||
@@ -204,7 +203,7 @@ label.fontSize = 30
|
|||||||
The default font is *Latin Modern Math*. This can be changed as:
|
The default font is *Latin Modern Math*. This can be changed as:
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
label.font = MTFontManager().termesFont(withSize:20)
|
label.font = MTFontManager.fontmanager.termesFont(withSize:20)
|
||||||
```
|
```
|
||||||
|
|
||||||
This project has 3 fonts bundled with it, but you can use any OTF math
|
This project has 3 fonts bundled with it, but you can use any OTF math
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
//
|
|
||||||
// MTBezierPath.swift
|
|
||||||
// MathRenderSwift
|
|
||||||
//
|
//
|
||||||
// Created by Mike Griebling on 2022-12-31.
|
// Created by Mike Griebling on 2022-12-31.
|
||||||
|
// Translated from an Objective-C implementation by 安志钢.
|
||||||
|
//
|
||||||
|
// This software may be modified and distributed under the terms of the
|
||||||
|
// MIT license. See the LICENSE file for details.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
//
|
|
||||||
// MTColor.swift
|
|
||||||
// MathRenderSwift
|
|
||||||
//
|
//
|
||||||
// Created by Mike Griebling on 2022-12-31.
|
// Created by Mike Griebling on 2022-12-31.
|
||||||
|
// Translated from an Objective-C implementation by Markus Sähn.
|
||||||
|
//
|
||||||
|
// This software may be modified and distributed under the terms of the
|
||||||
|
// MIT license. See the LICENSE file for details.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
@@ -19,7 +21,8 @@ extension MTColor {
|
|||||||
scanner.scanHexInt64(&rgbValue)
|
scanner.scanHexInt64(&rgbValue)
|
||||||
return MTColor(red: CGFloat((rgbValue & 0xFF0000) >> 16)/255.0,
|
return MTColor(red: CGFloat((rgbValue & 0xFF0000) >> 16)/255.0,
|
||||||
green: CGFloat((rgbValue & 0xFF00) >> 8)/255.0,
|
green: CGFloat((rgbValue & 0xFF00) >> 8)/255.0,
|
||||||
blue: CGFloat((rgbValue & 0xFF))/255.0, alpha: 1.0)
|
blue: CGFloat((rgbValue & 0xFF))/255.0,
|
||||||
|
alpha: 1.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
//
|
|
||||||
// MTConfig.swift
|
|
||||||
// MathRenderSwift
|
|
||||||
//
|
|
||||||
// Created by Mike Griebling on 2023-01-01.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
//
|
||||||
|
// Created by Mike Griebling on 2022-12-31.
|
||||||
|
// Translated from an Objective-C implementation by 安志钢.
|
||||||
|
//
|
||||||
|
// This software may be modified and distributed under the terms of the
|
||||||
|
// MIT license. See the LICENSE file for details.
|
||||||
|
//
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|||||||
@@ -1,17 +1,11 @@
|
|||||||
//
|
|
||||||
// MTFont.swift
|
|
||||||
// MathRenderSwift
|
|
||||||
//
|
|
||||||
// Created by Mike Griebling on 2022-12-31.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import CoreGraphics
|
import CoreGraphics
|
||||||
import CoreText
|
import CoreText
|
||||||
|
|
||||||
//
|
//
|
||||||
// Created by Kostub Deshmukh on 5/18/16.
|
// Created by Mike Griebling on 2022-12-31.
|
||||||
// Modified by Michael Griebling on 17 Jan 2023.
|
// Translated from an Objective-C implementation by Kostub Deshmukh.
|
||||||
//
|
//
|
||||||
// This software may be modified and distributed under the terms of the
|
// This software may be modified and distributed under the terms of the
|
||||||
// MIT license. See the LICENSE file for details.
|
// MIT license. See the LICENSE file for details.
|
||||||
@@ -26,10 +20,10 @@ public class MTFont {
|
|||||||
|
|
||||||
init() {}
|
init() {}
|
||||||
|
|
||||||
|
/// `MTFont(fontWithName:)` does not load the complete math font, it only has about half the glyphs of the full math font.
|
||||||
|
/// In particular it does not have the math italic characters which breaks our variable rendering.
|
||||||
|
/// So we first load a CGFont from the file and then convert it to a CTFont.
|
||||||
convenience init(fontWithName name: String, size:CGFloat) {
|
convenience init(fontWithName name: String, size:CGFloat) {
|
||||||
// CTFontCreateWithName does not load the complete math font, it only has about half the glyphs of the full math font.
|
|
||||||
// In particular it does not have the math italic characters which breaks our variable rendering.
|
|
||||||
// So we first load a CGFont from the file and then convert it to a CTFont.
|
|
||||||
self.init()
|
self.init()
|
||||||
print("Loading font \(name)")
|
print("Loading font \(name)")
|
||||||
let bundle = MTFont.fontBundle
|
let bundle = MTFont.fontBundle
|
||||||
@@ -51,7 +45,8 @@ public class MTFont {
|
|||||||
Bundle(url: Bundle.module.url(forResource: "mathFonts", withExtension: "bundle")!)!
|
Bundle(url: Bundle.module.url(forResource: "mathFonts", withExtension: "bundle")!)!
|
||||||
}
|
}
|
||||||
|
|
||||||
func copy(withSize size: CGFloat) -> MTFont {
|
/** Returns a copy of this font but with a different size. */
|
||||||
|
public func copy(withSize size: CGFloat) -> MTFont {
|
||||||
let newFont = MTFont()
|
let newFont = MTFont()
|
||||||
newFont.defaultCGFont = self.defaultCGFont
|
newFont.defaultCGFont = self.defaultCGFont
|
||||||
newFont.ctFont = CTFontCreateWithGraphicsFont(self.defaultCGFont, size, nil, nil)
|
newFont.ctFont = CTFontCreateWithGraphicsFont(self.defaultCGFont, size, nil, nil)
|
||||||
@@ -69,6 +64,7 @@ public class MTFont {
|
|||||||
defaultCGFont.getGlyphWithGlyphName(name: name as CFString)
|
defaultCGFont.getGlyphWithGlyphName(name: name as CFString)
|
||||||
}
|
}
|
||||||
|
|
||||||
var fontSize:CGFloat { CTFontGetSize(self.ctFont) }
|
/** The size of this font in points. */
|
||||||
|
public var fontSize:CGFloat { CTFontGetSize(self.ctFont) }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
//
|
|
||||||
// MTFontManager.swift
|
|
||||||
// MathRenderSwift
|
|
||||||
//
|
//
|
||||||
// Created by Mike Griebling on 2022-12-31.
|
// Created by Mike Griebling on 2022-12-31.
|
||||||
|
// Translated from an Objective-C implementation by Kostub Deshmukh.
|
||||||
|
//
|
||||||
|
// This software may be modified and distributed under the terms of the
|
||||||
|
// MIT license. See the LICENSE file for details.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
//
|
//
|
||||||
// MTFontMathTable.swift
|
|
||||||
// MathRenderSwift
|
|
||||||
//
|
|
||||||
// Created by Mike Griebling on 2022-12-31.
|
// Created by Mike Griebling on 2022-12-31.
|
||||||
|
// Translated from an Objective-C implementation by Kostub Deshmukh.
|
||||||
|
//
|
||||||
|
// This software may be modified and distributed under the terms of the
|
||||||
|
// MIT license. See the LICENSE file for details.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import CoreGraphics
|
import CoreGraphics
|
||||||
import CoreText
|
import CoreText
|
||||||
|
|
||||||
class GlyphPart {
|
struct GlyphPart {
|
||||||
/// The glyph that represents this part
|
/// The glyph that represents this part
|
||||||
var glyph: CGGlyph!
|
var glyph: CGGlyph!
|
||||||
|
|
||||||
@@ -33,14 +34,13 @@ class GlyphPart {
|
|||||||
How the constants in this class affect the display is documented here:
|
How the constants in this class affect the display is documented here:
|
||||||
http://www.tug.org/TUGboat/tb30-1/tb94vieth.pdf
|
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
|
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.
|
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
|
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.
|
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.
|
Remark: This class is not meant to be used outside of this library.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class MTFontMathTable {
|
class MTFontMathTable {
|
||||||
|
|
||||||
// The font for this math table.
|
// The font for this math table.
|
||||||
@@ -100,21 +100,21 @@ class MTFontMathTable {
|
|||||||
var skewedFractionVerticalGap:CGFloat { constantFromTable("SkewedFractionVerticalGap") } // \sigma_21 in TeX
|
var skewedFractionVerticalGap:CGFloat { constantFromTable("SkewedFractionVerticalGap") } // \sigma_21 in TeX
|
||||||
|
|
||||||
// MARK: - Non-standard
|
// MARK: - Non-standard
|
||||||
// FractionDelimiterSize and FractionDelimiterDisplayStyleSize are not constants
|
/// FractionDelimiterSize and FractionDelimiterDisplayStyleSize are not constants
|
||||||
// specified in the OpenType Math specification. Rather these are proposed LuaTeX extensions
|
/// 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
|
/// 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
|
/// 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
|
/// to determine these values. The constants used are the same as LuaTeX and KaTeX and match the
|
||||||
// metrics values of the original TeX fonts.
|
/// metrics values of the original TeX fonts.
|
||||||
// Note: An alternative approach is to use DelimitedSubFormulaMinHeight for \sigma21 and use a factor
|
/// 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.
|
/// of 2 to get \sigma 20 as proposed in Vieth paper.
|
||||||
// The XeTeX implementation sets \sigma21 = fontSize and \sigma20 = DelimitedSubFormulaMinHeight which
|
/// The XeTeX implementation sets \sigma21 = fontSize and \sigma20 = DelimitedSubFormulaMinHeight which
|
||||||
// will produce smaller delimiters.
|
/// will produce smaller delimiters.
|
||||||
// Of all the approaches we've implemented LuaTeX's approach since it mimics LaTeX most accurately.
|
/// Of all the approaches we've implemented LuaTeX's approach since it mimics LaTeX most accurately.
|
||||||
var fractionDelimiterSize: CGFloat { return 1.01 * _fontSize }
|
var fractionDelimiterSize: CGFloat { 1.01 * _fontSize }
|
||||||
|
|
||||||
/// Modified constant from 2.4 to 2.39, it matches KaTeX and looks better.
|
/// Modified constant from 2.4 to 2.39, it matches KaTeX and looks better.
|
||||||
var fractionDelimiterDisplayStyleSize: CGFloat { return 2.39 * _fontSize }
|
var fractionDelimiterDisplayStyleSize: CGFloat { 2.39 * _fontSize }
|
||||||
|
|
||||||
// MARK: - Stacks
|
// MARK: - Stacks
|
||||||
var stackTopDisplayStyleShiftUp:CGFloat { constantFromTable("StackTopDisplayStyleShiftUp") } // \sigma_8 in TeX
|
var stackTopDisplayStyleShiftUp:CGFloat { constantFromTable("StackTopDisplayStyleShiftUp") } // \sigma_8 in TeX
|
||||||
@@ -303,7 +303,7 @@ class MTFontMathTable {
|
|||||||
var rv = [GlyphPart]()
|
var rv = [GlyphPart]()
|
||||||
for part in parts! {
|
for part in parts! {
|
||||||
let partInfo = part as! NSDictionary?
|
let partInfo = part as! NSDictionary?
|
||||||
let part = GlyphPart()
|
var part = GlyphPart()
|
||||||
let adv = partInfo!["advance"] as! NSNumber?
|
let adv = partInfo!["advance"] as! NSNumber?
|
||||||
part.fullAdvance = self.fontUnitsToPt(adv!.intValue)
|
part.fullAdvance = self.fontUnitsToPt(adv!.intValue)
|
||||||
let end = partInfo!["endConnector"] as! NSNumber?
|
let end = partInfo!["endConnector"] as! NSNumber?
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
//
|
//
|
||||||
// MTLabel.swift
|
|
||||||
// MathRenderSwift
|
|
||||||
//
|
|
||||||
// Created by Mike Griebling on 2022-12-31.
|
// Created by Mike Griebling on 2022-12-31.
|
||||||
|
// Translated from an Objective-C implementation by 安志钢.
|
||||||
|
//
|
||||||
|
// This software may be modified and distributed under the terms of the
|
||||||
|
// MIT license. See the LICENSE file for details.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
@@ -25,6 +26,7 @@ public class MTLabel : NSTextField {
|
|||||||
super.init(coder: coder)
|
super.init(coder: coder)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Customized getter and setter methods for property text.
|
||||||
var text:String? {
|
var text:String? {
|
||||||
get { super.stringValue }
|
get { super.stringValue }
|
||||||
set { super.stringValue = newValue! }
|
set { super.stringValue = newValue! }
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
//
|
//
|
||||||
// MTMathAtomFactory.swift
|
|
||||||
// MathRenderSwift
|
|
||||||
//
|
|
||||||
// Created by Mike Griebling on 2022-12-31.
|
// Created by Mike Griebling on 2022-12-31.
|
||||||
|
// Translated from an Objective-C implementation by Kostub Deshmukh.
|
||||||
|
//
|
||||||
|
// This software may be modified and distributed under the terms of the
|
||||||
|
// MIT license. See the LICENSE file for details.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
/** A factory to create commonly used MTMathAtoms. */
|
||||||
public class MTMathAtomFactory {
|
public class MTMathAtomFactory {
|
||||||
|
|
||||||
public static let aliases = [
|
public static let aliases = [
|
||||||
@@ -60,11 +62,10 @@ public class MTMathAtomFactory {
|
|||||||
"rfloor" : "\u{230B}"
|
"rfloor" : "\u{230B}"
|
||||||
]
|
]
|
||||||
|
|
||||||
var _delimValueToName: [String: String]? = nil
|
static var _delimValueToName = [String: String]()
|
||||||
public var delimValueToName: [String: String] {
|
public static var delimValueToName: [String: String] {
|
||||||
if _delimValueToName == nil {
|
if _delimValueToName.isEmpty {
|
||||||
var output = [String: String]()
|
var output = [String: String]()
|
||||||
|
|
||||||
for (key, value) in Self.delimiters {
|
for (key, value) in Self.delimiters {
|
||||||
if let existingValue = output[value] {
|
if let existingValue = output[value] {
|
||||||
if key.count > existingValue.count {
|
if key.count > existingValue.count {
|
||||||
@@ -75,12 +76,11 @@ public class MTMathAtomFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
output[value] = key
|
output[value] = key
|
||||||
}
|
}
|
||||||
_delimValueToName = output
|
_delimValueToName = output
|
||||||
}
|
}
|
||||||
return _delimValueToName!
|
return _delimValueToName
|
||||||
}
|
}
|
||||||
|
|
||||||
public static let accents = [
|
public static let accents = [
|
||||||
@@ -389,8 +389,8 @@ public class MTMathAtomFactory {
|
|||||||
"scriptscriptstyle" : MTMathStyle(style: .scriptOfScript),
|
"scriptscriptstyle" : MTMathStyle(style: .scriptOfScript),
|
||||||
]
|
]
|
||||||
|
|
||||||
var _textToLatexSymbolName: [String: String]? = nil
|
static var _textToLatexSymbolName: [String: String]? = nil
|
||||||
public var textToLatexSymbolName: [String: String] {
|
public static var textToLatexSymbolName: [String: String] {
|
||||||
get {
|
get {
|
||||||
if self._textToLatexSymbolName == nil {
|
if self._textToLatexSymbolName == nil {
|
||||||
var output = [String: String]()
|
var output = [String: String]()
|
||||||
@@ -421,7 +421,7 @@ public class MTMathAtomFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static let sharedInstance = MTMathAtomFactory()
|
// public static let sharedInstance = MTMathAtomFactory()
|
||||||
|
|
||||||
static let fontStyles : [String: MTFontStyle] = [
|
static let fontStyles : [String: MTFontStyle] = [
|
||||||
"mathnormal" : .defaultStyle,
|
"mathnormal" : .defaultStyle,
|
||||||
@@ -449,7 +449,7 @@ public class MTMathAtomFactory {
|
|||||||
]
|
]
|
||||||
|
|
||||||
public static func fontStyleWithName(_ fontName:String) -> MTFontStyle? {
|
public static func fontStyleWithName(_ fontName:String) -> MTFontStyle? {
|
||||||
return fontStyles[fontName]
|
fontStyles[fontName]
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func fontNameForStyle(_ fontStyle:MTFontStyle) -> String {
|
public static func fontNameForStyle(_ fontStyle:MTFontStyle) -> String {
|
||||||
@@ -467,21 +467,22 @@ public class MTMathAtomFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return an atom for times sign \times or *
|
/// Returns an atom for the multiplication sign (i.e., \times or "*")
|
||||||
public static func times() -> MTMathAtom {
|
public static func times() -> MTMathAtom {
|
||||||
MTMathAtom(type: .binaryOperator, value: UnicodeSymbol.multiplication)
|
MTMathAtom(type: .binaryOperator, value: UnicodeSymbol.multiplication)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return an atom for division sign \div or /
|
/// Returns an atom for the division sign (i.e., \div or "/")
|
||||||
public static func divide() -> MTMathAtom {
|
public static func divide() -> MTMathAtom {
|
||||||
MTMathAtom(type: .binaryOperator, value: UnicodeSymbol.division)
|
MTMathAtom(type: .binaryOperator, value: UnicodeSymbol.division)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return an atom aka placeholder square
|
/// Returns an atom which is a placeholder square
|
||||||
public static func placeholder() -> MTMathAtom {
|
public static func placeholder() -> MTMathAtom {
|
||||||
MTMathAtom(type: .placeholder, value: UnicodeSymbol.whiteSquare)
|
MTMathAtom(type: .placeholder, value: UnicodeSymbol.whiteSquare)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns a fraction with a placeholder for the numerator and denominator */
|
||||||
public static func placeholderFraction() -> MTFraction {
|
public static func placeholderFraction() -> MTFraction {
|
||||||
let frac = MTFraction()
|
let frac = MTFraction()
|
||||||
frac.numerator = MTMathList()
|
frac.numerator = MTMathList()
|
||||||
@@ -491,6 +492,7 @@ public class MTMathAtomFactory {
|
|||||||
return frac
|
return frac
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns a square root with a placeholder as the radicand. */
|
||||||
public static func placeholderSquareRoot() -> MTRadical {
|
public static func placeholderSquareRoot() -> MTRadical {
|
||||||
let rad = MTRadical()
|
let rad = MTRadical()
|
||||||
rad.radicand = MTMathList()
|
rad.radicand = MTMathList()
|
||||||
@@ -498,6 +500,7 @@ public class MTMathAtomFactory {
|
|||||||
return rad
|
return rad
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns a radical with a placeholder as the radicand. */
|
||||||
public static func placeholderRadical() -> MTRadical {
|
public static func placeholderRadical() -> MTRadical {
|
||||||
let rad = MTRadical()
|
let rad = MTRadical()
|
||||||
rad.radicand = MTMathList()
|
rad.radicand = MTMathList()
|
||||||
@@ -507,6 +510,7 @@ public class MTMathAtomFactory {
|
|||||||
return rad
|
return rad
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: -
|
||||||
/** Gets the atom with the right type for the given character. If an atom
|
/** Gets the atom with the right type for the given character. If an atom
|
||||||
cannot be determined for a given character this returns nil.
|
cannot be determined for a given character this returns nil.
|
||||||
This function follows latex conventions for assigning types to the atoms.
|
This function follows latex conventions for assigning types to the atoms.
|
||||||
@@ -554,7 +558,7 @@ public class MTMathAtomFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Returns a `MTMathList` with one atom per character in the given string. This function
|
/** Returns a `MTMathList` with one atom per character in the given string. This function
|
||||||
does not do any LaTeX conversion or interpretation. It simply uses `atomForCharacter` to
|
does not do any LaTeX conversion or interpretation. It simply uses `atom(forCharacter:)` to
|
||||||
convert the characters to atoms. Any character that cannot be converted is ignored. */
|
convert the characters to atoms. Any character that cannot be converted is ignored. */
|
||||||
public static func atomList(for string: String) -> MTMathList {
|
public static func atomList(for string: String) -> MTMathList {
|
||||||
let list = MTMathList()
|
let list = MTMathList()
|
||||||
@@ -583,26 +587,23 @@ public class MTMathAtomFactory {
|
|||||||
/** Finds the name of the LaTeX symbol name for the given atom. This function is a reverse
|
/** Finds the name of the LaTeX symbol name for the given atom. This function is a reverse
|
||||||
of the above function. If no latex symbol name corresponds to the atom, then this returns `nil`
|
of the above function. If no latex symbol name corresponds to the atom, then this returns `nil`
|
||||||
If nucleus of the atom is empty, then this will return `nil`.
|
If nucleus of the atom is empty, then this will return `nil`.
|
||||||
@note: This is not an exact reverse of the above in the case of aliases. If an LaTeX alias
|
Note: This is not an exact reverse of the above in the case of aliases. If an LaTeX alias
|
||||||
points to a given symbol, then this function will return the original symbol name and not the
|
points to a given symbol, then this function will return the original symbol name and not the
|
||||||
alias.
|
alias.
|
||||||
@note: This function does not convert MathSpaces to latex command names either.
|
Note: This function does not convert MathSpaces to latex command names either.
|
||||||
*/
|
*/
|
||||||
public static func latexSymbolName(for atom: MTMathAtom) -> String? {
|
public static func latexSymbolName(for atom: MTMathAtom) -> String? {
|
||||||
if atom.nucleus.count == 0 {
|
guard !atom.nucleus.isEmpty else { return nil }
|
||||||
return nil
|
return Self.textToLatexSymbolName[atom.nucleus]
|
||||||
}
|
|
||||||
return sharedInstance.textToLatexSymbolName[atom.nucleus]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Define a latex symbol for rendering. This function allows defining custom symbols that are
|
/** Define a latex symbol for rendering. This function allows defining custom symbols that are
|
||||||
not already present in the default set, or override existing symbols with new meaning.
|
not already present in the default set, or override existing symbols with new meaning.
|
||||||
e.g. to define a symbol for "lcm" one can call:
|
e.g. to define a symbol for "lcm" one can call:
|
||||||
`[MTMathAtomFactory addLatexSymbol:@"lcm" value:[MTMathAtomFactory operatorWithName:@"lcm" limits: false)]` */
|
`MTMathAtomFactory.add(latexSymbol:"lcm", value:MTMathAtomFactory.operatorWithName("lcm", limits: false))` */
|
||||||
|
|
||||||
public static func add(latexSymbol name: String, value: MTMathAtom) {
|
public static func add(latexSymbol name: String, value: MTMathAtom) {
|
||||||
supportedLatexSymbols[name] = value
|
supportedLatexSymbols[name] = value
|
||||||
sharedInstance.textToLatexSymbolName[value.nucleus] = name
|
Self.textToLatexSymbolName[value.nucleus] = name
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns a large opertor for the given name. If limits is true, limits are set up on
|
/** Returns a large opertor for the given name. If limits is true, limits are set up on
|
||||||
@@ -647,10 +648,8 @@ public class MTMathAtomFactory {
|
|||||||
`<` and `langle`) and this function always returns the shorter name.
|
`<` and `langle`) and this function always returns the shorter name.
|
||||||
*/
|
*/
|
||||||
public static func getDelimiterName(of boundary: MTMathAtom) -> String? {
|
public static func getDelimiterName(of boundary: MTMathAtom) -> String? {
|
||||||
if boundary.type != .boundary {
|
guard boundary.type == .boundary else { return nil }
|
||||||
return nil
|
return Self.delimValueToName[boundary.nucleus]
|
||||||
}
|
|
||||||
return Self.sharedInstance.delimValueToName[boundary.nucleus]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns a fraction with the given numerator and denominator. */
|
/** Returns a fraction with the given numerator and denominator. */
|
||||||
@@ -672,21 +671,14 @@ public class MTMathAtomFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Simplification of above function when numerator and denominator are simple strings.
|
/** Simplification of above function when numerator and denominator are simple strings.
|
||||||
This function uses `mathListForCharacters` to convert the strings to `MTMathList`s. */
|
This function converts the strings to a `MTFraction`. */
|
||||||
public static func fraction(withNumeratorString numStr: String, denominatorString denomStr: String) -> MTFraction {
|
public static func fraction(withNumeratorString numStr: String, denominatorString denomStr: String) -> MTFraction {
|
||||||
let num = Self.atomList(for: numStr)
|
let num = Self.atomList(for: numStr)
|
||||||
let denom = Self.atomList(for: denomStr)
|
let denom = Self.atomList(for: denomStr)
|
||||||
|
|
||||||
return Self.fraction(withNumerator: num, denominator: denom)
|
return Self.fraction(withNumerator: num, denominator: denom)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Builds a table for a given environment with the given rows. Returns a `MTMathAtom` containing the
|
|
||||||
table and any other atoms necessary for the given environment. Returns nil and sets error
|
|
||||||
if the table could not be built.
|
|
||||||
@param env The environment to use to build the table. If the env is nil, then the default table is built.
|
|
||||||
@note The reason this function returns a `MTMathAtom` and not a `MTMathTable` is because some
|
|
||||||
matrix environments are have builtin delimiters added to the table and hence are returned as inner atoms.
|
|
||||||
*/
|
|
||||||
static let matrixEnvs = [
|
static let matrixEnvs = [
|
||||||
"matrix": [],
|
"matrix": [],
|
||||||
"pmatrix": ["(", ")"],
|
"pmatrix": ["(", ")"],
|
||||||
@@ -696,6 +688,13 @@ public class MTMathAtomFactory {
|
|||||||
"Vmatrix": ["Vert", "Vert"]
|
"Vmatrix": ["Vert", "Vert"]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
/** Builds a table for a given environment with the given rows. Returns a `MTMathAtom` containing the
|
||||||
|
table and any other atoms necessary for the given environment. Returns nil and sets error
|
||||||
|
if the table could not be built.
|
||||||
|
@param env The environment to use to build the table. If the env is nil, then the default table is built.
|
||||||
|
@note The reason this function returns a `MTMathAtom` and not a `MTMathTable` is because some
|
||||||
|
matrix environments are have builtin delimiters added to the table and hence are returned as inner atoms.
|
||||||
|
*/
|
||||||
public static func table(withEnvironment env: String?, rows: [[MTMathList]], error:inout NSError?) -> MTMathAtom? {
|
public static func table(withEnvironment env: String?, rows: [[MTMathList]], error:inout NSError?) -> MTMathAtom? {
|
||||||
let table = MTMathTable(environment: env)
|
let table = MTMathTable(environment: env)
|
||||||
|
|
||||||
|
|||||||
@@ -1,42 +1,76 @@
|
|||||||
//
|
//
|
||||||
// MTMathList.swift
|
|
||||||
// MathRenderSwift
|
|
||||||
//
|
|
||||||
// Created by Mike Griebling on 2022-12-31.
|
// Created by Mike Griebling on 2022-12-31.
|
||||||
|
// Translated from an Objective-C implementation by Kostub Deshmukh.
|
||||||
|
//
|
||||||
|
// This software may be modified and distributed under the terms of the
|
||||||
|
// MIT license. See the LICENSE file for details.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
// type defines spacing and how it is rendered
|
/**
|
||||||
public enum MTMathAtomType: Int, CustomStringConvertible, Comparable {
|
The type of atom in a `MTMathList`.
|
||||||
|
|
||||||
case ordinary = 1 // number or text
|
The type of the atom determines how it is rendered, and spacing between the atoms.
|
||||||
case number // number
|
*/
|
||||||
case variable // text in italic
|
public enum MTMathAtomType: Int, CustomStringConvertible, Comparable {
|
||||||
case largeOperator // sin/cos, integral
|
/// A number or text in ordinary format - Ord in TeX
|
||||||
case binaryOperator // \bin
|
case ordinary = 1
|
||||||
case unaryOperator //
|
/// A number - Does not exist in TeX
|
||||||
case relation // = < >
|
case number
|
||||||
case open // open bracket
|
/// A variable (i.e. text in italic format) - Does not exist in TeX
|
||||||
case close // close bracket
|
case variable
|
||||||
case fraction // \frac
|
/// A large operator such as (sin/cos, integral etc.) - Op in TeX
|
||||||
case radical // \sqrt
|
case largeOperator
|
||||||
case punctuation // ,
|
/// A binary operator - Bin in TeX
|
||||||
case placeholder // inner atom
|
case binaryOperator
|
||||||
case inner // embedded list
|
/// A unary operator - Does not exist in TeX.
|
||||||
case underline // underlined atom
|
case unaryOperator
|
||||||
case overline // overlined atom
|
/// A relation, e.g. = > < etc. - Rel in TeX
|
||||||
case accent // accented atom
|
case relation
|
||||||
|
/// Open brackets - Open in TeX
|
||||||
|
case open
|
||||||
|
/// Close brackets - Close in TeX
|
||||||
|
case close
|
||||||
|
/// A fraction e.g 1/2 - generalized fraction node in TeX
|
||||||
|
case fraction
|
||||||
|
/// A radical operator e.g. sqrt(2)
|
||||||
|
case radical
|
||||||
|
/// Punctuation such as , - Punct in TeX
|
||||||
|
case punctuation
|
||||||
|
/// A placeholder square for future input. Does not exist in TeX
|
||||||
|
case placeholder
|
||||||
|
/// An inner atom, i.e. an embedded math list - Inner in TeX
|
||||||
|
case inner
|
||||||
|
/// An underlined atom - Under in TeX
|
||||||
|
case underline
|
||||||
|
/// An overlined atom - Over in TeX
|
||||||
|
case overline
|
||||||
|
/// An accented atom - Accent in TeX
|
||||||
|
case accent
|
||||||
|
|
||||||
// these atoms do not support subscripts/superscripts:
|
// Atoms after this point do not support subscripts or superscripts
|
||||||
|
|
||||||
|
/// A left atom - Left & Right in TeX. We don't need two since we track boundaries separately.
|
||||||
case boundary = 101
|
case boundary = 101
|
||||||
|
|
||||||
|
// Atoms after this are non-math TeX nodes that are still useful in math mode. They do not have
|
||||||
|
// the usual structure.
|
||||||
|
|
||||||
|
/// Spacing between math atoms. This denotes both glue and kern for TeX. We do not
|
||||||
|
/// distinguish between glue and kern.
|
||||||
case space = 201
|
case space = 201
|
||||||
|
|
||||||
// Denotes style changes during randering
|
/// Denotes style changes during rendering.
|
||||||
case style
|
case style
|
||||||
case color
|
case color
|
||||||
case colorBox
|
case colorBox
|
||||||
|
|
||||||
|
// Atoms after this point are not part of TeX and do not have the usual structure.
|
||||||
|
|
||||||
|
/// An table atom. This atom does not exist in TeX. It is equivalent to the TeX command
|
||||||
|
/// halign which is handled outside of the TeX math rendering engine. We bring it into our
|
||||||
|
/// math typesetting to handle matrices and other tables.
|
||||||
case table = 1001
|
case table = 1001
|
||||||
|
|
||||||
func isNotBinaryOperator() -> Bool {
|
func isNotBinaryOperator() -> Bool {
|
||||||
@@ -79,9 +113,14 @@ public enum MTMathAtomType: Int, CustomStringConvertible, Comparable {
|
|||||||
|
|
||||||
// comparable support
|
// comparable support
|
||||||
public static func < (lhs: MTMathAtomType, rhs: MTMathAtomType) -> Bool { lhs.rawValue < rhs.rawValue }
|
public static func < (lhs: MTMathAtomType, rhs: MTMathAtomType) -> Bool { lhs.rawValue < rhs.rawValue }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
The font style of a character.
|
||||||
|
|
||||||
|
The fontstyle of the atom determines what style the character is rendered in. This only applies to atoms
|
||||||
|
of type kMTMathAtomVariable and kMTMathAtomNumber. None of the other atom types change their font style.
|
||||||
|
*/
|
||||||
public enum MTFontStyle:Int {
|
public enum MTFontStyle:Int {
|
||||||
/// The default latex rendering style. i.e. variables are italic and numbers are roman.
|
/// The default latex rendering style. i.e. variables are italic and numbers are roman.
|
||||||
case defaultStyle = 0,
|
case defaultStyle = 0,
|
||||||
@@ -107,9 +146,19 @@ public enum MTFontStyle:Int {
|
|||||||
|
|
||||||
// MARK: - MTMathAtom
|
// MARK: - MTMathAtom
|
||||||
|
|
||||||
|
/** A `MTMathAtom` is the basic unit of a math list. Each atom represents a single character
|
||||||
|
or mathematical operator in a list. However certain atoms can represent more complex structures
|
||||||
|
such as fractions and radicals. Each atom has a type which determines how the atom is rendered and
|
||||||
|
a nucleus. The nucleus contains the character(s) that need to be rendered. However the nucleus may
|
||||||
|
be empty for certain types of atoms. An atom has an optional subscript or superscript which represents
|
||||||
|
the subscript or superscript that is to be rendered.
|
||||||
|
|
||||||
|
Certain types of atoms inherit from `MTMathAtom` and may have additional fields.
|
||||||
|
*/
|
||||||
public class MTMathAtom: NSObject {
|
public class MTMathAtom: NSObject {
|
||||||
|
/** The type of the atom. */
|
||||||
public var type = MTMathAtomType.ordinary
|
public var type = MTMathAtomType.ordinary
|
||||||
|
/** An optional subscript. */
|
||||||
public var subScript: MTMathList? {
|
public var subScript: MTMathList? {
|
||||||
didSet {
|
didSet {
|
||||||
if subScript != nil && !self.isScriptAllowed() {
|
if subScript != nil && !self.isScriptAllowed() {
|
||||||
@@ -118,6 +167,7 @@ public class MTMathAtom: NSObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/** An optional superscript. */
|
||||||
public var superScript: MTMathList? {
|
public var superScript: MTMathList? {
|
||||||
didSet {
|
didSet {
|
||||||
if superScript != nil && !self.isScriptAllowed() {
|
if superScript != nil && !self.isScriptAllowed() {
|
||||||
@@ -127,11 +177,19 @@ public class MTMathAtom: NSObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** The nucleus of the atom. */
|
||||||
public var nucleus: String = ""
|
public var nucleus: String = ""
|
||||||
|
|
||||||
|
/// The index range in the MTMathList this MTMathAtom tracks. This is used by the finalizing and preprocessing steps
|
||||||
|
/// which fuse MTMathAtoms to track the position of the current MTMathAtom in the original list.
|
||||||
public var indexRange = NSRange(location: 0, length: 0) // indexRange in list that this atom tracks:
|
public var indexRange = NSRange(location: 0, length: 0) // indexRange in list that this atom tracks:
|
||||||
|
|
||||||
|
/** The font style to be used for the atom. */
|
||||||
var fontStyle: MTFontStyle = .defaultStyle
|
var fontStyle: MTFontStyle = .defaultStyle
|
||||||
var fusedAtoms = [MTMathAtom]() // atoms that fused to create this one
|
|
||||||
|
/// If this atom was formed by fusion of multiple atoms, then this stores the list of atoms that were fused to create this one.
|
||||||
|
/// This is used in the finalizing and preprocessing steps.
|
||||||
|
var fusedAtoms = [MTMathAtom]()
|
||||||
|
|
||||||
init(_ atom:MTMathAtom?) {
|
init(_ atom:MTMathAtom?) {
|
||||||
guard let atom = atom else { return }
|
guard let atom = atom else { return }
|
||||||
@@ -146,11 +204,15 @@ public class MTMathAtom: NSObject {
|
|||||||
|
|
||||||
override init() { }
|
override init() { }
|
||||||
|
|
||||||
|
/// Factory function to create an atom with a given type and value.
|
||||||
|
/// - parameter type: The type of the atom to instantiate.
|
||||||
|
/// - parameter value: The value of the atoms nucleus. The value is ignored for fractions and radicals.
|
||||||
init(type:MTMathAtomType, value:String) {
|
init(type:MTMathAtomType, value:String) {
|
||||||
self.type = type
|
self.type = type
|
||||||
self.nucleus = type == .radical ? "" : value
|
self.nucleus = type == .radical ? "" : value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a copy of `self`.
|
||||||
public func copy() -> MTMathAtom {
|
public func copy() -> MTMathAtom {
|
||||||
switch self.type {
|
switch self.type {
|
||||||
case .largeOperator:
|
case .largeOperator:
|
||||||
@@ -194,6 +256,7 @@ public class MTMathAtom: NSObject {
|
|||||||
return string
|
return string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a finalized copy of the atom
|
||||||
public var finalized: MTMathAtom {
|
public var finalized: MTMathAtom {
|
||||||
let finalized : MTMathAtom = self.copy()
|
let finalized : MTMathAtom = self.copy()
|
||||||
finalized.superScript = finalized.superScript?.finalized
|
finalized.superScript = finalized.superScript?.finalized
|
||||||
@@ -212,6 +275,7 @@ public class MTMathAtom: NSObject {
|
|||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fuse the given atom with this one by combining their nucleii.
|
||||||
func fuse(with atom: MTMathAtom) {
|
func fuse(with atom: MTMathAtom) {
|
||||||
assert(self.subScript == nil, "Cannot fuse into an atom which has a subscript: \(self)");
|
assert(self.subScript == nil, "Cannot fuse into an atom which has a subscript: \(self)");
|
||||||
assert(self.superScript == nil, "Cannot fuse into an atom which has a superscript: \(self)");
|
assert(self.superScript == nil, "Cannot fuse into an atom which has a superscript: \(self)");
|
||||||
@@ -240,7 +304,9 @@ public class MTMathAtom: NSObject {
|
|||||||
self.subScript = atom.subScript
|
self.subScript = atom.subScript
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns true if this atom allows scripts (sub or super). */
|
||||||
func isScriptAllowed() -> Bool { self.type.isScriptAllowed() }
|
func isScriptAllowed() -> Bool { self.type.isScriptAllowed() }
|
||||||
|
|
||||||
func isNotBinaryOperator() -> Bool { self.type.isNotBinaryOperator() }
|
func isNotBinaryOperator() -> Bool { self.type.isNotBinaryOperator() }
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -254,19 +320,21 @@ func isNotBinaryOperator(_ prevNode:MTMathAtom?) -> Bool {
|
|||||||
|
|
||||||
public class MTFraction: MTMathAtom {
|
public class MTFraction: MTMathAtom {
|
||||||
public var hasRule: Bool = true
|
public var hasRule: Bool = true
|
||||||
public var leftDelimiter: String?
|
public var leftDelimiter = ""
|
||||||
public var rightDelimiter: String?
|
public var rightDelimiter = ""
|
||||||
public var numerator: MTMathList?
|
public var numerator: MTMathList?
|
||||||
public var denominator: MTMathList?
|
public var denominator: MTMathList?
|
||||||
|
|
||||||
init(_ frac: MTFraction?) {
|
init(_ frac: MTFraction?) {
|
||||||
super.init(frac)
|
super.init(frac)
|
||||||
self.type = .fraction
|
self.type = .fraction
|
||||||
self.numerator = MTMathList(frac!.numerator)
|
if let frac = frac {
|
||||||
self.denominator = MTMathList(frac!.denominator)
|
self.numerator = MTMathList(frac.numerator)
|
||||||
self.hasRule = frac!.hasRule
|
self.denominator = MTMathList(frac.denominator)
|
||||||
self.leftDelimiter = frac!.leftDelimiter
|
self.hasRule = frac.hasRule
|
||||||
self.rightDelimiter = frac!.rightDelimiter
|
self.leftDelimiter = frac.leftDelimiter
|
||||||
|
self.rightDelimiter = frac.rightDelimiter
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init(hasRule rule:Bool = true) {
|
init(hasRule rule:Bool = true) {
|
||||||
@@ -276,28 +344,20 @@ public class MTFraction: MTMathAtom {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override public var description: String {
|
override public var description: String {
|
||||||
var string = ""
|
var string = self.hasRule ? "\\frac" : "\\atop"
|
||||||
if self.hasRule {
|
if !self.leftDelimiter.isEmpty {
|
||||||
string += "\\atop"
|
string += "[\(self.leftDelimiter)]"
|
||||||
} else {
|
|
||||||
string += "\\frac"
|
|
||||||
}
|
}
|
||||||
if self.leftDelimiter != nil {
|
if !self.rightDelimiter.isEmpty {
|
||||||
string += "[\(self.leftDelimiter!)]"
|
string += "[\(self.rightDelimiter)]"
|
||||||
}
|
}
|
||||||
if self.rightDelimiter != nil {
|
|
||||||
string += "[\(self.rightDelimiter!)]"
|
|
||||||
}
|
|
||||||
|
|
||||||
string += "{\(self.numerator?.description ?? "placeholder")}{\(self.denominator?.description ?? "placeholder")}"
|
string += "{\(self.numerator?.description ?? "placeholder")}{\(self.denominator?.description ?? "placeholder")}"
|
||||||
|
|
||||||
if self.superScript != nil {
|
if self.superScript != nil {
|
||||||
string += "^{\(self.superScript!.description)}"
|
string += "^{\(self.superScript!.description)}"
|
||||||
}
|
}
|
||||||
if self.subScript != nil {
|
if self.subScript != nil {
|
||||||
string += "_{\(self.subScript!.description)}"
|
string += "_{\(self.subScript!.description)}"
|
||||||
}
|
}
|
||||||
|
|
||||||
return string
|
return string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,12 +371,13 @@ public class MTFraction: MTMathAtom {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - MTRadical
|
// MARK: - MTRadical
|
||||||
|
/** An atom of type radical (square root). */
|
||||||
public class MTRadical: MTMathAtom {
|
public class MTRadical: MTMathAtom {
|
||||||
// Under the roof
|
/// Denotes the term under the square root sign
|
||||||
public var radicand: MTMathList?
|
public var radicand: MTMathList?
|
||||||
|
|
||||||
// Value on radical sign
|
/// Denotes the degree of the radical, i.e. the value to the top left of the radical sign
|
||||||
|
/// This can be null if there is no degree.
|
||||||
public var degree: MTMathList?
|
public var degree: MTMathList?
|
||||||
|
|
||||||
init(_ rad:MTRadical?) {
|
init(_ rad:MTRadical?) {
|
||||||
@@ -335,22 +396,18 @@ public class MTRadical: MTMathAtom {
|
|||||||
|
|
||||||
override public var description: String {
|
override public var description: String {
|
||||||
var string = "\\sqrt"
|
var string = "\\sqrt"
|
||||||
|
|
||||||
if self.degree != nil {
|
if self.degree != nil {
|
||||||
string += "[\(self.degree!.description)]"
|
string += "[\(self.degree!.description)]"
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.radicand != nil {
|
if self.radicand != nil {
|
||||||
string += "{\(self.radicand?.description ?? "placeholder")}"
|
string += "{\(self.radicand?.description ?? "placeholder")}"
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.superScript != nil {
|
if self.superScript != nil {
|
||||||
string += "^{\(self.superScript!.description)}"
|
string += "^{\(self.superScript!.description)}"
|
||||||
}
|
}
|
||||||
if self.subScript != nil {
|
if self.subScript != nil {
|
||||||
string += "_{\(self.subScript!.description)}"
|
string += "_{\(self.subScript!.description)}"
|
||||||
}
|
}
|
||||||
|
|
||||||
return string
|
return string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -363,8 +420,13 @@ public class MTRadical: MTMathAtom {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - MTLargeOperator
|
// MARK: - MTLargeOperator
|
||||||
|
/** A `MTMathAtom` of type `kMTMathAtom.largeOperator`. */
|
||||||
public class MTLargeOperator: MTMathAtom {
|
public class MTLargeOperator: MTMathAtom {
|
||||||
|
|
||||||
|
/** Indicates whether the limits (if present) should be displayed
|
||||||
|
above and below the operator in display mode. If limits is false
|
||||||
|
then the limits (if present) are displayed like a regular subscript/superscript.
|
||||||
|
*/
|
||||||
public var limits: Bool = false
|
public var limits: Bool = false
|
||||||
|
|
||||||
init(_ op:MTLargeOperator?) {
|
init(_ op:MTLargeOperator?) {
|
||||||
@@ -380,20 +442,25 @@ public class MTLargeOperator: MTMathAtom {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - MTInner
|
// MARK: - MTInner
|
||||||
|
/** An inner atom. This denotes an atom which contains a math list inside it. An inner atom
|
||||||
|
has optional boundaries. Note: Only one boundary may be present, it is not required to have
|
||||||
|
both. */
|
||||||
public class MTInner: MTMathAtom {
|
public class MTInner: MTMathAtom {
|
||||||
|
/// The inner math list
|
||||||
public var innerList: MTMathList?
|
public var innerList: MTMathList?
|
||||||
|
/// The left boundary atom. This must be a node of type kMTMathAtomBoundary
|
||||||
public var leftBoundary: MTMathAtom? {
|
public var leftBoundary: MTMathAtom? {
|
||||||
didSet {
|
didSet {
|
||||||
if leftBoundary != nil && leftBoundary!.type != .boundary {
|
if let left = leftBoundary, left.type != .boundary {
|
||||||
leftBoundary = nil
|
leftBoundary = nil
|
||||||
NSException(name: NSExceptionName(rawValue: "Error"), reason: "Left boundary must be of type .boundary").raise()
|
NSException(name: NSExceptionName(rawValue: "Error"), reason: "Left boundary must be of type .boundary").raise()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// The right boundary atom. This must be a node of type kMTMathAtomBoundary
|
||||||
public var rightBoundary: MTMathAtom? {
|
public var rightBoundary: MTMathAtom? {
|
||||||
didSet {
|
didSet {
|
||||||
if rightBoundary != nil && rightBoundary!.type != .boundary {
|
if let right = rightBoundary, right.type != .boundary {
|
||||||
rightBoundary = nil
|
rightBoundary = nil
|
||||||
NSException(name: NSExceptionName(rawValue: "Error"), reason: "Right boundary must be of type .boundary").raise()
|
NSException(name: NSExceptionName(rawValue: "Error"), reason: "Right boundary must be of type .boundary").raise()
|
||||||
}
|
}
|
||||||
@@ -415,23 +482,19 @@ public class MTInner: MTMathAtom {
|
|||||||
|
|
||||||
override public var description: String {
|
override public var description: String {
|
||||||
var string = "\\inner"
|
var string = "\\inner"
|
||||||
|
|
||||||
if self.leftBoundary != nil {
|
if self.leftBoundary != nil {
|
||||||
string += "[\(self.leftBoundary!.nucleus)]"
|
string += "[\(self.leftBoundary!.nucleus)]"
|
||||||
}
|
}
|
||||||
string += "{\(self.innerList!.description)}"
|
string += "{\(self.innerList!.description)}"
|
||||||
|
|
||||||
if self.rightBoundary != nil {
|
if self.rightBoundary != nil {
|
||||||
string += "[\(self.rightBoundary!.nucleus)]"
|
string += "[\(self.rightBoundary!.nucleus)]"
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.superScript != nil {
|
if self.superScript != nil {
|
||||||
string += "^{\(self.superScript!.description)}"
|
string += "^{\(self.superScript!.description)}"
|
||||||
}
|
}
|
||||||
if self.subScript != nil {
|
if self.subScript != nil {
|
||||||
string += "_{\(self.subScript!.description)}"
|
string += "_{\(self.subScript!.description)}"
|
||||||
}
|
}
|
||||||
|
|
||||||
return string
|
return string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -443,7 +506,7 @@ public class MTInner: MTMathAtom {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - MTOverLIne
|
// MARK: - MTOverLIne
|
||||||
|
/** An atom with a line over the contained math list. */
|
||||||
public class MTOverLine: MTMathAtom {
|
public class MTOverLine: MTMathAtom {
|
||||||
public var innerList: MTMathList?
|
public var innerList: MTMathList?
|
||||||
|
|
||||||
@@ -466,7 +529,7 @@ public class MTOverLine: MTMathAtom {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - MTUnderLine
|
// MARK: - MTUnderLine
|
||||||
|
/** An atom with a line under the contained math list. */
|
||||||
public class MTUnderLine: MTMathAtom {
|
public class MTUnderLine: MTMathAtom {
|
||||||
public var innerList: MTMathList?
|
public var innerList: MTMathList?
|
||||||
|
|
||||||
@@ -513,10 +576,16 @@ public class MTAccent: MTMathAtom {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - MTMathSpace
|
// MARK: - MTMathSpace
|
||||||
|
/** An atom representing space.
|
||||||
|
Note: None of the usual fields of the `MTMathAtom` apply even though this
|
||||||
|
class inherits from `MTMathAtom`. i.e. it is meaningless to have a value
|
||||||
|
in the nucleus, subscript or superscript fields. */
|
||||||
public class MTMathSpace: MTMathAtom {
|
public class MTMathSpace: MTMathAtom {
|
||||||
|
/** The amount of space represented by this object in mu units. */
|
||||||
public var space: CGFloat = 0
|
public var space: CGFloat = 0
|
||||||
|
|
||||||
|
/// Creates a new `MTMathSpace` with the given spacing.
|
||||||
|
/// - parameter space: The amount of space in mu units.
|
||||||
init(_ space: MTMathSpace?) {
|
init(_ space: MTMathSpace?) {
|
||||||
super.init(space)
|
super.init(space)
|
||||||
self.type = .space
|
self.type = .space
|
||||||
@@ -530,11 +599,17 @@ public class MTMathSpace: MTMathAtom {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Styling of a line of math
|
||||||
|
*/
|
||||||
public enum MTLineStyle:Int, Comparable {
|
public enum MTLineStyle:Int, Comparable {
|
||||||
|
/// Display style
|
||||||
case display
|
case display
|
||||||
|
/// Text style (inline)
|
||||||
case text
|
case text
|
||||||
|
/// Script style (for sub/super scripts)
|
||||||
case script
|
case script
|
||||||
|
/// Script script style (for scripts of scripts)
|
||||||
case scriptOfScript
|
case scriptOfScript
|
||||||
|
|
||||||
public func inc() -> MTLineStyle {
|
public func inc() -> MTLineStyle {
|
||||||
@@ -548,7 +623,10 @@ public enum MTLineStyle:Int, Comparable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - MTMathStyle
|
// MARK: - MTMathStyle
|
||||||
|
/** An atom representing a style change.
|
||||||
|
Note: None of the usual fields of the `MTMathAtom` apply even though this
|
||||||
|
class inherits from `MTMathAtom`. i.e. it is meaningless to have a value
|
||||||
|
in the nucleus, subscript or superscript fields. */
|
||||||
public class MTMathStyle: MTMathAtom {
|
public class MTMathStyle: MTMathAtom {
|
||||||
public var style: MTLineStyle = .display
|
public var style: MTLineStyle = .display
|
||||||
|
|
||||||
@@ -566,7 +644,10 @@ public class MTMathStyle: MTMathAtom {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - MTMathColor
|
// MARK: - MTMathColor
|
||||||
|
/** An atom representing an color element.
|
||||||
|
Note: None of the usual fields of the `MTMathAtom` apply even though this
|
||||||
|
class inherits from `MTMathAtom`. i.e. it is meaningless to have a value
|
||||||
|
in the nucleus, subscript or superscript fields. */
|
||||||
public class MTMathColor: MTMathAtom {
|
public class MTMathColor: MTMathAtom {
|
||||||
public var colorString:String=""
|
public var colorString:String=""
|
||||||
public var innerList:MTMathList?
|
public var innerList:MTMathList?
|
||||||
@@ -595,9 +676,12 @@ public class MTMathColor: MTMathAtom {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - MTMathColorbox
|
// MARK: - MTMathColorbox
|
||||||
|
/** An atom representing an colorbox element.
|
||||||
|
Note: None of the usual fields of the `MTMathAtom` apply even though this
|
||||||
|
class inherits from `MTMathAtom`. i.e. it is meaningless to have a value
|
||||||
|
in the nucleus, subscript or superscript fields. */
|
||||||
public class MTMathColorbox: MTMathAtom {
|
public class MTMathColorbox: MTMathAtom {
|
||||||
public var colorString:String=""
|
public var colorString=""
|
||||||
public var innerList:MTMathList?
|
public var innerList:MTMathList?
|
||||||
|
|
||||||
init(_ cbox: MTMathColorbox?) {
|
init(_ cbox: MTMathColorbox?) {
|
||||||
@@ -623,6 +707,9 @@ public class MTMathColorbox: MTMathAtom {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Alignment for a column of MTMathTable
|
||||||
|
*/
|
||||||
public enum MTColumnAlignment {
|
public enum MTColumnAlignment {
|
||||||
case left
|
case left
|
||||||
case center
|
case center
|
||||||
@@ -630,13 +717,28 @@ public enum MTColumnAlignment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - MTMathTable
|
// MARK: - MTMathTable
|
||||||
|
/** An atom representing an table element. This atom is not like other
|
||||||
|
atoms and is not present in TeX. We use it to represent the `\halign` command
|
||||||
|
in TeX with some simplifications. This is used for matrices, equation
|
||||||
|
alignments and other uses of multiline environments.
|
||||||
|
|
||||||
|
The cells in the table are represented as a two dimensional array of
|
||||||
|
`MTMathList` objects. The `MTMathList`s could be empty to denote a missing
|
||||||
|
value in the cell. Additionally an array of alignments indicates how each
|
||||||
|
column will be aligned.
|
||||||
|
*/
|
||||||
public class MTMathTable: MTMathAtom {
|
public class MTMathTable: MTMathAtom {
|
||||||
|
/// The alignment for each column (left, right, center). The default alignment
|
||||||
|
/// for a column (if not set) is center.
|
||||||
public var alignments = [MTColumnAlignment]()
|
public var alignments = [MTColumnAlignment]()
|
||||||
|
/// The cells in the table as a two dimensional array.
|
||||||
public var cells = [[MTMathList]]()
|
public var cells = [[MTMathList]]()
|
||||||
|
/// The name of the environment that this table denotes.
|
||||||
public var environment: String?
|
public var environment = ""
|
||||||
|
/// Spacing between each column in mu units.
|
||||||
public var interColumnSpacing: CGFloat = 0
|
public var interColumnSpacing: CGFloat = 0
|
||||||
|
/// Additional spacing between rows in jots (one jot is 0.3 times font size).
|
||||||
|
/// If the additional spacing is 0, then normal row spacing is used are used.
|
||||||
public var interRowAdditionalSpacing: CGFloat = 0
|
public var interRowAdditionalSpacing: CGFloat = 0
|
||||||
|
|
||||||
override public var finalized: MTMathAtom {
|
override public var finalized: MTMathAtom {
|
||||||
@@ -652,7 +754,7 @@ public class MTMathTable: MTMathAtom {
|
|||||||
init(environment: String?) {
|
init(environment: String?) {
|
||||||
super.init()
|
super.init()
|
||||||
self.type = .table
|
self.type = .table
|
||||||
self.environment = environment
|
self.environment = environment ?? ""
|
||||||
}
|
}
|
||||||
|
|
||||||
init(_ table:MTMathTable) {
|
init(_ table:MTMathTable) {
|
||||||
@@ -678,6 +780,7 @@ public class MTMathTable: MTMathAtom {
|
|||||||
self.type = .table
|
self.type = .table
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the value of a given cell. The table is automatically resized to contain this cell.
|
||||||
public func set(cell list: MTMathList, forRow row:Int, column:Int) {
|
public func set(cell list: MTMathList, forRow row:Int, column:Int) {
|
||||||
if self.cells.count <= row {
|
if self.cells.count <= row {
|
||||||
for _ in self.cells.count...row {
|
for _ in self.cells.count...row {
|
||||||
@@ -693,6 +796,8 @@ public class MTMathTable: MTMathAtom {
|
|||||||
self.cells[row][column] = list
|
self.cells[row][column] = list
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the alignment of a particular column. The table is automatically resized to
|
||||||
|
/// contain this column and any new columns added have their alignment set to center.
|
||||||
public func set(alignment: MTColumnAlignment, forColumn col: Int) {
|
public func set(alignment: MTColumnAlignment, forColumn col: Int) {
|
||||||
if self.alignments.count <= col {
|
if self.alignments.count <= col {
|
||||||
for _ in self.alignments.count...col {
|
for _ in self.alignments.count...col {
|
||||||
@@ -703,6 +808,8 @@ public class MTMathTable: MTMathAtom {
|
|||||||
self.alignments[col] = alignment
|
self.alignments[col] = alignment
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the alignment for a given column. If the alignment is not specified it defaults
|
||||||
|
/// to center.
|
||||||
public func get(alignmentForColumn col: Int) -> MTColumnAlignment {
|
public func get(alignmentForColumn col: Int) -> MTColumnAlignment {
|
||||||
if self.alignments.count <= col {
|
if self.alignments.count <= col {
|
||||||
return MTColumnAlignment.center
|
return MTColumnAlignment.center
|
||||||
@@ -724,12 +831,21 @@ public class MTMathTable: MTMathAtom {
|
|||||||
|
|
||||||
// MARK: - MTMathList
|
// MARK: - MTMathList
|
||||||
|
|
||||||
// represent list of math objects
|
|
||||||
extension MTMathList {
|
extension MTMathList {
|
||||||
public override var description: String { self.atoms.description }
|
public override var description: String { self.atoms.description }
|
||||||
|
/// converts the MTMathList to a string form. Note: This is not the LaTeX form.
|
||||||
public var string: String { self.description }
|
public var string: String { self.description }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** A representation of a list of math objects.
|
||||||
|
|
||||||
|
This list can be constructed directly or built with
|
||||||
|
the help of the MTMathListBuilder. It is not required that the mathematics represented make sense
|
||||||
|
(i.e. this can represent something like "x 2 = +". This list can be used for display using MTLine
|
||||||
|
or can be a list of tokens to be used by a parser after finalizedMathList is called.
|
||||||
|
|
||||||
|
Note: This class is for **advanced** usage only.
|
||||||
|
*/
|
||||||
public class MTMathList : NSObject {
|
public class MTMathList : NSObject {
|
||||||
|
|
||||||
init?(_ list:MTMathList?) {
|
init?(_ list:MTMathList?) {
|
||||||
@@ -739,8 +855,12 @@ public class MTMathList : NSObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A list of MathAtoms
|
||||||
public var atoms = [MTMathAtom]()
|
public var atoms = [MTMathAtom]()
|
||||||
|
|
||||||
|
/// Create a new math list as a final expression and update atoms
|
||||||
|
/// by combining like atoms that occur together and converting unary operators to binary operators.
|
||||||
|
/// This function does not modify the current MTMathList
|
||||||
public var finalized: MTMathList {
|
public var finalized: MTMathList {
|
||||||
let finalizedList = MTMathList()
|
let finalizedList = MTMathList()
|
||||||
let zeroRange = NSMakeRange(0, 0)
|
let zeroRange = NSMakeRange(0, 0)
|
||||||
@@ -800,7 +920,10 @@ public class MTMathList : NSObject {
|
|||||||
NSException(name: NSExceptionName(rawValue: "Error"), reason: "Index \(index) out of bounds").raise()
|
NSException(name: NSExceptionName(rawValue: "Error"), reason: "Index \(index) out of bounds").raise()
|
||||||
}
|
}
|
||||||
|
|
||||||
func add(_ atom: MTMathAtom?) {
|
/// Add an atom to the end of the list.
|
||||||
|
/// - parameter atom: The atom to be inserted. This cannot be `nil` and cannot have the type `kMTMathAtomBoundary`.
|
||||||
|
/// - throws NSException if the atom is of type `kMTMathAtomBoundary`
|
||||||
|
public func add(_ atom: MTMathAtom?) {
|
||||||
guard let atom = atom else { return }
|
guard let atom = atom else { return }
|
||||||
if self.isAtomAllowed(atom) {
|
if self.isAtomAllowed(atom) {
|
||||||
self.atoms.append(atom)
|
self.atoms.append(atom)
|
||||||
@@ -809,7 +932,14 @@ public class MTMathList : NSObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func insert(_ atom: MTMathAtom?, at index: Int) {
|
/// Inserts an atom at the given index. If index is already occupied, the objects at index and beyond are
|
||||||
|
/// shifted by adding 1 to their indices to make room. An insert to an `index` greater than the number of atoms
|
||||||
|
/// is ignored. Insertions of nil atoms is ignored.
|
||||||
|
/// - parameter atom: The atom to be inserted. This cannot be `nil` and cannot have the type `kMTMathAtom.boundary`.
|
||||||
|
/// - parameter index: The index where the atom is to be inserted. The index should be less than or equal to the
|
||||||
|
/// number of elements in the math list.
|
||||||
|
/// - throws NSException if the atom is of type kMTMathAtomBoundary
|
||||||
|
public func insert(_ atom: MTMathAtom?, at index: Int) {
|
||||||
// NSParamException(atom)
|
// NSParamException(atom)
|
||||||
guard let atom = atom else { return }
|
guard let atom = atom else { return }
|
||||||
guard self.atoms.indices.contains(index) || index == self.atoms.endIndex else { return }
|
guard self.atoms.indices.contains(index) || index == self.atoms.endIndex else { return }
|
||||||
@@ -822,23 +952,30 @@ public class MTMathList : NSObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func append(_ list: MTMathList?) {
|
/// Append the given list to the end of the current list.
|
||||||
|
/// - parameter list: The list to append.
|
||||||
|
public func append(_ list: MTMathList?) {
|
||||||
guard let list = list else { return }
|
guard let list = list else { return }
|
||||||
self.atoms += list.atoms
|
self.atoms += list.atoms
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeLastAtom() {
|
/** Removes the last atom from the math list. If there are no atoms in the list this does nothing. */
|
||||||
|
public func removeLastAtom() {
|
||||||
if !self.atoms.isEmpty {
|
if !self.atoms.isEmpty {
|
||||||
self.atoms.removeLast()
|
self.atoms.removeLast()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeAtom(at index: Int) {
|
/// Removes the atom at the given index.
|
||||||
|
/// - parameter index: The index at which to remove the atom. Must be less than the number of atoms
|
||||||
|
/// in the list.
|
||||||
|
public func removeAtom(at index: Int) {
|
||||||
NSIndexException(self.atoms, index:index)
|
NSIndexException(self.atoms, index:index)
|
||||||
self.atoms.remove(at: index)
|
self.atoms.remove(at: index)
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeAtoms(in range: ClosedRange<Int>) {
|
/** Removes all the atoms within the given range. */
|
||||||
|
public func removeAtoms(in range: ClosedRange<Int>) {
|
||||||
NSIndexException(self.atoms, index: range.lowerBound)
|
NSIndexException(self.atoms, index: range.lowerBound)
|
||||||
NSIndexException(self.atoms, index: range.upperBound)
|
NSIndexException(self.atoms, index: range.upperBound)
|
||||||
self.atoms.removeSubrange(range)
|
self.atoms.removeSubrange(range)
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
//
|
//
|
||||||
// MTMathListBuilder.swift
|
|
||||||
// MathRenderSwift
|
|
||||||
//
|
|
||||||
// Created by Mike Griebling on 2022-12-31.
|
// Created by Mike Griebling on 2022-12-31.
|
||||||
|
// Translated from an Objective-C implementation by Kostub Deshmukh.
|
||||||
|
//
|
||||||
|
// This software may be modified and distributed under the terms of the
|
||||||
|
// MIT license. See the LICENSE file for details.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
@@ -23,10 +24,9 @@ struct MTEnvProperties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@typedef case s
|
The error encountered when parsing a LaTeX string.
|
||||||
@brief The error encountered when parsing a LaTeX string.
|
|
||||||
|
|
||||||
The `code` in the `NSError` is one of the following indiciating why the LaTeX string
|
The `code` in the `NSError` is one of the following indicating why the LaTeX string
|
||||||
could not be parsed.
|
could not be parsed.
|
||||||
*/
|
*/
|
||||||
enum MTParseErrors:Int {
|
enum MTParseErrors:Int {
|
||||||
@@ -62,7 +62,10 @@ enum MTParseErrors:Int {
|
|||||||
|
|
||||||
let MTParseError = "ParseError"
|
let MTParseError = "ParseError"
|
||||||
|
|
||||||
public class MTMathListBuilder {
|
/** `MTMathListBuilder` is a class for parsing LaTeX into an `MTMathList` that
|
||||||
|
can be rendered and processed mathematically.
|
||||||
|
*/
|
||||||
|
public struct MTMathListBuilder {
|
||||||
var string: String
|
var string: String
|
||||||
var currentCharIndex: String.Index
|
var currentCharIndex: String.Index
|
||||||
var currentInnerAtom: MTInner?
|
var currentInnerAtom: MTInner?
|
||||||
@@ -73,8 +76,42 @@ public class MTMathListBuilder {
|
|||||||
/** Contains any error that occurred during parsing. */
|
/** Contains any error that occurred during parsing. */
|
||||||
var error:NSError?
|
var error:NSError?
|
||||||
|
|
||||||
|
// MARK: - Character-handling routines
|
||||||
|
|
||||||
var hasCharacters: Bool { currentCharIndex < string.endIndex }
|
var hasCharacters: Bool { currentCharIndex < string.endIndex }
|
||||||
|
|
||||||
|
// gets the next character and increments the index
|
||||||
|
mutating func getNextCharacter() -> Character {
|
||||||
|
assert(self.hasCharacters, "Retrieving character at index \(self.currentCharIndex) beyond length \(self.string.count)")
|
||||||
|
let ch = string[currentCharIndex]
|
||||||
|
currentCharIndex = string.index(after: currentCharIndex)
|
||||||
|
return ch
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func unlookCharacter() {
|
||||||
|
assert(currentCharIndex > string.startIndex, "Unlooking when at the first character.")
|
||||||
|
if currentCharIndex > string.startIndex {
|
||||||
|
currentCharIndex = string.index(before: currentCharIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func expectCharacter(_ ch: Character) -> Bool {
|
||||||
|
MTAssertNotSpace(ch)
|
||||||
|
self.skipSpaces()
|
||||||
|
|
||||||
|
if self.hasCharacters {
|
||||||
|
let nextChar = self.getNextCharacter()
|
||||||
|
MTAssertNotSpace(nextChar)
|
||||||
|
if nextChar == ch {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
self.unlookCharacter()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
public static let spaceToCommands: [CGFloat: String] = [
|
public static let spaceToCommands: [CGFloat: String] = [
|
||||||
3 : ",",
|
3 : ",",
|
||||||
4 : ">",
|
4 : ">",
|
||||||
@@ -99,7 +136,10 @@ public class MTMathListBuilder {
|
|||||||
self.spacesAllowed = false
|
self.spacesAllowed = false
|
||||||
}
|
}
|
||||||
|
|
||||||
public func build() -> MTMathList? {
|
// MARK: - MTMathList builder functions
|
||||||
|
|
||||||
|
/// Builds a mathlist from the internal `string`. Returns nil if there is an error.
|
||||||
|
public mutating func build() -> MTMathList? {
|
||||||
let list = self.buildInternal(false)
|
let list = self.buildInternal(false)
|
||||||
if self.hasCharacters && error == nil {
|
if self.hasCharacters && error == nil {
|
||||||
self.setError(.mismatchBraces, message: "Mismatched braces: \(self.string)")
|
self.setError(.mismatchBraces, message: "Mismatched braces: \(self.string)")
|
||||||
@@ -111,13 +151,20 @@ public class MTMathListBuilder {
|
|||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Construct a math list from a given string. If there is parse error, returns
|
||||||
|
nil. To retrieve the error use the function `MTMathListBuilder.build(fromString:error:)`.
|
||||||
|
*/
|
||||||
public static func build(fromString string: String) -> MTMathList? {
|
public static func build(fromString string: String) -> MTMathList? {
|
||||||
let builder = MTMathListBuilder(string: string)
|
var builder = MTMathListBuilder(string: string)
|
||||||
return builder.build()
|
return builder.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Construct a math list from a given string. If there is an error while
|
||||||
|
constructing the string, this returns nil. The error is returned in the
|
||||||
|
`error` parameter.
|
||||||
|
*/
|
||||||
public static func build(fromString string: String, error:inout NSError?) -> MTMathList? {
|
public static func build(fromString string: String, error:inout NSError?) -> MTMathList? {
|
||||||
let builder = MTMathListBuilder(string: string)
|
var builder = MTMathListBuilder(string: string)
|
||||||
let output = builder.build()
|
let output = builder.build()
|
||||||
if builder.error != nil {
|
if builder.error != nil {
|
||||||
error = builder.error
|
error = builder.error
|
||||||
@@ -126,227 +173,29 @@ public class MTMathListBuilder {
|
|||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func mathListToString(_ ml: MTMathList?) -> String {
|
public mutating func buildInternal(_ oneCharOnly: Bool) -> MTMathList? {
|
||||||
var str = ""
|
self.buildInternal(oneCharOnly, stopChar: nil)
|
||||||
var currentfontStyle = MTFontStyle.defaultStyle
|
|
||||||
if let atomList = ml {
|
|
||||||
for atom in atomList.atoms {
|
|
||||||
if currentfontStyle != atom.fontStyle {
|
|
||||||
if currentfontStyle != .defaultStyle {
|
|
||||||
str += "}"
|
|
||||||
}
|
|
||||||
if atom.fontStyle != .defaultStyle {
|
|
||||||
let fontStyleName = MTMathAtomFactory.fontNameForStyle(atom.fontStyle)
|
|
||||||
str += "\\\(fontStyleName){"
|
|
||||||
}
|
|
||||||
currentfontStyle = atom.fontStyle
|
|
||||||
}
|
|
||||||
if atom.type == .fraction {
|
|
||||||
if let frac = atom as? MTFraction {
|
|
||||||
if frac.hasRule {
|
|
||||||
str += "\\frac{\(mathListToString(frac.numerator!))}{\(mathListToString(frac.denominator!))}"
|
|
||||||
} else {
|
|
||||||
var command: String? = nil
|
|
||||||
if frac.leftDelimiter == nil && frac.rightDelimiter == nil {
|
|
||||||
command = "atop"
|
|
||||||
} else if frac.leftDelimiter == "(" && frac.rightDelimiter == ")" {
|
|
||||||
command = "choose"
|
|
||||||
} else if frac.leftDelimiter == "{" && frac.rightDelimiter == "}" {
|
|
||||||
command = "brace"
|
|
||||||
} else if frac.leftDelimiter == "[" && frac.rightDelimiter == "]" {
|
|
||||||
command = "brack"
|
|
||||||
} else {
|
|
||||||
command = "atopwithdelims\(frac.leftDelimiter!)\(frac.rightDelimiter!)"
|
|
||||||
}
|
|
||||||
str += "{\(mathListToString(frac.numerator!)) \\\(command!) \(mathListToString(frac.denominator!))}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if atom.type == .radical {
|
|
||||||
str += "\\sqrt"
|
|
||||||
if let rad = atom as? MTRadical {
|
|
||||||
if rad.degree != nil {
|
|
||||||
str += "[\(mathListToString(rad.degree!))]"
|
|
||||||
}
|
|
||||||
str += "{\(mathListToString(rad.radicand!))}"
|
|
||||||
}
|
|
||||||
} else if atom.type == .inner {
|
|
||||||
if let inner = atom as? MTInner {
|
|
||||||
if inner.leftBoundary != nil || inner.rightBoundary != nil {
|
|
||||||
if inner.leftBoundary != nil {
|
|
||||||
str += "\\left\(delimToString(delim: inner.leftBoundary!)) "
|
|
||||||
} else {
|
|
||||||
str += "\\left. "
|
|
||||||
}
|
|
||||||
|
|
||||||
str += mathListToString(inner.innerList!)
|
|
||||||
|
|
||||||
if inner.rightBoundary != nil {
|
|
||||||
str += "\\right\(delimToString(delim: inner.rightBoundary!)) "
|
|
||||||
} else {
|
|
||||||
str += "\\right. "
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
str += "{\(mathListToString(inner.innerList!))}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if atom.type == .table {
|
|
||||||
if let table = atom as? MTMathTable {
|
|
||||||
if table.environment != nil {
|
|
||||||
str += "\\begin{\(table.environment!)}"
|
|
||||||
}
|
|
||||||
|
|
||||||
for i in 0..<table.numRows {
|
|
||||||
let row = table.cells[i]
|
|
||||||
for j in 0..<row.count {
|
|
||||||
let cell = row[j]
|
|
||||||
if table.environment == "matrix" {
|
|
||||||
if cell.atoms.count >= 1 && cell.atoms[0].type == .style {
|
|
||||||
// remove first atom
|
|
||||||
cell.atoms.removeFirst()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if table.environment == "eqalign" || table.environment == "aligned" || table.environment == "split" {
|
|
||||||
if j == 1 && cell.atoms.count >= 1 && cell.atoms[0].type == .ordinary && cell.atoms[0].nucleus.count == 0 {
|
|
||||||
// remove empty nucleus added for spacing
|
|
||||||
cell.atoms.removeFirst()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
str += mathListToString(cell)
|
|
||||||
if j < row.count - 1 {
|
|
||||||
str += "&"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if i < table.numRows - 1 {
|
|
||||||
str += "\\\\ "
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if table.environment != nil {
|
|
||||||
str += "\\end{\(table.environment!)}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if atom.type == .overline {
|
|
||||||
if let overline = atom as? MTOverLine {
|
|
||||||
str += "\\overline"
|
|
||||||
str += "{\(mathListToString(overline.innerList!))}"
|
|
||||||
}
|
|
||||||
} else if atom.type == .underline {
|
|
||||||
if let underline = atom as? MTUnderLine {
|
|
||||||
str += "\\underline"
|
|
||||||
str += "{\(mathListToString(underline.innerList!))}"
|
|
||||||
}
|
|
||||||
} else if atom.type == .accent {
|
|
||||||
if let accent = atom as? MTAccent {
|
|
||||||
str += "\\\(MTMathAtomFactory.accentName(accent)!){\(mathListToString(accent.innerList!))}"
|
|
||||||
}
|
|
||||||
} else if atom.type == .largeOperator {
|
|
||||||
let op = atom as! MTLargeOperator
|
|
||||||
let command = MTMathAtomFactory.latexSymbolName(for: atom)
|
|
||||||
let originalOp = MTMathAtomFactory.atom(forLatexSymbol: command!) as! MTLargeOperator
|
|
||||||
str += "\\\(command!) "
|
|
||||||
if originalOp.limits != op.limits {
|
|
||||||
if op.limits {
|
|
||||||
str += "\\limits "
|
|
||||||
} else {
|
|
||||||
str += "\\nolimits "
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if atom.type == .space {
|
|
||||||
if let space = atom as? MTMathSpace {
|
|
||||||
if let command = MTMathListBuilder.spaceToCommands[space.space] {
|
|
||||||
str += "\\\(command) "
|
|
||||||
} else {
|
|
||||||
str += String(format: "\\mkern%.1fmu", space.space)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if atom.type == .style {
|
|
||||||
if let style = atom as? MTMathStyle {
|
|
||||||
if let command = MTMathListBuilder.styleToCommands[style.style] {
|
|
||||||
str += "\\\(command) "
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if atom.nucleus.isEmpty {
|
|
||||||
str += "{}"
|
|
||||||
} else if atom.nucleus == "\u{2236}" {
|
|
||||||
// math colon
|
|
||||||
str += ":"
|
|
||||||
} else if atom.nucleus == "\u{2212}" {
|
|
||||||
// math minus
|
|
||||||
str += "-"
|
|
||||||
} else {
|
|
||||||
if let command = MTMathAtomFactory.latexSymbolName(for: atom) {
|
|
||||||
str += "\\\(command) "
|
|
||||||
} else {
|
|
||||||
str += "\(atom.nucleus)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if atom.superScript != nil {
|
|
||||||
str += "^{\(mathListToString(atom.superScript!))}"
|
|
||||||
}
|
|
||||||
|
|
||||||
if atom.subScript != nil {
|
|
||||||
str += "_{\(mathListToString(atom.subScript!))}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if currentfontStyle != .defaultStyle {
|
|
||||||
str += "}"
|
|
||||||
}
|
|
||||||
return str
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func delimToString(delim: MTMathAtom) -> String {
|
public mutating func buildInternal(_ oneCharOnly: Bool, stopChar stop: Character?) -> MTMathList? {
|
||||||
if let command = MTMathAtomFactory.getDelimiterName(of: delim) {
|
|
||||||
let singleChars = [ "(", ")", "[", "]", "<", ">", "|", ".", "/"]
|
|
||||||
if singleChars.contains(command) {
|
|
||||||
return command
|
|
||||||
} else if command == "||" {
|
|
||||||
return "\\|"
|
|
||||||
} else {
|
|
||||||
return "\\\(command)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func getNextCharacter() -> Character {
|
|
||||||
assert(self.hasCharacters, "Retrieving character at index \(self.currentCharIndex) beyond length \(self.string.count)")
|
|
||||||
let ch = string[currentCharIndex]
|
|
||||||
currentCharIndex = string.index(after: currentCharIndex)
|
|
||||||
return ch
|
|
||||||
}
|
|
||||||
|
|
||||||
func unlookCharacter() {
|
|
||||||
assert(currentCharIndex > string.startIndex, "Unlooking when at the first character.")
|
|
||||||
if currentCharIndex > string.startIndex {
|
|
||||||
currentCharIndex = string.index(before: currentCharIndex)
|
|
||||||
} else {
|
|
||||||
print("unlooking at first character")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func buildInternal(_ oneCharOnly: Bool) -> MTMathList? {
|
|
||||||
return self.buildInternal(oneCharOnly, stopChar: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func buildInternal(_ oneCharOnly: Bool, stopChar stop: Character?) -> MTMathList? {
|
|
||||||
let list = MTMathList()
|
let list = MTMathList()
|
||||||
assert(!(oneCharOnly && stop != nil), "Cannot set both oneCharOnly and stopChar.")
|
assert(!(oneCharOnly && stop != nil), "Cannot set both oneCharOnly and stopChar.")
|
||||||
var prevAtom: MTMathAtom? = nil
|
var prevAtom: MTMathAtom? = nil
|
||||||
while self.hasCharacters {
|
while self.hasCharacters {
|
||||||
if error != nil { return nil }
|
if error != nil { return nil } // If there is an error thus far then bail out.
|
||||||
|
|
||||||
var atom: MTMathAtom? = nil
|
var atom: MTMathAtom? = nil
|
||||||
let char = self.getNextCharacter()
|
let char = self.getNextCharacter()
|
||||||
|
|
||||||
if oneCharOnly {
|
if oneCharOnly {
|
||||||
if char == "^" || char == "}" || char == "_" || char == "&" {
|
if char == "^" || char == "}" || char == "_" || char == "&" {
|
||||||
|
// this is not the character we are looking for.
|
||||||
|
// They are meant for the caller to look at.
|
||||||
self.unlookCharacter()
|
self.unlookCharacter()
|
||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// If there is a stop character, keep scanning 'til we find it
|
||||||
if stop != nil && char == stop! {
|
if stop != nil && char == stop! {
|
||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
@@ -359,7 +208,8 @@ public class MTMathListBuilder {
|
|||||||
prevAtom = MTMathAtom(type: .ordinary, value: "")
|
prevAtom = MTMathAtom(type: .ordinary, value: "")
|
||||||
list.add(prevAtom!)
|
list.add(prevAtom!)
|
||||||
}
|
}
|
||||||
|
// this is a superscript for the previous atom
|
||||||
|
// note: if the next char is the stopChar it will be consumed by the ^ and so it doesn't count as stop
|
||||||
prevAtom!.superScript = self.buildInternal(true)
|
prevAtom!.superScript = self.buildInternal(true)
|
||||||
continue
|
continue
|
||||||
} else if char == "_" {
|
} else if char == "_" {
|
||||||
@@ -370,6 +220,8 @@ public class MTMathListBuilder {
|
|||||||
prevAtom = MTMathAtom(type: .ordinary, value: "")
|
prevAtom = MTMathAtom(type: .ordinary, value: "")
|
||||||
list.add(prevAtom!)
|
list.add(prevAtom!)
|
||||||
}
|
}
|
||||||
|
// this is a subscript for the previous atom
|
||||||
|
// note: if the next char is the stopChar it will be consumed by the _ and so it doesn't count as stop
|
||||||
prevAtom!.subScript = self.buildInternal(true)
|
prevAtom!.subScript = self.buildInternal(true)
|
||||||
continue
|
continue
|
||||||
} else if char == "{" {
|
} else if char == "{" {
|
||||||
@@ -382,6 +234,7 @@ public class MTMathListBuilder {
|
|||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
} else if char == "}" {
|
} else if char == "}" {
|
||||||
|
// \ means a command
|
||||||
assert(!oneCharOnly, "This should have been handled before")
|
assert(!oneCharOnly, "This should have been handled before")
|
||||||
assert(stop == nil, "This should have been handled before")
|
assert(stop == nil, "This should have been handled before")
|
||||||
// We encountered a closing brace when there is no stop set, that means there was no
|
// We encountered a closing brace when there is no stop set, that means there was no
|
||||||
@@ -428,18 +281,22 @@ public class MTMathListBuilder {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
} else if char == "&" {
|
} else if char == "&" {
|
||||||
|
// used for column separation in tables
|
||||||
assert(!oneCharOnly, "This should have been handled before")
|
assert(!oneCharOnly, "This should have been handled before")
|
||||||
if self.currentEnv != nil {
|
if self.currentEnv != nil {
|
||||||
return list
|
return list
|
||||||
} else {
|
} else {
|
||||||
|
// Create a new table with the current list and a default env
|
||||||
let table = self.buildTable(env: nil, firstList: list, isRow: false)
|
let table = self.buildTable(env: nil, firstList: list, isRow: false)
|
||||||
return MTMathList(atom: table!)
|
return MTMathList(atom: table!)
|
||||||
}
|
}
|
||||||
} else if spacesAllowed && char == " " {
|
} else if spacesAllowed && char == " " {
|
||||||
|
// If spaces are allowed then spaces do not need escaping with a \ before being used.
|
||||||
atom = MTMathAtomFactory.atom(forLatexSymbol: " ")
|
atom = MTMathAtomFactory.atom(forLatexSymbol: " ")
|
||||||
} else {
|
} else {
|
||||||
atom = MTMathAtomFactory.atom(forCharacter: char)
|
atom = MTMathAtomFactory.atom(forCharacter: char)
|
||||||
if atom == nil {
|
if atom == nil {
|
||||||
|
// Not a recognized character
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -453,7 +310,6 @@ public class MTMathListBuilder {
|
|||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if stop != nil {
|
if stop != nil {
|
||||||
if stop == "}" {
|
if stop == "}" {
|
||||||
// We did not find a corresponding closing brace.
|
// We did not find a corresponding closing brace.
|
||||||
@@ -467,7 +323,194 @@ public class MTMathListBuilder {
|
|||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
func atomForCommand(_ command:String) -> MTMathAtom? {
|
|
||||||
|
// MARK: - MTMathList to LaTeX conversion
|
||||||
|
|
||||||
|
/// This converts the MTMathList to LaTeX.
|
||||||
|
public static func mathListToString(_ ml: MTMathList?) -> String {
|
||||||
|
var str = ""
|
||||||
|
var currentfontStyle = MTFontStyle.defaultStyle
|
||||||
|
if let atomList = ml {
|
||||||
|
for atom in atomList.atoms {
|
||||||
|
if currentfontStyle != atom.fontStyle {
|
||||||
|
if currentfontStyle != .defaultStyle {
|
||||||
|
str += "}"
|
||||||
|
}
|
||||||
|
if atom.fontStyle != .defaultStyle {
|
||||||
|
let fontStyleName = MTMathAtomFactory.fontNameForStyle(atom.fontStyle)
|
||||||
|
str += "\\\(fontStyleName){"
|
||||||
|
}
|
||||||
|
currentfontStyle = atom.fontStyle
|
||||||
|
}
|
||||||
|
if atom.type == .fraction {
|
||||||
|
if let frac = atom as? MTFraction {
|
||||||
|
if frac.hasRule {
|
||||||
|
str += "\\frac{\(mathListToString(frac.numerator!))}{\(mathListToString(frac.denominator!))}"
|
||||||
|
} else {
|
||||||
|
let command: String
|
||||||
|
if frac.leftDelimiter.isEmpty && frac.rightDelimiter.isEmpty {
|
||||||
|
command = "atop"
|
||||||
|
} else if frac.leftDelimiter == "(" && frac.rightDelimiter == ")" {
|
||||||
|
command = "choose"
|
||||||
|
} else if frac.leftDelimiter == "{" && frac.rightDelimiter == "}" {
|
||||||
|
command = "brace"
|
||||||
|
} else if frac.leftDelimiter == "[" && frac.rightDelimiter == "]" {
|
||||||
|
command = "brack"
|
||||||
|
} else {
|
||||||
|
command = "atopwithdelims\(frac.leftDelimiter)\(frac.rightDelimiter)"
|
||||||
|
}
|
||||||
|
str += "{\(mathListToString(frac.numerator!)) \\\(command) \(mathListToString(frac.denominator!))}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if atom.type == .radical {
|
||||||
|
str += "\\sqrt"
|
||||||
|
if let rad = atom as? MTRadical {
|
||||||
|
if rad.degree != nil {
|
||||||
|
str += "[\(mathListToString(rad.degree!))]"
|
||||||
|
}
|
||||||
|
str += "{\(mathListToString(rad.radicand!))}"
|
||||||
|
}
|
||||||
|
} else if atom.type == .inner {
|
||||||
|
if let inner = atom as? MTInner {
|
||||||
|
if inner.leftBoundary != nil || inner.rightBoundary != nil {
|
||||||
|
if inner.leftBoundary != nil {
|
||||||
|
str += "\\left\(delimToString(delim: inner.leftBoundary!)) "
|
||||||
|
} else {
|
||||||
|
str += "\\left. "
|
||||||
|
}
|
||||||
|
|
||||||
|
str += mathListToString(inner.innerList!)
|
||||||
|
|
||||||
|
if inner.rightBoundary != nil {
|
||||||
|
str += "\\right\(delimToString(delim: inner.rightBoundary!)) "
|
||||||
|
} else {
|
||||||
|
str += "\\right. "
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
str += "{\(mathListToString(inner.innerList!))}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if atom.type == .table {
|
||||||
|
if let table = atom as? MTMathTable {
|
||||||
|
if !table.environment.isEmpty {
|
||||||
|
str += "\\begin{\(table.environment)}"
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..<table.numRows {
|
||||||
|
let row = table.cells[i]
|
||||||
|
for j in 0..<row.count {
|
||||||
|
let cell = row[j]
|
||||||
|
if table.environment == "matrix" {
|
||||||
|
if cell.atoms.count >= 1 && cell.atoms[0].type == .style {
|
||||||
|
// remove first atom
|
||||||
|
cell.atoms.removeFirst()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if table.environment == "eqalign" || table.environment == "aligned" || table.environment == "split" {
|
||||||
|
if j == 1 && cell.atoms.count >= 1 && cell.atoms[0].type == .ordinary && cell.atoms[0].nucleus.count == 0 {
|
||||||
|
// remove empty nucleus added for spacing
|
||||||
|
cell.atoms.removeFirst()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
str += mathListToString(cell)
|
||||||
|
if j < row.count - 1 {
|
||||||
|
str += "&"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if i < table.numRows - 1 {
|
||||||
|
str += "\\\\ "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !table.environment.isEmpty {
|
||||||
|
str += "\\end{\(table.environment)}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if atom.type == .overline {
|
||||||
|
if let overline = atom as? MTOverLine {
|
||||||
|
str += "\\overline"
|
||||||
|
str += "{\(mathListToString(overline.innerList!))}"
|
||||||
|
}
|
||||||
|
} else if atom.type == .underline {
|
||||||
|
if let underline = atom as? MTUnderLine {
|
||||||
|
str += "\\underline"
|
||||||
|
str += "{\(mathListToString(underline.innerList!))}"
|
||||||
|
}
|
||||||
|
} else if atom.type == .accent {
|
||||||
|
if let accent = atom as? MTAccent {
|
||||||
|
str += "\\\(MTMathAtomFactory.accentName(accent)!){\(mathListToString(accent.innerList!))}"
|
||||||
|
}
|
||||||
|
} else if atom.type == .largeOperator {
|
||||||
|
let op = atom as! MTLargeOperator
|
||||||
|
let command = MTMathAtomFactory.latexSymbolName(for: atom)
|
||||||
|
let originalOp = MTMathAtomFactory.atom(forLatexSymbol: command!) as! MTLargeOperator
|
||||||
|
str += "\\\(command!) "
|
||||||
|
if originalOp.limits != op.limits {
|
||||||
|
if op.limits {
|
||||||
|
str += "\\limits "
|
||||||
|
} else {
|
||||||
|
str += "\\nolimits "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if atom.type == .space {
|
||||||
|
if let space = atom as? MTMathSpace {
|
||||||
|
if let command = Self.spaceToCommands[space.space] {
|
||||||
|
str += "\\\(command) "
|
||||||
|
} else {
|
||||||
|
str += String(format: "\\mkern%.1fmu", space.space)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if atom.type == .style {
|
||||||
|
if let style = atom as? MTMathStyle {
|
||||||
|
if let command = Self.styleToCommands[style.style] {
|
||||||
|
str += "\\\(command) "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if atom.nucleus.isEmpty {
|
||||||
|
str += "{}"
|
||||||
|
} else if atom.nucleus == "\u{2236}" {
|
||||||
|
// math colon
|
||||||
|
str += ":"
|
||||||
|
} else if atom.nucleus == "\u{2212}" {
|
||||||
|
// math minus
|
||||||
|
str += "-"
|
||||||
|
} else {
|
||||||
|
if let command = MTMathAtomFactory.latexSymbolName(for: atom) {
|
||||||
|
str += "\\\(command) "
|
||||||
|
} else {
|
||||||
|
str += "\(atom.nucleus)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if atom.superScript != nil {
|
||||||
|
str += "^{\(mathListToString(atom.superScript!))}"
|
||||||
|
}
|
||||||
|
|
||||||
|
if atom.subScript != nil {
|
||||||
|
str += "_{\(mathListToString(atom.subScript!))}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if currentfontStyle != .defaultStyle {
|
||||||
|
str += "}"
|
||||||
|
}
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func delimToString(delim: MTMathAtom) -> String {
|
||||||
|
if let command = MTMathAtomFactory.getDelimiterName(of: delim) {
|
||||||
|
let singleChars = [ "(", ")", "[", "]", "<", ">", "|", ".", "/"]
|
||||||
|
if singleChars.contains(command) {
|
||||||
|
return command
|
||||||
|
} else if command == "||" {
|
||||||
|
return "\\|"
|
||||||
|
} else {
|
||||||
|
return "\\\(command)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func atomForCommand(_ command:String) -> MTMathAtom? {
|
||||||
if let atom = MTMathAtomFactory.atom(forLatexSymbol: command) {
|
if let atom = MTMathAtomFactory.atom(forLatexSymbol: command) {
|
||||||
return atom
|
return atom
|
||||||
}
|
}
|
||||||
@@ -557,7 +600,7 @@ public class MTMathListBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func readColor() -> String? {
|
mutating func readColor() -> String? {
|
||||||
if !self.expectCharacter("{") {
|
if !self.expectCharacter("{") {
|
||||||
// We didn't find an opening brace, so no env found.
|
// We didn't find an opening brace, so no env found.
|
||||||
self.setError(.characterNotFound, message:"Missing {")
|
self.setError(.characterNotFound, message:"Missing {")
|
||||||
@@ -588,7 +631,7 @@ public class MTMathListBuilder {
|
|||||||
return mutable;
|
return mutable;
|
||||||
}
|
}
|
||||||
|
|
||||||
func skipSpaces() {
|
mutating func skipSpaces() {
|
||||||
while self.hasCharacters {
|
while self.hasCharacters {
|
||||||
let ch = self.getNextCharacter().utf32Char
|
let ch = self.getNextCharacter().utf32Char
|
||||||
if ch < 0x21 || ch > 0x7E {
|
if ch < 0x21 || ch > 0x7E {
|
||||||
@@ -611,7 +654,7 @@ public class MTMathListBuilder {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
func stopCommand(_ command: String, list:MTMathList, stopChar:Character?) -> MTMathList? {
|
mutating func stopCommand(_ command: String, list:MTMathList, stopChar:Character?) -> MTMathList? {
|
||||||
if command == "right" {
|
if command == "right" {
|
||||||
if currentInnerAtom == nil {
|
if currentInnerAtom == nil {
|
||||||
let errorMessage = "Missing \\left";
|
let errorMessage = "Missing \\left";
|
||||||
@@ -676,7 +719,7 @@ public class MTMathListBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Applies the modifier to the atom. Returns true if modifier applied.
|
// Applies the modifier to the atom. Returns true if modifier applied.
|
||||||
func applyModifier(_ modifier:String, atom:MTMathAtom?) -> Bool {
|
mutating func applyModifier(_ modifier:String, atom:MTMathAtom?) -> Bool {
|
||||||
if modifier == "limits" {
|
if modifier == "limits" {
|
||||||
if atom?.type != .largeOperator {
|
if atom?.type != .largeOperator {
|
||||||
let errorMessage = "Limits can only be applied to an operator."
|
let errorMessage = "Limits can only be applied to an operator."
|
||||||
@@ -699,14 +742,14 @@ public class MTMathListBuilder {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func setError(_ code:MTParseErrors, message:String) {
|
mutating func setError(_ code:MTParseErrors, message:String) {
|
||||||
// Only record the first error.
|
// Only record the first error.
|
||||||
if error == nil {
|
if error == nil {
|
||||||
error = NSError(domain: MTParseError, code: code.rawValue, userInfo: [ NSLocalizedDescriptionKey : message ])
|
error = NSError(domain: MTParseError, code: code.rawValue, userInfo: [ NSLocalizedDescriptionKey : message ])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func atom(forCommand command: String) -> MTMathAtom? {
|
mutating func atom(forCommand command: String) -> MTMathAtom? {
|
||||||
if let atom = MTMathAtomFactory.atom(forLatexSymbol: command) {
|
if let atom = MTMathAtomFactory.atom(forLatexSymbol: command) {
|
||||||
return atom
|
return atom
|
||||||
}
|
}
|
||||||
@@ -786,7 +829,7 @@ public class MTMathListBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func readEnvironment() -> String? {
|
mutating func readEnvironment() -> String? {
|
||||||
if !self.expectCharacter("{") {
|
if !self.expectCharacter("{") {
|
||||||
// We didn't find an opening brace, so no env found.
|
// We didn't find an opening brace, so no env found.
|
||||||
self.setError(.characterNotFound, message: "Missing {")
|
self.setError(.characterNotFound, message: "Missing {")
|
||||||
@@ -808,24 +851,7 @@ public class MTMathListBuilder {
|
|||||||
assert(ch >= "\u{21}" && ch <= "\u{7E}", "Expected non-space character \(ch)")
|
assert(ch >= "\u{21}" && ch <= "\u{7E}", "Expected non-space character \(ch)")
|
||||||
}
|
}
|
||||||
|
|
||||||
func expectCharacter(_ ch: Character) -> Bool {
|
mutating func buildTable(env: String?, firstList: MTMathList?, isRow: Bool) -> MTMathAtom? {
|
||||||
MTAssertNotSpace(ch)
|
|
||||||
self.skipSpaces()
|
|
||||||
|
|
||||||
if self.hasCharacters {
|
|
||||||
let nextChar = self.getNextCharacter()
|
|
||||||
MTAssertNotSpace(nextChar)
|
|
||||||
if nextChar == ch {
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
self.unlookCharacter()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildTable(env: String?, firstList: MTMathList?, isRow: Bool) -> MTMathAtom? {
|
|
||||||
// Save the current env till an new one gets built.
|
// Save the current env till an new one gets built.
|
||||||
let oldEnv = self.currentEnv
|
let oldEnv = self.currentEnv
|
||||||
|
|
||||||
@@ -876,7 +902,7 @@ public class MTMathListBuilder {
|
|||||||
return table
|
return table
|
||||||
}
|
}
|
||||||
|
|
||||||
func getBoundaryAtom(_ delimiterType: String) -> MTMathAtom? {
|
mutating func getBoundaryAtom(_ delimiterType: String) -> MTMathAtom? {
|
||||||
let delim = self.readDelimiter()
|
let delim = self.readDelimiter()
|
||||||
if delim == nil {
|
if delim == nil {
|
||||||
let errorMessage = "Missing delimiter for \\\(delimiterType)"
|
let errorMessage = "Missing delimiter for \\\(delimiterType)"
|
||||||
@@ -892,7 +918,7 @@ public class MTMathListBuilder {
|
|||||||
return boundary
|
return boundary
|
||||||
}
|
}
|
||||||
|
|
||||||
func readDelimiter() -> String? {
|
mutating func readDelimiter() -> String? {
|
||||||
self.skipSpaces()
|
self.skipSpaces()
|
||||||
while self.hasCharacters {
|
while self.hasCharacters {
|
||||||
let char = self.getNextCharacter()
|
let char = self.getNextCharacter()
|
||||||
@@ -910,7 +936,7 @@ public class MTMathListBuilder {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func readCommand() -> String {
|
mutating func readCommand() -> String {
|
||||||
let singleChars = "{}$#%_| ,>;!\\"
|
let singleChars = "{}$#%_| ,>;!\\"
|
||||||
if self.hasCharacters {
|
if self.hasCharacters {
|
||||||
let char = self.getNextCharacter()
|
let char = self.getNextCharacter()
|
||||||
@@ -923,7 +949,7 @@ public class MTMathListBuilder {
|
|||||||
return self.readString()
|
return self.readString()
|
||||||
}
|
}
|
||||||
|
|
||||||
func readString() -> String {
|
mutating func readString() -> String {
|
||||||
// a string of all upper and lower case characters.
|
// a string of all upper and lower case characters.
|
||||||
var output = ""
|
var output = ""
|
||||||
while self.hasCharacters {
|
while self.hasCharacters {
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
//
|
//
|
||||||
// MTMathListDisplay.swift
|
|
||||||
// MathRenderSwift
|
|
||||||
//
|
|
||||||
// Created by Mike Griebling on 2022-12-31.
|
// Created by Mike Griebling on 2022-12-31.
|
||||||
|
// Translated from an Objective-C implementation by Kostub Deshmukh.
|
||||||
|
//
|
||||||
|
// This software may be modified and distributed under the terms of the
|
||||||
|
// MIT license. See the LICENSE file for details.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
@@ -26,6 +27,7 @@ func isIos6Supported() -> Bool {
|
|||||||
return MTDisplay.supported
|
return MTDisplay.supported
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The Downshift protocol allows an MTDisplay to be shifted down by a given amount.
|
||||||
protocol DownShift {
|
protocol DownShift {
|
||||||
var shiftDown:CGFloat { set get }
|
var shiftDown:CGFloat { set get }
|
||||||
}
|
}
|
||||||
@@ -52,7 +54,7 @@ public class MTDisplay:NSObject {
|
|||||||
|
|
||||||
/// Gets the bounding rectangle for the MTDisplay
|
/// Gets the bounding rectangle for the MTDisplay
|
||||||
func displayBounds() -> CGRect {
|
func displayBounds() -> CGRect {
|
||||||
return CGRectMake(self.position.x, self.position.y - self.descent, self.width, self.ascent + self.descent)
|
CGRectMake(self.position.x, self.position.y - self.descent, self.width, self.ascent + self.descent)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// For debugging. Shows the object in quick look in Xcode.
|
/// For debugging. Shows the object in quick look in Xcode.
|
||||||
@@ -81,17 +83,17 @@ public class MTDisplay:NSObject {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// The distance from the axis to the top of the display
|
/// The distance from the axis to the top of the display
|
||||||
var ascent:CGFloat = 0
|
public var ascent:CGFloat = 0
|
||||||
/// The distance from the axis to the bottom of the display
|
/// The distance from the axis to the bottom of the display
|
||||||
var descent:CGFloat = 0
|
public var descent:CGFloat = 0
|
||||||
/// The width of the display
|
/// The width of the display
|
||||||
var width:CGFloat = 0
|
public var width:CGFloat = 0
|
||||||
/// Position of the display with respect to the parent view or display.
|
/// Position of the display with respect to the parent view or display.
|
||||||
var position=CGPoint.zero
|
var position = CGPoint.zero
|
||||||
/// The range of characters supported by this item
|
/// The range of characters supported by this item
|
||||||
var range:NSRange=NSMakeRange(0, 0)
|
public var range:NSRange=NSMakeRange(0, 0)
|
||||||
/// Whether the display has a subscript/superscript following it.
|
/// Whether the display has a subscript/superscript following it.
|
||||||
var hasScript:Bool = false
|
public var hasScript:Bool = false
|
||||||
/// The text color for this display
|
/// The text color for this display
|
||||||
var textColor: MTColor?
|
var textColor: MTColor?
|
||||||
/// The local color, if the color was mutated local with the color command
|
/// The local color, if the color was mutated local with the color command
|
||||||
@@ -101,6 +103,7 @@ public class MTDisplay:NSObject {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Special class to be inherited from that implements the DownShift protocol
|
||||||
class MTDisplayDS : MTDisplay, DownShift {
|
class MTDisplayDS : MTDisplay, DownShift {
|
||||||
|
|
||||||
var shiftDown: CGFloat = 0
|
var shiftDown: CGFloat = 0
|
||||||
@@ -110,10 +113,10 @@ class MTDisplayDS : MTDisplay, DownShift {
|
|||||||
// MARK: - MTCTLineDisplay
|
// MARK: - MTCTLineDisplay
|
||||||
|
|
||||||
/// A rendering of a single CTLine as an MTDisplay
|
/// A rendering of a single CTLine as an MTDisplay
|
||||||
class MTCTLineDisplay : MTDisplay {
|
public class MTCTLineDisplay : MTDisplay {
|
||||||
|
|
||||||
/// The CTLine being displayed
|
/// The CTLine being displayed
|
||||||
var line:CTLine!
|
public var line:CTLine!
|
||||||
/// The attributed string used to generate the CTLineRef. Note setting this does not reset the dimensions of
|
/// The attributed string used to generate the CTLineRef. Note setting this does not reset the dimensions of
|
||||||
/// the display. So set only when
|
/// the display. So set only when
|
||||||
var attributedString:NSAttributedString? {
|
var attributedString:NSAttributedString? {
|
||||||
@@ -123,7 +126,7 @@ class MTCTLineDisplay : MTDisplay {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// An array of MTMathAtoms that this CTLine displays. Used for indexing back into the MTMathList
|
/// An array of MTMathAtoms that this CTLine displays. Used for indexing back into the MTMathList
|
||||||
var atoms = [MTMathAtom]()
|
public fileprivate(set) var atoms = [MTMathAtom]()
|
||||||
|
|
||||||
init(withString attrString:NSAttributedString?, position:CGPoint, range:NSRange, font:MTFont?, atoms:[MTMathAtom]) {
|
init(withString attrString:NSAttributedString?, position:CGPoint, range:NSRange, font:MTFont?, atoms:[MTMathAtom]) {
|
||||||
super.init()
|
super.init()
|
||||||
@@ -197,10 +200,9 @@ class MTCTLineDisplay : MTDisplay {
|
|||||||
public class MTMathListDisplay : MTDisplay {
|
public class MTMathListDisplay : MTDisplay {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@typedef MTLinePosition
|
The type of position for a line, i.e. subscript/superscript or regular.
|
||||||
@brief The type of position for a line, i.e. subscript/superscript or regular.
|
|
||||||
*/
|
*/
|
||||||
enum LinePosition : Int {
|
public enum LinePosition : Int {
|
||||||
/// Regular
|
/// Regular
|
||||||
case regular
|
case regular
|
||||||
/// Positioned at a subscript
|
/// Positioned at a subscript
|
||||||
@@ -210,13 +212,13 @@ public class MTMathListDisplay : MTDisplay {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Where the line is positioned
|
/// Where the line is positioned
|
||||||
var type:LinePosition = .regular
|
public var type:LinePosition = .regular
|
||||||
/// An array of MTDisplays which are positioned relative to the position of the
|
/// An array of MTDisplays which are positioned relative to the position of the
|
||||||
/// the current display.
|
/// the current display.
|
||||||
var subDisplays = [MTDisplay]()
|
public fileprivate(set) var subDisplays = [MTDisplay]()
|
||||||
/// If a subscript or superscript this denotes the location in the parent MTList. For a
|
/// If a subscript or superscript this denotes the location in the parent MTList. For a
|
||||||
/// regular list this is NSNotFound
|
/// regular list this is NSNotFound
|
||||||
var index: Int = 0
|
public var index: Int = 0
|
||||||
|
|
||||||
init(withDisplays displays:[MTDisplay], range:NSRange) {
|
init(withDisplays displays:[MTDisplay], range:NSRange) {
|
||||||
super.init()
|
super.init()
|
||||||
@@ -287,23 +289,19 @@ public class MTMathListDisplay : MTDisplay {
|
|||||||
// MARK: - MTFractionDisplay
|
// MARK: - MTFractionDisplay
|
||||||
|
|
||||||
/// Rendering of an MTFraction as an MTDisplay
|
/// Rendering of an MTFraction as an MTDisplay
|
||||||
class MTFractionDisplay : MTDisplay {
|
public class MTFractionDisplay : MTDisplay {
|
||||||
|
|
||||||
/** A display representing the numerator of the fraction. Its position is relative
|
/** A display representing the numerator of the fraction. Its position is relative
|
||||||
to the parent and is not treated as a sub-display.
|
to the parent and is not treated as a sub-display.
|
||||||
*/
|
*/
|
||||||
var numerator:MTMathListDisplay?
|
public fileprivate(set) var numerator:MTMathListDisplay?
|
||||||
/** A display representing the denominator of the fraction. Its position is relative
|
/** A display representing the denominator of the fraction. Its position is relative
|
||||||
to the parent is not treated as a sub-display.
|
to the parent is not treated as a sub-display.
|
||||||
*/
|
*/
|
||||||
var denominator:MTMathListDisplay?
|
public fileprivate(set) var denominator:MTMathListDisplay?
|
||||||
|
|
||||||
var numeratorUp:CGFloat=0 {
|
var numeratorUp:CGFloat=0 { didSet { self.updateNumeratorPosition() } }
|
||||||
didSet { self.updateNumeratorPosition() }
|
var denominatorDown:CGFloat=0 { didSet { self.updateDenominatorPosition() } }
|
||||||
}
|
|
||||||
var denominatorDown:CGFloat=0 {
|
|
||||||
didSet { self.updateDenominatorPosition() }
|
|
||||||
}
|
|
||||||
var linePosition:CGFloat=0
|
var linePosition:CGFloat=0
|
||||||
var lineThickness:CGFloat=0
|
var lineThickness:CGFloat=0
|
||||||
|
|
||||||
@@ -316,17 +314,17 @@ class MTFractionDisplay : MTDisplay {
|
|||||||
assert(self.range.length == 1, "Fraction range length not 1 - range (\(range.location), \(range.length)")
|
assert(self.range.length == 1, "Fraction range length not 1 - range (\(range.location), \(range.length)")
|
||||||
}
|
}
|
||||||
|
|
||||||
override var ascent:CGFloat {
|
override public var ascent:CGFloat {
|
||||||
set { super.ascent = newValue }
|
set { super.ascent = newValue }
|
||||||
get { numerator!.ascent + self.numeratorUp }
|
get { numerator!.ascent + self.numeratorUp }
|
||||||
}
|
}
|
||||||
|
|
||||||
override var descent:CGFloat {
|
override public var descent:CGFloat {
|
||||||
set { super.descent = newValue }
|
set { super.descent = newValue }
|
||||||
get { denominator!.descent + self.denominatorDown }
|
get { denominator!.descent + self.denominatorDown }
|
||||||
}
|
}
|
||||||
|
|
||||||
override var width:CGFloat {
|
override public var width:CGFloat {
|
||||||
set { super.width = newValue }
|
set { super.width = newValue }
|
||||||
get { max(numerator!.width, denominator!.width) }
|
get { max(numerator!.width, denominator!.width) }
|
||||||
}
|
}
|
||||||
@@ -391,11 +389,11 @@ class MTRadicalDisplay : MTDisplay {
|
|||||||
/** A display representing the radicand of the radical. Its position is relative
|
/** A display representing the radicand of the radical. Its position is relative
|
||||||
to the parent is not treated as a sub-display.
|
to the parent is not treated as a sub-display.
|
||||||
*/
|
*/
|
||||||
var radicand:MTMathListDisplay?
|
public fileprivate(set) var radicand:MTMathListDisplay?
|
||||||
/** A display representing the degree of the radical. Its position is relative
|
/** A display representing the degree of the radical. Its position is relative
|
||||||
to the parent is not treated as a sub-display.
|
to the parent is not treated as a sub-display.
|
||||||
*/
|
*/
|
||||||
var degree:MTMathListDisplay?
|
public fileprivate(set) var degree:MTMathListDisplay?
|
||||||
|
|
||||||
override var position: CGPoint {
|
override var position: CGPoint {
|
||||||
set {
|
set {
|
||||||
@@ -536,21 +534,13 @@ class MTGlyphDisplay : MTDisplayDS {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override var ascent:CGFloat {
|
override var ascent:CGFloat {
|
||||||
set {
|
set { super.ascent = newValue }
|
||||||
super.ascent = newValue
|
get { super.ascent - self.shiftDown }
|
||||||
}
|
|
||||||
get {
|
|
||||||
return super.ascent - self.shiftDown;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override var descent:CGFloat {
|
override var descent:CGFloat {
|
||||||
set {
|
set { super.descent = newValue }
|
||||||
super.descent = newValue
|
get { super.descent + self.shiftDown }
|
||||||
}
|
|
||||||
get {
|
|
||||||
return super.descent + self.shiftDown;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -593,21 +583,13 @@ class MTGlyphConstructionDisplay:MTDisplayDS {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override var ascent:CGFloat {
|
override var ascent:CGFloat {
|
||||||
set {
|
set { super.ascent = newValue }
|
||||||
super.ascent = newValue
|
get { super.ascent - self.shiftDown }
|
||||||
}
|
|
||||||
get {
|
|
||||||
return super.ascent - self.shiftDown;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override var descent:CGFloat {
|
override var descent:CGFloat {
|
||||||
set {
|
set { super.descent = newValue }
|
||||||
super.descent = newValue
|
get { super.descent + self.shiftDown }
|
||||||
}
|
|
||||||
get {
|
|
||||||
return super.descent + self.shiftDown;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -627,16 +609,8 @@ class MTLargeOpLimitsDisplay : MTDisplay {
|
|||||||
var lowerLimit:MTMathListDisplay?
|
var lowerLimit:MTMathListDisplay?
|
||||||
|
|
||||||
var limitShift:CGFloat=0
|
var limitShift:CGFloat=0
|
||||||
var upperLimitGap:CGFloat=0 {
|
var upperLimitGap:CGFloat=0 { didSet { self.updateUpperLimitPosition() } }
|
||||||
didSet {
|
var lowerLimitGap:CGFloat=0 { didSet { self.updateLowerLimitPosition() } }
|
||||||
self.updateUpperLimitPosition()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var lowerLimitGap:CGFloat=0 {
|
|
||||||
didSet {
|
|
||||||
self.updateUpperLimitPosition()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var extraPadding:CGFloat=0
|
var extraPadding:CGFloat=0
|
||||||
|
|
||||||
var nucleus:MTDisplay?
|
var nucleus:MTDisplay?
|
||||||
@@ -658,9 +632,7 @@ class MTLargeOpLimitsDisplay : MTDisplay {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override var ascent:CGFloat {
|
override var ascent:CGFloat {
|
||||||
set {
|
set { super.ascent = newValue }
|
||||||
super.ascent = newValue
|
|
||||||
}
|
|
||||||
get {
|
get {
|
||||||
if self.upperLimit != nil {
|
if self.upperLimit != nil {
|
||||||
return nucleus!.ascent + extraPadding + self.upperLimit!.ascent + upperLimitGap + self.upperLimit!.descent
|
return nucleus!.ascent + extraPadding + self.upperLimit!.ascent + upperLimitGap + self.upperLimit!.descent
|
||||||
@@ -671,9 +643,7 @@ class MTLargeOpLimitsDisplay : MTDisplay {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override var descent:CGFloat {
|
override var descent:CGFloat {
|
||||||
set {
|
set { super.descent = newValue }
|
||||||
super.descent = newValue
|
|
||||||
}
|
|
||||||
get {
|
get {
|
||||||
if self.lowerLimit != nil {
|
if self.lowerLimit != nil {
|
||||||
return nucleus!.descent + extraPadding + lowerLimitGap + self.lowerLimit!.descent + self.lowerLimit!.ascent;
|
return nucleus!.descent + extraPadding + lowerLimitGap + self.lowerLimit!.descent + self.lowerLimit!.ascent;
|
||||||
|
|||||||
@@ -1,22 +1,51 @@
|
|||||||
//
|
//
|
||||||
// MTMathListIndex.swift
|
|
||||||
// MathRenderSwift
|
|
||||||
//
|
|
||||||
// Created by Mike Griebling on 2022-12-31.
|
// Created by Mike Griebling on 2022-12-31.
|
||||||
|
// Translated from an Objective-C implementation by Kostub Deshmukh.
|
||||||
|
//
|
||||||
|
// This software may be modified and distributed under the terms of the
|
||||||
|
// MIT license. See the LICENSE file for details.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An index that points to a particular character in the MTMathList. The index is a LinkedList that represents
|
||||||
|
* a path from the beginning of the MTMathList to reach a particular atom in the list. The next node of the path
|
||||||
|
* is represented by the subIndex. The path terminates when the subIndex is nil.
|
||||||
|
*
|
||||||
|
* If there is a subIndex, the subIndexType denotes what branch the path takes (i.e. superscript, subscript,
|
||||||
|
* numerator, denominator etc.).
|
||||||
|
* e.g in the expression 25^{2/4} the index of the character 4 is represented as:
|
||||||
|
* (1, superscript) -> (0, denominator) -> (0, none)
|
||||||
|
* This can be interpreted as start at index 1 (i.e. the 5) go up to the superscript.
|
||||||
|
* Then look at index 0 (i.e. 2/4) and go to the denominator. Then look up index 0 (i.e. the 4) which this final
|
||||||
|
* index.
|
||||||
|
*
|
||||||
|
* The level of an index is the number of nodes in the LinkedList to get to the final path.
|
||||||
|
*/
|
||||||
public class MTMathListIndex {
|
public class MTMathListIndex {
|
||||||
|
|
||||||
|
/**
|
||||||
|
The type of the subindex.
|
||||||
|
|
||||||
|
The type of the subindex denotes what branch the path to the atom that this index points to takes.
|
||||||
|
*/
|
||||||
public enum MTMathListSubIndexType: Int {
|
public enum MTMathListSubIndexType: Int {
|
||||||
case none = 0
|
/// The index denotes the whole atom, subIndex is nil.
|
||||||
|
case none = 0
|
||||||
|
/// The position in the subindex is an index into the nucleus
|
||||||
case nucleus
|
case nucleus
|
||||||
case superScript
|
/// The subindex indexes into the superscript.
|
||||||
case subScript
|
case superscript
|
||||||
|
/// The subindex indexes into the subscript
|
||||||
|
case ssubscript
|
||||||
|
/// The subindex indexes into the numerator (only valid for fractions)
|
||||||
case numerator
|
case numerator
|
||||||
|
/// The subindex indexes into the denominator (only valid for fractions)
|
||||||
case denominator
|
case denominator
|
||||||
|
/// The subindex indexes into the radicand (only valid for radicals)
|
||||||
case radicand
|
case radicand
|
||||||
|
/// The subindex indexes into the degree (only valid for radicals)
|
||||||
case degree
|
case degree
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,6 +66,7 @@ public class MTMathListIndex {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the previous index if present. Returns `nil` if there is no previous index.
|
||||||
func prevIndex() -> MTMathListIndex? {
|
func prevIndex() -> MTMathListIndex? {
|
||||||
if self.subIndexType == .none {
|
if self.subIndexType == .none {
|
||||||
if self.atomIndex > 0 {
|
if self.atomIndex > 0 {
|
||||||
@@ -50,6 +80,7 @@ public class MTMathListIndex {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the next index.
|
||||||
func nextIndex() -> MTMathListIndex {
|
func nextIndex() -> MTMathListIndex {
|
||||||
if self.subIndexType == .none {
|
if self.subIndexType == .none {
|
||||||
return MTMathListIndex(level0Index: self.atomIndex + 1)
|
return MTMathListIndex(level0Index: self.atomIndex + 1)
|
||||||
@@ -65,9 +96,7 @@ public class MTMathListIndex {
|
|||||||
* e.g. a superscript or a fraction numerator. This returns true if the innermost subindex points to the beginning of a
|
* e.g. a superscript or a fraction numerator. This returns true if the innermost subindex points to the beginning of a
|
||||||
* line.
|
* line.
|
||||||
*/
|
*/
|
||||||
func isBeginningOfLine() -> Bool {
|
func isBeginningOfLine() -> Bool { self.finalIndex == 0 }
|
||||||
return self.finalIndex == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func isAtSameLevel(with index: MTMathListIndex?) -> Bool {
|
func isAtSameLevel(with index: MTMathListIndex?) -> Bool {
|
||||||
if self.subIndexType != index?.subIndexType {
|
if self.subIndexType != index?.subIndexType {
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
//
|
//
|
||||||
// MTMathUILabel.swift
|
// Created by Mike Griebling on 2022-12-31.
|
||||||
// MathRenderSwift
|
// Translated from an Objective-C implementation by Kostub Deshmukh.
|
||||||
//
|
//
|
||||||
// Created by Mike Griebling on 2023-01-01.
|
// This software may be modified and distributed under the terms of the
|
||||||
|
// MIT license. See the LICENSE file for details.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
//
|
//
|
||||||
// MTTypesetter.swift
|
// Created by Mike Griebling on 2022-12-31.
|
||||||
// MathRenderSwift
|
// Translated from an Objective-C implementation by Kostub Deshmukh.
|
||||||
//
|
//
|
||||||
// Created by Mike Griebling on 2023-01-01.
|
// This software may be modified and distributed under the terms of the
|
||||||
|
// MIT license. See the LICENSE file for details.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import CoreGraphics
|
import CoreGraphics
|
||||||
import CoreText
|
import CoreText
|
||||||
|
|
||||||
// MARK: - - Inter Element Spacing
|
// MARK: - Inter Element Spacing
|
||||||
|
|
||||||
enum InterElementSpaceType : Int {
|
enum InterElementSpaceType : Int {
|
||||||
case invalid = -1
|
case invalid = -1
|
||||||
@@ -76,7 +77,7 @@ func getInterElementSpaceArrayIndexForType(_ type:MTMathAtomType, row:Bool) -> I
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - - Italics
|
// MARK: - Italics
|
||||||
// mathit
|
// mathit
|
||||||
func getItalicized(_ ch:Character) -> UTF32Char {
|
func getItalicized(_ ch:Character) -> UTF32Char {
|
||||||
var unicode = ch.utf32Char
|
var unicode = ch.utf32Char
|
||||||
@@ -235,7 +236,7 @@ func getSansSerif(_ ch:Character) -> UTF32Char {
|
|||||||
// mathfrak
|
// mathfrak
|
||||||
func getFraktur(_ ch:Character) -> UTF32Char {
|
func getFraktur(_ ch:Character) -> UTF32Char {
|
||||||
// Fraktur has exceptions:
|
// Fraktur has exceptions:
|
||||||
switch(ch) {
|
switch ch {
|
||||||
case "C":
|
case "C":
|
||||||
return 0x212D; // C Fraktur
|
return 0x212D; // C Fraktur
|
||||||
case "H":
|
case "H":
|
||||||
@@ -337,7 +338,7 @@ func getBboxDetails(_ bbox:CGRect, ascent:inout CGFloat, descent:inout CGFloat)
|
|||||||
descent = max(0, 0 - CGRectGetMinY(bbox))
|
descent = max(0, 0 - CGRectGetMinY(bbox))
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - - MTTypesetter
|
// MARK: - MTTypesetter
|
||||||
|
|
||||||
class MTTypesetter {
|
class MTTypesetter {
|
||||||
var font:MTFont!
|
var font:MTFont!
|
||||||
@@ -879,7 +880,7 @@ class MTTypesetter {
|
|||||||
currentPosition.x += max(superScript!.width + delta, ssubscript!.width) + styleFont.mathTable!.spaceAfterScript;
|
currentPosition.x += max(superScript!.width + delta, ssubscript!.width) + styleFont.mathTable!.spaceAfterScript;
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - - Fractions
|
// MARK: - Fractions
|
||||||
|
|
||||||
func numeratorShiftUp(_ hasRule:Bool) -> CGFloat {
|
func numeratorShiftUp(_ hasRule:Bool) -> CGFloat {
|
||||||
if hasRule {
|
if hasRule {
|
||||||
@@ -1002,7 +1003,7 @@ class MTTypesetter {
|
|||||||
display.denominatorDown = denominatorShiftDown;
|
display.denominatorDown = denominatorShiftDown;
|
||||||
display.lineThickness = barThickness;
|
display.lineThickness = barThickness;
|
||||||
display.linePosition = barLocation;
|
display.linePosition = barLocation;
|
||||||
if frac!.leftDelimiter == nil && frac!.rightDelimiter == nil {
|
if frac!.leftDelimiter.isEmpty && frac!.rightDelimiter.isEmpty {
|
||||||
return display
|
return display
|
||||||
} else {
|
} else {
|
||||||
return self.addDelimitersToFractionDisplay(display, forFraction:frac)
|
return self.addDelimitersToFractionDisplay(display, forFraction:frac)
|
||||||
@@ -1010,13 +1011,13 @@ class MTTypesetter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func addDelimitersToFractionDisplay(_ display:MTFractionDisplay?, forFraction frac:MTFraction?) -> MTDisplay? {
|
func addDelimitersToFractionDisplay(_ display:MTFractionDisplay?, forFraction frac:MTFraction?) -> MTDisplay? {
|
||||||
assert(frac!.leftDelimiter != nil || frac!.rightDelimiter != nil, "Fraction should have a delimiters to call this function");
|
assert(!frac!.leftDelimiter.isEmpty || !frac!.rightDelimiter.isEmpty, "Fraction should have a delimiters to call this function");
|
||||||
|
|
||||||
var innerElements = [MTDisplay]()
|
var innerElements = [MTDisplay]()
|
||||||
let glyphHeight = self.fractionDelimiterHeight
|
let glyphHeight = self.fractionDelimiterHeight
|
||||||
var position = CGPoint.zero
|
var position = CGPoint.zero
|
||||||
if !frac!.leftDelimiter!.isEmpty {
|
if !frac!.leftDelimiter.isEmpty {
|
||||||
let leftGlyph = self.findGlyphForBoundary(frac!.leftDelimiter!, withHeight:glyphHeight())
|
let leftGlyph = self.findGlyphForBoundary(frac!.leftDelimiter, withHeight:glyphHeight())
|
||||||
leftGlyph!.position = position
|
leftGlyph!.position = position
|
||||||
position.x += leftGlyph!.width
|
position.x += leftGlyph!.width
|
||||||
innerElements.append(leftGlyph!)
|
innerElements.append(leftGlyph!)
|
||||||
@@ -1026,8 +1027,8 @@ class MTTypesetter {
|
|||||||
position.x += display!.width
|
position.x += display!.width
|
||||||
innerElements.append(display!)
|
innerElements.append(display!)
|
||||||
|
|
||||||
if !frac!.rightDelimiter!.isEmpty {
|
if !frac!.rightDelimiter.isEmpty {
|
||||||
let rightGlyph = self.findGlyphForBoundary(frac!.rightDelimiter!, withHeight:glyphHeight())
|
let rightGlyph = self.findGlyphForBoundary(frac!.rightDelimiter, withHeight:glyphHeight())
|
||||||
rightGlyph!.position = position
|
rightGlyph!.position = position
|
||||||
position.x += rightGlyph!.width
|
position.x += rightGlyph!.width
|
||||||
innerElements.append(rightGlyph!)
|
innerElements.append(rightGlyph!)
|
||||||
|
|||||||
@@ -1,63 +1,64 @@
|
|||||||
//
|
//
|
||||||
// MTUnicode.swift
|
|
||||||
// MathRenderSwift
|
|
||||||
//
|
|
||||||
// Created by Mike Griebling on 2022-12-31.
|
// Created by Mike Griebling on 2022-12-31.
|
||||||
|
// Translated from an Objective-C implementation by Kostub Deshmukh.
|
||||||
|
//
|
||||||
|
// This software may be modified and distributed under the terms of the
|
||||||
|
// MIT license. See the LICENSE file for details.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public struct UnicodeSymbol {
|
public struct UnicodeSymbol {
|
||||||
static let multiplication = "\u{00D7}"
|
static let multiplication = "\u{00D7}"
|
||||||
static let division = "\u{00F7}"
|
static let division = "\u{00F7}"
|
||||||
static let fractionSlash = "\u{2044}"
|
static let fractionSlash = "\u{2044}"
|
||||||
static let whiteSquare = "\u{25A1}"
|
static let whiteSquare = "\u{25A1}"
|
||||||
static let blackSquare = "\u{25A0}"
|
static let blackSquare = "\u{25A0}"
|
||||||
static let lessEqual = "\u{2264}"
|
static let lessEqual = "\u{2264}"
|
||||||
static let greaterEqual = "\u{2265}"
|
static let greaterEqual = "\u{2265}"
|
||||||
static let notEqual = "\u{2260}"
|
static let notEqual = "\u{2260}"
|
||||||
static let squareRoot = "\u{221A}" // \sqrt
|
static let squareRoot = "\u{221A}" // \sqrt
|
||||||
static let cubeRoot = "\u{221B}"
|
static let cubeRoot = "\u{221B}"
|
||||||
static let infinity = "\u{221E}" // \infty
|
static let infinity = "\u{221E}" // \infty
|
||||||
static let angle = "\u{2220}" // \angle
|
static let angle = "\u{2220}" // \angle
|
||||||
static let degree = "\u{00B0}" // \circ
|
static let degree = "\u{00B0}" // \circ
|
||||||
|
|
||||||
static let capitalGreekStart = UInt32(0x0391)
|
static let capitalGreekStart = UInt32(0x0391)
|
||||||
static let capitalGreekEnd = UInt32(0x03A9)
|
static let capitalGreekEnd = UInt32(0x03A9)
|
||||||
static let lowerGreekStart = UInt32(0x03B1)
|
static let lowerGreekStart = UInt32(0x03B1)
|
||||||
static let lowerGreekEnd = UInt32(0x03C9)
|
static let lowerGreekEnd = UInt32(0x03C9)
|
||||||
static let planksConstant = UInt32(0x210e)
|
static let planksConstant = UInt32(0x210e)
|
||||||
static let lowerItalicStart = UInt32(0x1D44E)
|
static let lowerItalicStart = UInt32(0x1D44E)
|
||||||
static let capitalItalicStart = UInt32(0x1D434)
|
static let capitalItalicStart = UInt32(0x1D434)
|
||||||
static let greekLowerItalicStart = UInt32(0x1D6FC)
|
static let greekLowerItalicStart = UInt32(0x1D6FC)
|
||||||
static let greekCapitalItalicStart = UInt32(0x1D6E2)
|
static let greekCapitalItalicStart = UInt32(0x1D6E2)
|
||||||
static let greekSymbolItalicStart = UInt32(0x1D716)
|
static let greekSymbolItalicStart = UInt32(0x1D716)
|
||||||
|
|
||||||
static let mathCapitalBoldStart = UInt32(0x1D400)
|
static let mathCapitalBoldStart = UInt32(0x1D400)
|
||||||
static let mathLowerBoldStart = UInt32(0x1D41A)
|
static let mathLowerBoldStart = UInt32(0x1D41A)
|
||||||
static let greekCapitalBoldStart = UInt32(0x1D6A8)
|
static let greekCapitalBoldStart = UInt32(0x1D6A8)
|
||||||
static let greekLowerBoldStart = UInt32(0x1D6C2)
|
static let greekLowerBoldStart = UInt32(0x1D6C2)
|
||||||
static let greekSymbolBoldStart = UInt32(0x1D6DC)
|
static let greekSymbolBoldStart = UInt32(0x1D6DC)
|
||||||
static let numberBoldStart = UInt32(0x1D7CE)
|
static let numberBoldStart = UInt32(0x1D7CE)
|
||||||
|
|
||||||
static let mathCapitalBoldItalicStart = UInt32(0x1D468)
|
static let mathCapitalBoldItalicStart = UInt32(0x1D468)
|
||||||
static let mathLowerBoldItalicStart = UInt32(0x1D482)
|
static let mathLowerBoldItalicStart = UInt32(0x1D482)
|
||||||
static let greekCapitalBoldItalicStart = UInt32(0x1D71C)
|
static let greekCapitalBoldItalicStart = UInt32(0x1D71C)
|
||||||
static let greekLowerBoldItalicStart = UInt32(0x1D736)
|
static let greekLowerBoldItalicStart = UInt32(0x1D736)
|
||||||
static let greekSymbolBoldItalicStart = UInt32(0x1D750)
|
static let greekSymbolBoldItalicStart = UInt32(0x1D750)
|
||||||
|
|
||||||
static let mathCapitalScriptStart = UInt32(0x1D49C)
|
static let mathCapitalScriptStart = UInt32(0x1D49C)
|
||||||
static let mathCapitalTTStart = UInt32(0x1D670)
|
static let mathCapitalTTStart = UInt32(0x1D670)
|
||||||
static let mathLowerTTStart = UInt32(0x1D68A)
|
static let mathLowerTTStart = UInt32(0x1D68A)
|
||||||
static let numberTTStart = UInt32(0x1D7F6)
|
static let numberTTStart = UInt32(0x1D7F6)
|
||||||
static let mathCapitalSansSerifStart = UInt32(0x1D5A0)
|
static let mathCapitalSansSerifStart = UInt32(0x1D5A0)
|
||||||
static let mathLowerSansSerifStart = UInt32(0x1D5BA)
|
static let mathLowerSansSerifStart = UInt32(0x1D5BA)
|
||||||
static let numberSansSerifStart = UInt32(0x1D7E2)
|
static let numberSansSerifStart = UInt32(0x1D7E2)
|
||||||
static let mathCapitalFrakturStart = UInt32(0x1D504)
|
static let mathCapitalFrakturStart = UInt32(0x1D504)
|
||||||
static let mathLowerFrakturStart = UInt32(0x1D51E)
|
static let mathLowerFrakturStart = UInt32(0x1D51E)
|
||||||
static let mathCapitalBlackboardStart = UInt32(0x1D538)
|
static let mathCapitalBlackboardStart = UInt32(0x1D538)
|
||||||
static let mathLowerBlackboardStart = UInt32(0x1D552)
|
static let mathLowerBlackboardStart = UInt32(0x1D552)
|
||||||
static let numberBlackboardStart = UInt32(0x1D7D8)
|
static let numberBlackboardStart = UInt32(0x1D7D8)
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Character {
|
extension Character {
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -352,8 +352,8 @@ final class MTMathListBuilderTests: XCTestCase {
|
|||||||
XCTAssertEqual(frac.type, .fraction, desc)
|
XCTAssertEqual(frac.type, .fraction, desc)
|
||||||
XCTAssertEqual(frac.nucleus, "", desc)
|
XCTAssertEqual(frac.nucleus, "", desc)
|
||||||
XCTAssertTrue(frac.hasRule);
|
XCTAssertTrue(frac.hasRule);
|
||||||
XCTAssertNil(frac.rightDelimiter);
|
XCTAssertTrue(frac.rightDelimiter.isEmpty)
|
||||||
XCTAssertNil(frac.leftDelimiter);
|
XCTAssertTrue(frac.leftDelimiter.isEmpty)
|
||||||
|
|
||||||
var subList = frac.numerator!
|
var subList = frac.numerator!
|
||||||
XCTAssertNotNil(subList, desc)
|
XCTAssertNotNil(subList, desc)
|
||||||
@@ -550,8 +550,8 @@ final class MTMathListBuilderTests: XCTestCase {
|
|||||||
XCTAssertEqual(frac.type, .fraction, desc);
|
XCTAssertEqual(frac.type, .fraction, desc);
|
||||||
XCTAssertEqual(frac.nucleus, "", desc);
|
XCTAssertEqual(frac.nucleus, "", desc);
|
||||||
XCTAssertTrue(frac.hasRule);
|
XCTAssertTrue(frac.hasRule);
|
||||||
XCTAssertNil(frac.rightDelimiter);
|
XCTAssertTrue(frac.rightDelimiter.isEmpty)
|
||||||
XCTAssertNil(frac.leftDelimiter);
|
XCTAssertTrue(frac.leftDelimiter.isEmpty)
|
||||||
|
|
||||||
var subList = frac.numerator!
|
var subList = frac.numerator!
|
||||||
XCTAssertNotNil(subList, desc);
|
XCTAssertNotNil(subList, desc);
|
||||||
@@ -587,8 +587,8 @@ final class MTMathListBuilderTests: XCTestCase {
|
|||||||
XCTAssertEqual(frac.type, .fraction, desc);
|
XCTAssertEqual(frac.type, .fraction, desc);
|
||||||
XCTAssertEqual(frac.nucleus, "", desc);
|
XCTAssertEqual(frac.nucleus, "", desc);
|
||||||
XCTAssertTrue(frac.hasRule);
|
XCTAssertTrue(frac.hasRule);
|
||||||
XCTAssertNil(frac.rightDelimiter);
|
XCTAssertTrue(frac.rightDelimiter.isEmpty)
|
||||||
XCTAssertNil(frac.leftDelimiter);
|
XCTAssertTrue(frac.leftDelimiter.isEmpty)
|
||||||
|
|
||||||
var subList = frac.numerator!
|
var subList = frac.numerator!
|
||||||
XCTAssertNotNil(subList, desc);
|
XCTAssertNotNil(subList, desc);
|
||||||
@@ -621,8 +621,8 @@ final class MTMathListBuilderTests: XCTestCase {
|
|||||||
XCTAssertEqual(frac.type, .fraction, desc);
|
XCTAssertEqual(frac.type, .fraction, desc);
|
||||||
XCTAssertEqual(frac.nucleus, "", desc);
|
XCTAssertEqual(frac.nucleus, "", desc);
|
||||||
XCTAssertFalse(frac.hasRule);
|
XCTAssertFalse(frac.hasRule);
|
||||||
XCTAssertNil(frac.rightDelimiter);
|
XCTAssertTrue(frac.rightDelimiter.isEmpty)
|
||||||
XCTAssertNil(frac.leftDelimiter);
|
XCTAssertTrue(frac.leftDelimiter.isEmpty)
|
||||||
|
|
||||||
var subList = frac.numerator!
|
var subList = frac.numerator!
|
||||||
XCTAssertNotNil(subList, desc);
|
XCTAssertNotNil(subList, desc);
|
||||||
@@ -658,8 +658,8 @@ final class MTMathListBuilderTests: XCTestCase {
|
|||||||
XCTAssertEqual(frac.type, .fraction, desc);
|
XCTAssertEqual(frac.type, .fraction, desc);
|
||||||
XCTAssertEqual(frac.nucleus, "", desc);
|
XCTAssertEqual(frac.nucleus, "", desc);
|
||||||
XCTAssertFalse(frac.hasRule);
|
XCTAssertFalse(frac.hasRule);
|
||||||
XCTAssertNil(frac.rightDelimiter);
|
XCTAssertTrue(frac.rightDelimiter.isEmpty)
|
||||||
XCTAssertNil(frac.leftDelimiter);
|
XCTAssertTrue(frac.leftDelimiter.isEmpty)
|
||||||
|
|
||||||
var subList = frac.numerator!
|
var subList = frac.numerator!
|
||||||
XCTAssertNotNil(subList, desc);
|
XCTAssertNotNil(subList, desc);
|
||||||
@@ -1020,7 +1020,7 @@ final class MTMathListBuilderTests: XCTestCase {
|
|||||||
let table = list.atoms[0] as! MTMathTable
|
let table = list.atoms[0] as! MTMathTable
|
||||||
XCTAssertEqual(table.type, .table);
|
XCTAssertEqual(table.type, .table);
|
||||||
XCTAssertEqual(table.nucleus, "");
|
XCTAssertEqual(table.nucleus, "");
|
||||||
XCTAssertNil(table.environment);
|
XCTAssertTrue(table.environment.isEmpty);
|
||||||
XCTAssertEqual(table.interRowAdditionalSpacing, 1);
|
XCTAssertEqual(table.interRowAdditionalSpacing, 1);
|
||||||
XCTAssertEqual(table.interColumnSpacing, 0);
|
XCTAssertEqual(table.interColumnSpacing, 0);
|
||||||
XCTAssertEqual(table.numRows, 2);
|
XCTAssertEqual(table.numRows, 2);
|
||||||
@@ -1051,7 +1051,7 @@ final class MTMathListBuilderTests: XCTestCase {
|
|||||||
let table = list.atoms[0] as! MTMathTable
|
let table = list.atoms[0] as! MTMathTable
|
||||||
XCTAssertEqual(table.type, .table);
|
XCTAssertEqual(table.type, .table);
|
||||||
XCTAssertEqual(table.nucleus, "");
|
XCTAssertEqual(table.nucleus, "");
|
||||||
XCTAssertNil(table.environment);
|
XCTAssertTrue(table.environment.isEmpty);
|
||||||
XCTAssertEqual(table.interRowAdditionalSpacing, 1);
|
XCTAssertEqual(table.interRowAdditionalSpacing, 1);
|
||||||
XCTAssertEqual(table.interColumnSpacing, 0);
|
XCTAssertEqual(table.interColumnSpacing, 0);
|
||||||
XCTAssertEqual(table.numRows, 2);
|
XCTAssertEqual(table.numRows, 2);
|
||||||
|
|||||||
Reference in New Issue
Block a user