Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 11 additions & 8 deletions core/src/avm2/globals/flash/text/font.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ pub fn enumerate_fonts<'gc>(
_this: Value<'gc>,
args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
let mut fonts: Vec<Font<'gc>> = Vec::new();
let mut fonts: Vec<(Font<'gc>, _)> = Vec::new();

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?
Expand All @@ -107,7 +107,7 @@ pub fn enumerate_fonts<'gc>(
);
}

fonts.append(&mut activation.context.library.global_fonts());
let default_font_class = activation.avm2().classes().font;

if let Some(library) = activation
.context
Expand All @@ -118,24 +118,27 @@ pub fn enumerate_fonts<'gc>(
// 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.has_layout() && font.font_type() == FontType::Embedded {
fonts.push(font);
fonts.push((font, default_font_class));
}
}
}

for (font, class) in activation.context.library.global_fonts_with_classes() {
fonts.push((font, class));
}

// The output from Flash is sorted by font name (case insensitive).
// If two fonts have the same name (e.g. bold/italic variants),
// the order is nondeterministic.
fonts.sort_unstable_by(|a, b| {
a.descriptor()
a.0.descriptor()
.lowercase_name()
.cmp(b.descriptor().lowercase_name())
.cmp(b.0.descriptor().lowercase_name())
});

let font_class = activation.avm2().classes().font;
let storage = fonts
.into_iter()
.map(|font| FontObject::for_font(activation.gc(), font_class, font))
.map(|(font, class)| FontObject::for_font(activation.gc(), class, font))
.collect();

Ok(ArrayObject::from_storage(activation.context, storage).into())
Expand All @@ -158,7 +161,7 @@ pub fn register_font<'gc>(
{
if let Some(lib) = activation.context.library.library_for_movie(movie) {
if let Some(Character::Font(font)) = lib.character_by_id(id) {
activation.context.library.register_global_font(font);
activation.context.library.register_global_font(font, class);
return Ok(Value::Undefined);
}
}
Expand Down
4 changes: 4 additions & 0 deletions core/src/font.rs
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,10 @@
}

impl<'gc> Font<'gc> {
pub fn ptr_eq(this: Font<'gc>, other: Font<'gc>) -> bool {
Gc::ptr_eq(this.0, other.0)
}

Check warning on line 592 in core/src/font.rs

View workflow job for this annotation

GitHub Actions / Coverage Report

Coverage

Uncovered lines (590–592)

pub fn from_font_file(
gc_context: &Mutation<'gc>,
descriptor: FontDescriptor,
Expand Down
32 changes: 24 additions & 8 deletions core/src/library.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::avm1::{PropertyMap as Avm1PropertyMap, PropertyMap};
use crate::avm2::object::ClassObject;
use crate::avm2::{Class as Avm2Class, Domain as Avm2Domain};
use crate::backend::audio::SoundHandle;
use crate::character::Character;
Expand Down Expand Up @@ -723,12 +724,13 @@ impl<'gc> Library<'gc> {
None
}

pub fn global_fonts(&self) -> Vec<Font<'gc>> {
self.global_fonts.all()
pub fn register_global_font(&mut self, font: Font<'gc>, class: ClassObject<'gc>) {
self.global_fonts.register_with_class(font, class);
}

pub fn register_global_font(&mut self, font: Font<'gc>) {
self.global_fonts.register(font);
/// Get the globally registered fonts with their associated AVM2 classes.
pub fn global_fonts_with_classes(&self) -> Vec<(Font<'gc>, ClassObject<'gc>)> {
self.global_fonts.all_with_classes()
}

/// Get the AVM2 class registry.
Expand All @@ -744,18 +746,25 @@ impl<'gc> Library<'gc> {

#[derive(Collect, Default)]
#[collect(no_drop)]
struct FontMap<'gc>(FnvHashMap<FontQuery, Font<'gc>>);
struct FontMap<'gc>(FnvHashMap<FontQuery, (Font<'gc>, Option<ClassObject<'gc>>)>);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we store the Option<ClassObject> directly on the Font?


impl<'gc> FontMap<'gc> {
pub fn register(&mut self, font: Font<'gc>) {
let descriptor = font.descriptor();
self.0
.entry(FontQuery::from_descriptor(font.font_type(), descriptor))
.or_insert(font);
.or_insert((font, None));
}

pub fn register_with_class(&mut self, font: Font<'gc>, class: ClassObject<'gc>) {
let descriptor = font.descriptor();
self.0
.entry(FontQuery::from_descriptor(font.font_type(), descriptor))
.or_insert((font, Some(class)));
}

pub fn get(&self, font_query: &FontQuery) -> Option<&Font<'gc>> {
self.0.get(font_query)
self.0.get(font_query).map(|(f, _)| f)
}

pub fn find(&self, font_query: &FontQuery) -> Option<Font<'gc>> {
Expand Down Expand Up @@ -831,6 +840,13 @@ impl<'gc> FontMap<'gc> {
}

pub fn all(&self) -> Vec<Font<'gc>> {
self.0.values().copied().collect()
self.0.values().map(|(f, _)| *f).collect()
}

pub fn all_with_classes(&self) -> Vec<(Font<'gc>, ClassObject<'gc>)> {
self.0
.values()
.filter_map(|(f, c)| c.map(|c| (*f, c)))
.collect()
}
}
49 changes: 49 additions & 0 deletions tests/tests/swfs/avm2/font_enumeratefonts_instances/Test.as
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package {
import flash.display.Sprite;
import flash.text.Font;

public class Test extends Sprite {
[Embed(source="TestFont.ttf", fontName="TestFont", embedAsCFF="false", unicodeRange="U+0061-U+0061")]
private var TestFont:Class;

public function Test() {
var a:Array = Font.enumerateFonts(false);
var b:Array = Font.enumerateFonts(false);

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

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

trace("// a[0] === b[0]");
trace(a[0] === b[0]);

trace("// a[0].fontName === b[0].fontName");
trace(a[0].fontName === b[0].fontName);
trace("");

trace("// a[0] is TestFont");
trace(a[0] is TestFont);
trace("");

Font.registerFont(TestFont);

var c:Array = Font.enumerateFonts(false);

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

for (var i:int = 0; i < c.length; i++) {
trace("// c[" + i + "].fontName");
trace(c[i].fontName);
trace("// c[" + i + "] is TestFont");
trace(c[i] is TestFont);
trace("");
}
}
}
}
Binary file not shown.
27 changes: 27 additions & 0 deletions tests/tests/swfs/avm2/font_enumeratefonts_instances/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// a.length
1

// b.length
1

// a[0] === b[0]
false
// a[0].fontName === b[0].fontName
true

// a[0] is TestFont
false

// c.length
2

// c[0].fontName
TestFont
// c[0] is TestFont
false

// c[1].fontName
TestFont
// c[1] is TestFont
true

Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
num_frames = 1
Loading