diff --git a/Sources/SwiftMath/MathRender/MTMathUILabel.swift b/Sources/SwiftMath/MathRender/MTMathUILabel.swift index 65a560b..cb3a276 100644 --- a/Sources/SwiftMath/MathRender/MTMathUILabel.swift +++ b/Sources/SwiftMath/MathRender/MTMathUILabel.swift @@ -244,6 +244,7 @@ public class MTMathUILabel : MTView { override public func draw(_ dirtyRect: MTRect) { super.draw(dirtyRect) if self.mathList == nil { return } + if self.font == nil { return } // drawing code let context = MTGraphicsGetCurrentContext()! @@ -253,48 +254,64 @@ public class MTMathUILabel : MTView { } func _layoutSubviews() { - if _mathList != nil { - // Use the effective width for layout - let effectiveWidth = _preferredMaxLayoutWidth > 0 ? _preferredMaxLayoutWidth : bounds.size.width - let availableWidth = effectiveWidth - contentInsets.left - contentInsets.right - - print("🔧 MTMathUILabel _layoutSubviews:") - print(" preferredMaxLayoutWidth: \(_preferredMaxLayoutWidth)") - print(" bounds.size.width: \(bounds.size.width)") - print(" effectiveWidth: \(effectiveWidth)") - print(" availableWidth: \(availableWidth)") - print(" LaTeX: \(_latex.prefix(60))...") - - // print("Pre list = \(_mathList!)") - _displayList = MTTypesetter.createLineForMathList(_mathList, font: font, style: currentStyle, maxWidth: availableWidth) - _displayList!.textColor = textColor - - print(" Display subDisplays count: \(_displayList!.subDisplays.count)") - for (index, subDisplay) in _displayList!.subDisplays.enumerated() { - print(" Display \(index): type=\(type(of: subDisplay)), x=\(subDisplay.position.x), width=\(subDisplay.width)") - if let lineDisplay = subDisplay as? MTCTLineDisplay { - print(" Content: '\(lineDisplay.attributedString?.string ?? "")'") - } - } - // print("Post list = \(_mathList!)") - var textX = CGFloat(0) - switch self.textAlignment { - case .left: textX = contentInsets.left - case .center: textX = (bounds.size.width - contentInsets.left - contentInsets.right - _displayList!.width) / 2 + contentInsets.left - case .right: textX = bounds.size.width - _displayList!.width - contentInsets.right - } - let availableHeight = bounds.size.height - contentInsets.bottom - contentInsets.top - - // center things vertically - var height = _displayList!.ascent + _displayList!.descent - if height < fontSize/2 { - height = fontSize/2 // set height to half the font size - } - let textY = (availableHeight - height) / 2 + _displayList!.descent + contentInsets.bottom - _displayList!.position = CGPointMake(textX, textY) - } else { + guard _mathList != nil && self.font != nil else { _displayList = nil + errorLabel?.frame = self.bounds + self.setNeedsDisplay() + return } + // Ensure we have a valid font before attempting to typeset + if self.font == nil { + // No valid font - try to get default font + if let defaultFont = MTFontManager.fontManager.defaultFont { + self._font = defaultFont + } else { + // Cannot typeset without a font, clear display list + _displayList = nil + errorLabel?.frame = self.bounds + self.setNeedsDisplay() + return + } + } + + // Use the effective width for layout + let effectiveWidth = _preferredMaxLayoutWidth > 0 ? _preferredMaxLayoutWidth : bounds.size.width + let availableWidth = effectiveWidth - contentInsets.left - contentInsets.right + + print("🔧 MTMathUILabel _layoutSubviews:") + print(" preferredMaxLayoutWidth: \(_preferredMaxLayoutWidth)") + print(" bounds.size.width: \(bounds.size.width)") + print(" effectiveWidth: \(effectiveWidth)") + print(" availableWidth: \(availableWidth)") + print(" LaTeX: \(_latex.prefix(60))...") + + // print("Pre list = \(_mathList!)") + _displayList = MTTypesetter.createLineForMathList(_mathList, font: self.font, style: currentStyle, maxWidth: availableWidth) + _displayList!.textColor = textColor + + print(" Display subDisplays count: \(_displayList!.subDisplays.count)") + for (index, subDisplay) in _displayList!.subDisplays.enumerated() { + print(" Display \(index): type=\(type(of: subDisplay)), x=\(subDisplay.position.x), width=\(subDisplay.width)") + if let lineDisplay = subDisplay as? MTCTLineDisplay { + print(" Content: '\(lineDisplay.attributedString?.string ?? "")'") + } + } + // print("Post list = \(_mathList!)") + var textX = CGFloat(0) + switch self.textAlignment { + case .left: textX = contentInsets.left + case .center: textX = (bounds.size.width - contentInsets.left - contentInsets.right - _displayList!.width) / 2 + contentInsets.left + case .right: textX = bounds.size.width - _displayList!.width - contentInsets.right + } + let availableHeight = bounds.size.height - contentInsets.bottom - contentInsets.top + + // center things vertically + var height = _displayList!.ascent + _displayList!.descent + if height < fontSize/2 { + height = fontSize/2 // set height to half the font size + } + let textY = (availableHeight - height) / 2 + _displayList!.descent + contentInsets.bottom + _displayList!.position = CGPointMake(textX, textY) errorLabel?.frame = self.bounds self.setNeedsDisplay() } @@ -305,6 +322,17 @@ public class MTMathUILabel : MTView { return CGSize(width: -1, height: -1) } + // Ensure we have a valid font before attempting to typeset + if self.font == nil { + // No valid font - try to get default font + if let defaultFont = MTFontManager.fontManager.defaultFont { + self._font = defaultFont + } else { + // Cannot typeset without a font + return CGSize(width: -1, height: -1) + } + } + // Determine the maximum width to use var maxWidth: CGFloat = 0 if _preferredMaxLayoutWidth > 0 { @@ -314,7 +342,7 @@ public class MTMathUILabel : MTView { } var displayList:MTMathListDisplay? = nil - displayList = MTTypesetter.createLineForMathList(_mathList, font: font, style: currentStyle, maxWidth: maxWidth) + displayList = MTTypesetter.createLineForMathList(_mathList, font: self.font, style: currentStyle, maxWidth: maxWidth) guard displayList != nil else { // Failed to create display list diff --git a/Sources/SwiftMath/MathRender/MTTypesetter.swift b/Sources/SwiftMath/MathRender/MTTypesetter.swift index 8bc4e5e..8df9b8b 100644 --- a/Sources/SwiftMath/MathRender/MTTypesetter.swift +++ b/Sources/SwiftMath/MathRender/MTTypesetter.swift @@ -73,12 +73,18 @@ func getInterElementSpaceArrayIndexForType(_ type:MTMathAtomType, row:Bool) -> I // They have the same spacing as ordinary except with ordinary. return 8; } else { - assert(false, "Interelement space undefined for radical on the right. Treat radical as ordinary.") - return Int.max + // Treat radical as ordinary on the right side + return 0 } - default: - assert(false, "Interelement space undefined for type \(type)") - return Int.max + // Numbers, variables, and unary operators are treated as ordinary + case .number, .variable, .unaryOperator: + return 0 + // Decorative types (accent, underline, overline) are treated as ordinary + case .accent, .underline, .overline: + return 0 + // Special types that don't typically participate in spacing are treated as ordinary + case .boundary, .space, .style, .table: + return 0 } } @@ -958,30 +964,12 @@ class MTTypesetter { self.addDisplayLine() } - // Create the large operator display to check if we need line breaking - let op = atom as! MTLargeOperator? + // Add inter-element spacing before operator + self.addInterElementSpace(prevNode, currentType:atom.type) - // Save state before creating display (makeLargeOp may add scripts to displayAtoms) - let savedDisplayAtomsCount = displayAtoms.count - let savedPosition = currentPosition - let tempDisplay = self.makeLargeOp(op) - let tempIsTooTall = (tempDisplay!.ascent + tempDisplay!.descent) > styleFont.fontSize * 2.5 - let tempIsTooWide = shouldBreakBeforeDisplay(tempDisplay!, prevNode: prevNode, displayType: atom.type) - let shouldBreak = tempIsTooTall || tempIsTooWide - - // Restore state (remove any scripts that were added) - displayAtoms.removeLast(displayAtoms.count - savedDisplayAtomsCount) - currentPosition = savedPosition - - // Perform line break if needed - if shouldBreak { - performLineBreak() - } else { - self.addInterElementSpace(prevNode, currentType:atom.type) - } - - // Now create the display at the correct position (after spacing/line break) + // Create and position the large operator display // makeLargeOp sets position, advances currentPosition.x, and adds scripts + let op = atom as! MTLargeOperator? let display = self.makeLargeOp(op) displayAtoms.append(display!)