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/avm1/edittext_tag_indent/output.expected.png b/tests/tests/swfs/avm1/edittext_tag_indent/output.expected.png index 3fe41f5209e4..8850e003ff10 100644 Binary files a/tests/tests/swfs/avm1/edittext_tag_indent/output.expected.png and b/tests/tests/swfs/avm1/edittext_tag_indent/output.expected.png differ 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 c8d6941e5267..a3c5d76940d1 100644 Binary files a/tests/tests/swfs/avm1/edittext_tag_indent/test.swf and b/tests/tests/swfs/avm1/edittext_tag_indent/test.swf differ 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 diff --git a/tests/tests/swfs/from_shumway/flash_text_TextField2/output.ruffle.png b/tests/tests/swfs/from_shumway/flash_text_TextField2/output.ruffle.png deleted file mode 100644 index 5882d4de5c84..000000000000 Binary files a/tests/tests/swfs/from_shumway/flash_text_TextField2/output.ruffle.png and /dev/null differ 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 }