Implement line breaking for scripted atoms and fix atom ordering

This commit is contained in:
Nicolas Guillot
2025-11-14 10:45:51 +01:00
parent 15269e87e5
commit 4441528f46
3 changed files with 293 additions and 38 deletions

View File

@@ -580,7 +580,37 @@ class MTTypesetter {
currentPosition.y -= styleFont.fontSize * 1.5
currentPosition.x = 0
}
/// Estimate the width of an atom including its scripts (without actually creating the displays)
/// This is used for width-checking decisions for atoms with super/subscripts
func estimateAtomWidthWithScripts(_ atom: MTMathAtom) -> CGFloat {
// Estimate base atom width
var atomWidth = CGFloat(atom.nucleus.count) * styleFont.fontSize * 0.5 // rough estimate
// If atom has scripts, estimate their contribution
if atom.superScript != nil || atom.subScript != nil {
let scriptFontSize = Self.getStyleSize(self.scriptStyle(), font: font)
var scriptWidth: CGFloat = 0
if let superScript = atom.superScript {
// Estimate superscript width
let superScriptAtomCount = superScript.atoms.count
scriptWidth = max(scriptWidth, CGFloat(superScriptAtomCount) * scriptFontSize * 0.5)
}
if let subScript = atom.subScript {
// Estimate subscript width
let subScriptAtomCount = subScript.atoms.count
scriptWidth = max(scriptWidth, CGFloat(subScriptAtomCount) * scriptFontSize * 0.5)
}
// Add script width plus space after script
atomWidth += scriptWidth + styleFont.mathTable!.spaceAfterScript
}
return atomWidth
}
func createDisplayAtoms(_ preprocessed:[MTMathAtom]) {
// items should contain all the nodes that need to be layed out.
// convert to a list of DisplayAtoms
@@ -1089,6 +1119,23 @@ class MTTypesetter {
// If no break point found, let it overflow (better than breaking mid-word)
}
}
// Check if atom with scripts would exceed width constraint (improved script handling)
if maxWidth > 0 && (atom.subScript != nil || atom.superScript != nil) && currentLine.length > 0 {
// Estimate width including scripts
let atomWidthWithScripts = estimateAtomWidthWithScripts(atom)
let interElementSpace = self.getInterElementSpace(prevNode?.type ?? .ordinary, right: atom.type)
let currentWidth = getCurrentLineWidth()
let projectedWidth = currentWidth + interElementSpace + atomWidthWithScripts
// If adding this scripted atom would exceed width, break line first
if projectedWidth > maxWidth {
self.addDisplayLine()
currentPosition.y -= styleFont.fontSize * 1.5
currentPosition.x = 0
}
}
// add the atom to the current range
if currentLineIndexRange.location == NSNotFound {
currentLineIndexRange = atom.indexRange
@@ -1101,7 +1148,7 @@ class MTTypesetter {
} else {
currentAtoms.append(atom)
}
// add super scripts || subscripts
if atom.subScript != nil || atom.superScript != nil {
// stash the existing line