Clean up and fix issue with LargeOpLimitsDisplay.

This commit is contained in:
Michael Griebling
2023-01-17 09:27:19 -05:00
parent 4c5bdf99a2
commit 10b44d5258
6 changed files with 91 additions and 116 deletions

View File

@@ -9,11 +9,9 @@ import Foundation
import CoreGraphics import CoreGraphics
import CoreText import CoreText
//
// Font.swift
// iosMath
// //
// Created by Kostub Deshmukh on 5/18/16. // Created by Kostub Deshmukh on 5/18/16.
// Modified by Michael Griebling on 17 Jan 2023.
// //
// 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.
@@ -42,16 +40,15 @@ public class MTFont {
self.ctFont = CTFontCreateWithGraphicsFont(self.defaultCGFont, size, nil, nil); self.ctFont = CTFontCreateWithGraphicsFont(self.defaultCGFont, size, nil, nil);
print("Loading associated .plist")
let mathTablePlist = bundle.url(forResource:name, withExtension:"plist") let mathTablePlist = bundle.url(forResource:name, withExtension:"plist")
let dict = NSDictionary(contentsOf: mathTablePlist!) // dictionaryWithContentsOfFile:mathTablePlist]; self.rawMathTable = NSDictionary(contentsOf: mathTablePlist!)
self.rawMathTable = dict
self.mathTable = MTFontMathTable(withFont:self, mathTable:rawMathTable!) self.mathTable = MTFontMathTable(withFont:self, mathTable:rawMathTable!)
} }
static var fontBundle:Bundle { static var fontBundle:Bundle {
// Uses bundle for class so that this can be access by the unit tests. // Uses bundle for class so that this can be access by the unit tests.
let url = Bundle.module.url(forResource: "mathFonts", withExtension: "bundle")! Bundle(url: Bundle.module.url(forResource: "mathFonts", withExtension: "bundle")!)!
return Bundle(url: url)!
} }
func copy(withSize size: CGFloat) -> MTFont { func copy(withSize size: CGFloat) -> MTFont {
@@ -65,15 +62,13 @@ public class MTFont {
func get(nameForGlyph glyph:CGGlyph) -> String { func get(nameForGlyph glyph:CGGlyph) -> String {
let name = defaultCGFont.name(for: glyph) as? String let name = defaultCGFont.name(for: glyph) as? String
return name! return name ?? ""
} }
func get(glyphWithName name:String) -> CGGlyph { func get(glyphWithName name:String) -> CGGlyph {
return defaultCGFont.getGlyphWithGlyphName(name: name as CFString) defaultCGFont.getGlyphWithGlyphName(name: name as CFString)
} }
var fontSize:CGFloat { var fontSize:CGFloat { CTFontGetSize(self.ctFont) }
return CTFontGetSize(self.ctFont)
}
} }

View File

@@ -10,6 +10,7 @@ import Foundation
public class MTFontManager { public class MTFontManager {
static var manager:MTFontManager? = nil static var manager:MTFontManager? = nil
let kDefaultFontSize = CGFloat(20) let kDefaultFontSize = CGFloat(20)
static var fontManager : MTFontManager { static var fontManager : MTFontManager {

View File

@@ -56,7 +56,7 @@ class MTFontMathTable {
var muUnit:CGFloat { _fontSize/18 } var muUnit:CGFloat { _fontSize/18 }
func fontUnitsToPt(_ fontUnits:Int) -> CGFloat { func fontUnitsToPt(_ fontUnits:Int) -> CGFloat {
return CGFloat(fontUnits) * _fontSize / CGFloat(_unitsPerEm) CGFloat(fontUnits) * _fontSize / CGFloat(_unitsPerEm)
} }
init(withFont font: MTFont?, mathTable:NSDictionary) { init(withFont font: MTFont?, mathTable:NSDictionary) {
@@ -286,7 +286,7 @@ class MTFontMathTable {
let kAssemblyParts = "parts" let kAssemblyParts = "parts"
/** Returns an array of the glyph parts to be used for constructing vertical variants /** Returns an array of the glyph parts to be used for constructing vertical variants
of this glyph. If there is no glyph assembly defined, returns nil. */ of this glyph. If there is no glyph assembly defined, returns an empty array. */
func getVerticalGlyphAssembly(forGlyph glyph:CGGlyph) -> [GlyphPart] { func getVerticalGlyphAssembly(forGlyph glyph:CGGlyph) -> [GlyphPart] {
let assemblyTable = _mathTable[kVertAssembly] as! NSDictionary? let assemblyTable = _mathTable[kVertAssembly] as! NSDictionary?
let glyphName = self.font?.get(nameForGlyph: glyph) // getGlyphName:glyph]; let glyphName = self.font?.get(nameForGlyph: glyph) // getGlyphName:glyph];

View File

@@ -46,9 +46,7 @@ public enum MTMathAtomType: Int, CustomStringConvertible, Comparable {
} }
} }
func isScriptAllowed() -> Bool { func isScriptAllowed() -> Bool { self < .boundary }
return self != .boundary && self != .space && self != .style && self != .table
}
// we want string representations to be capitalized // we want string representations to be capitalized
public var description: String { public var description: String {
@@ -80,9 +78,7 @@ public enum MTMathAtomType: Int, CustomStringConvertible, Comparable {
} }
// comparable support // comparable support
public static func < (lhs: MTMathAtomType, rhs: MTMathAtomType) -> Bool { public static func < (lhs: MTMathAtomType, rhs: MTMathAtomType) -> Bool { lhs.rawValue < rhs.rawValue }
lhs.rawValue < rhs.rawValue
}
} }
@@ -109,6 +105,8 @@ public enum MTFontStyle:Int {
boldItalic boldItalic
} }
// MARK: - MTMathAtom
public class MTMathAtom: NSObject { public class MTMathAtom: NSObject {
public var type = MTMathAtomType.ordinary public var type = MTMathAtomType.ordinary
@@ -128,6 +126,7 @@ public class MTMathAtom: NSObject {
} }
} }
} }
public var nucleus: String = "" public var nucleus: String = ""
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:
@@ -183,22 +182,6 @@ public class MTMathAtom: NSObject {
} }
} }
public func setSuperScript(_ list: MTMathList?) {
if self.isScriptAllowed() {
self.superScript = list
} else {
NSException(name: NSExceptionName(rawValue: "Error"), reason: "Superscripts not allowed for atom \(self.type.rawValue)").raise()
}
}
public func setSubScript(_ list: MTMathList?) {
if self.isScriptAllowed() {
self.subScript = list
} else {
NSException(name: NSExceptionName(rawValue: "Error"), reason: "Subscripts not allowed for atom \(self.type.rawValue)").raise()
}
}
public override var description: String { public override var description: String {
var string = "" var string = ""
string += self.nucleus string += self.nucleus
@@ -267,25 +250,27 @@ public class MTMathAtom: NSObject {
} }
func isNotBinaryOperator(_ prevNode:MTMathAtom?) -> Bool { func isNotBinaryOperator(_ prevNode:MTMathAtom?) -> Bool {
if prevNode == nil { return true } guard let prevNode = prevNode else { return true }
return prevNode!.type.isNotBinaryOperator() return prevNode.type.isNotBinaryOperator()
} }
// MARK: - MTFraction
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: String?
public var rightDelimiter: String? public var rightDelimiter: String?
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) self.numerator = MTMathList(frac!.numerator)
self.denominator = MTMathList(frac?.denominator) self.denominator = MTMathList(frac!.denominator)
self.hasRule = frac?.hasRule ?? true self.hasRule = frac!.hasRule
self.leftDelimiter = frac?.leftDelimiter self.leftDelimiter = frac!.leftDelimiter
self.rightDelimiter = frac?.rightDelimiter self.rightDelimiter = frac!.rightDelimiter
} }
init(hasRule rule:Bool = true) { init(hasRule rule:Bool = true) {
@@ -322,15 +307,15 @@ public class MTFraction: MTMathAtom {
override public var finalized: MTMathAtom { override public var finalized: MTMathAtom {
let finalized: MTFraction = super.finalized as! MTFraction let finalized: MTFraction = super.finalized as! MTFraction
finalized.numerator = finalized.numerator?.finalized finalized.numerator = finalized.numerator?.finalized
finalized.denominator = finalized.denominator?.finalized finalized.denominator = finalized.denominator?.finalized
return finalized return finalized
} }
} }
// MARK: - MTRadical
public class MTRadical: MTMathAtom { public class MTRadical: MTMathAtom {
// Under the roof // Under the roof
public var radicand: MTMathList? public var radicand: MTMathList?
@@ -383,19 +368,19 @@ public class MTRadical: MTMathAtom {
} }
} }
// MARK: - MTLargeOperator
public class MTLargeOperator: MTMathAtom { public class MTLargeOperator: MTMathAtom {
public var limits: Bool = false public var limits: Bool = false
init(_ op:MTLargeOperator?) { init(_ op:MTLargeOperator?) {
super.init(op) super.init(op)
self.type = .largeOperator self.type = .largeOperator
self.limits = op?.limits ?? false self.limits = op!.limits
} }
init(value: String, limits: Bool) { init(value: String, limits: Bool) {
super.init() super.init(type: .largeOperator, value: value)
self.type = .largeOperator
self.nucleus = value
self.limits = limits self.limits = limits
} }
} }
@@ -463,6 +448,8 @@ public class MTInner: MTMathAtom {
} }
} }
// MARK: - MTOverLIne
public class MTOverLine: MTMathAtom { public class MTOverLine: MTMathAtom {
public var innerList: MTMathList? public var innerList: MTMathList?
@@ -484,6 +471,8 @@ public class MTOverLine: MTMathAtom {
} }
} }
// MARK: - MTUnderLine
public class MTUnderLine: MTMathAtom { public class MTUnderLine: MTMathAtom {
public var innerList: MTMathList? public var innerList: MTMathList?
@@ -505,6 +494,8 @@ public class MTUnderLine: MTMathAtom {
} }
} }
// MARK: - MTAccent
public class MTAccent: MTMathAtom { public class MTAccent: MTMathAtom {
public var innerList: MTMathList? public var innerList: MTMathList?
@@ -527,6 +518,8 @@ public class MTAccent: MTMathAtom {
} }
} }
// MARK: - MTMathSpace
public class MTMathSpace: MTMathAtom { public class MTMathSpace: MTMathAtom {
public var space: CGFloat = 0 public var space: CGFloat = 0
@@ -543,33 +536,32 @@ public class MTMathSpace: MTMathAtom {
} }
} }
public enum MTLineStyle { public enum MTLineStyle:Int, Comparable {
case display case display
case text case text
case script case script
case scriptOfScript case scriptOfScript
public func inc() -> MTLineStyle { public func inc() -> MTLineStyle {
switch self { let raw = self.rawValue + 1
case .display: return .text if let style = MTLineStyle(rawValue: raw) { return style }
case .text: return .script return .display
case .script: return .scriptOfScript
case .scriptOfScript: return .display
}
} }
public var isNotScript:Bool { public var isNotScript:Bool { self < .script }
self == .display || self == .text public static func < (lhs: MTLineStyle, rhs: MTLineStyle) -> Bool { lhs.rawValue < rhs.rawValue }
}
} }
// MARK: - MTMathStyle
public class MTMathStyle: MTMathAtom { public class MTMathStyle: MTMathAtom {
public var style: MTLineStyle = .display public var style: MTLineStyle = .display
init(_ style:MTMathStyle?) { init(_ style:MTMathStyle?) {
super.init(style) super.init(style)
self.type = .style self.type = .style
self.style = style?.style ?? .display self.style = style!.style
} }
init(style:MTLineStyle) { init(style:MTLineStyle) {
@@ -579,6 +571,8 @@ public class MTMathStyle: MTMathAtom {
} }
} }
// MARK: - MTMathColor
public class MTMathColor: MTMathAtom { public class MTMathColor: MTMathAtom {
public var colorString:String="" public var colorString:String=""
public var innerList:MTMathList? public var innerList:MTMathList?
@@ -600,6 +594,8 @@ public class MTMathColor: MTMathAtom {
} }
} }
// MARK: - MTMathColorbox
public class MTMathColorbox: MTMathAtom { public class MTMathColorbox: MTMathAtom {
public var colorString:String="" public var colorString:String=""
public var innerList:MTMathList? public var innerList:MTMathList?
@@ -627,6 +623,8 @@ public enum MTColumnAlignment {
case right case right
} }
// MARK: - MTMathTable
public class MTMathTable: MTMathAtom { public class MTMathTable: MTMathAtom {
public var alignments = [MTColumnAlignment]() public var alignments = [MTColumnAlignment]()
public var cells = [[MTMathList]]() public var cells = [[MTMathList]]()
@@ -711,19 +709,17 @@ public class MTMathTable: MTMathAtom {
public var numColumns: Int { public var numColumns: Int {
var numberOfCols = 0 var numberOfCols = 0
for row in self.cells { for row in self.cells {
numberOfCols = max(numberOfCols, row.count) numberOfCols = max(numberOfCols, row.count)
} }
return numberOfCols return numberOfCols
} }
public var numRows: Int { public var numRows: Int { self.cells.count }
return self.cells.count
}
} }
// MARK: - MTMathList
// represent list of math objects // 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 }
@@ -759,22 +755,15 @@ public class MTMathList : NSObject {
if prevNode == nil || prevNode!.isNotBinaryOperator() { if prevNode == nil || prevNode!.isNotBinaryOperator() {
newNode.type = .unaryOperator newNode.type = .unaryOperator
} }
break
case .relation, .punctuation, .close: case .relation, .punctuation, .close:
if prevNode != nil && if prevNode != nil && prevNode!.type == .binaryOperator {
prevNode!.type == .binaryOperator {
prevNode!.type = .unaryOperator prevNode!.type = .unaryOperator
} }
break
case .number: case .number:
if prevNode != nil && if prevNode != nil && prevNode!.type == .number && prevNode!.subScript == nil && prevNode!.superScript == nil {
prevNode!.type == .number &&
prevNode!.subScript == nil &&
prevNode!.superScript == nil {
prevNode!.fuse(with: newNode) prevNode!.fuse(with: newNode)
continue continue
} }
break
default: break default: break
} }
@@ -840,7 +829,7 @@ public class MTMathList : NSObject {
} }
func removeLastAtom() { func removeLastAtom() {
if self.atoms.count > 0 { if !self.atoms.isEmpty {
self.atoms.removeLast() self.atoms.removeLast()
} }
} }
@@ -856,7 +845,5 @@ public class MTMathList : NSObject {
self.atoms.removeSubrange(range) self.atoms.removeSubrange(range)
} }
func isAtomAllowed(_ atom: MTMathAtom?) -> Bool { func isAtomAllowed(_ atom: MTMathAtom?) -> Bool { atom?.type != .boundary }
return atom?.type != .boundary
}
} }

