diff --git a/core/src/avm2/globals.rs b/core/src/avm2/globals.rs index 2b445606c..b07528d6b 100644 --- a/core/src/avm2/globals.rs +++ b/core/src/avm2/globals.rs @@ -166,6 +166,7 @@ pub struct SystemClasses<'gc> { pub shaderfilter: ClassObject<'gc>, pub statusevent: ClassObject<'gc>, pub contextmenuevent: ClassObject<'gc>, + pub font: ClassObject<'gc>, } impl<'gc> SystemClasses<'gc> { @@ -291,6 +292,7 @@ impl<'gc> SystemClasses<'gc> { shaderfilter: object, statusevent: object, contextmenuevent: object, + font: object, } } } @@ -794,6 +796,7 @@ fn load_playerglobal<'gc>( ("flash.net", "URLVariables", urlvariables), ("flash.utils", "ByteArray", bytearray), ("flash.system", "ApplicationDomain", application_domain), + ("flash.text", "Font", font), ("flash.text", "StaticText", statictext), ("flash.text", "TextFormat", textformat), ("flash.text", "TextField", textfield), diff --git a/core/src/avm2/globals/flash/text/Font.as b/core/src/avm2/globals/flash/text/Font.as index 616c438e4..ccebcecad 100644 --- a/core/src/avm2/globals/flash/text/Font.as +++ b/core/src/avm2/globals/flash/text/Font.as @@ -1,4 +1,5 @@ package flash.text { + [Ruffle(InstanceAllocator)] public class Font { public static native function enumerateFonts(enumerateDeviceFonts:Boolean = false):Array; public static native function registerFont(font:Class):void; diff --git a/core/src/avm2/globals/flash/text/font.rs b/core/src/avm2/globals/flash/text/font.rs index 4cfbbb472..e9c096fc4 100644 --- a/core/src/avm2/globals/flash/text/font.rs +++ b/core/src/avm2/globals/flash/text/font.rs @@ -6,95 +6,56 @@ use crate::avm2::parameters::ParametersExt; use crate::avm2::value::Value; use crate::avm2::{ArrayObject, ArrayStorage, Error}; use crate::avm2_stub_method; -use crate::character::Character; use crate::string::AvmString; +pub use crate::avm2::object::font_allocator; + /// Implements `Font.fontName` pub fn get_font_name<'gc>( activation: &mut Activation<'_, 'gc>, this: Object<'gc>, _args: &[Value<'gc>], ) -> Result, Error<'gc>> { - if let Some((movie, character_id)) = this.instance_of().and_then(|this| { - activation - .context - .library - .avm2_class_registry() - .class_symbol(this) - }) { - if let Some(Character::Font(font)) = activation - .context - .library - .library_for_movie_mut(movie) - .character_by_id(character_id) - { - return Ok(AvmString::new_utf8( - activation.context.gc_context, - font.descriptor().name(), - ) - .into()); - } + if let Some(font) = this.as_font() { + return Ok( + AvmString::new_utf8(activation.context.gc_context, font.descriptor().name()).into(), + ); } - Ok(Value::Undefined) + Ok(Value::Null) } /// Implements `Font.fontStyle` pub fn get_font_style<'gc>( - activation: &mut Activation<'_, 'gc>, + _activation: &mut Activation<'_, 'gc>, this: Object<'gc>, _args: &[Value<'gc>], ) -> Result, Error<'gc>> { - if let Some((movie, character_id)) = this.instance_of().and_then(|this| { - activation - .context - .library - .avm2_class_registry() - .class_symbol(this) - }) { - if let Some(Character::Font(font)) = activation - .context - .library - .library_for_movie_mut(movie) - .character_by_id(character_id) - { - return match (font.descriptor().bold(), font.descriptor().italic()) { - (false, false) => Ok("regular".into()), - (false, true) => Ok("italic".into()), - (true, false) => Ok("bold".into()), - (true, true) => Ok("boldItalic".into()), - }; - } + if let Some(font) = this.as_font() { + return match (font.descriptor().bold(), font.descriptor().italic()) { + (false, false) => Ok("regular".into()), + (false, true) => Ok("italic".into()), + (true, false) => Ok("bold".into()), + (true, true) => Ok("boldItalic".into()), + }; } - Ok(Value::Undefined) + Ok(Value::Null) } /// Implements `Font.fontType` pub fn get_font_type<'gc>( - activation: &mut Activation<'_, 'gc>, + _activation: &mut Activation<'_, 'gc>, this: Object<'gc>, _args: &[Value<'gc>], ) -> Result, Error<'gc>> { - if let Some((movie, character_id)) = this.instance_of().and_then(|this| { - activation - .context - .library - .avm2_class_registry() - .class_symbol(this) - }) { - if let Some(Character::Font(_)) = activation - .context - .library - .library_for_movie_mut(movie) - .character_by_id(character_id) - { - //TODO: How do we distinguish between CFF and non-CFF embedded fonts? - return Ok("embedded".into()); - } + if let Some(_font) = this.as_font() { + // [?] How do we distinguish between CFF and non-CFF embedded fonts? + // [NA] DefineFont4 is CFF. This should be a property of Font struct + return Ok("embedded".into()); } - Ok(Value::Undefined) + Ok(Value::Null) } /// Implements `Font.hasGlyphs` @@ -103,26 +64,12 @@ pub fn has_glyphs<'gc>( this: Object<'gc>, args: &[Value<'gc>], ) -> Result, Error<'gc>> { - if let Some((movie, character_id)) = this.instance_of().and_then(|this| { - activation - .context - .library - .avm2_class_registry() - .class_symbol(this) - }) { + if let Some(font) = this.as_font() { let my_str = args.get_string(activation, 0)?; - - if let Some(Character::Font(font)) = activation - .context - .library - .library_for_movie_mut(movie) - .character_by_id(character_id) - { - return Ok(font.has_glyphs_for_str(&my_str).into()); - } + return Ok(font.has_glyphs_for_str(&my_str).into()); } - Ok(Value::Undefined) + Ok(Value::Bool(false)) } /// `Font.enumerateFonts` diff --git a/core/src/avm2/object.rs b/core/src/avm2/object.rs index 88a31e7a3..2035837df 100644 --- a/core/src/avm2/object.rs +++ b/core/src/avm2/object.rs @@ -40,6 +40,7 @@ mod dispatch_object; mod domain_object; mod error_object; mod event_object; +mod font_object; mod function_object; mod index_buffer_3d_object; mod loaderinfo_object; @@ -83,6 +84,7 @@ pub use crate::avm2::object::domain_object::{ }; pub use crate::avm2::object::error_object::{error_allocator, ErrorObject, ErrorObjectWeak}; pub use crate::avm2::object::event_object::{event_allocator, EventObject, EventObjectWeak}; +pub use crate::avm2::object::font_object::{font_allocator, FontObject, FontObjectWeak}; pub use crate::avm2::object::function_object::{ function_allocator, FunctionObject, FunctionObjectWeak, }; @@ -132,6 +134,7 @@ pub use crate::avm2::object::xml_list_object::{ xml_list_allocator, E4XOrXml, XmlListObject, XmlListObjectWeak, }; pub use crate::avm2::object::xml_object::{xml_allocator, XmlObject, XmlObjectWeak}; +use crate::font::Font; /// Represents an object that can be directly interacted with by the AVM2 /// runtime. @@ -173,7 +176,8 @@ pub use crate::avm2::object::xml_object::{xml_allocator, XmlObject, XmlObjectWea Program3DObject(Program3DObject<'gc>), NetStreamObject(NetStreamObject<'gc>), ShaderDataObject(ShaderDataObject<'gc>), - SocketObject(SocketObject<'gc>) + SocketObject(SocketObject<'gc>), + FontObject(FontObject<'gc>) } )] pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy { @@ -1276,6 +1280,11 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy None } + /// Unwrap this object as a font. + fn as_font(&self) -> Option> { + None + } + /// Unwrap this object as a mutable regexp. fn as_regexp_mut(&self, _mc: &Mutation<'gc>) -> Option>> { None @@ -1424,7 +1433,8 @@ impl<'gc> Object<'gc> { Self::Program3DObject(o) => WeakObject::Program3DObject(Program3DObjectWeak(Gc::downgrade(o.0))), Self::NetStreamObject(o) => WeakObject::NetStreamObject(NetStreamObjectWeak(GcCell::downgrade(o.0))), Self::ShaderDataObject(o) => WeakObject::ShaderDataObject(ShaderDataObjectWeak(Gc::downgrade(o.0))), - Self::SocketObject(o) => WeakObject::SocketObject(SocketObjectWeak(Gc::downgrade(o.0))) + Self::SocketObject(o) => WeakObject::SocketObject(SocketObjectWeak(Gc::downgrade(o.0))), + Self::FontObject(o) => WeakObject::FontObject(FontObjectWeak(GcCell::downgrade(o.0))), } } } @@ -1481,6 +1491,7 @@ pub enum WeakObject<'gc> { NetStreamObject(NetStreamObjectWeak<'gc>), ShaderDataObject(ShaderDataObjectWeak<'gc>), SocketObject(SocketObjectWeak<'gc>), + FontObject(FontObjectWeak<'gc>), } impl<'gc> WeakObject<'gc> { @@ -1520,6 +1531,7 @@ impl<'gc> WeakObject<'gc> { Self::NetStreamObject(o) => NetStreamObject(o.0.upgrade(mc)?).into(), Self::ShaderDataObject(o) => ShaderDataObject(o.0.upgrade(mc)?).into(), Self::SocketObject(o) => SocketObject(o.0.upgrade(mc)?).into(), + Self::FontObject(o) => FontObject(o.0.upgrade(mc)?).into(), }) } } diff --git a/core/src/avm2/object/font_object.rs b/core/src/avm2/object/font_object.rs new file mode 100644 index 000000000..63d3392fb --- /dev/null +++ b/core/src/avm2/object/font_object.rs @@ -0,0 +1,88 @@ +use crate::avm2::object::script_object::ScriptObjectData; +use crate::avm2::object::{Object, ObjectPtr, TObject}; +use crate::avm2::value::Value; +use crate::avm2::{Activation, ClassObject, Error}; +use crate::character::Character; +use crate::font::Font; +use gc_arena::Mutation; +use gc_arena::{Collect, GcCell, GcWeakCell}; +use std::cell::{Ref, RefMut}; +use std::fmt; + +/// A class instance allocator that allocates Font objects. +pub fn font_allocator<'gc>( + class: ClassObject<'gc>, + activation: &mut Activation<'_, 'gc>, +) -> Result, Error<'gc>> { + let base = ScriptObjectData::new(class); + + let font = if let Some((movie, id)) = activation + .context + .library + .avm2_class_registry() + .class_symbol(class) + { + if let Some(lib) = activation.context.library.library_for_movie(movie) { + if let Some(Character::Font(font)) = lib.character_by_id(id) { + Some(*font) + } else { + None + } + } else { + None + } + } else { + None + }; + + Ok(FontObject(GcCell::new( + activation.context.gc_context, + FontObjectData { base, font }, + )) + .into()) +} + +#[derive(Clone, Collect, Copy)] +#[collect(no_drop)] +pub struct FontObject<'gc>(pub GcCell<'gc, FontObjectData<'gc>>); + +#[derive(Clone, Collect, Copy, Debug)] +#[collect(no_drop)] +pub struct FontObjectWeak<'gc>(pub GcWeakCell<'gc, FontObjectData<'gc>>); + +impl<'gc> TObject<'gc> for FontObject<'gc> { + fn base(&self) -> Ref> { + Ref::map(self.0.read(), |read| &read.base) + } + + fn base_mut(&self, mc: &Mutation<'gc>) -> RefMut> { + RefMut::map(self.0.write(mc), |write| &mut write.base) + } + + fn as_ptr(&self) -> *const ObjectPtr { + self.0.as_ptr() as *const ObjectPtr + } + + fn value_of(&self, _mc: &Mutation<'gc>) -> Result, Error<'gc>> { + Ok(Value::Object(Object::from(*self))) + } + + fn as_font(&self) -> Option> { + self.0.read().font + } +} + +#[derive(Collect)] +#[collect(no_drop)] +pub struct FontObjectData<'gc> { + /// Base script object + base: ScriptObjectData<'gc>, + + font: Option>, +} + +impl fmt::Debug for FontObject<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "FontObject") + } +}