core: Add font and style structures for TextSpan

They make operating on text spans more natural, e.g. easy comparison.
This commit is contained in:
Kamil Jarosz 2024-01-26 03:02:50 +01:00 committed by Nathan Adams
parent 119a093c27
commit 5422792eb7
5 changed files with 216 additions and 208 deletions

View File

@ -194,13 +194,13 @@ impl DisplayObjectWindow {
ui.label(format.span_length.to_string());
ui.label(format.url.to_string());
ui.label(format.font.to_string());
ui.label(format.font.face.to_string());
if format.bold && format.italic {
if format.style.bold && format.style.italic {
ui.label("Bold Italic");
} else if format.bold {
} else if format.style.bold {
ui.label("Bold");
} else if format.italic {
} else if format.style.italic {
ui.label("Italic");
} else {
ui.label("Regular");

View File

@ -88,9 +88,9 @@ impl EvalParameters {
/// parameters.
pub fn from_span(span: &TextSpan) -> Self {
Self {
height: Twips::from_pixels(span.size),
letter_spacing: Twips::from_pixels(span.letter_spacing),
kerning: span.kerning,
height: Twips::from_pixels(span.font.size),
letter_spacing: Twips::from_pixels(span.font.letter_spacing),
kerning: span.font.kerning,
}
}

View File

@ -432,7 +432,7 @@ impl<'a, 'gc> LayoutContext<'a, 'gc> {
/// tab index.
fn tab(&mut self) {
if self.current_line_span.tab_stops.is_empty() {
let modulo_factor = Twips::from_pixels(self.current_line_span.size * 2.7);
let modulo_factor = Twips::from_pixels(self.current_line_span.font.size * 2.7);
let stop_modulo_tab =
((self.cursor.x().get() / modulo_factor.get()) + 1) * modulo_factor.get();
self.cursor.set_x(Twips::new(stop_modulo_tab));
@ -451,9 +451,9 @@ impl<'a, 'gc> LayoutContext<'a, 'gc> {
fn newspan(&mut self, first_span: &TextSpan) {
if self.is_start_of_line() {
self.current_line_span = first_span.clone();
self.max_font_size = Twips::from_pixels(first_span.size);
self.max_font_size = Twips::from_pixels(first_span.font.size);
} else {
self.max_font_size = max(self.max_font_size, Twips::from_pixels(first_span.size));
self.max_font_size = max(self.max_font_size, Twips::from_pixels(first_span.font.size));
}
}
@ -463,7 +463,7 @@ impl<'a, 'gc> LayoutContext<'a, 'gc> {
span: &TextSpan,
is_device_font: bool,
) -> Option<Font<'gc>> {
let font_name = span.font.to_utf8_lossy();
let font_name = span.font.face.to_utf8_lossy();
// Note that the SWF can still contain a DefineFont tag with no glyphs/layout info in this case (see #451).
// In an ideal world, device fonts would search for a matching font on the system and render it in some way.
@ -472,8 +472,8 @@ impl<'a, 'gc> LayoutContext<'a, 'gc> {
.library
.get_embedded_font_by_name(
&font_name,
span.bold,
span.italic,
span.style.bold,
span.style.italic,
Some(self.movie.clone()),
)
.filter(|f| f.has_glyphs())
@ -500,8 +500,8 @@ impl<'a, 'gc> LayoutContext<'a, 'gc> {
.library
.default_font(
default_font,
span.bold,
span.italic,
span.style.bold,
span.style.italic,
context.ui,
context.renderer,
context.gc_context,
@ -512,8 +512,8 @@ impl<'a, 'gc> LayoutContext<'a, 'gc> {
if let Some(font) = context.library.get_or_load_device_font(
&font_name,
span.bold,
span.italic,
span.style.bold,
span.style.italic,
context.ui,
context.renderer,
context.gc_context,
@ -544,8 +544,8 @@ impl<'a, 'gc> LayoutContext<'a, 'gc> {
.library
.default_font(
default_font,
span.bold,
span.italic,
span.style.bold,
span.style.italic,
context.ui,
context.renderer,
context.gc_context,
@ -794,7 +794,7 @@ impl<'gc> LayoutBox<'gc> {
text_format: span.get_text_format(),
font,
params,
color: span.color,
color: span.font.color,
},
}
}
@ -809,7 +809,7 @@ impl<'gc> LayoutBox<'gc> {
text_format: span.get_text_format(),
font,
params,
color: span.color,
color: span.font.color,
},
}
}

View File

@ -336,14 +336,15 @@ fn formatspans_set_default() {
#[test]
fn formatspans_resolve_position() {
let tf = Default::default();
let fs = FormatSpans::from_str_and_spans(
WStr::from_units(b"abcdefghi"),
&[
TextSpan::with_length_and_format(2, Default::default()),
TextSpan::with_length_and_format(3, Default::default()),
TextSpan::with_length_and_format(1, Default::default()),
TextSpan::with_length_and_format(1, Default::default()),
TextSpan::with_length_and_format(2, Default::default()),
TextSpan::with_length_and_format(2, &tf),
TextSpan::with_length_and_format(3, &tf),
TextSpan::with_length_and_format(1, &tf),
TextSpan::with_length_and_format(1, &tf),
TextSpan::with_length_and_format(2, &tf),
],
);
@ -361,14 +362,15 @@ fn formatspans_resolve_position() {
#[test]
fn formatspans_ensure_span_break() {
let tf = Default::default();
let mut fs = FormatSpans::from_str_and_spans(
WStr::from_units(b"abcdefghi"),
&[
TextSpan::with_length_and_format(2, Default::default()),
TextSpan::with_length_and_format(3, Default::default()),
TextSpan::with_length_and_format(1, Default::default()),
TextSpan::with_length_and_format(1, Default::default()),
TextSpan::with_length_and_format(2, Default::default()),
TextSpan::with_length_and_format(2, &tf),
TextSpan::with_length_and_format(3, &tf),
TextSpan::with_length_and_format(1, &tf),
TextSpan::with_length_and_format(1, &tf),
TextSpan::with_length_and_format(2, &tf),
],
);
@ -389,14 +391,15 @@ fn formatspans_ensure_span_break() {
#[test]
fn formatspans_ensure_span_break_redundant() {
let tf = &Default::default();
let mut fs = FormatSpans::from_str_and_spans(
WStr::from_units(b"abcdefghi"),
&[
TextSpan::with_length_and_format(2, Default::default()),
TextSpan::with_length_and_format(3, Default::default()),
TextSpan::with_length_and_format(1, Default::default()),
TextSpan::with_length_and_format(1, Default::default()),
TextSpan::with_length_and_format(2, Default::default()),
TextSpan::with_length_and_format(2, tf),
TextSpan::with_length_and_format(3, tf),
TextSpan::with_length_and_format(1, tf),
TextSpan::with_length_and_format(1, tf),
TextSpan::with_length_and_format(2, tf),
],
);
@ -417,14 +420,15 @@ fn formatspans_ensure_span_break_redundant() {
#[test]
fn formatspans_span_boundaries() {
let tf = &Default::default();
let fs = FormatSpans::from_str_and_spans(
WStr::from_units(b"abcdefghi"),
&[
TextSpan::with_length_and_format(2, Default::default()),
TextSpan::with_length_and_format(3, Default::default()),
TextSpan::with_length_and_format(1, Default::default()),
TextSpan::with_length_and_format(1, Default::default()),
TextSpan::with_length_and_format(2, Default::default()),
TextSpan::with_length_and_format(2, tf),
TextSpan::with_length_and_format(3, tf),
TextSpan::with_length_and_format(1, tf),
TextSpan::with_length_and_format(1, tf),
TextSpan::with_length_and_format(2, tf),
],
);
@ -453,11 +457,11 @@ fn formatspans_get_text_format() {
let fs = FormatSpans::from_str_and_spans(
WStr::from_units(b"abcdefghi"),
&[
TextSpan::with_length_and_format(2, tf1.clone()),
TextSpan::with_length_and_format(3, tf1.clone()),
TextSpan::with_length_and_format(1, tf2),
TextSpan::with_length_and_format(1, tf1.clone()),
TextSpan::with_length_and_format(2, tf1.clone()),
TextSpan::with_length_and_format(2, &tf1),
TextSpan::with_length_and_format(3, &tf1),
TextSpan::with_length_and_format(1, &tf2),
TextSpan::with_length_and_format(1, &tf1),
TextSpan::with_length_and_format(2, &tf1),
],
);
@ -510,8 +514,8 @@ fn formatspans_normalize_short_spans() {
let mut fs = FormatSpans::from_str_and_spans(
WStr::from_units(b"abcdefghi"),
&[
TextSpan::with_length_and_format(5, tf1),
TextSpan::with_length_and_format(1, tf2),
TextSpan::with_length_and_format(5, &tf1),
TextSpan::with_length_and_format(1, &tf2),
],
);
@ -539,8 +543,8 @@ fn formatspans_normalize_exact_spans() {
let mut fs = FormatSpans::from_str_and_spans(
WStr::from_units(b"abcdefghi"),
&[
TextSpan::with_length_and_format(5, tf1),
TextSpan::with_length_and_format(4, tf2),
TextSpan::with_length_and_format(5, &tf1),
TextSpan::with_length_and_format(4, &tf2),
],
);
@ -568,9 +572,9 @@ fn formatspans_normalize_long_spans() {
let mut fs = FormatSpans::from_str_and_spans(
WStr::from_units(b"abcdefghi"),
&[
TextSpan::with_length_and_format(5, tf1.clone()),
TextSpan::with_length_and_format(2000, tf2),
TextSpan::with_length_and_format(5, tf1),
TextSpan::with_length_and_format(5, &tf1),
TextSpan::with_length_and_format(2000, &tf2),
TextSpan::with_length_and_format(5, &tf1),
],
);
@ -592,8 +596,8 @@ fn formatspans_normalize_merge_spans() {
let mut fs = FormatSpans::from_str_and_spans(
WStr::from_units(b"abcdefghi"),
&[
TextSpan::with_length_and_format(5, tf1.clone()),
TextSpan::with_length_and_format(4, tf1),
TextSpan::with_length_and_format(5, &tf1),
TextSpan::with_length_and_format(4, &tf1),
],
);
@ -613,12 +617,12 @@ fn formatspans_normalize_merge_many_spans() {
let mut fs = FormatSpans::from_str_and_spans(
WStr::from_units(b"abcdefghi"),
&[
TextSpan::with_length_and_format(1, tf1.clone()),
TextSpan::with_length_and_format(1, tf1.clone()),
TextSpan::with_length_and_format(2, tf1.clone()),
TextSpan::with_length_and_format(1, tf1.clone()),
TextSpan::with_length_and_format(1, tf1.clone()),
TextSpan::with_length_and_format(3, tf1),
TextSpan::with_length_and_format(1, &tf1),
TextSpan::with_length_and_format(1, &tf1),
TextSpan::with_length_and_format(2, &tf1),
TextSpan::with_length_and_format(1, &tf1),
TextSpan::with_length_and_format(1, &tf1),
TextSpan::with_length_and_format(3, &tf1),
],
);
@ -644,9 +648,9 @@ fn formatspans_normalize_long_spans_with_merge() {
let mut fs = FormatSpans::from_str_and_spans(
WStr::from_units(b"abcdefghi"),
&[
TextSpan::with_length_and_format(5, tf1.clone()),
TextSpan::with_length_and_format(5, tf1),
TextSpan::with_length_and_format(2000, tf2),
TextSpan::with_length_and_format(5, &tf1),
TextSpan::with_length_and_format(5, &tf1),
TextSpan::with_length_and_format(2000, &tf2),
],
);
@ -671,8 +675,8 @@ fn formatspans_normalize_set_text_format_double_cut() {
let mut fs = FormatSpans::from_str_and_spans(
WStr::from_units(b"abcdefghi"),
&[
TextSpan::with_length_and_format(5, tf1),
TextSpan::with_length_and_format(4, tf2),
TextSpan::with_length_and_format(5, &tf1),
TextSpan::with_length_and_format(4, &tf2),
],
);
@ -703,8 +707,8 @@ fn formatspans_normalize_set_text_format_single_cut() {
let mut fs = FormatSpans::from_str_and_spans(
WStr::from_units(b"abcdefghi"),
&[
TextSpan::with_length_and_format(5, tf1),
TextSpan::with_length_and_format(4, tf2),
TextSpan::with_length_and_format(5, &tf1),
TextSpan::with_length_and_format(4, &tf2),
],
);
@ -735,8 +739,8 @@ fn formatspans_normalize_set_text_format_no_cut() {
let mut fs = FormatSpans::from_str_and_spans(
WStr::from_units(b"abcdefghi"),
&[
TextSpan::with_length_and_format(5, tf1),
TextSpan::with_length_and_format(4, tf2),
TextSpan::with_length_and_format(5, &tf1),
TextSpan::with_length_and_format(4, &tf2),
],
);
@ -766,8 +770,8 @@ fn formatspans_replace_text_inbounds() {
let mut fs = FormatSpans::from_str_and_spans(
WStr::from_units(b"abcdefghi"),
&[
TextSpan::with_length_and_format(5, tf1),
TextSpan::with_length_and_format(4, tf2),
TextSpan::with_length_and_format(5, &tf1),
TextSpan::with_length_and_format(4, &tf2),
],
);
@ -794,8 +798,8 @@ fn formatspans_replace_text_edgebounds() {
let mut fs = FormatSpans::from_str_and_spans(
WStr::from_units(b"abcdefghi"),
&[
TextSpan::with_length_and_format(5, tf1),
TextSpan::with_length_and_format(4, tf2),
TextSpan::with_length_and_format(5, &tf1),
TextSpan::with_length_and_format(4, &tf2),
],
);
@ -823,8 +827,8 @@ fn formatspans_replace_text_oob() {
let mut fs = FormatSpans::from_str_and_spans(
WStr::from_units(b"abcdefghi"),
&[
TextSpan::with_length_and_format(5, tf1),
TextSpan::with_length_and_format(4, tf2),
TextSpan::with_length_and_format(5, &tf1),
TextSpan::with_length_and_format(4, &tf2),
],
);
@ -852,8 +856,8 @@ fn formatspans_replace_text_degenerate() {
let mut fs = FormatSpans::from_str_and_spans(
WStr::from_units(b"abcdefghi"),
&[
TextSpan::with_length_and_format(5, tf1),
TextSpan::with_length_and_format(4, tf2),
TextSpan::with_length_and_format(5, &tf1),
TextSpan::with_length_and_format(4, &tf2),
],
);

View File

@ -329,49 +329,48 @@ pub struct TextSpan {
/// length of the underlying source string.
pub span_length: usize,
pub font: WString,
pub size: f64,
pub color: swf::Color,
pub font: TextSpanFont,
pub style: TextSpanStyle,
pub align: swf::TextAlign,
pub bold: bool,
pub italic: bool,
pub underline: bool,
pub left_margin: f64,
pub right_margin: f64,
pub indent: f64,
pub block_indent: f64,
pub kerning: bool,
pub leading: f64,
pub letter_spacing: f64,
pub tab_stops: Vec<f64>,
pub bullet: bool,
pub url: WString,
pub target: WString,
}
#[derive(Clone, Debug, PartialEq)]
pub struct TextSpanFont {
pub face: WString,
pub size: f64,
pub color: swf::Color,
pub letter_spacing: f64,
pub kerning: bool,
}
#[derive(Clone, Debug, PartialEq, Default)]
pub struct TextSpanStyle {
pub bold: bool,
pub italic: bool,
pub underline: bool,
}
impl Default for TextSpan {
fn default() -> Self {
Self {
span_length: 0,
font: WString::new(),
size: 12.0,
color: swf::Color {
r: 0,
g: 0,
b: 0,
a: 0,
},
font: TextSpanFont::default(),
style: TextSpanStyle::default(),
align: swf::TextAlign::Left,
bold: false,
italic: false,
underline: false,
left_margin: 0.0,
right_margin: 0.0,
indent: 0.0,
block_indent: 0.0,
kerning: false,
leading: 0.0,
letter_spacing: 0.0,
tab_stops: vec![],
bullet: false,
url: WString::new(),
@ -380,50 +379,27 @@ impl Default for TextSpan {
}
}
impl TextSpan {
pub fn with_length_and_format(length: usize, tf: TextFormat) -> Self {
let mut data = Self {
span_length: length,
..Default::default()
};
data.set_text_format(&tf);
data
impl Default for TextSpanFont {
fn default() -> Self {
Self {
face: WString::new(),
size: 12.0,
color: swf::Color {
r: 0,
g: 0,
b: 0,
a: 0,
},
kerning: false,
letter_spacing: 0.0,
}
}
}
/// Determine if this and another span have identical text formats.
///
/// It is assumed that the two text spans being considered are adjacent;
/// and we have no way of checking, so this function doesn't check that.
#[allow(clippy::float_cmp)]
fn can_merge(&self, rhs: &Self) -> bool {
self.font == rhs.font
&& self.size == rhs.size
&& self.color == rhs.color
&& self.align == rhs.align
&& self.bold == rhs.bold
&& self.italic == rhs.italic
&& self.underline == rhs.underline
&& self.left_margin == rhs.left_margin
&& self.right_margin == rhs.right_margin
&& self.indent == rhs.indent
&& self.block_indent == rhs.block_indent
&& self.kerning == rhs.kerning
&& self.leading == rhs.leading
&& self.letter_spacing == rhs.letter_spacing
&& self.tab_stops == rhs.tab_stops
&& self.bullet == rhs.bullet
&& self.url == rhs.url
&& self.target == rhs.target
}
/// Apply a text format to this text span.
///
/// Properties marked `None` on the `TextFormat` will remain unchanged.
impl TextSpanFont {
fn set_text_format(&mut self, tf: &TextFormat) {
if let Some(font) = &tf.font {
self.font = font.clone();
self.face = font.clone();
}
if let Some(size) = &tf.size {
@ -434,20 +410,65 @@ impl TextSpan {
self.color = color;
}
if let Some(kerning) = &tf.kerning {
self.kerning = *kerning;
}
if let Some(letter_spacing) = &tf.letter_spacing {
self.letter_spacing = *letter_spacing;
}
}
}
impl TextSpan {
pub fn with_length_and_format(length: usize, tf: &TextFormat) -> Self {
let mut data = Self {
span_length: length,
..Default::default()
};
data.set_text_format(tf);
data
}
/// Determine if this and another span have identical text formats.
///
/// It is assumed that the two text spans being considered are adjacent;
/// and we have no way of checking, so this function doesn't check that.
fn can_merge(&self, rhs: &Self) -> bool {
self.font == rhs.font
&& self.style == rhs.style
&& self.align == rhs.align
&& self.left_margin == rhs.left_margin
&& self.right_margin == rhs.right_margin
&& self.indent == rhs.indent
&& self.block_indent == rhs.block_indent
&& self.leading == rhs.leading
&& self.tab_stops == rhs.tab_stops
&& self.bullet == rhs.bullet
&& self.url == rhs.url
&& self.target == rhs.target
}
/// Apply a text format to this text span.
///
/// Properties marked `None` on the `TextFormat` will remain unchanged.
fn set_text_format(&mut self, tf: &TextFormat) {
if let Some(align) = &tf.align {
self.align = *align;
}
if let Some(bold) = &tf.bold {
self.bold = *bold;
self.style.bold = *bold;
}
if let Some(italic) = &tf.italic {
self.italic = *italic;
self.style.italic = *italic;
}
if let Some(underline) = &tf.underline {
self.underline = *underline;
self.style.underline = *underline;
}
if let Some(left_margin) = &tf.left_margin {
@ -466,18 +487,10 @@ impl TextSpan {
self.block_indent = *block_indent;
}
if let Some(kerning) = &tf.kerning {
self.kerning = *kerning;
}
if let Some(leading) = &tf.leading {
self.leading = *leading;
}
if let Some(letter_spacing) = &tf.letter_spacing {
self.letter_spacing = *letter_spacing;
}
if let Some(tab_stops) = &tf.tab_stops {
self.tab_stops = tab_stops.clone();
}
@ -493,6 +506,8 @@ impl TextSpan {
if let Some(target) = &tf.target {
self.target = target.clone();
}
self.font.set_text_format(tf);
}
/// Convert the text span into a format.
@ -500,20 +515,20 @@ impl TextSpan {
/// The text format returned will have all properties defined.
pub fn get_text_format(&self) -> TextFormat {
TextFormat {
font: Some(self.font.clone()),
size: Some(self.size),
color: Some(self.color),
font: Some(self.font.face.clone()),
size: Some(self.font.size),
color: Some(self.font.color),
align: Some(self.align),
bold: Some(self.bold),
italic: Some(self.italic),
underline: Some(self.underline),
bold: Some(self.style.bold),
italic: Some(self.style.italic),
underline: Some(self.style.underline),
left_margin: Some(self.left_margin),
right_margin: Some(self.right_margin),
indent: Some(self.indent),
block_indent: Some(self.block_indent),
kerning: Some(self.kerning),
kerning: Some(self.font.kerning),
leading: Some(self.leading),
letter_spacing: Some(self.letter_spacing),
letter_spacing: Some(self.font.letter_spacing),
tab_stops: Some(self.tab_stops.clone()),
bullet: Some(self.bullet),
url: Some(self.url.clone()),
@ -563,7 +578,7 @@ impl FormatSpans {
Self {
text,
displayed_text: WString::new(),
spans: vec![TextSpan::with_length_and_format(len, format.clone())],
spans: vec![TextSpan::with_length_and_format(len, &format)],
default_format: format,
}
}
@ -644,7 +659,7 @@ impl FormatSpans {
span.span_length += 1;
} else {
// This must be at the start; make an empty span so our total length is correct
spans.push(TextSpan::with_length_and_format(1, format));
spans.push(TextSpan::with_length_and_format(1, &format));
}
}
@ -659,7 +674,7 @@ impl FormatSpans {
span.span_length += 1;
} else {
// This must be at the start; make an empty span so our total length is correct
spans.push(TextSpan::with_length_and_format(1, format));
spans.push(TextSpan::with_length_and_format(1, &format));
}
// Skip push to `format_stack`.
@ -776,7 +791,7 @@ impl FormatSpans {
Ok(Event::Text(e)) if !e.is_empty() => {
let e = decode_to_wstr(&e.into_inner());
let e = process_html_entity(&e).unwrap_or(e);
let format = format_stack.last().unwrap().clone();
let format = format_stack.last().unwrap();
text.push_str(&e);
spans.push(TextSpan::with_length_and_format(e.len(), format));
}
@ -807,7 +822,7 @@ impl FormatSpans {
// This must be at the start; make an empty span so our total length is correct
spans.push(TextSpan::with_length_and_format(
1,
format_stack.last().unwrap().clone(),
format_stack.last().unwrap(),
));
}
}
@ -988,7 +1003,7 @@ impl FormatSpans {
match span_length.cmp(&self.text.len()) {
Ordering::Less => self.spans.push(TextSpan::with_length_and_format(
self.text.len() - span_length,
self.default_format.clone(),
&self.default_format,
)),
Ordering::Greater => {
let mut deficiency = span_length - self.text.len();
@ -1049,7 +1064,7 @@ impl FormatSpans {
if self.spans.is_empty() {
self.spans.push(TextSpan::with_length_and_format(
self.text.len(),
self.default_format.clone(),
&self.default_format,
));
}
}
@ -1129,14 +1144,12 @@ impl FormatSpans {
self.spans.drain(start_pos..end_pos);
self.spans.insert(
start_pos,
TextSpan::with_length_and_format(with.len(), new_tf),
TextSpan::with_length_and_format(with.len(), &new_tf),
);
} else {
self.spans.push(TextSpan::with_length_and_format(
with.len(),
new_tf
.cloned()
.unwrap_or_else(|| self.default_format.clone()),
new_tf.unwrap_or(&self.default_format),
));
}
@ -1269,13 +1282,13 @@ impl<'a> FormatState<'a> {
let _ = write!(
self.result,
"<FONT FACE=\"{}\" SIZE=\"{}\" COLOR=\"#{:0>2X}{:0>2X}{:0>2X}\" LETTERSPACING=\"{}\" KERNING=\"{}\">",
self.span.font,
self.span.size,
self.span.color.r,
self.span.color.g,
self.span.color.b,
self.span.letter_spacing,
if self.span.kerning { "1" } else { "0" },
self.span.font.face,
self.span.font.size,
self.span.font.color.r,
self.span.font.color.g,
self.span.font.color.b,
self.span.font.letter_spacing,
if self.span.font.kerning { "1" } else { "0" },
);
self.font_stack.push_front(self.span);
@ -1287,15 +1300,15 @@ impl<'a> FormatState<'a> {
);
}
if self.span.bold {
if self.span.style.bold {
self.result.push_str(WStr::from_units(b"<B>"));
}
if self.span.italic {
if self.span.style.italic {
self.result.push_str(WStr::from_units(b"<I>"));
}
if self.span.underline {
if self.span.style.underline {
self.result.push_str(WStr::from_units(b"<U>"));
}
@ -1307,15 +1320,15 @@ impl<'a> FormatState<'a> {
return;
}
if self.span.underline {
if self.span.style.underline {
self.result.push_str(WStr::from_units(b"</U>"));
}
if self.span.italic {
if self.span.style.italic {
self.result.push_str(WStr::from_units(b"</I>"));
}
if self.span.bold {
if self.span.style.bold {
self.result.push_str(WStr::from_units(b"</B>"));
}
@ -1347,15 +1360,15 @@ impl<'a> FormatState<'a> {
}
fn set_span(&mut self, span: &'a TextSpan) {
if !span.underline && self.span.underline {
if !span.style.underline && self.span.style.underline {
self.result.push_str(WStr::from_units(b"</U>"));
}
if !span.italic && self.span.italic {
if !span.style.italic && self.span.style.italic {
self.result.push_str(WStr::from_units(b"</I>"));
}
if !span.bold && self.span.bold {
if !span.style.bold && self.span.style.bold {
self.result.push_str(WStr::from_units(b"</B>"));
}
@ -1363,18 +1376,9 @@ impl<'a> FormatState<'a> {
self.result.push_str(WStr::from_units(b"</A>"));
}
if span.font != self.span.font
|| span.size != self.span.size
|| span.color != self.span.color
|| span.letter_spacing != self.span.letter_spacing
|| span.kerning != self.span.kerning
{
if span.font != self.span.font {
let pos = self.font_stack.iter().position(|font| {
span.font == font.font
&& span.size == font.size
&& span.color == font.color
&& span.letter_spacing == font.letter_spacing
&& span.kerning == font.kerning
});
if let Some(pos) = pos {
self.result
@ -1382,27 +1386,27 @@ impl<'a> FormatState<'a> {
self.font_stack.drain(0..pos);
} else {
self.result.push_str(WStr::from_units(b"<FONT"));
if span.font != self.span.font {
let _ = write!(self.result, " FACE=\"{}\"", span.font);
if span.font.face != self.span.font.face {
let _ = write!(self.result, " FACE=\"{}\"", span.font.face);
}
if span.size != self.span.size {
let _ = write!(self.result, " SIZE=\"{}\"", span.size);
if span.font.size != self.span.font.size {
let _ = write!(self.result, " SIZE=\"{}\"", span.font.size);
}
if span.color != self.span.color {
if span.font.color != self.span.font.color {
let _ = write!(
self.result,
" COLOR=\"#{:0>2X}{:0>2X}{:0>2X}\"",
span.color.r, span.color.g, span.color.b
span.font.color.r, span.font.color.g, span.font.color.b
);
}
if span.letter_spacing != self.span.letter_spacing {
let _ = write!(self.result, " LETTERSPACING=\"{}\"", span.letter_spacing);
if span.font.letter_spacing != self.span.font.letter_spacing {
let _ = write!(self.result, " LETTERSPACING=\"{}\"", span.font.letter_spacing);
}
if span.kerning != self.span.kerning {
if span.font.kerning != self.span.font.kerning {
let _ = write!(
self.result,
" KERNING=\"{}\"",
if span.kerning { "1" } else { "0" }
if span.font.kerning { "1" } else { "0" }
);
}
self.result.push_byte(b'>');
@ -1418,15 +1422,15 @@ impl<'a> FormatState<'a> {
);
}
if span.bold && !self.span.bold {
if span.style.bold && !self.span.style.bold {
self.result.push_str(WStr::from_units(b"<B>"));
}
if span.italic && !self.span.italic {
if span.style.italic && !self.span.style.italic {
self.result.push_str(WStr::from_units(b"<I>"));
}
if span.underline && !self.span.underline {
if span.style.underline && !self.span.style.underline {
self.result.push_str(WStr::from_units(b"<U>"));
}