use cosmic_text::{ fontdb, Align, Attrs, AttrsList, BidiParagraphs, Buffer, Family, FontSystem, LayoutLine, Metrics, ShapeLine, Shaping, Weight, Wrap, }; // Test for https://github.com/pop-os/cosmic-text/issues/134 // // Being able to get the same wrapping when feeding the measured width back into ShapeLine::layout // as the new width limit is very useful for certain UI layout use cases. #[test] fn stable_wrap() { let font_size = 18.0; let attrs = AttrsList::new( Attrs::new() .family(Family::Name("FiraMono")) .weight(Weight::MEDIUM), ); let mut font_system = FontSystem::new_with_locale_and_db("en-US".into(), fontdb::Database::new()); let font = std::fs::read("fonts/FiraMono-Medium.ttf").unwrap(); font_system.db_mut().load_font_data(font); let mut check_wrap = |text: &_, wrap, align_opt, start_width_opt| { let line = ShapeLine::new(&mut font_system, text, &attrs, Shaping::Advanced, 8); let layout_unbounded = line.layout(font_size, start_width_opt, wrap, align_opt, None); let max_width = layout_unbounded.iter().map(|l| l.w).fold(0.0, f32::max); let new_limit = match start_width_opt { Some(start_width) => f32::min(start_width, max_width), None => max_width, }; let layout_bounded = line.layout(font_size, Some(new_limit), wrap, align_opt, None); let bounded_max_width = layout_bounded.iter().map(|l| l.w).fold(0.0, f32::max); // For debugging: // dbg_layout_lines(text, &layout_unbounded); // dbg_layout_lines(text, &layout_bounded); assert_eq!( (max_width, layout_unbounded.len()), (bounded_max_width, layout_bounded.len()), "Wrap \"{wrap:?}\" and align \"{align_opt:?}\" with text: \"{text}\"", ); for (u, b) in layout_unbounded[1..].iter().zip(layout_bounded[1..].iter()) { assert_eq!( u.w, b.w, "Wrap {wrap:?} and align \"{align_opt:?}\" with text: \"{text}\"", ); } }; let hello_sample = std::fs::read_to_string("sample/hello.txt").unwrap(); let cases = [ "(6) SomewhatBoringDisplayTransform", "", " ", " ", " ", " ", ] .into_iter() // This has several cases where the line would wrap when the computed width was used as the // width limit. .chain(BidiParagraphs::new(&hello_sample)); for text in cases { for wrap in [Wrap::None, Wrap::Glyph, Wrap::Word, Wrap::WordOrGlyph] { for align_opt in [ None, Some(Align::Left), Some(Align::Right), Some(Align::Center), //TODO: Align::Justified Some(Align::End), ] { for start_width_opt in [ None, Some(f32::MAX), Some(80.0), Some(198.2132), Some(20.0), Some(4.0), Some(300.0), ] { check_wrap(text, wrap, align_opt, start_width_opt); let with_spaces = format!("{text} "); check_wrap(&with_spaces, wrap, align_opt, start_width_opt); let with_spaces_2 = format!("{text} "); check_wrap(&with_spaces_2, wrap, align_opt, start_width_opt); } } } } } #[test] fn wrap_extra_line() { let mut font_system = FontSystem::new(); 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); // Add some text! buffer.set_wrap(Wrap::Word); buffer.set_text("Lorem ipsum dolor sit amet, qui minim labore adipisicing\n\nweeewoooo minim sint cillum sint consectetur cupidatat.", Attrs::new().family(cosmic_text::Family::Name("Inter")), Shaping::Advanced); // Set a size for the text buffer, in pixels buffer.set_size(Some(50.0), Some(1000.0)); // Perform shaping as desired buffer.shape_until_scroll(false); let empty_lines = buffer.layout_runs().filter(|x| x.line_w == 0.).count(); let overflow_lines = buffer.layout_runs().filter(|x| x.line_w > 50.).count(); assert_eq!(empty_lines, 1); assert_eq!(overflow_lines, 4); } #[allow(dead_code)] fn dbg_layout_lines(text: &str, lines: &[LayoutLine]) { for line in lines { let mut s = String::new(); for glyph in line.glyphs.iter() { s.push_str(&text[glyph.start..glyph.end]); } println!("\"{s}\""); } }