diff --git a/examples/text/src/font/matches.rs b/examples/text/src/font/matches.rs index 1f078ec..02d77fd 100644 --- a/examples/text/src/font/matches.rs +++ b/examples/text/src/font/matches.rs @@ -5,7 +5,7 @@ pub struct FontMatches<'a> { } impl<'a> FontMatches<'a> { - fn shape_word(&self, font_i: usize, line: &str, start_word: usize, end_word: usize, span_rtl: bool) -> FontShapeWord { + fn shape_word(&self, font_i: usize, line: &str, start_word: usize, end_word: usize, span_rtl: bool, blank: bool) -> FontShapeWord { let word = &line[start_word..end_word]; let font_scale = self.fonts[font_i].rustybuzz.units_per_em() as f32; @@ -27,7 +27,12 @@ impl<'a> FontMatches<'a> { assert_eq!(rtl, span_rtl); if font_i == 0 { - println!(" Word {}: '{}'", if rtl { "RTL" } else { "LTR" }, word); + println!( + " Word {}{}: '{}'", + if rtl { "RTL" } else { "LTR" }, + if blank { " BLANK" } else { "" }, + word + ); } let glyph_buffer = rustybuzz::shape(&self.fonts[font_i].rustybuzz, &[], buffer); @@ -138,7 +143,7 @@ impl<'a> FontMatches<'a> { } } - let mut fb_word = self.shape_word(font_i + 1, line, start_glyph, end_glyph, span_rtl); + let mut fb_word = self.shape_word(font_i + 1, line, start_glyph, end_glyph, span_rtl, blank); // Insert all matching glyphs // println!("Locate fallback for {},{} '{}' from font {} to font {}", start_glyph, end_glyph, &line[start_glyph..end_glyph], font_i + 1, font_i); @@ -161,7 +166,7 @@ impl<'a> FontMatches<'a> { } */ - FontShapeWord { glyphs } + FontShapeWord { blank, glyphs } } fn shape_span(&self, line: &str, start_span: usize, end_span: usize, line_rtl: bool, span_rtl: bool) -> FontShapeSpan { @@ -171,12 +176,23 @@ impl<'a> FontMatches<'a> { let mut words = Vec::new(); - let mut start = 0; - for (linebreak, _) in unicode_linebreak::linebreaks(span) { - words.push(self.shape_word(0, line, start_span + start, start_span + linebreak, span_rtl)); - start = linebreak; + let mut start_word = 0; + for (end_lb, _) in unicode_linebreak::linebreaks(span) { + let mut start_lb = end_lb; + if end_lb != span.len() { + while start_lb > 1 { + start_lb -= 1; + if span.is_char_boundary(start_lb) { + break; + } + } + } + words.push(self.shape_word(0, line, start_span + start_word, start_span + start_lb, span_rtl, false)); + if start_lb < end_lb { + words.push(self.shape_word(0, line, start_span + start_lb, start_span + end_lb, span_rtl, true)); + } + start_word = end_lb; } - words.push(self.shape_word(0, line, start_span + start, end_span, span_rtl)); // Reverse glyphs in RTL lines if line_rtl { diff --git a/examples/text/src/font/shape.rs b/examples/text/src/font/shape.rs index 77d81df..63f3433 100644 --- a/examples/text/src/font/shape.rs +++ b/examples/text/src/font/shape.rs @@ -103,6 +103,7 @@ impl<'a> FontShapeGlyph<'a> { } pub struct FontShapeWord<'a> { + pub blank: bool, pub glyphs: Vec>, } @@ -122,49 +123,127 @@ impl<'a> FontShapeLine<'a> { let mut push_line = true; let mut glyphs = Vec::new(); - let mut x = if self.rtl { line_width as f32 } else { 0.0 }; + let start_x = if self.rtl { line_width as f32 } else { 0.0 }; + let end_x = if self.rtl { 0.0 } else { line_width as f32 }; + let mut x = start_x; let mut y = 0.0; for span in self.spans.iter() { - for word in span.words.iter() { - let mut word_size = 0.0; - for glyph in word.glyphs.iter() { - word_size += font_size as f32 * glyph.x_advance; - } + //TODO: improve performance! + let mut word_ranges = Vec::new(); + if self.rtl != span.rtl { + let mut fit_x = x; + let mut fitting_end = span.words.len(); + for i in (0..span.words.len()).rev() { + let word = &span.words[i]; - //TODO: make wrapping optional - let wrap = if self.rtl { - x - word_size < 0.0 - } else { - x + word_size > line_width as f32 - }; - if wrap && ! glyphs.is_empty() { - let mut glyphs_swap = Vec::new(); - std::mem::swap(&mut glyphs, &mut glyphs_swap); - layout_lines.insert(layout_i, FontLayoutLine { - line_i: self.line_i, - glyphs: glyphs_swap - }); - layout_i += 1; + let mut word_size = 0.0; + for glyph in word.glyphs.iter() { + word_size += font_size as f32 * glyph.x_advance; + } - x = if self.rtl { line_width as f32 } else { 0.0 }; - y = 0.0; - } + let wrap = if self.rtl { + fit_x - word_size < end_x + } else { + fit_x + word_size > end_x + }; - for glyph in word.glyphs.iter() { - let x_advance = font_size as f32 * glyph.x_advance; - let y_advance = font_size as f32 * glyph.y_advance; + if wrap { + word_ranges.push((i + 1..fitting_end)); + if word.blank { + fitting_end = i; + } else { + fitting_end = i + 1; + } + + fit_x = start_x; + } if self.rtl { - x -= x_advance + fit_x -= word_size; + } else { + fit_x += word_size; + } + } + word_ranges.push((0..fitting_end)); + } else { + let mut fit_x = x; + let mut fitting_start = 0; + for i in 0..span.words.len() { + let word = &span.words[i]; + + let mut word_size = 0.0; + for glyph in word.glyphs.iter() { + word_size += font_size as f32 * glyph.x_advance; } - glyphs.push(glyph.layout(font_size, x, y)); - push_line = true; + let wrap = if self.rtl { + fit_x - word_size < end_x + } else { + fit_x + word_size > end_x + }; - if ! self.rtl { - x += x_advance; + if wrap { + word_ranges.push((fitting_start..i)); + if word.blank { + fitting_start = i + 1; + } else { + fitting_start = i; + } + + fit_x = start_x; + } + + if self.rtl { + fit_x -= word_size; + } else { + fit_x += word_size; + } + } + word_ranges.push((fitting_start..span.words.len())); + } + + for range in word_ranges { + for word in span.words[range].iter() { + let mut word_size = 0.0; + for glyph in word.glyphs.iter() { + word_size += font_size as f32 * glyph.x_advance; + } + + //TODO: make wrapping optional + let wrap = if self.rtl { + x - word_size < end_x + } else { + x + word_size > end_x + }; + if wrap && ! glyphs.is_empty() { + let mut glyphs_swap = Vec::new(); + std::mem::swap(&mut glyphs, &mut glyphs_swap); + layout_lines.insert(layout_i, FontLayoutLine { + line_i: self.line_i, + glyphs: glyphs_swap + }); + layout_i += 1; + + x = start_x; + y = 0.0; + } + + for glyph in word.glyphs.iter() { + let x_advance = font_size as f32 * glyph.x_advance; + let y_advance = font_size as f32 * glyph.y_advance; + + if self.rtl { + x -= x_advance + } + + glyphs.push(glyph.layout(font_size, x, y)); + push_line = true; + + if ! self.rtl { + x += x_advance; + } + y += y_advance; } - y += y_advance; } } }