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:
parent
119a093c27
commit
5422792eb7
|
@ -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");
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
],
|
||||
);
|
||||
|
||||
|
|
|
@ -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>"));
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue