bugfix: mixed ltr+rtl overflow

This commit is contained in:
Hojjat 2023-03-12 15:33:34 -06:00
parent 1bc198fd63
commit 8d503af956

View file

@ -49,7 +49,6 @@ fn shape_fallback(
let start_glyph = start_run + info.cluster as usize; let start_glyph = start_run + info.cluster as usize;
//println!(" {:?} {:?}", info, pos);
if info.glyph_id == 0 { if info.glyph_id == 0 {
missing.push(start_glyph); missing.push(start_glyph);
} }
@ -433,6 +432,13 @@ pub struct ShapeLine {
// Visual Line Ranges: (span_index, (first_word_index, first_glyph_index), (last_word_index, last_glyph_index)) // Visual Line Ranges: (span_index, (first_word_index, first_glyph_index), (last_word_index, last_glyph_index))
type VlRange = (usize, (usize, usize), (usize, usize)); type VlRange = (usize, (usize, usize), (usize, usize));
#[derive(Default)]
struct VisualLine {
ranges: Vec<VlRange>,
spaces: u32,
w: f32,
}
impl ShapeLine { impl ShapeLine {
/// # Panics /// # Panics
/// ///
@ -609,6 +615,24 @@ impl ShapeLine {
runs runs
} }
fn add_to_visual_line(
&self,
vl: &mut VisualLine,
span_index: usize,
start: (usize, usize),
end: (usize, usize),
width: f32,
number_of_blanks: u32,
) {
if end == start {
return;
}
vl.ranges.push((span_index, start, end));
vl.w += width;
vl.spaces += number_of_blanks;
}
pub fn layout( pub fn layout(
&self, &self,
font_size: f32, font_size: f32,
@ -629,20 +653,13 @@ impl ShapeLine {
// This is used to create a visual line for empty lines (e.g. lines with only a <CR>) // This is used to create a visual line for empty lines (e.g. lines with only a <CR>)
let mut push_line = true; let mut push_line = true;
#[derive(Default)]
struct VisualLine {
ranges: Vec<VlRange>,
spaces: u32,
w: f32,
}
// For each visual line a list of (span index, and range of words in that span) // For each visual line a list of (span index, and range of words in that span)
// Note that a BiDi visual line could have multiple spans or parts of them // Note that a BiDi visual line could have multiple spans or parts of them
// let mut vl_range_of_spans = Vec::with_capacity(1); // let mut vl_range_of_spans = Vec::with_capacity(1);
let mut vl_range_of_spans: Vec<VisualLine> = Vec::with_capacity(1); let mut visual_lines: Vec<VisualLine> = Vec::with_capacity(1);
let start_x = if self.rtl { line_width } else { 0.0 }; let start_x = if self.rtl { line_width } else { 0.0 };
let end_x = if self.rtl { 0.0 } else { line_width }; let mut x;
let mut x = start_x;
let mut y; let mut y;
// This would keep the maximum number of spans that would fit on a visual line // This would keep the maximum number of spans that would fit on a visual line
@ -660,148 +677,179 @@ impl ShapeLine {
} else { } else {
let mut fit_x = line_width; let mut fit_x = line_width;
for (span_index, span) in self.spans.iter().enumerate() { for (span_index, span) in self.spans.iter().enumerate() {
let mut word_ranges = Vec::new();
let mut word_range_width = 0.; let mut word_range_width = 0.;
let mut number_of_blanks = 0; let mut number_of_blanks: u32 = 0;
// Create the word ranges that fits in a visual line // Create the word ranges that fits in a visual line
if self.rtl != span.level.is_rtl() { if self.rtl != span.level.is_rtl() {
// incongruent directions // incongruent directions
let mut fitting_start = (span.words.len(), 0); let mut fitting_start = (span.words.len(), 0);
for (i, word) in span.words.iter().enumerate().rev() { for (i, word) in span.words.iter().enumerate().rev() {
let word_size = font_size * word.x_advance; let word_width = font_size * word.x_advance;
if fit_x - word_size >= 0. { if fit_x - word_width >= 0. {
// fits // fits
fit_x -= word_size; fit_x -= word_width;
word_range_width += word_size; word_range_width += word_width;
if word.blank { if word.blank {
number_of_blanks += 1; number_of_blanks += 1;
} }
continue; continue;
} else if wrap == Wrap::Glyph { } else if wrap == Wrap::Glyph {
for (glyph_i, glyph) in word.glyphs.iter().enumerate().rev() { for (glyph_i, glyph) in word.glyphs.iter().enumerate().rev() {
let glyph_size = font_size * glyph.x_advance; let glyph_width = font_size * glyph.x_advance;
if fit_x - glyph_size >= 0. { if fit_x - glyph_width >= 0. {
fit_x -= glyph_size; fit_x -= glyph_width;
word_range_width += glyph_size; word_range_width += glyph_width;
continue; continue;
} else { } else {
word_ranges.push(( self.add_to_visual_line(
&mut current_visual_line,
span_index,
(i, glyph_i + 1), (i, glyph_i + 1),
fitting_start, fitting_start,
word_range_width, word_range_width,
number_of_blanks, number_of_blanks,
)); );
visual_lines.push(current_visual_line);
current_visual_line = VisualLine::default();
number_of_blanks = 0; number_of_blanks = 0;
fit_x = line_width - glyph_size; fit_x = line_width - glyph_width;
word_range_width = glyph_size; word_range_width = glyph_width;
fitting_start = (i, glyph_i + 1); fitting_start = (i, glyph_i + 1);
} }
} }
} else { } else {
// Wrap::Word // Wrap::Word
let mut prev_word_width = None; let mut trialing_space_width = None;
if word.blank && number_of_blanks > 0 { if i > 0 {
// current word causing a wrap is a space so we ignore it if let Some(previous_word) = span.words.get(i - 1) {
number_of_blanks -= 1; // Current word causing a wrap is not whitespace, so we ignore the
} else if let Some(previous_word) = span.words.get(i - 1) { // previous word if it's a whitespace
// Current word causing a wrap is not whitespace, so we ignore the if previous_word.blank {
// previous word if it's a whitespace trialing_space_width =
if previous_word.blank { Some(previous_word.x_advance * font_size);
number_of_blanks -= 1; number_of_blanks = number_of_blanks.saturating_sub(1);
prev_word_width = Some(previous_word.x_advance * font_size); }
} }
} }
if let Some(width) = prev_word_width { if let Some(width) = trialing_space_width {
word_ranges.push(( self.add_to_visual_line(
(i, 0), &mut current_visual_line,
span_index,
(i + 2, 0),
fitting_start, fitting_start,
word_range_width - width, word_range_width - width,
number_of_blanks, number_of_blanks,
)); );
} else { } else {
word_ranges.push(( self.add_to_visual_line(
&mut current_visual_line,
span_index,
(i + 1, 0), (i + 1, 0),
fitting_start, fitting_start,
word_range_width, word_range_width,
number_of_blanks, number_of_blanks,
)); );
} }
visual_lines.push(current_visual_line);
current_visual_line = VisualLine::default();
number_of_blanks = 0; number_of_blanks = 0;
if word.blank { if word.blank {
fit_x = line_width; fit_x = line_width;
word_range_width = 0.; word_range_width = 0.;
fitting_start = (i + 1, 0); fitting_start = (i + 1, 0);
} else { } else {
fit_x = line_width - word_size; fit_x = line_width - word_width;
word_range_width = word_size; word_range_width = word_width;
fitting_start = (i + 1, 0); fitting_start = (i + 1, 0);
} }
} }
} }
word_ranges.push(((0, 0), fitting_start, word_range_width, number_of_blanks)); self.add_to_visual_line(
&mut current_visual_line,
span_index,
(0, 0),
fitting_start,
word_range_width,
number_of_blanks,
);
} else { } else {
// congruent direction // congruent direction
let mut fitting_start = (0, 0); let mut fitting_start = (0, 0);
for (i, word) in span.words.iter().enumerate() { for (i, word) in span.words.iter().enumerate() {
let word_size = font_size * word.x_advance; let word_width = font_size * word.x_advance;
if fit_x - word_size >= 0. { if fit_x - word_width >= 0. {
// fits // fits
fit_x -= word_size; fit_x -= word_width;
word_range_width += word_size; word_range_width += word_width;
if word.blank { if word.blank {
number_of_blanks += 1; number_of_blanks += 1;
} }
continue; continue;
} else if wrap == Wrap::Glyph { } else if wrap == Wrap::Glyph {
for (glyph_i, glyph) in word.glyphs.iter().enumerate() { for (glyph_i, glyph) in word.glyphs.iter().enumerate() {
let glyph_size = font_size * glyph.x_advance; let glyph_width = font_size * glyph.x_advance;
if fit_x - glyph_size >= 0. { if fit_x - glyph_width >= 0. {
fit_x -= glyph_size; fit_x -= glyph_width;
word_range_width += glyph_size; word_range_width += glyph_width;
continue; continue;
} else { } else {
word_ranges.push(( self.add_to_visual_line(
&mut current_visual_line,
span_index,
fitting_start, fitting_start,
(i, glyph_i), (i, glyph_i),
word_range_width, word_range_width,
number_of_blanks, number_of_blanks,
)); );
visual_lines.push(current_visual_line);
current_visual_line = VisualLine::default();
number_of_blanks = 0; number_of_blanks = 0;
fit_x = line_width - glyph_size; fit_x = line_width - glyph_width;
word_range_width = glyph_size; word_range_width = glyph_width;
fitting_start = (i, glyph_i); fitting_start = (i, glyph_i);
} }
} }
} else { } else {
// Wrap::Word // Wrap::Word
let mut prev_word_width = None; let mut prev_word_width = None;
if word.blank && number_of_blanks > 0 { if word.blank {
// current word causing a wrap is a space so we ignore it // current word causing a wrap is a space so we ignore it
number_of_blanks -= 1; // number_of_blanks = number_of_blanks.saturating_sub(1);
} else if let Some(previous_word) = span.words.get(i - 1) { } else if i > 0 {
// Current word causing a wrap is not whitespace, so we ignore the if let Some(previous_word) = span.words.get(i - 1) {
// previous word if it's a whitespace // Current word causing a wrap is not whitespace, so we ignore the
if previous_word.blank { // previous word if it's a whitespace
number_of_blanks -= 1; if previous_word.blank {
prev_word_width = Some(previous_word.x_advance * font_size); prev_word_width = Some(previous_word.x_advance * font_size);
number_of_blanks = number_of_blanks.saturating_sub(1);
}
} }
} }
if let Some(width) = prev_word_width { if let Some(width) = prev_word_width {
word_ranges.push(( self.add_to_visual_line(
&mut current_visual_line,
span_index,
fitting_start, fitting_start,
(i - 1, 0), (i, 0),
word_range_width - width, word_range_width - width,
number_of_blanks, number_of_blanks,
)); );
} else { } else {
word_ranges.push(( self.add_to_visual_line(
&mut current_visual_line,
span_index,
fitting_start, fitting_start,
(i, 0), (i, 0),
word_range_width, word_range_width,
number_of_blanks, number_of_blanks,
)); );
} }
visual_lines.push(current_visual_line);
current_visual_line = VisualLine::default();
number_of_blanks = 0; number_of_blanks = 0;
if word.blank { if word.blank {
@ -809,88 +857,34 @@ impl ShapeLine {
word_range_width = 0.; word_range_width = 0.;
fitting_start = (i + 1, 0); fitting_start = (i + 1, 0);
} else { } else {
fit_x = line_width - word_size; fit_x = line_width - word_width;
word_range_width = word_size; word_range_width = word_width;
fitting_start = (i, 0); fitting_start = (i, 0);
} }
} }
} }
word_ranges.push(( self.add_to_visual_line(
&mut current_visual_line,
span_index,
fitting_start, fitting_start,
(span.words.len(), 0), (span.words.len(), 0),
word_range_width, word_range_width,
number_of_blanks, number_of_blanks,
)); );
}
// Create a visual line
for (
(starting_word, starting_glyph),
(ending_word, ending_glyph),
word_range_width,
number_of_blanks,
) in word_ranges
{
// To simplify the algorithm above, we might push empty ranges but we ignore them here
if ending_word == starting_word && starting_glyph == ending_glyph {
continue;
}
let fits = !if self.rtl {
x - word_range_width < end_x
} else {
x + word_range_width > end_x
};
if fits {
current_visual_line.ranges.push((
span_index,
(starting_word, starting_glyph),
(ending_word, ending_glyph),
));
current_visual_line.w += word_range_width;
current_visual_line.spaces += number_of_blanks;
if self.rtl {
x -= word_range_width;
} else {
x += word_range_width;
}
} else {
if !current_visual_line.ranges.is_empty() {
vl_range_of_spans.push(current_visual_line);
current_visual_line = VisualLine::default();
x = start_x;
}
current_visual_line.ranges.push((
span_index,
(starting_word, starting_glyph),
(ending_word, ending_glyph),
));
current_visual_line.w += word_range_width;
current_visual_line.spaces += number_of_blanks;
if self.rtl {
x -= word_range_width;
} else {
x += word_range_width;
}
if word_range_width > line_width {
// single word is bigger than line_width
vl_range_of_spans.push(current_visual_line);
current_visual_line = VisualLine::default();
x = start_x;
}
}
} }
} }
} }
if !current_visual_line.ranges.is_empty() { if !current_visual_line.ranges.is_empty() {
vl_range_of_spans.push(current_visual_line); visual_lines.push(current_visual_line);
} }
// Create the LayoutLines using the ranges inside visual lines // Create the LayoutLines using the ranges inside visual lines
let number_of_visual_lines = vl_range_of_spans.len(); let number_of_visual_lines = visual_lines.len();
for (index, visual_line) in vl_range_of_spans.iter().enumerate() { for (index, visual_line) in visual_lines.iter().enumerate() {
if visual_line.ranges.is_empty() {
continue;
}
let new_order = self.reorder(&visual_line.ranges); let new_order = self.reorder(&visual_line.ranges);
let mut glyphs = Vec::with_capacity(1); let mut glyphs = Vec::with_capacity(1);
x = start_x; x = start_x;