Keep 1st collapsible space after a preserved one (#32037)

The logic was to remove any collapsible white space preceded by other
white space. However, this should only happen if the preceding space
is also collapsible.

Also fixing the logic in ContentSizesComputation, which was wrong
but previously it didn't matter.
This commit is contained in:
Oriol Brufau 2024-04-15 14:02:09 +02:00 committed by GitHub
parent 5083dc7d17
commit 1898394cb3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 19 additions and 17 deletions

View file

@ -2306,7 +2306,8 @@ struct ContentSizesComputation<'a> {
current_line: ContentSizes, current_line: ContentSizes,
/// Size for whitepsace pending to be added to this line. /// Size for whitepsace pending to be added to this line.
pending_whitespace: Au, pending_whitespace: Au,
/// Whether or not this IFC has seen any content, excluding collapsed whitespace. /// Whether or not the current line has seen any content (excluding collapsed whitespace),
/// when sizing under a max-content constraint.
had_content_yet: bool, had_content_yet: bool,
/// Stack of ending padding, margin, and border to add to the length /// Stack of ending padding, margin, and border to add to the length
/// when an inline box finishes. /// when an inline box finishes.
@ -2366,7 +2367,6 @@ impl<'a> ContentSizesComputation<'a> {
// If this run is a forced line break, we *must* break the line // If this run is a forced line break, we *must* break the line
// and start measuring from the inline origin once more. // and start measuring from the inline origin once more.
if text_run.glyph_run_is_preserved_newline(run) { if text_run.glyph_run_is_preserved_newline(run) {
self.had_content_yet = true;
self.forced_line_break(); self.forced_line_break();
self.current_line = ContentSizes::zero(); self.current_line = ContentSizes::zero();
continue; continue;
@ -2375,12 +2375,12 @@ impl<'a> ContentSizesComputation<'a> {
let white_space = let white_space =
text_run.parent_style.get_inherited_text().white_space; text_run.parent_style.get_inherited_text().white_space;
if !white_space.preserve_spaces() { if !white_space.preserve_spaces() {
// Discard any leading whitespace in the IFC. This will always be trimmed. // TODO: need to handle !white_space.allow_wrap().
self.line_break_opportunity();
// Discard any leading whitespace in the line. This will always be trimmed.
if self.had_content_yet { if self.had_content_yet {
// Wait to take into account other whitespace until we see more content. // Wait to take into account other whitespace until we see more content.
// Whitespace at the end of the IFC will always be trimmed. // Whitespace at the end of the line will always be trimmed.
// TODO: need to handle !white_space.allow_wrap().
self.line_break_opportunity();
self.pending_whitespace += advance; self.pending_whitespace += advance;
} }
continue; continue;
@ -2433,6 +2433,7 @@ impl<'a> ContentSizesComputation<'a> {
self.paragraph.max_content = self.paragraph.max_content =
std::cmp::max(self.paragraph.max_content, self.current_line.max_content); std::cmp::max(self.paragraph.max_content, self.current_line.max_content);
self.current_line.max_content = Au::zero(); self.current_line.max_content = Au::zero();
self.had_content_yet = false;
} }
fn commit_pending_whitespace(&mut self) { fn commit_pending_whitespace(&mut self) {

View file

@ -211,13 +211,13 @@ impl TextRun {
font_context: &mut FontContext<FontCacheThread>, font_context: &mut FontContext<FontCacheThread>,
linebreaker: &mut Option<LineBreakLeafIter>, linebreaker: &mut Option<LineBreakLeafIter>,
font_cache: &mut Vec<FontKeyAndMetrics>, font_cache: &mut Vec<FontKeyAndMetrics>,
last_inline_box_ended_with_white_space: &mut bool, last_inline_box_ended_with_collapsible_white_space: &mut bool,
on_word_boundary: &mut bool, on_word_boundary: &mut bool,
) { ) {
let segment_results = self.segment_text( let segment_results = self.segment_text(
font_context, font_context,
font_cache, font_cache,
last_inline_box_ended_with_white_space, last_inline_box_ended_with_collapsible_white_space,
on_word_boundary, on_word_boundary,
); );
let inherited_text_style = self.parent_style.get_inherited_text().clone(); let inherited_text_style = self.parent_style.get_inherited_text().clone();
@ -282,7 +282,7 @@ impl TextRun {
&mut self, &mut self,
font_context: &mut FontContext<FontCacheThread>, font_context: &mut FontContext<FontCacheThread>,
font_cache: &mut Vec<FontKeyAndMetrics>, font_cache: &mut Vec<FontKeyAndMetrics>,
last_inline_box_ended_with_white_space: &mut bool, last_inline_box_ended_with_collapsible_white_space: &mut bool,
on_word_boundary: &mut bool, on_word_boundary: &mut bool,
) -> Vec<(TextRunSegment, FontRef)> { ) -> Vec<(TextRunSegment, FontRef)> {
let font_group = font_context.font_group(self.parent_style.clone_font()); let font_group = font_context.font_group(self.parent_style.clone_font());
@ -291,10 +291,11 @@ impl TextRun {
// TODO: Eventually the text should come directly from the Cow strings of the DOM nodes. // TODO: Eventually the text should come directly from the Cow strings of the DOM nodes.
let text = std::mem::take(&mut self.text); let text = std::mem::take(&mut self.text);
let white_space = self.parent_style.clone_white_space();
let collapsed = WhitespaceCollapse::new( let collapsed = WhitespaceCollapse::new(
text.as_str().chars(), text.as_str().chars(),
self.parent_style.clone_white_space(), white_space,
*last_inline_box_ended_with_white_space, *last_inline_box_ended_with_collapsible_white_space,
); );
let text_transform = self.parent_style.clone_text_transform(); let text_transform = self.parent_style.clone_text_transform();
@ -304,8 +305,9 @@ impl TextRun {
// `TextTransformation` doesn't support capitalization, so we must capitalize the whole // `TextTransformation` doesn't support capitalization, so we must capitalize the whole
// string at once and make a copy. Here `on_word_boundary` indicates whether or not the // string at once and make a copy. Here `on_word_boundary` indicates whether or not the
// inline formatting context as a whole is on a word boundary. This is different from // inline formatting context as a whole is on a word boundary. This is different from
// `last_inline_box_ended_with_white_space` because the word boundaries are between // `last_inline_box_ended_with_collapsible_white_space` because the word boundaries are
// atomic inlines and at the start of the IFC. // between atomic inlines and at the start of the IFC, and because preserved spaces
// are a word boundary.
let collapsed_string: String = collapsed.collect(); let collapsed_string: String = collapsed.collect();
collected_text = capitalize_string(&collapsed_string, *on_word_boundary); collected_text = capitalize_string(&collapsed_string, *on_word_boundary);
Box::new(collected_text.chars()) Box::new(collected_text.chars())
@ -323,8 +325,9 @@ impl TextRun {
let current_byte_index = next_byte_index; let current_byte_index = next_byte_index;
next_byte_index += character.len_utf8(); next_byte_index += character.len_utf8();
*last_inline_box_ended_with_white_space = character.is_whitespace(); *on_word_boundary = character.is_whitespace();
*on_word_boundary = *last_inline_box_ended_with_white_space; *last_inline_box_ended_with_collapsible_white_space =
*on_word_boundary && !white_space.preserve_spaces();
let prevents_soft_wrap_opportunity = let prevents_soft_wrap_opportunity =
char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character); char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character);

View file

@ -1,2 +0,0 @@
[white-space-mixed-002.xht]
expected: FAIL