Skip to content

Commit

Permalink
avm2: Implement Font.enumerateFonts
Browse files Browse the repository at this point in the history
  • Loading branch information
Dinnerbone committed Oct 30, 2023
1 parent 8be05d8 commit 9ecb7d2
Show file tree
Hide file tree
Showing 10 changed files with 178 additions and 12 deletions.
48 changes: 40 additions & 8 deletions core/src/avm2/globals/flash/text/font.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
//! `flash.text.Font` builtin/prototype
use crate::avm2::activation::Activation;
use crate::avm2::object::{Object, TObject};
use crate::avm2::object::{FontObject, Object, TObject};
use crate::avm2::parameters::ParametersExt;
use crate::avm2::value::Value;
use crate::avm2::{ArrayObject, ArrayStorage, Error};
use crate::avm2_stub_method;
use crate::string::AvmString;

pub use crate::avm2::object::font_allocator;
use crate::font::FontType;

/// Implements `Font.fontName`
pub fn get_font_name<'gc>(
Expand Down Expand Up @@ -49,10 +50,13 @@ pub fn get_font_type<'gc>(
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
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());
if let Some(font) = this.as_font() {
return Ok(match font.font_type() {
FontType::Embedded => "embedded",
FontType::EmbeddedCFF => "embeddedCFF",
FontType::Device => "device",
}
.into());
}

Ok(Value::Null)
Expand All @@ -76,10 +80,38 @@ pub fn has_glyphs<'gc>(
pub fn enumerate_fonts<'gc>(
activation: &mut Activation<'_, 'gc>,
_this: Object<'gc>,
_args: &[Value<'gc>],
args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
avm2_stub_method!(activation, "flash.text.Font", "enumerateFonts");
Ok(ArrayObject::from_storage(activation, ArrayStorage::new(0))?.into())
let mut storage = ArrayStorage::new(0);
let font_class = activation.avm2().classes().font;

if args.get_bool(0) {
// We could include the ones we know about, but what to do for the ones that weren't eagerly loaded?
avm2_stub_method!(
activation,
"flash.text.Font",
"enumerateFonts",
"with device fonts"
);
}

if let Some(library) = activation
.context
.library
.library_for_movie(activation.context.swf.clone())
{
for font in library.embedded_fonts() {
// TODO: EmbeddedCFF isn't supposed to show until it's been used (some kind of internal initialization method?)
// Device is only supposed to show when arg0 is true - but that's supposed to be "all known" device fonts, not just loaded ones
if font.font_type() == FontType::Embedded {
storage.push(
FontObject::for_font(activation.context.gc_context, font_class, font).into(),
);
}
}
}

Ok(ArrayObject::from_storage(activation, storage)?.into())
}

/// `Font.registerFont`
Expand Down
14 changes: 14 additions & 0 deletions core/src/avm2/object/font_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,20 @@ pub fn font_allocator<'gc>(
#[collect(no_drop)]
pub struct FontObject<'gc>(pub GcCell<'gc, FontObjectData<'gc>>);

impl<'gc> FontObject<'gc> {
pub fn for_font(mc: &Mutation<'gc>, class: ClassObject<'gc>, font: Font<'gc>) -> Object<'gc> {
let base = ScriptObjectData::new(class);
FontObject(GcCell::new(
mc,
FontObjectData {
base,
font: Some(font),
},
))
.into()
}
}

#[derive(Clone, Collect, Copy, Debug)]
#[collect(no_drop)]
pub struct FontObjectWeak<'gc>(pub GcWeakCell<'gc, FontObjectData<'gc>>);
Expand Down
6 changes: 5 additions & 1 deletion core/src/display_object/movie_clip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use crate::display_object::{
};
use crate::drawing::Drawing;
use crate::events::{ButtonKeyCode, ClipEvent, ClipEventResult};
use crate::font::Font;
use crate::font::{Font, FontType};
use crate::limits::ExecutionLimit;
use crate::loader;
use crate::loader::Loader;
Expand Down Expand Up @@ -3788,6 +3788,7 @@ impl<'gc, 'a> MovieClipData<'gc> {
context.renderer,
font,
reader.encoding(),
FontType::Embedded,
);
context
.library
Expand All @@ -3809,6 +3810,7 @@ impl<'gc, 'a> MovieClipData<'gc> {
context.renderer,
font,
reader.encoding(),
FontType::Embedded,
);
context
.library
Expand All @@ -3830,6 +3832,7 @@ impl<'gc, 'a> MovieClipData<'gc> {
context.renderer,
font,
reader.encoding(),
FontType::Embedded,
);
context
.library
Expand Down Expand Up @@ -3861,6 +3864,7 @@ impl<'gc, 'a> MovieClipData<'gc> {
flags: FontFlag::empty(),
},
reader.encoding(),
FontType::EmbeddedCFF,
);
context
.library
Expand Down
18 changes: 17 additions & 1 deletion core/src/font.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,13 @@ impl GlyphSource {
}
}

#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum FontType {
Embedded,
EmbeddedCFF,
Device,
}