View File

@@ -682,6 +682,7 @@ class MTLargeOpLimitsDisplay : MTDisplay {
override var position: CGPoint { override var position: CGPoint {
set { set {
super.position = newValue
self.updateLowerLimitPosition() self.updateLowerLimitPosition()
self.updateUpperLimitPosition() self.updateUpperLimitPosition()
self.updateNucleusPosition() self.updateNucleusPosition()

View File

@@ -346,9 +346,13 @@ class MTTypesetter {
var currentLine:NSMutableAttributedString! var currentLine:NSMutableAttributedString!
var currentAtoms = [MTMathAtom]() // List of atoms that make the line var currentAtoms = [MTMathAtom]() // List of atoms that make the line
var currentLineIndexRange = NSMakeRange(0, 0) var currentLineIndexRange = NSMakeRange(0, 0)
var style:MTLineStyle var style:MTLineStyle { didSet { _styleFont = nil } }
private var _styleFont:MTFont?
var styleFont:MTFont { var styleFont:MTFont {
self.font.copy(withSize: Self.getStyleSize(self.style, font: self.font)) if _styleFont == nil {
_styleFont = font.copy(withSize: Self.getStyleSize(style, font: font))
}
return _styleFont!
} }
var cramped = false var cramped = false
var spaced = false var spaced = false
@@ -752,18 +756,12 @@ class MTTypesetter {
func getSpacingInMu(_ type: InterElementSpaceType) -> Int { func getSpacingInMu(_ type: InterElementSpaceType) -> Int {
// let valid = [MTLineStyle.display, .text] // let valid = [MTLineStyle.display, .text]
switch type { switch type {
case .invalid: case .invalid: return -1
return -1; case .none: return 0
case .none: case .thin: return 3
return 0; case .nsThin: return style.isNotScript ? 3 : 0;
case .thin: case .nsMedium: return style.isNotScript ? 4 : 0;
return 3; case .nsThick: return style.isNotScript ? 5 : 0;
case .nsThin:
return style.isNotScript ? 3 : 0;
case .nsMedium:
return style.isNotScript ? 4 : 0;
case .nsThick:
return style.isNotScript ? 5 : 0;
} }
} }
@@ -788,25 +786,19 @@ class MTTypesetter {
func scriptStyle() -> MTLineStyle { func scriptStyle() -> MTLineStyle {
switch style { switch style {
case .display, .text: case .display, .text: return .script
return .script case .script, .scriptOfScript: return .scriptOfScript
case .script, .scriptOfScript:
return .scriptOfScript
} }
} }
// subscript is always cramped // subscript is always cramped
func subscriptCramped() -> Bool { func subscriptCramped() -> Bool { true }
return true;
}
// superscript is cramped only if the current style is cramped // superscript is cramped only if the current style is cramped
func superScriptCramped() -> Bool { func superScriptCramped() -> Bool { cramped }
return cramped;
}
func superScriptShiftUp() -> CGFloat { func superScriptShiftUp() -> CGFloat {
if (cramped) { if cramped {
return styleFont.mathTable!.superscriptShiftUpCramped; return styleFont.mathTable!.superscriptShiftUpCramped;
} else { } else {
return styleFont.mathTable!.superscriptShiftUp; return styleFont.mathTable!.superscriptShiftUp;
@@ -822,7 +814,6 @@ class MTTypesetter {
var subscriptShiftDown = 0.0 var subscriptShiftDown = 0.0
display?.hasScript = true display?.hasScript = true
// let classy = display is MTCTLineDisplay
if !(display is MTCTLineDisplay) { if !(display is MTCTLineDisplay) {
// get the font in script style // get the font in script style
let scriptFontSize = Self.getStyleSize(self.scriptStyle(), font:font) let scriptFontSize = Self.getStyleSize(self.scriptStyle(), font:font)
@@ -893,21 +884,21 @@ class MTTypesetter {
func numeratorShiftUp(_ hasRule:Bool) -> CGFloat { func numeratorShiftUp(_ hasRule:Bool) -> CGFloat {
if hasRule { if hasRule {
if style == .display { if style == .display {
return styleFont.mathTable!.fractionNumeratorDisplayStyleShiftUp; return styleFont.mathTable!.fractionNumeratorDisplayStyleShiftUp
} else { } else {
return styleFont.mathTable!.fractionNumeratorShiftUp; return styleFont.mathTable!.fractionNumeratorShiftUp
} }
} else { } else {
if (style == .display) { if style == .display {
return styleFont.mathTable!.stackTopDisplayStyleShiftUp; return styleFont.mathTable!.stackTopDisplayStyleShiftUp
} else { } else {
return styleFont.mathTable!.stackTopShiftUp; return styleFont.mathTable!.stackTopShiftUp
} }
} }
} }
func numeratorGapMin() -> CGFloat { func numeratorGapMin() -> CGFloat {
if (style == .display) { if style == .display {
return styleFont.mathTable!.fractionNumeratorDisplayStyleGapMin; return styleFont.mathTable!.fractionNumeratorDisplayStyleGapMin;
} else { } else {
return styleFont.mathTable!.fractionNumeratorGapMin; return styleFont.mathTable!.fractionNumeratorGapMin;
@@ -916,13 +907,13 @@ class MTTypesetter {
func denominatorShiftDown(_ hasRule:Bool) -> CGFloat { func denominatorShiftDown(_ hasRule:Bool) -> CGFloat {
if hasRule { if hasRule {
if (style == .display) { if style == .display {
return styleFont.mathTable!.fractionDenominatorDisplayStyleShiftDown; return styleFont.mathTable!.fractionDenominatorDisplayStyleShiftDown;
} else { } else {
return styleFont.mathTable!.fractionDenominatorShiftDown; return styleFont.mathTable!.fractionDenominatorShiftDown;
} }
} else { } else {
if (style == .display) { if style == .display {
return styleFont.mathTable!.stackBottomDisplayStyleShiftDown; return styleFont.mathTable!.stackBottomDisplayStyleShiftDown;
} else { } else {
return styleFont.mathTable!.stackBottomShiftDown; return styleFont.mathTable!.stackBottomShiftDown;
@@ -1295,7 +1286,7 @@ class MTTypesetter {
currentPosition.x += display!.width currentPosition.x += display!.width
return display; return display;
} }
if (op.limits && style == .display) { if op.limits && style == .display {
// make limits // make limits
var superScript:MTMathListDisplay? = nil, subScript:MTMathListDisplay? = nil var superScript:MTMathListDisplay? = nil, subScript:MTMathListDisplay? = nil
if op.superScript != nil { if op.superScript != nil {
@@ -1304,7 +1295,7 @@ class MTTypesetter {
if op.subScript != nil { if op.subScript != nil {
subScript = MTTypesetter.createLineForMathList(op.subScript, font:font, style:self.scriptStyle(), cramped:self.subscriptCramped()) subScript = MTTypesetter.createLineForMathList(op.subScript, font:font, style:self.scriptStyle(), cramped:self.subscriptCramped())
} }
assert((superScript != nil) || (subScript != nil), "Atleast one of superscript or subscript should have been present."); assert((superScript != nil) || (subScript != nil), "At least one of superscript or subscript should have been present.");
let opsDisplay = MTLargeOpLimitsDisplay(withNucleus:display, upperLimit:superScript, lowerLimit:subScript, limitShift:delta/2, extraPadding:0) let opsDisplay = MTLargeOpLimitsDisplay(withNucleus:display, upperLimit:superScript, lowerLimit:subScript, limitShift:delta/2, extraPadding:0)
if superScript != nil { if superScript != nil {
let upperLimitGap = max(styleFont.mathTable!.upperLimitGapMin, styleFont.mathTable!.upperLimitBaselineRiseMin - superScript!.descent); let upperLimitGap = max(styleFont.mathTable!.upperLimitGapMin, styleFont.mathTable!.upperLimitBaselineRiseMin - superScript!.descent);