From 799f2213fa203c5c2a3bdb08b4368f04ae626757 Mon Sep 17 00:00:00 2001 From: Adrian Wielgosik Date: Wed, 7 Jan 2026 22:43:02 +0100 Subject: [PATCH 1/2] core: Reimplement text wrapping for compatibility --- core/src/font.rs | 337 ++++++++++++++---- core/src/html/layout.rs | 50 +-- .../edittext_wrap_breaks/output.ruffle.txt | 43 --- .../swfs/avm2/edittext_wrap_breaks/test.toml | 1 - .../TextField-v8/output.fp13-17.ruffle.txt | 8 +- .../TextField-v8/output.fp16.ruffle.txt | 8 +- .../TextField-v8/output.fp9-14.ruffle.txt | 8 +- 7 files changed, 310 insertions(+), 145 deletions(-) delete mode 100644 tests/tests/swfs/avm2/edittext_wrap_breaks/output.ruffle.txt diff --git a/core/src/font.rs b/core/src/font.rs index 3f2239646022..06aafea6994f 100644 --- a/core/src/font.rs +++ b/core/src/font.rs @@ -927,23 +927,116 @@ pub trait FontLike<'gc> { width } + // FP line wrapping behavior summary. + // NOTE: this is derived from experiment and is not guaranteed to fully match FP. + // SWF >= 8: + // - In ASCII, only allowed wrapping points are after '-' and after (runs of) spaces. + // - Given "aaa bbb", the splitting (observed by getLineLength()) works as if + // the text was split into "aaa " and "bbb", + // but for the purposes of width measurement, they behave like "aaa" and " bbb". + // (this behaves as-if spaces were "collapsed") + // - Break is allowed on any side of CJK characters + // - BUT if CJK has a matching opening/closing character on its side, don't break. + // the opening/closing character doesn't need to be CJK, can be just '('. + // - Fallback when even 1st slice doesn't fit: break on last fitting char. + // - Final fallback: + // if nothing fits, fit one character per line (it'll get cut off visually). + + // SWF <= 7 differences: + // - Breaks are allowed on both sides of '-'. + // - Any space is a valid break point. + // - Spaces are not collapsed; only the final space of the slice (if present) + // is dropped for measurements. + // (this means we can never split "a " apart, since its space never counts) + // - Final fallback: + // if resulting length has <=1 char, yield entire line without wrapping. + + fn is_cjk_like_char(c: char) -> bool { + // NOTE: not guaranteed to be equivalent to FP's set. + matches!(c, '\u{2300}'..) + } + + #[rustfmt::skip] + fn is_opening_char(c: char) -> bool { + // NOTE: not guaranteed to be equivalent to FP's set. + matches!(c, + '(' | '[' | '{' + | '(' | '[' | '{' | '〈' | '《' | '「' | '⁅' | '『' + | '【' | '〖' | '〚' + | '﴾' + | '﹙' + | '〝' + | '︻' | '﹁' | '﹃' + | '︵' + ) + } + + #[rustfmt::skip] + fn is_closing_char(c: char) -> bool { + // NOTE: not guaranteed to be equivalent to FP's set. + matches!(c, + ')' | ']' | '}' + | ')' | ']' | '}' | '〉' | '》' + | '?' | '!' | ';' | ':' | ',' | '.' | '」' | '⁆' | '』' + | '】' | '〕' | '〙' | '﹞' + | '﴿' + | '﹚' + | '?' | '!' | ';' | ':' | ',' | '.' + | '、' | '。' + | '〜' + | '︺' | '﹀' | '﹂' | '﹄' | '︶' + | '〟' + ) + } + + // IMO way more readable the way it is rn + #[allow(clippy::if_same_then_else)] + #[allow(clippy::collapsible_if)] + fn find_allowed_breaks(text: &WStr, swf7: bool) -> Vec { + let mut ret = vec![]; + if text.is_empty() { + return ret; + } + // note: FP probably handles bad utf16 in some universal way + const FALLBACK: char = char::REPLACEMENT_CHARACTER; + let mut prev = text.chars().next().unwrap().unwrap_or(FALLBACK); + for (i, curr) in text.char_indices().skip(1) { + let curr = curr.unwrap_or(FALLBACK); + if !swf7 && curr == ' ' { + // in swf>=8, only last space is a break + prev = curr; + continue; + } + if prev == ' ' { + ret.push(i); + } else if prev == '-' || (swf7 && curr == '-') { + ret.push(i); + } else if Self::is_cjk_like_char(prev) || Self::is_cjk_like_char(curr) { + if !Self::is_opening_char(prev) && !Self::is_closing_char(curr) { + ret.push(i); + } + } + prev = curr; + } + ret.push(text.len()); + ret + } + /// Given a line of text, find the first breakpoint within the text. + /// This implements the SWF <= 7 variant of the comment above. /// - /// This function assumes only `" "` is valid whitespace to split words on, - /// and will not attempt to break words that are longer than `width`, nor - /// will it break at newlines. + /// This assumes the input doesn't contain any mandatory breaks (newlines). /// /// The given `offset` determines the start of the initial line, while the /// `width` indicates how long the line is supposed to be. Be careful to /// note that it is possible for this function to return `0`; that /// indicates that the string itself cannot fit on the line and should /// break onto the next one. + /// If is_start_of_line, the function is guaranteed to not return 0 + /// (see "final fallback" in comment above). /// /// This function yields `None` if the line is not broken. - /// - /// TODO: This function and, more generally, this entire file will need to - /// be internationalized to implement AS3 `flash.text.engine`. - fn wrap_line( + fn wrap_line_swf7( &self, text: &WStr, params: EvalParameters, @@ -951,63 +1044,169 @@ pub trait FontLike<'gc> { offset: Twips, mut is_start_of_line: bool, ) -> Option { + if text.is_empty() { + return None; + } + let mut remaining_width = width - offset; if remaining_width < Twips::ZERO { - return Some(0); + // If even 1st char doesn't fit, give up on wrapping. + return None; } let mut line_end = 0; - for word in text.split(b' ') { - let word_start = word.offset_in(text).unwrap(); - let word_end = word_start + word.len(); + let allowed_breaks = Self::find_allowed_breaks(text, true); - let measure = self.measure( - // +1 is fine because ' ' is 1 unit - text.slice(word_start..word_end + 1).unwrap_or(word), - params, - ); + let mut last_stop = 0; + for (i, word_end) in allowed_breaks.into_iter().enumerate() { + let word_start = last_stop; - if is_start_of_line && measure > remaining_width { - //Failsafe for if we get a word wider than the field. - let mut last_passing_breakpoint = Twips::ZERO; + // For details, see the comment in wrap_line_swf8. + // The difference is that every space is a break point + // and we only trim the final space, + // So given "a b ", the checked words are: + // - word: "a ", trimmed_word: "a", + // - word: " ", trimmed_word: " ", + // - word: " b ", trimmed_word: " b", - let cur_slice = &text[word_start..]; - let mut char_iter = cur_slice.char_indices(); - let mut char_index = word_start; - let mut prev_char_index = char_index; - let mut prev_frag_end = 0; + let word = &text[word_start..word_end]; - char_iter.next(); // No need to check cur_slice[0..0] - while last_passing_breakpoint <= remaining_width { - prev_char_index = char_index; - char_index = word_start + prev_frag_end; + assert!(!word.is_empty(), "trying to line-break an empty string?"); - if let Some((frag_end, _)) = char_iter.next() { - last_passing_breakpoint = self.measure(&cur_slice[..frag_end], params); + let trimmed_word = if word.get(word.len() - 1) == Some(b' ' as u16) { + &word[..word.len() - 1] + } else { + word + }; - prev_frag_end = frag_end; - } else { - break; + let trimmed_word_end = word_start + trimmed_word.len(); + last_stop = trimmed_word_end; + + let measure = self.measure(trimmed_word, params); + + if measure <= remaining_width { + //Space remains for our current word, move up the word pointer. + line_end = word_end; + + is_start_of_line = false; + + remaining_width -= measure; + } else { + if is_start_of_line { + //Failsafe: we get a word wider than the field, break anywhere. + + assert!(i == 0); + assert!(word_start == 0); + + let mut last_fitting_end = 0; + for (frag_end, _) in trimmed_word.char_indices() { + let width = self.measure(&trimmed_word[..frag_end], params); + if width > remaining_width { + break; + } + last_fitting_end = frag_end; } - } + line_end = last_fitting_end; - return Some(prev_char_index); - } else if measure > remaining_width { - //The word is wider than our remaining width, return the end of - //the line. + // If result has <= 1 char, give up on wrapping. + if line_end <= 1 { + return None; + }; + } return Some(line_end); - } else { + } + } + + None + } + + /// Given a line of text, find the first breakpoint within the text. + /// This implements the SWF >= 8 variant of the comment above. + /// + /// This assumes the input doesn't contain any mandatory breaks (newlines). + /// + /// The given `offset` determines the start of the initial line, while the + /// `width` indicates how long the line is supposed to be. Be careful to + /// note that it is possible for this function to return `0`; that + /// indicates that the string itself cannot fit on the line and should + /// break onto the next one. + /// If is_start_of_line, the function is guaranteed to not return 0 + /// (see "final fallback" in comment above). + /// + /// This function yields `None` if the line is not broken. + fn wrap_line_swf8( + &self, + text: &WStr, + params: EvalParameters, + width: Twips, + offset: Twips, + mut is_start_of_line: bool, + ) -> Option { + if text.is_empty() { + return None; + } + + let mut remaining_width = width - offset; + if remaining_width < Twips::ZERO { + // If even 1st char doesn't fit, let's write _anything_. + return Some(1); + } + + let mut line_end = 0; + + let allowed_breaks = Self::find_allowed_breaks(text, false); + + let mut last_stop = 0; + for (i, word_end) in allowed_breaks.into_iter().enumerate() { + let word_start = last_stop; + + // Given "aaa bbb", + // the allowed break "splits" the text to "aaa " and "bbb". + // but we want to measure "aaa", then check if " bbb" still fits. + // The way we do it is, when measuring "aaa ", we pretend it's "aaa" + // and when measuring "bbb", we pretend it's " bbb". + + // So given "a b ", the checked words are: + // - word: "a ", trimmed_word: "a", + // - word: " b ", trimmed_word: " b", + + let word = &text[word_start..word_end]; + let trimmed_word = word.trim_end(); + + let trimmed_word_end = word_start + trimmed_word.len(); + last_stop = trimmed_word_end; + + let measure = self.measure(trimmed_word, params); + + if measure <= remaining_width { //Space remains for our current word, move up the word pointer. line_end = word_end; - is_start_of_line = is_start_of_line && text[0..line_end].trim().is_empty(); - //If the additional space were to cause an overflow, then - //return now. + is_start_of_line = false; + remaining_width -= measure; - if remaining_width < Twips::ZERO { - return Some(word_end); + } else { + if is_start_of_line { + //Failsafe: we get a word wider than the field, break anywhere. + + assert!(i == 0); + assert!(word_start == 0); + + let mut last_fitting_end = 0; + for (frag_end, _) in trimmed_word.char_indices() { + let width = self.measure(&trimmed_word[..frag_end], params); + if width > remaining_width { + break; + } + last_fitting_end = frag_end; + } + line_end = last_fitting_end; + + // If even 1st char doesn't fit, let's write _anything_. + line_end = line_end.max(1); } + return Some(line_end); } } @@ -1660,7 +1859,7 @@ mod tests { let params = eval_parameters_from_parts(Twips::from_pixels(12.0), Twips::ZERO, true); let string = WStr::from_units(b"abcdefghijklmnopqrstuv"); let breakpoint = - df.wrap_line(string, params, Twips::from_pixels(200.0), Twips::ZERO, true); + df.wrap_line_swf8(string, params, Twips::from_pixels(200.0), Twips::ZERO, true); assert_eq!(None, breakpoint); }); @@ -1673,13 +1872,13 @@ mod tests { let string = WStr::from_units(b"abcd efgh ijkl mnop"); let mut last_bp = 0; let breakpoint = - df.wrap_line(string, params, Twips::from_pixels(35.0), Twips::ZERO, true); + df.wrap_line_swf8(string, params, Twips::from_pixels(35.0), Twips::ZERO, true); - assert_eq!(Some(4), breakpoint); + assert_eq!(Some(5), breakpoint); - last_bp += breakpoint.unwrap() + 1; + last_bp += breakpoint.unwrap(); - let breakpoint2 = df.wrap_line( + let breakpoint2 = df.wrap_line_swf8( &string[last_bp..], params, Twips::from_pixels(35.0), @@ -1687,11 +1886,11 @@ mod tests { true, ); - assert_eq!(Some(4), breakpoint2); + assert_eq!(Some(5), breakpoint2); - last_bp += breakpoint2.unwrap() + 1; + last_bp += breakpoint2.unwrap(); - let breakpoint3 = df.wrap_line( + let breakpoint3 = df.wrap_line_swf8( &string[last_bp..], params, Twips::from_pixels(35.0), @@ -1699,11 +1898,11 @@ mod tests { true, ); - assert_eq!(Some(4), breakpoint3); + assert_eq!(Some(5), breakpoint3); - last_bp += breakpoint3.unwrap() + 1; + last_bp += breakpoint3.unwrap(); - let breakpoint4 = df.wrap_line( + let breakpoint4 = df.wrap_line_swf8( &string[last_bp..], params, Twips::from_pixels(35.0), @@ -1720,7 +1919,7 @@ mod tests { with_device_font(|_mc, df| { let params = eval_parameters_from_parts(Twips::from_pixels(12.0), Twips::ZERO, true); let string = WStr::from_units(b"abcd efgh ijkl mnop"); - let breakpoint = df.wrap_line( + let breakpoint = df.wrap_line_swf8( string, params, Twips::from_pixels(30.0), @@ -1739,13 +1938,13 @@ mod tests { let string = WStr::from_units(b"abcdi j kl mnop q rstuv"); let mut last_bp = 0; let breakpoint = - df.wrap_line(string, params, Twips::from_pixels(37.0), Twips::ZERO, true); + df.wrap_line_swf8(string, params, Twips::from_pixels(35.0), Twips::ZERO, true); - assert_eq!(Some(5), breakpoint); + assert_eq!(Some(6), breakpoint); - last_bp += breakpoint.unwrap() + 1; + last_bp += breakpoint.unwrap(); - let breakpoint2 = df.wrap_line( + let breakpoint2 = df.wrap_line_swf8( &string[last_bp..], params, Twips::from_pixels(37.0), @@ -1753,11 +1952,11 @@ mod tests { true, ); - assert_eq!(Some(4), breakpoint2); + assert_eq!(Some(5), breakpoint2); - last_bp += breakpoint2.unwrap() + 1; + last_bp += breakpoint2.unwrap(); - let breakpoint3 = df.wrap_line( + let breakpoint3 = df.wrap_line_swf8( &string[last_bp..], params, Twips::from_pixels(37.0), @@ -1765,11 +1964,11 @@ mod tests { true, ); - assert_eq!(Some(4), breakpoint3); + assert_eq!(Some(5), breakpoint3); - last_bp += breakpoint3.unwrap() + 1; + last_bp += breakpoint3.unwrap(); - let breakpoint4 = df.wrap_line( + let breakpoint4 = df.wrap_line_swf8( &string[last_bp..], params, Twips::from_pixels(37.0), @@ -1777,11 +1976,11 @@ mod tests { true, ); - assert_eq!(Some(1), breakpoint4); + assert_eq!(Some(2), breakpoint4); - last_bp += breakpoint4.unwrap() + 1; + last_bp += breakpoint4.unwrap(); - let breakpoint5 = df.wrap_line( + let breakpoint5 = df.wrap_line_swf8( &string[last_bp..], params, Twips::from_pixels(37.0), diff --git a/core/src/html/layout.rs b/core/src/html/layout.rs index 50143b6d835c..9268603887aa 100644 --- a/core/src/html/layout.rs +++ b/core/src/html/layout.rs @@ -5,7 +5,7 @@ use crate::drawing::Drawing; use crate::font::{DefaultFont, EvalParameters, Font, FontLike, FontSet, FontType}; use crate::html::dimensions::{BoxBounds, Position, Size}; use crate::html::text_format::{FormatSpans, TextFormat, TextSpan}; -use crate::string::{WStr, utils as string_utils}; +use crate::string::WStr; use crate::tag_utils::SwfMovie; use gc_arena::Collect; use std::cmp::{Ordering, max, min}; @@ -177,26 +177,36 @@ impl<'a, 'gc> LayoutContext<'a, 'gc> { if self.is_word_wrap { let (mut width, mut offset) = self.wrap_dimensions(span); - while let Some(breakpoint) = font_set.wrap_line( - &text[last_breakpoint..], - params, - width, - offset, - self.is_start_of_line(), - ) { - // This ensures that the space causing the line break - // is included in the line it broke. - let next_breakpoint = - string_utils::next_char_boundary(text, last_breakpoint + breakpoint); - - // If text doesn't fit at the start of a line, it - // won't fit on the next either, abort and put the - // whole text on the line (will be cut-off). This - // can happen for small text fields with single - // characters. - if breakpoint == 0 && self.is_start_of_line() { + loop { + let breakpoint = if self.movie.version() >= 8 { + font_set.wrap_line_swf8( + &text[last_breakpoint..], + params, + width, + offset, + self.is_start_of_line(), + ) + } else { + font_set.wrap_line_swf7( + &text[last_breakpoint..], + params, + width, + offset, + self.is_start_of_line(), + ) + }; + let Some(breakpoint) = breakpoint else { break; - } else if breakpoint == 0 { + }; + + let next_breakpoint = last_breakpoint + breakpoint; + + if breakpoint == 0 { + if self.is_start_of_line() { + unreachable!( + "wrap_line is supposed to return nonzero if is_start_of_line" + ); + } self.newline(context, start + next_breakpoint, span, false); let next_dim = self.wrap_dimensions(span); diff --git a/tests/tests/swfs/avm2/edittext_wrap_breaks/output.ruffle.txt b/tests/tests/swfs/avm2/edittext_wrap_breaks/output.ruffle.txt deleted file mode 100644 index 8740fd6973b0..000000000000 --- a/tests/tests/swfs/avm2/edittext_wrap_breaks/output.ruffle.txt +++ /dev/null @@ -1,43 +0,0 @@ - a: breaks - : breaks - !: breaks - @: breaks - #: breaks - $: breaks - %: breaks - ^: breaks - &: breaks - *: breaks - (: breaks - ): breaks - _: breaks - +: breaks - -: breaks - =: breaks - [: breaks - ]: breaks - {: breaks - }: breaks - `: breaks - |: breaks - ;: breaks - :: breaks - ': breaks - ~: breaks - /: breaks - ?: breaks - .: breaks - >: breaks - ,: breaks - <: breaks - ": breaks - \: breaks - a: breaks - ź: breaks - 形: breaks - 글: breaks - の: breaks - ¥: breaks - 1: breaks - a: breaks -Done diff --git a/tests/tests/swfs/avm2/edittext_wrap_breaks/test.toml b/tests/tests/swfs/avm2/edittext_wrap_breaks/test.toml index 29f3cef79022..cf6123969a1d 100644 --- a/tests/tests/swfs/avm2/edittext_wrap_breaks/test.toml +++ b/tests/tests/swfs/avm2/edittext_wrap_breaks/test.toml @@ -1,2 +1 @@ num_ticks = 1 -known_failure = true diff --git a/tests/tests/swfs/from_gnash/actionscript.all/TextField-v8/output.fp13-17.ruffle.txt b/tests/tests/swfs/from_gnash/actionscript.all/TextField-v8/output.fp13-17.ruffle.txt index 328a2ab6546a..ebcb7461cea3 100644 --- a/tests/tests/swfs/from_gnash/actionscript.all/TextField-v8/output.fp13-17.ruffle.txt +++ b/tests/tests/swfs/from_gnash/actionscript.all/TextField-v8/output.fp13-17.ruffle.txt @@ -446,9 +446,9 @@ PASSED: tf._width > 10 [./TextField.as:965] PASSED: tf.textWidth == origTextWidth [./TextField.as:966] After setting wordWrap flat: textWidth: 63 origTextWidth:63 PASSED: tf.textWidth == origTextWidth [./TextField.as:970] -After reducing _width: textWidth: 63 origTextWidth:63 +After reducing _width: textWidth: 9 origTextWidth:63 PASSED: tf._width == 10 [./TextField.as:973] -FAILED: origTextWidth > tf.textWidth [./TextField.as:978] +PASSED: origTextWidth > tf.textWidth [./TextField.as:978] PASSED: tf._width == 10 [./TextField.as:986] PASSED: tf._width == 10 [./TextField.as:990] PASSED: tf._width == 10 [./TextField.as:995] @@ -566,6 +566,6 @@ FAILED: expected: 0 obtained: 3 [./TextField.as:1289] PASSED: this[2] == 3 [./TextField.as:1290] FAILED: expected: "number" obtained: undefined [./TextField.as:1295] PASSED: typeof(this.getTime()) == "undefined" [./TextField.as:1299] -#passed: 468 -#failed: 88 +#passed: 469 +#failed: 87 #total tests run: 556 diff --git a/tests/tests/swfs/from_gnash/actionscript.all/TextField-v8/output.fp16.ruffle.txt b/tests/tests/swfs/from_gnash/actionscript.all/TextField-v8/output.fp16.ruffle.txt index 328a2ab6546a..ebcb7461cea3 100644 --- a/tests/tests/swfs/from_gnash/actionscript.all/TextField-v8/output.fp16.ruffle.txt +++ b/tests/tests/swfs/from_gnash/actionscript.all/TextField-v8/output.fp16.ruffle.txt @@ -446,9 +446,9 @@ PASSED: tf._width > 10 [./TextField.as:965] PASSED: tf.textWidth == origTextWidth [./TextField.as:966] After setting wordWrap flat: textWidth: 63 origTextWidth:63 PASSED: tf.textWidth == origTextWidth [./TextField.as:970] -After reducing _width: textWidth: 63 origTextWidth:63 +After reducing _width: textWidth: 9 origTextWidth:63 PASSED: tf._width == 10 [./TextField.as:973] -FAILED: origTextWidth > tf.textWidth [./TextField.as:978] +PASSED: origTextWidth > tf.textWidth [./TextField.as:978] PASSED: tf._width == 10 [./TextField.as:986] PASSED: tf._width == 10 [./TextField.as:990] PASSED: tf._width == 10 [./TextField.as:995] @@ -566,6 +566,6 @@ FAILED: expected: 0 obtained: 3 [./TextField.as:1289] PASSED: this[2] == 3 [./TextField.as:1290] FAILED: expected: "number" obtained: undefined [./TextField.as:1295] PASSED: typeof(this.getTime()) == "undefined" [./TextField.as:1299] -#passed: 468 -#failed: 88 +#passed: 469 +#failed: 87 #total tests run: 556 diff --git a/tests/tests/swfs/from_gnash/actionscript.all/TextField-v8/output.fp9-14.ruffle.txt b/tests/tests/swfs/from_gnash/actionscript.all/TextField-v8/output.fp9-14.ruffle.txt index 328a2ab6546a..ebcb7461cea3 100644 --- a/tests/tests/swfs/from_gnash/actionscript.all/TextField-v8/output.fp9-14.ruffle.txt +++ b/tests/tests/swfs/from_gnash/actionscript.all/TextField-v8/output.fp9-14.ruffle.txt @@ -446,9 +446,9 @@ PASSED: tf._width > 10 [./TextField.as:965] PASSED: tf.textWidth == origTextWidth [./TextField.as:966] After setting wordWrap flat: textWidth: 63 origTextWidth:63 PASSED: tf.textWidth == origTextWidth [./TextField.as:970] -After reducing _width: textWidth: 63 origTextWidth:63 +After reducing _width: textWidth: 9 origTextWidth:63 PASSED: tf._width == 10 [./TextField.as:973] -FAILED: origTextWidth > tf.textWidth [./TextField.as:978] +PASSED: origTextWidth > tf.textWidth [./TextField.as:978] PASSED: tf._width == 10 [./TextField.as:986] PASSED: tf._width == 10 [./TextField.as:990] PASSED: tf._width == 10 [./TextField.as:995] @@ -566,6 +566,6 @@ FAILED: expected: 0 obtained: 3 [./TextField.as:1289] PASSED: this[2] == 3 [./TextField.as:1290] FAILED: expected: "number" obtained: undefined [./TextField.as:1295] PASSED: typeof(this.getTime()) == "undefined" [./TextField.as:1299] -#passed: 468 -#failed: 88 +#passed: 469 +#failed: 87 #total tests run: 556 From 8121fec9777e284b1363d855e8df655c9cc5a56b Mon Sep 17 00:00:00 2001 From: Adrian Wielgosik Date: Thu, 15 Jan 2026 19:45:04 +0100 Subject: [PATCH 2/2] tests: Update tests for improved text wrapping compat --- .../edittext_tag_indent/output.expected.png | Bin 508 -> 647 bytes .../swfs/avm1/edittext_tag_indent/output.txt | 30 +++++++++++++++--- .../swfs/avm1/edittext_tag_indent/test.swf | Bin 743 -> 1342 bytes .../flash_text_TextField2/output.ruffle.png | Bin 6429 -> 0 bytes .../flash_text_TextField2/output.ruffle.txt | 2 +- .../flash_text_TextField2/test.toml | 12 ++++--- 6 files changed, 34 insertions(+), 10 deletions(-) delete mode 100644 tests/tests/swfs/from_shumway/flash_text_TextField2/output.ruffle.png diff --git a/tests/tests/swfs/avm1/edittext_tag_indent/output.expected.png b/tests/tests/swfs/avm1/edittext_tag_indent/output.expected.png index 3fe41f5209e484852343f082f163e24d5126039e..8850e003ff10320f3a1f747a68682f3b9bf0cc84 100644 GIT binary patch literal 647 zcmeAS@N?(olHy`uVBq!ia0vp^Mhpy$Ivi|3R^yZh?|>9zage(c!@6@aFM%AEbVpxD z28NCO+Pnv;&NygK~F{C2y?XAEpB}Wm5z_Z6np6}W$ zsV%r)>R1MUjQn#4!9SWimUzGTB+K7^c>e8Q755d*?dRLwxc>M3ap554C2k(Q6QmSP zow8c4aI6#r3Oo}mvXR?=U2fg=*U9_Kul=mq_x?w|+*%YhX+9@5)m^QPnEySZdPj`j z?CN~o{;;z%CKyiUFHQ?|`(vFa^9`s$?M2qJxf9pk{t$jmE^g=9?`uO*?Car-ye9o9 z?^@mab(de3+<#jsw|17HVqPHJDNwg1JNd9*wNQq-(al_8P;&5@`JA^CzxNxuu=Wf8OWP z&ugndp$gY?tVQQF@5lX-zxVH+H*?A7jH^AIh26*sK=B|coYb`Lm?Stp;5uOuM4Z61 c!w>aeU12g_@Lp#=F!3;Wy85}Sb4q9e0FyfjlmGw# literal 508 zcmeAS@N?(olHy`uVBq!ia0vp^Mhpy$Ivi|3R^yZh?|>9bx}&cn1H;CC?mvmFKt5w} zkh>G(&67IyK#oGPN02WALzNl>LqiJ#!!Mvv!wUw6QUeBtR|yOZRx=nF#0%!^3bX<0 ztMYVl45_&F_EsR@Aq4@~i|vK+IdkVel*+nLrq+HW-}a0}NcLaklw6t>V$#lyj9o zzf0&&h<`sZ`1QK_lpZk-IPkc$S^LEsK>-l(FJqnCq{n4-Tpupo(R41OQR4-M4u}&) zO1O5hJFl8n;-?EX07OjqRT^-05>Qu&=8Jb)FEMl??25JY*gEOlN>(NqxU`&aRoEp- x-3eK&clUc+Ek?*N34MRVWmU~W0(i3eE92tJ(Y4DB+d=Wn;OXk;vd$@?2>>+Qm;L|% diff --git a/tests/tests/swfs/avm1/edittext_tag_indent/output.txt b/tests/tests/swfs/avm1/edittext_tag_indent/output.txt index af4bf219d169..01b7bf677b0a 100644 --- a/tests/tests/swfs/avm1/edittext_tag_indent/output.txt +++ b/tests/tests/swfs/avm1/edittext_tag_indent/output.txt @@ -1,16 +1,16 @@ -Indent for _level0.text1: +Indent for text1: 1638 1638 -Indent for _level0.text2: +Indent for text2: -1638 -1638 -Indent for _level0.text3: +Indent for text3: -10 -10 -Indent for _level0.text4: +Indent for text4: 2 2 -Indent for _level0.text5: +Indent for text5: -4 -4 Setting 0 -> 0 @@ -29,3 +29,23 @@ Setting 2 -> 2 Setting -10 -> -10 Setting 100 -> 100 Setting 64010 -> 64010 +lines for text1: +a +bcdabcd + +lines for text2: +abcdabcd + + +lines for text3: +abcdabcd + + +lines for text4: +abcdabcd + + +lines for text5: +abcdabcd + + diff --git a/tests/tests/swfs/avm1/edittext_tag_indent/test.swf b/tests/tests/swfs/avm1/edittext_tag_indent/test.swf index c8d6941e5267e008b545cc565adcbdb1e459cce5..a3c5d76940d1f667950876757bbd860adac3d93e 100644 GIT binary patch literal 1342 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 diff --git a/tests/tests/swfs/from_shumway/flash_text_TextField2/output.ruffle.txt b/tests/tests/swfs/from_shumway/flash_text_TextField2/output.ruffle.txt index 5f3a5770afa9..a57529608c9d 100644 --- a/tests/tests/swfs/from_shumway/flash_text_TextField2/output.ruffle.txt +++ b/tests/tests/swfs/from_shumway/flash_text_TextField2/output.ruffle.txt @@ -11,4 +11,4 @@ FI RS T LI -10 +23 diff --git a/tests/tests/swfs/from_shumway/flash_text_TextField2/test.toml b/tests/tests/swfs/from_shumway/flash_text_TextField2/test.toml index c90160d16054..97fc99ed847e 100644 --- a/tests/tests/swfs/from_shumway/flash_text_TextField2/test.toml +++ b/tests/tests/swfs/from_shumway/flash_text_TextField2/test.toml @@ -1,9 +1,13 @@ num_frames = 1 known_failure = true -[image_comparisons.output] -known_failure = true -tolerance = 3 +[[image_comparisons.output.checks]] +tolerance = 200 +max_outliers = 10 + +[[image_comparisons.output.checks]] +tolerance = 128 +max_outliers = 250 [player_options] -with_renderer = { optional = false, sample_count = 1 } +with_renderer = { optional = false, sample_count = 4 }