Collect font height, letter spacing and the kerning flag into a single `EvalParameters` structure.
This commit is contained in:
parent
1f6d6018dc
commit
d8a38d06bb
|
@ -443,16 +443,12 @@ impl<'gc> EditText<'gc> {
|
|||
// We're cheating a bit and not actually rendering text using the OS/web.
|
||||
// Instead, we embed an SWF version of Noto Sans to use as the "device font", and render
|
||||
// it the same as any other SWF outline text.
|
||||
if let Some((start, end, _tf, font, font_size, letter_spacing, kerning, color)) =
|
||||
lbox.read().text_node()
|
||||
{
|
||||
if let Some((start, end, _tf, font, params, color)) = lbox.read().text_node() {
|
||||
if let Some(chunk) = edit_text.text_spans.text().get(start..end) {
|
||||
font.evaluate(
|
||||
&chunk,
|
||||
self.text_transform(color),
|
||||
font_size,
|
||||
letter_spacing,
|
||||
kerning,
|
||||
params,
|
||||
|transform, glyph: &Glyph, _advance| {
|
||||
// Render glyph.
|
||||
context.transform_stack.push(transform);
|
||||
|
|
138
core/src/font.rs
138
core/src/font.rs
|
@ -1,4 +1,5 @@
|
|||
use crate::backend::render::{RenderBackend, ShapeHandle};
|
||||
use crate::html::TextSpan;
|
||||
use crate::prelude::*;
|
||||
use crate::transform::Transform;
|
||||
use gc_arena::{Collect, Gc, MutationContext};
|
||||
|
@ -10,6 +11,47 @@ pub fn round_down_to_pixel(t: Twips) -> Twips {
|
|||
|
||||
type Error = Box<dyn std::error::Error>;
|
||||
|
||||
/// Parameters necessary to evaluate a font.
|
||||
#[derive(Copy, Clone, Debug, Collect)]
|
||||
#[collect(require_static)]
|
||||
pub struct EvalParameters {
|
||||
/// The height of each glyph, equivalent to a font size.
|
||||
height: Twips,
|
||||
|
||||
/// Additional letter spacing to be added to or removed from each glyph
|
||||
/// after normal or kerned glyph advances are applied.
|
||||
letter_spacing: Twips,
|
||||
|
||||
/// Whether or not to allow use of font-provided kerning metrics.
|
||||
///
|
||||
/// Fonts can optionally add or remove additional spacing between specific
|
||||
/// pairs of letters, separate from the ordinary width between glyphs. This
|
||||
/// parameter allows enabling or disabling that feature.
|
||||
kerning: bool,
|
||||
}
|
||||
|
||||
impl EvalParameters {
|
||||
/// Construct eval parameters from their individual parts.
|
||||
#[allow(dead_code)]
|
||||
fn from_parts(height: Twips, letter_spacing: Twips, kerning: bool) -> Self {
|
||||
Self {
|
||||
height,
|
||||
letter_spacing,
|
||||
kerning,
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the formatting on a text span over to font evaluation
|
||||
/// 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Collect, Copy)]
|
||||
#[collect(no_drop)]
|
||||
pub struct Font<'gc>(Gc<'gc, FontData>);
|
||||
|
@ -167,15 +209,13 @@ impl<'gc> Font<'gc> {
|
|||
self,
|
||||
text: &str,
|
||||
mut transform: Transform,
|
||||
height: Twips,
|
||||
letter_spacing: Twips,
|
||||
kerning: bool,
|
||||
params: EvalParameters,
|
||||
mut glyph_func: FGlyph,
|
||||
) where
|
||||
FGlyph: FnMut(&Transform, &Glyph, Twips),
|
||||
{
|
||||
transform.matrix.ty += height;
|
||||
let scale = height.get() as f32 / self.scale();
|
||||
transform.matrix.ty += params.height;
|
||||
let scale = params.height.get() as f32 / self.scale();
|
||||
|
||||
transform.matrix.a = scale;
|
||||
transform.matrix.d = scale;
|
||||
|
@ -184,11 +224,11 @@ impl<'gc> Font<'gc> {
|
|||
while let Some(c) = chars.next() {
|
||||
if let Some(glyph) = self.get_glyph_for_char(c) {
|
||||
let mut advance = Twips::new(glyph.advance);
|
||||
if has_kerning_info && kerning {
|
||||
if has_kerning_info && params.kerning {
|
||||
advance += self.get_kerning_offset(c, chars.peek().cloned().unwrap_or('\0'));
|
||||
}
|
||||
let twips_advance =
|
||||
Twips::new((advance.get() as f32 * scale) as i32) + letter_spacing;
|
||||
Twips::new((advance.get() as f32 * scale) as i32) + params.letter_spacing;
|
||||
|
||||
glyph_func(&transform, &glyph, twips_advance);
|
||||
|
||||
|
@ -202,22 +242,13 @@ impl<'gc> Font<'gc> {
|
|||
///
|
||||
/// The `round` flag causes the returned coordinates to be rounded down to
|
||||
/// the nearest pixel.
|
||||
pub fn measure(
|
||||
self,
|
||||
text: &str,
|
||||
font_size: Twips,
|
||||
letter_spacing: Twips,
|
||||
kerning: bool,
|
||||
round: bool,
|
||||
) -> (Twips, Twips) {
|
||||
pub fn measure(self, text: &str, params: EvalParameters, round: bool) -> (Twips, Twips) {
|
||||
let mut size = (Twips::new(0), Twips::new(0));
|
||||
|
||||
self.evaluate(
|
||||
text,
|
||||
Default::default(),
|
||||
font_size,
|
||||
letter_spacing,
|
||||
kerning,
|
||||
params,
|
||||
|transform, _glyph, advance| {
|
||||
let tx = transform.matrix.tx;
|
||||
let ty = transform.matrix.ty;
|
||||
|
@ -254,9 +285,7 @@ impl<'gc> Font<'gc> {
|
|||
pub fn wrap_line(
|
||||
self,
|
||||
text: &str,
|
||||
font_size: Twips,
|
||||
letter_spacing: Twips,
|
||||
kerning: bool,
|
||||
params: EvalParameters,
|
||||
width: Twips,
|
||||
offset: Twips,
|
||||
mut is_start_of_line: bool,
|
||||
|
@ -276,9 +305,7 @@ impl<'gc> Font<'gc> {
|
|||
|
||||
let measure = self.measure(
|
||||
text.get(word_start..word_end + 1).unwrap_or(word),
|
||||
font_size,
|
||||
letter_spacing,
|
||||
kerning,
|
||||
params,
|
||||
true,
|
||||
);
|
||||
|
||||
|
@ -288,13 +315,8 @@ impl<'gc> Font<'gc> {
|
|||
let mut frag_end = word_start;
|
||||
while last_passing_breakpoint.0 < remaining_width {
|
||||
frag_end += 1;
|
||||
last_passing_breakpoint = self.measure(
|
||||
text.get(word_start..frag_end).unwrap(),
|
||||
font_size,
|
||||
letter_spacing,
|
||||
kerning,
|
||||
true,
|
||||
);
|
||||
last_passing_breakpoint =
|
||||
self.measure(text.get(word_start..frag_end).unwrap(), params, true);
|
||||
}
|
||||
|
||||
return Some(frag_end - 1);
|
||||
|
@ -377,7 +399,7 @@ impl FontDescriptor {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::backend::render::{NullRenderer, RenderBackend};
|
||||
use crate::font::Font;
|
||||
use crate::font::{EvalParameters, Font};
|
||||
use crate::player::{Player, DEVICE_FONT_TAG};
|
||||
use gc_arena::{rootless_arena, MutationContext};
|
||||
use swf::Twips;
|
||||
|
@ -397,12 +419,12 @@ mod tests {
|
|||
#[test]
|
||||
fn wrap_line_no_breakpoint() {
|
||||
with_device_font(|_mc, df| {
|
||||
let params =
|
||||
EvalParameters::from_parts(Twips::from_pixels(12.0), Twips::from_pixels(0.0), true);
|
||||
let string = "abcdefghijklmnopqrstuv";
|
||||
let breakpoint = df.wrap_line(
|
||||
&string,
|
||||
Twips::from_pixels(12.0),
|
||||
Twips::from_pixels(0.0),
|
||||
true,
|
||||
params,
|
||||
Twips::from_pixels(200.0),
|
||||
Twips::from_pixels(0.0),
|
||||
true,
|
||||
|
@ -415,13 +437,13 @@ mod tests {
|
|||
#[test]
|
||||
fn wrap_line_breakpoint_every_word() {
|
||||
with_device_font(|_mc, df| {
|
||||
let params =
|
||||
EvalParameters::from_parts(Twips::from_pixels(12.0), Twips::from_pixels(0.0), true);
|
||||
let string = "abcd efgh ijkl mnop";
|
||||
let mut last_bp = 0;
|
||||
let breakpoint = df.wrap_line(
|
||||
&string,
|
||||
Twips::from_pixels(12.0),
|
||||
Twips::from_pixels(0.0),
|
||||
true,
|
||||
params,
|
||||
Twips::from_pixels(35.0),
|
||||
Twips::from_pixels(0.0),
|
||||
true,
|
||||
|
@ -433,9 +455,7 @@ mod tests {
|
|||
|
||||
let breakpoint2 = df.wrap_line(
|
||||
&string[last_bp..],
|
||||
Twips::from_pixels(12.0),
|
||||
Twips::from_pixels(0.0),
|
||||
true,
|
||||
params,
|
||||
Twips::from_pixels(35.0),
|
||||
Twips::from_pixels(0.0),
|
||||
true,
|
||||
|
@ -447,9 +467,7 @@ mod tests {
|
|||
|
||||
let breakpoint3 = df.wrap_line(
|
||||
&string[last_bp..],
|
||||
Twips::from_pixels(12.0),
|
||||
Twips::from_pixels(0.0),
|
||||
true,
|
||||
params,
|
||||
Twips::from_pixels(35.0),
|
||||
Twips::from_pixels(0.0),
|
||||
true,
|
||||
|
@ -461,9 +479,7 @@ mod tests {
|
|||
|
||||
let breakpoint4 = df.wrap_line(
|
||||
&string[last_bp..],
|
||||
Twips::from_pixels(12.0),
|
||||
Twips::from_pixels(0.0),
|
||||
true,
|
||||
params,
|
||||
Twips::from_pixels(35.0),
|
||||
Twips::from_pixels(0.0),
|
||||
true,
|
||||
|
@ -476,12 +492,12 @@ mod tests {
|
|||
#[test]
|
||||
fn wrap_line_breakpoint_no_room() {
|
||||
with_device_font(|_mc, df| {
|
||||
let params =
|
||||
EvalParameters::from_parts(Twips::from_pixels(12.0), Twips::from_pixels(0.0), true);
|
||||
let string = "abcd efgh ijkl mnop";
|
||||
let breakpoint = df.wrap_line(
|
||||
&string,
|
||||
Twips::from_pixels(12.0),
|
||||
Twips::from_pixels(0.0),
|
||||
true,
|
||||
params,
|
||||
Twips::from_pixels(30.0),
|
||||
Twips::from_pixels(29.0),
|
||||
false,
|
||||
|
@ -494,13 +510,13 @@ mod tests {
|
|||
#[test]
|
||||
fn wrap_line_breakpoint_irregular_sized_words() {
|
||||
with_device_font(|_mc, df| {
|
||||
let params =
|
||||
EvalParameters::from_parts(Twips::from_pixels(12.0), Twips::from_pixels(0.0), true);
|
||||
let string = "abcdi j kl mnop q rstuv";
|
||||
let mut last_bp = 0;
|
||||
let breakpoint = df.wrap_line(
|
||||
&string,
|
||||
Twips::from_pixels(12.0),
|
||||
Twips::from_pixels(0.0),
|
||||
true,
|
||||
params,
|
||||
Twips::from_pixels(37.0),
|
||||
Twips::from_pixels(0.0),
|
||||
true,
|
||||
|
@ -512,9 +528,7 @@ mod tests {
|
|||
|
||||
let breakpoint2 = df.wrap_line(
|
||||
&string[last_bp..],
|
||||
Twips::from_pixels(12.0),
|
||||
Twips::from_pixels(0.0),
|
||||
true,
|
||||
params,
|
||||
Twips::from_pixels(37.0),
|
||||
Twips::from_pixels(0.0),
|
||||
true,
|
||||
|
@ -526,9 +540,7 @@ mod tests {
|
|||
|
||||
let breakpoint3 = df.wrap_line(
|
||||
&string[last_bp..],
|
||||
Twips::from_pixels(12.0),
|
||||
Twips::from_pixels(0.0),
|
||||
true,
|
||||
params,
|
||||
Twips::from_pixels(37.0),
|
||||
Twips::from_pixels(0.0),
|
||||
true,
|
||||
|
@ -540,9 +552,7 @@ mod tests {
|
|||
|
||||
let breakpoint4 = df.wrap_line(
|
||||
&string[last_bp..],
|
||||
Twips::from_pixels(12.0),
|
||||
Twips::from_pixels(0.0),
|
||||
true,
|
||||
params,
|
||||
Twips::from_pixels(37.0),
|
||||
Twips::from_pixels(0.0),
|
||||
true,
|
||||
|
@ -554,9 +564,7 @@ mod tests {
|
|||
|
||||
let breakpoint5 = df.wrap_line(
|
||||
&string[last_bp..],
|
||||
Twips::from_pixels(12.0),
|
||||
Twips::from_pixels(0.0),
|
||||
true,
|
||||
params,
|
||||
Twips::from_pixels(37.0),
|
||||
Twips::from_pixels(0.0),
|
||||
true,
|
||||
|
|
|
@ -9,7 +9,7 @@ pub use dimensions::BoxBounds;
|
|||
pub use dimensions::Position;
|
||||
pub use dimensions::Size;
|
||||
pub use layout::LayoutBox;
|
||||
pub use text_format::{FormatSpans, TextFormat};
|
||||
pub use text_format::{FormatSpans, TextFormat, TextSpan};
|
||||
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//! Layout box structure
|
||||
|
||||
use crate::context::UpdateContext;
|
||||
use crate::font::Font;
|
||||
use crate::font::{EvalParameters, Font};
|
||||
use crate::html::dimensions::{BoxBounds, Position, Size};
|
||||
use crate::html::text_format::{FormatSpans, TextFormat, TextSpan};
|
||||
use crate::tag_utils::SwfMovie;
|
||||
|
@ -124,16 +124,13 @@ impl<'a, 'gc> LayoutContext<'a, 'gc> {
|
|||
while let Some(linebox) = line {
|
||||
let mut write = linebox.write(mc);
|
||||
line = write.next_sibling();
|
||||
let (start, end, _tf, font, size, letter_spacing, kerning, _color) =
|
||||
write.text_node().expect("text");
|
||||
let (start, end, _tf, font, params, _color) = write.text_node().expect("text");
|
||||
|
||||
//Flash ignores trailing spaces when aligning lines, so should we
|
||||
if self.current_line_span.align != swf::TextAlign::Left {
|
||||
write.bounds = write.bounds.with_size(Size::from(font.measure(
|
||||
self.text[start..end].trim_end(),
|
||||
size,
|
||||
letter_spacing,
|
||||
kerning,
|
||||
params,
|
||||
false,
|
||||
)));
|
||||
}
|
||||
|
@ -342,15 +339,8 @@ impl<'a, 'gc> LayoutContext<'a, 'gc> {
|
|||
end: usize,
|
||||
span: &TextSpan,
|
||||
) {
|
||||
let font_size = Twips::from_pixels(span.size);
|
||||
let letter_spacing = Twips::from_pixels(span.letter_spacing);
|
||||
let text_size = Size::from(self.font.unwrap().measure(
|
||||
text,
|
||||
font_size,
|
||||
letter_spacing,
|
||||
span.kerning,
|
||||
false,
|
||||
));
|
||||
let params = EvalParameters::from_span(span);
|
||||
let text_size = Size::from(self.font.unwrap().measure(text, params, false));
|
||||
let text_bounds = BoxBounds::from_position_and_size(self.cursor, text_size);
|
||||
let new_text = LayoutBox::from_text(mc, start, end, self.font.unwrap(), span);
|
||||
let mut write = new_text.write(mc);
|
||||
|
@ -480,9 +470,7 @@ pub enum LayoutContent<'gc> {
|
|||
end: usize,
|
||||
text_format: TextFormat,
|
||||
font: Font<'gc>,
|
||||
font_size: Collec<Twips>,
|
||||
letter_spacing: Collec<Twips>,
|
||||
kerning: bool,
|
||||
params: EvalParameters,
|
||||
color: Collec<swf::Color>,
|
||||
},
|
||||
}
|
||||
|
@ -496,8 +484,7 @@ impl<'gc> LayoutBox<'gc> {
|
|||
font: Font<'gc>,
|
||||
span: &TextSpan,
|
||||
) -> GcCell<'gc, Self> {
|
||||
let font_size = Twips::from_pixels(span.size);
|
||||
let letter_spacing = Twips::from_pixels(span.letter_spacing);
|
||||
let params = EvalParameters::from_span(span);
|
||||
|
||||
GcCell::allocate(
|
||||
mc,
|
||||
|
@ -509,9 +496,7 @@ impl<'gc> LayoutBox<'gc> {
|
|||
end,
|
||||
text_format: span.get_text_format(),
|
||||
font,
|
||||
font_size: Collec(font_size),
|
||||
letter_spacing: Collec(letter_spacing),
|
||||
kerning: span.kerning,
|
||||
params,
|
||||
color: Collec(span.color.clone()),
|
||||
},
|
||||
},
|
||||
|
@ -540,8 +525,7 @@ impl<'gc> LayoutBox<'gc> {
|
|||
if let Some(font) = layout_context.resolve_font(context, movie.clone(), &span) {
|
||||
layout_context.newspan(span);
|
||||
|
||||
let font_size = Twips::from_pixels(span.size);
|
||||
let letter_spacing = Twips::from_pixels(span.letter_spacing);
|
||||
let params = EvalParameters::from_span(span);
|
||||
|
||||
for text in span_text.split(&['\n', '\t'][..]) {
|
||||
let slice_start = text.as_ptr() as usize - span_text.as_ptr() as usize;
|
||||
|
@ -568,9 +552,7 @@ impl<'gc> LayoutBox<'gc> {
|
|||
|
||||
while let Some(breakpoint) = font.wrap_line(
|
||||
&text[last_breakpoint..],
|
||||
font_size,
|
||||
letter_spacing,
|
||||
span.kerning,
|
||||
params,
|
||||
width,
|
||||
offset,
|
||||
layout_context.is_start_of_line(),
|
||||
|
@ -645,9 +627,7 @@ impl<'gc> LayoutBox<'gc> {
|
|||
usize,
|
||||
&TextFormat,
|
||||
Font<'gc>,
|
||||
Twips,
|
||||
Twips,
|
||||
bool,
|
||||
EvalParameters,
|
||||
swf::Color,
|
||||
)> {
|
||||
match &self.content {
|
||||
|
@ -656,20 +636,9 @@ impl<'gc> LayoutBox<'gc> {
|
|||
end,
|
||||
text_format,
|
||||
font,
|
||||
font_size,
|
||||
letter_spacing,
|
||||
kerning,
|
||||
params,
|
||||
color,
|
||||
} => Some((
|
||||
*start,
|
||||
*end,
|
||||
&text_format,
|
||||
*font,
|
||||
font_size.0,
|
||||
letter_spacing.0,
|
||||
*kerning,
|
||||
color.0.clone(),
|
||||
)),
|
||||
} => Some((*start, *end, &text_format, *font, *params, color.0.clone())),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue