From d391a3a166850fcd13f2585fb17a00aa840ea679 Mon Sep 17 00:00:00 2001 From: Adam Kowalski Date: Thu, 8 Jan 2026 21:24:40 -0800 Subject: [PATCH] chore: add unit test and improve comment --- src/shape.rs | 5 ++++- tests/ligature_segmentation.rs | 30 ------------------------------ tests/shaping_and_rendering.rs | 30 ++++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 31 deletions(-) delete mode 100644 tests/ligature_segmentation.rs diff --git a/src/shape.rs b/src/shape.rs index 491c69b..19d0e31 100644 --- a/src/shape.rs +++ b/src/shape.rs @@ -826,7 +826,10 @@ impl ShapeSpan { let mut start_word = 0; for (end_lb, _) in unicode_linebreak::linebreaks(span) { - // Workaround for broken |> ligature in code fonts + // The unicode-linebreak crate treats the pipe character '|' as a break opportunity (BA/AL class). + // This causes ShapeSpan::build to split text like '|>' into separate ShapeWords. + // When these words are shaped independently, the font shaping engine cannot form ligatures that cross the word boundary. + // We manually check for the '|>' sequence during segmentation and skip the break opportunity to ensure they remain in the same shaping run. if end_lb > 0 && end_lb < span.len() { let b = span.as_bytes(); if b[end_lb - 1] == b'|' && b[end_lb] == b'>' { diff --git a/tests/ligature_segmentation.rs b/tests/ligature_segmentation.rs deleted file mode 100644 index c1936ac..0000000 --- a/tests/ligature_segmentation.rs +++ /dev/null @@ -1,30 +0,0 @@ -use cosmic_text::{Attrs, Buffer, FontSystem, Metrics, Shaping}; - -#[test] -fn ligature_segmentation() { - let mut font_system = - FontSystem::new_with_locale_and_db("en-US".into(), fontdb::Database::new()); - let font = std::fs::read("fonts/Inter-Regular.ttf").unwrap(); - font_system.db_mut().load_font_data(font); - let metrics = Metrics::new(14.0, 20.0); - - let mut buffer = Buffer::new(&mut font_system, metrics); - let mut buffer = buffer.borrow_with(&mut font_system); - - buffer.set_text("|>", &Attrs::new(), Shaping::Advanced, None); - buffer.shape_until_scroll(false); - - let line = &buffer.lines[0]; - let shape = line.shape_opt().expect("ShapeLine not found"); - let span = &shape.spans[0]; - - // The pipe character | is typically a line break opportunity. - // This test ensures that our patch prevents splitting |> into separate words, - // which would break ligature formation in fonts that support it. - assert_eq!( - span.words.len(), - 1, - "Expected '|>' to be a single word (preserved for ligature), but found {} words.", - span.words.len() - ); -} diff --git a/tests/shaping_and_rendering.rs b/tests/shaping_and_rendering.rs index cb90ff0..1841111 100644 --- a/tests/shaping_and_rendering.rs +++ b/tests/shaping_and_rendering.rs @@ -73,3 +73,33 @@ fn test_english_mixed_with_arabic_paragraph_rendering() { .canvas(400, 110) .validate_text_rendering(); } + +#[test] +fn test_ligature_segmentation() { + use cosmic_text::{Buffer, FontSystem, Metrics, Shaping}; + + let mut font_system = FontSystem::new_with_locale_and_db("en-US".into(), fontdb::Database::new()); + let font = std::fs::read("fonts/Inter-Regular.ttf").unwrap(); + font_system.db_mut().load_font_data(font); + let metrics = Metrics::new(14.0, 20.0); + + let mut buffer = Buffer::new(&mut font_system, metrics); + let mut buffer = buffer.borrow_with(&mut font_system); + + buffer.set_text("|>", &Attrs::new(), Shaping::Advanced, None); + buffer.shape_until_scroll(false); + + let line = &buffer.lines[0]; + let shape = line.shape_opt().expect("ShapeLine not found"); + let span = &shape.spans[0]; + + // The pipe character | is typically a line break opportunity. + // This test ensures that our patch prevents splitting |> into separate words, + // which would break ligature formation in fonts that support it. + assert_eq!( + span.words.len(), + 1, + "Expected '|>' to be a single word (preserved for ligature), but found {} words.", + span.words.len() + ); +} \ No newline at end of file