Fix word breaking: prevent splitting words with accented characters

Fixed line breaking that would split words like "équivaut" into "é" on one
  line and "quivaut" on the next line, even though they're part of the same word.

  Root cause analysis (from debug logging):
  When text contains accented characters in decomposed form (e + combining
  accent), the system processes them as separate atoms:
    1. "é" is processed as an accent atom, composed, and added to currentLine
    2. "quivaut " is processed as the next ordinary atom
    3. Before adding "quivaut ", checkAndPerformInteratomLineBreak() is called
    4. This function sees that adding "quivaut " would exceed maxWidth
    5. It breaks and flushes the line with "é" at the end
    6. "quivaut " starts on a new line

  Result: "é" appears alone at the end of one line, "quivaut " on the next.

  The fix:
  Modified checkAndPerformInteratomLineBreak() in MTTypesetter.swift to detect
  when we're about to break in the middle of a word.
This commit is contained in:
Nicolas Guillot
2025-11-17 10:45:32 +01:00
parent cc1a7b8023
commit 43c69240bd
2 changed files with 69 additions and 4 deletions

View File

@@ -192,6 +192,39 @@ class MTMathUILabelLineWrappingTests: XCTestCase {
XCTAssertNil(label.error, "Should have no rendering error")
}
func testUnicodeWordBreaking_EquivautCase() {
// Specific test for the reported issue: "équivaut" should not break at "é"
let label = MTMathUILabel()
label.latex = "\\(\\text{Rappelons la conversion : 1 km équivaut à 1000 m.}\\)"
label.font = MTFontManager.fontManager.defaultFont
label.labelMode = .text
// Set the exact width constraint from the bug report
label.preferredMaxLayoutWidth = 235
let constrainedSize = label.intrinsicContentSize
// Verify the label can render without errors
label.frame = CGRect(origin: .zero, size: constrainedSize)
#if os(macOS)
label.layout()
#else
label.layoutSubviews()
#endif
XCTAssertNotNil(label.displayList, "Display list should be created")
XCTAssertNil(label.error, "Should have no rendering error")
// Verify that the text wrapped (multiple lines)
XCTAssertGreaterThan(constrainedSize.height, 20, "Should have wrapped to multiple lines")
// The critical check: ensure "équivaut" is not broken in the middle
// We can't easily check the exact line breaks, but we can verify:
// 1. The rendering succeeded without crashes
// 2. The display has reasonable dimensions
XCTAssertGreaterThan(constrainedSize.width, 100, "Width should be reasonable")
XCTAssertLessThan(constrainedSize.width, 250, "Width should respect constraint")
}
func testNumberProtection_FrenchDecimal() {
let label = MTMathUILabel()
// French decimal number should NOT be broken