#[derive(Debug, Clone, Collect, Copy)]
#[collect(no_drop)]
pub struct Font<'gc>(Gc<'gc, FontData>);
Expand Down Expand Up @@ -169,6 +176,8 @@ struct FontData {
/// The identity of the font.
#[collect(require_static)]
descriptor: FontDescriptor,

font_type: FontType,
}

impl<'gc> Font<'gc> {
Expand All @@ -177,6 +186,7 @@ impl<'gc> Font<'gc> {
renderer: &mut dyn RenderBackend,
tag: swf::Font,
encoding: &'static swf::Encoding,
font_type: FontType,
) -> Font<'gc> {
let mut code_point_to_glyph = fnv::FnvHashMap::default();

Expand Down Expand Up @@ -240,6 +250,7 @@ impl<'gc> Font<'gc> {
descent,
leading,
descriptor,
font_type,
},
))
}
Expand Down Expand Up @@ -480,6 +491,10 @@ impl<'gc> Font<'gc> {
pub fn descriptor(&self) -> &FontDescriptor {
&self.0.descriptor
}

pub fn font_type(&self) -> FontType {
self.0.font_type
}
}

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -818,7 +833,7 @@ impl Default for TextRenderSettings {

#[cfg(test)]
mod tests {
use crate::font::{EvalParameters, Font};
use crate::font::{EvalParameters, Font, FontType};
use crate::string::WStr;
use gc_arena::{rootless_arena, Mutation};
use ruffle_render::backend::{null::NullRenderer, ViewportDimensions};
Expand All @@ -844,6 +859,7 @@ mod tests {
.read_define_font_2(3)
.expect("Built-in font should compile"),
reader.encoding(),
FontType::Device,
);

callback(mc, device_font);
Expand Down
9 changes: 7 additions & 2 deletions core/src/library.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::backend::audio::SoundHandle;
use crate::character::Character;

use crate::display_object::{Bitmap, Graphic, MorphShape, TDisplayObject, Text};
use crate::font::{Font, FontDescriptor};
use crate::font::{Font, FontDescriptor, FontType};
use crate::prelude::*;
use crate::string::AvmString;
use crate::tag_utils::SwfMovie;
Expand Down Expand Up @@ -258,6 +258,10 @@ impl<'gc> MovieLibrary<'gc> {
}
}

pub fn embedded_fonts(&self) -> Vec<Font<'gc>> {
self.fonts.values().cloned().collect()
}

/// Find a font by it's name and parameters.
pub fn get_embedded_font_by_name(
&self,
Expand Down Expand Up @@ -590,7 +594,8 @@ impl<'gc> Library<'gc> {
) {
match definition {
FontDefinition::SwfTag(tag, encoding) => {
let font = Font::from_swf_tag(gc_context, renderer, tag, encoding);
let font =
Font::from_swf_tag(gc_context, renderer, tag, encoding, FontType::Device);
let name = font.descriptor().name().to_owned();
info!("Loaded new device font \"{name}\" from swf tag");
self.device_fonts.insert(name, font);
Expand Down
48 changes: 48 additions & 0 deletions tests/tests/swfs/avm2/font_enumeratefonts/Test.as
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package {

import flash.display.MovieClip;
import flash.text.Font;
import flash.utils.describeType;


public class Test extends MovieClip {


public function Test() {
trace("// var fonts = enumerateFonts(false)");
var fonts = Font.enumerateFonts(false);

trace("// fonts.length");
trace(fonts.length);
trace("");

for (var i = 0; i < fonts.length; i++) {
dumpFont("fonts[" + i + "]", fonts[i]);
}

dumpFont("new Font1()", new Font1());
dumpFont("new Font()", new Font());
}

function dumpFont(name: String, font: Font) {
trace("// " + name + ".fontName");
trace(font.fontName);
trace("");

trace("// " + name + ".fontStyle");
trace(font.fontStyle);
trace("");

trace("// " + name + ".fontType");
trace(font.fontType);
trace("");

trace("// " + name + " is Font1");
trace(font is Font1);
trace("");

trace("");
}
}

}
43 changes: 43 additions & 0 deletions tests/tests/swfs/avm2/font_enumeratefonts/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// var fonts = enumerateFonts(false)
// fonts.length
1

// fonts[0].fontName
Source Code Pro

// fonts[0].fontStyle
bold

// fonts[0].fontType
embedded

// fonts[0] is Font1
false


// new Font1().fontName
Source Code Pro

// new Font1().fontStyle
bold

// new Font1().fontType
embedded

// new Font1() is Font1
true


// new Font().fontName
null

// new Font().fontStyle
null

// new Font().fontType
null

// new Font() is Font1
false


Binary file not shown.
Binary file not shown.
4 changes: 4 additions & 0 deletions tests/tests/swfs/avm2/font_enumeratefonts/test.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# There's a CFF (definefont4) font embedded here, which should **not** show up in the list until referenced by something
# (which it won't be)

num_frames = 1

0 comments on commit 9ecb7d2

Please sign in to comment.