Implement the `leading` attribute as defined by fonts.
`EditText` supports two different forms of leading: 1. Font-provided leading, specified relative to the EM square and scaled with font size 2. User-specificed leading, specified in pixels Notably, the former appears to apply to the first line in the text and pushes it down. This showed up in the `edittext_font_size` test, and according to that test result the leading is rounded *up* to the nearest pixel, plus one. That last bit seems possibly wrong and is subject to further change, but it matches the tests at multiple scales.
This commit is contained in:
parent
6e81f30a70
commit
06dc2f5fe0
|
@ -3,11 +3,16 @@ use crate::prelude::*;
|
|||
use crate::transform::Transform;
|
||||
use gc_arena::{Collect, Gc, MutationContext};
|
||||
|
||||
/// Certain Flash routines measure text up to the nearest whole pixel.
|
||||
fn round_to_pixel(t: Twips) -> Twips {
|
||||
/// Certain Flash routines measure text by rounding down to the nearest whole pixel.
|
||||
fn round_down_to_pixel(t: Twips) -> Twips {
|
||||
Twips::from_pixels(t.to_pixels().floor())
|
||||
}
|
||||
|
||||
/// Certain Flash routines measure text by rounding up to the nearest whole pixel.
|
||||
pub fn round_up_to_pixel(t: Twips) -> Twips {
|
||||
Twips::from_pixels(t.to_pixels().ceil())
|
||||
}
|
||||
|
||||
type Error = Box<dyn std::error::Error>;
|
||||
|
||||
#[derive(Debug, Clone, Collect, Copy)]
|
||||
|
@ -33,6 +38,18 @@ struct FontData {
|
|||
/// Maps from a pair of unicode code points to horizontal offset value.
|
||||
kerning_pairs: fnv::FnvHashMap<(u16, u16), Twips>,
|
||||
|
||||
/// The distance from the top of each glyph to the baseline of the font, in
|
||||
/// EM-square coordinates.
|
||||
ascent: u16,
|
||||
|
||||
/// The distance from the baseline of the font to the bottom of each glyph,
|
||||
/// in EM-square coordinates.
|
||||
descent: u16,
|
||||
|
||||
/// The distance between the bottom of any one glyph and the top of
|
||||
/// another, in EM-square coordinates.
|
||||
leading: i16,
|
||||
|
||||
/// The identity of the font.
|
||||
descriptor: FontDescriptor,
|
||||
}
|
||||
|
@ -65,6 +82,11 @@ impl<'gc> Font<'gc> {
|
|||
};
|
||||
|
||||
let descriptor = FontDescriptor::from_swf_tag(tag);
|
||||
let (ascent, descent, leading) = if let Some(layout) = &tag.layout {
|
||||
(layout.ascent, layout.descent, layout.leading)
|
||||
} else {
|
||||
(0, 0, 0)
|
||||
};
|
||||
|
||||
Ok(Font(Gc::allocate(
|
||||
gc_context,
|
||||
|
@ -76,6 +98,9 @@ impl<'gc> Font<'gc> {
|
|||
/// (SWF19 p.164)
|
||||
scale: if tag.version >= 3 { 20480.0 } else { 1024.0 },
|
||||
kerning_pairs,
|
||||
ascent,
|
||||
descent,
|
||||
leading,
|
||||
descriptor,
|
||||
},
|
||||
)))
|
||||
|
@ -119,6 +144,13 @@ impl<'gc> Font<'gc> {
|
|||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Return the leading for this font at a given height.
|
||||
pub fn get_leading_for_height(self, height: Twips) -> Twips {
|
||||
let scale = height.get() as f32 / self.scale();
|
||||
|
||||
Twips::new((self.0.leading as f32 * scale) as i32)
|
||||
}
|
||||
|
||||
/// Returns whether this font contains kerning information.
|
||||
pub fn has_kerning_info(self) -> bool {
|
||||
!self.0.kerning_pairs.is_empty()
|
||||
|
@ -179,8 +211,8 @@ impl<'gc> Font<'gc> {
|
|||
|transform, _glyph, advance| {
|
||||
let tx = transform.matrix.tx;
|
||||
let ty = transform.matrix.ty;
|
||||
size.0 = std::cmp::max(size.0, round_to_pixel(tx + advance));
|
||||
size.1 = std::cmp::max(size.1, round_to_pixel(ty));
|
||||
size.0 = std::cmp::max(size.0, round_down_to_pixel(tx + advance));
|
||||
size.1 = std::cmp::max(size.1, round_down_to_pixel(ty));
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//! Layout box structure
|
||||
|
||||
use crate::context::UpdateContext;
|
||||
use crate::font::Font;
|
||||
use crate::font::{round_up_to_pixel, Font};
|
||||
use crate::html::dimensions::{BoxBounds, Position, Size};
|
||||
use crate::html::text_format::{FormatSpans, TextFormat, TextSpan};
|
||||
use crate::tag_utils::SwfMovie;
|
||||
|
@ -117,6 +117,14 @@ impl<'a, 'gc> LayoutContext<'a, 'gc> {
|
|||
Twips::from_pixels(0.0),
|
||||
);
|
||||
|
||||
// Flash appears to round up the font's leading to the nearest pixel
|
||||
// and adds one. I'm not sure why.
|
||||
let font_leading_adjustment = round_up_to_pixel(
|
||||
self.font
|
||||
.map(|f| f.get_leading_for_height(self.max_font_size))
|
||||
.unwrap_or_else(|| Twips::new(0)),
|
||||
) + Twips::from_pixels(1.0);
|
||||
|
||||
line = self.current_line;
|
||||
while let Some(linebox) = line {
|
||||
let mut write = linebox.write(mc);
|
||||
|
@ -126,8 +134,10 @@ impl<'a, 'gc> LayoutContext<'a, 'gc> {
|
|||
// which is information we don't have yet.
|
||||
let font_size_adjustment = self.max_font_size - write.bounds.height();
|
||||
|
||||
write.bounds +=
|
||||
Position::from((left_adjustment + align_adjustment, font_size_adjustment));
|
||||
write.bounds += Position::from((
|
||||
left_adjustment + align_adjustment,
|
||||
font_size_adjustment + font_leading_adjustment,
|
||||
));
|
||||
line = write.next_sibling();
|
||||
}
|
||||
|
||||
|
|
|
@ -197,6 +197,7 @@ swf_tests! {
|
|||
(as1_constructor_v6, "avm1/as1_constructor_v6", 1),
|
||||
(as1_constructor_v7, "avm1/as1_constructor_v7", 1),
|
||||
(issue_710, "avm1/issue_710", 1),
|
||||
(edittext_font_size, "avm1/edittext_font_size", 1),
|
||||
}
|
||||
|
||||
// TODO: These tests have some inaccuracies currently, so we use approx_eq to test that numeric values are close enough.
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
//Creating left aligned text @ 10px
|
||||
W: 7
|
||||
H: 15
|
||||
//Creating left aligned text @ 12px
|
||||
W: 9
|
||||
H: 18
|
||||
//Creating left aligned text @ 20px
|
||||
W: 15
|
||||
H: 29
|
||||
//Creating left aligned text @ 30px
|
||||
W: 23
|
||||
H: 42
|
||||
//Creating left aligned text @ 100px
|
||||
W: 78
|
||||
H: 138
|
||||
//Creating left aligned text 'Qe' @ 10px
|
||||
W: 13
|
||||
H: 15
|
||||
//Creating left aligned text 'Qe' @ 12px
|
||||
W: 16
|
||||
H: 18
|
||||
//Creating left aligned text 'Qe' @ 20px
|
||||
W: 26
|
||||
H: 29
|
||||
//Creating left aligned text 'Qe' @ 30px
|
||||
W: 40
|
||||
H: 42
|
||||
//Creating left aligned text 'Qe' @ 100px
|
||||
W: 134
|
||||
H: 138
|
||||
//Creating left aligned text 'Q e' @ 10px
|
||||
W: 15
|
||||
H: 15
|
||||
//Creating left aligned text 'Q e' @ 12px
|
||||
W: 19
|
||||
H: 18
|
||||
//Creating left aligned text 'Q e' @ 20px
|
||||
W: 32
|
||||
H: 29
|
||||
//Creating left aligned text 'Q e' @ 30px
|
||||
W: 48
|
||||
H: 42
|
||||
//Creating left aligned text 'Q e' @ 100px
|
||||
W: 160
|
||||
H: 138
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue