From 1efa29baa687baf4be38d07765eb1ef7d194ce13 Mon Sep 17 00:00:00 2001 From: David Wendt Date: Sat, 15 Oct 2022 14:44:06 -0400 Subject: [PATCH] core: Add methods for introspecting text metrics on an `EditText`. --- core/src/avm2/globals.rs | 3 ++ core/src/display_object/edit_text.rs | 74 +++++++++++++++++++++++++++- core/src/font.rs | 8 ++- core/src/html.rs | 2 +- core/src/html/layout.rs | 13 ++++- 5 files changed, 96 insertions(+), 4 deletions(-) diff --git a/core/src/avm2/globals.rs b/core/src/avm2/globals.rs index 27d2310c4..35b8d9f30 100644 --- a/core/src/avm2/globals.rs +++ b/core/src/avm2/globals.rs @@ -112,6 +112,7 @@ pub struct SystemClasses<'gc> { pub ioerror: ClassObject<'gc>, pub eoferror: ClassObject<'gc>, pub uncaughterrorevents: ClassObject<'gc>, + pub textlinemetrics: ClassObject<'gc>, } impl<'gc> SystemClasses<'gc> { @@ -191,6 +192,7 @@ impl<'gc> SystemClasses<'gc> { ioerror: object, eoferror: object, uncaughterrorevents: object, + textlinemetrics: object, } } } @@ -730,6 +732,7 @@ fn load_playerglobal<'gc>( ("flash.geom", "Transform", transform), ("flash.geom", "ColorTransform", colortransform), ("flash.utils", "ByteArray", bytearray), + ("flash.text", "TextLineMetrics", textlinemetrics), ] ); diff --git a/core/src/display_object/edit_text.rs b/core/src/display_object/edit_text.rs index 99e2231e3..570de8c50 100644 --- a/core/src/display_object/edit_text.rs +++ b/core/src/display_object/edit_text.rs @@ -19,7 +19,7 @@ use crate::display_object::{DisplayObjectBase, DisplayObjectPtr, TDisplayObject} use crate::drawing::Drawing; use crate::events::{ButtonKeyCode, ClipEvent, ClipEventResult, KeyCode}; use crate::font::{round_down_to_pixel, Glyph, TextRenderSettings}; -use crate::html::{BoxBounds, FormatSpans, LayoutBox, LayoutContent, TextFormat}; +use crate::html::{BoxBounds, FormatSpans, LayoutBox, LayoutContent, LayoutMetrics, TextFormat}; use crate::prelude::*; use crate::string::{utils as string_utils, AvmString, WStr, WString}; use crate::tag_utils::SwfMovie; @@ -1383,6 +1383,78 @@ impl<'gc> EditText<'gc> { ), } } + + /// Count the number of lines in the text box's layout. + pub fn layout_lines(self) -> usize { + self.0.read().line_data.len() + } + + /// Calculate the layout metrics for a given line. + /// + /// Returns None if the line does not exist or there is not enough data + /// about the line to calculate metrics with. + pub fn layout_metrics(self, line: usize) -> Option { + let line = self.0.read().line_data.get(line).copied()?; + let mut union_bounds = None; + let mut font = None; + let mut text_format = None; + + let read = self.0.read(); + + for layout_box in read.layout.iter() { + if layout_box.bounds().offset_y() < line.offset + || layout_box.bounds().extent_y() > line.extent + { + continue; + } + + log::error!("{:?}", layout_box); + + if let Some(bounds) = &mut union_bounds { + *bounds += layout_box.bounds(); + } else { + union_bounds = Some(layout_box.bounds()); + } + + if font.is_none() { + match layout_box.content() { + LayoutContent::Text { + font: box_font, + text_format: box_text_format, + .. + } => { + font = Some(box_font); + text_format = Some(box_text_format); + } + LayoutContent::Bullet { + font: box_font, + text_format: box_text_format, + .. + } => { + font = Some(box_font); + text_format = Some(box_text_format); + } + LayoutContent::Drawing { .. } => {} + } + } + } + + let union_bounds = union_bounds?; + let font = font?; + let size = Twips::from_pixels(text_format?.size?); + let ascent = font.get_baseline_for_height(size); + let descent = font.get_descent_for_height(size); + let leading = font.get_leading_for_height(size); + + Some(LayoutMetrics { + ascent, + descent, + leading, + width: union_bounds.width(), + height: ascent + descent + leading, + x: union_bounds.offset_x() + Twips::from_pixels(EditText::INTERNAL_PADDING), + }) + } } impl<'gc> TDisplayObject<'gc> for EditText<'gc> { diff --git a/core/src/font.rs b/core/src/font.rs index 42c8ddd10..680d91397 100644 --- a/core/src/font.rs +++ b/core/src/font.rs @@ -88,7 +88,6 @@ struct FontData { /// The distance from the baseline of the font to the bottom of each glyph, /// in EM-square coordinates. - #[allow(dead_code)] descent: u16, /// The distance between the bottom of any one glyph and the top of @@ -227,6 +226,13 @@ impl<'gc> Font<'gc> { Twips::new((self.0.ascent as f32 * scale) as i32) } + /// Get the descent from the baseline to the bottom of the glyph at a given height. + pub fn get_descent_for_height(&self, height: Twips) -> Twips { + let scale = height.get() as f32 / self.scale(); + + Twips::new((self.0.descent 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() diff --git a/core/src/html.rs b/core/src/html.rs index 0e4dfe648..0336fc094 100644 --- a/core/src/html.rs +++ b/core/src/html.rs @@ -8,7 +8,7 @@ mod text_format; pub use dimensions::BoxBounds; pub use dimensions::Position; pub use dimensions::Size; -pub use layout::{LayoutBox, LayoutContent}; +pub use layout::{LayoutBox, LayoutContent, LayoutMetrics}; pub use text_format::{FormatSpans, TextFormat, TextSpan}; #[cfg(test)] diff --git a/core/src/html/layout.rs b/core/src/html/layout.rs index 6dbc3802d..931407d2b 100644 --- a/core/src/html/layout.rs +++ b/core/src/html/layout.rs @@ -555,7 +555,7 @@ impl<'a, 'gc> LayoutContext<'a, 'gc> { #[derive(Clone, Debug, Collect)] #[collect(no_drop)] pub struct LayoutBox<'gc> { - /// The rectangle corresponding to the outer boundaries of the + /// The rectangle corresponding to the outer boundaries of the content box. bounds: BoxBounds, /// What content is contained by the content box. @@ -841,3 +841,14 @@ impl<'gc> LayoutBox<'gc> { matches!(&self.content, LayoutContent::Bullet { .. }) } } + +pub struct LayoutMetrics { + pub ascent: Twips, + pub descent: Twips, + pub leading: Twips, + + pub width: Twips, + pub height: Twips, + + pub x: Twips, +}