core,web: Add the ability to load TTF/OTF/OTC as default fonts for the extension

This commit is contained in:
wsxarcher 2024-09-05 15:39:45 +02:00 committed by Tom Schuster
parent e6e92dd175
commit 7d1373368b
3 changed files with 98 additions and 39 deletions

View File

@ -71,3 +71,4 @@ pub use player::{Player, PlayerBuilder, PlayerRuntime, StaticCallstack};
pub use ruffle_render::backend::ViewportDimensions;
pub use swf;
pub use swf::Color;
pub use ttf_parser;

View File

@ -595,7 +595,9 @@ impl<'gc> Library<'gc> {
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");
let is_bold = font.descriptor().bold();
let is_italic = font.descriptor().italic();
info!("Loaded new device font \"{name}\" (bold: {is_bold}, italic: {is_italic}) from swf tag");
self.device_fonts.register(font);
}
FontDefinition::FontFile {
@ -614,7 +616,7 @@ impl<'gc> Library<'gc> {
FontType::Device,
) {
let name = font.descriptor().name().to_owned();
info!("Loaded new device font \"{name}\" from file");
info!("Loaded new device font \"{name}\" (bold: {is_bold}, italic: {is_italic}) from file");
self.device_fonts.register(font);
} else {
warn!("Failed to load device font from file");

View File

@ -11,6 +11,7 @@ use ruffle_core::backend::storage::{MemoryStorageBackend, StorageBackend};
use ruffle_core::backend::ui::FontDefinition;
use ruffle_core::compatibility_rules::CompatibilityRules;
use ruffle_core::config::{Letterbox, NetworkingAccessMode};
use ruffle_core::ttf_parser;
use ruffle_core::{
swf, Color, DefaultFont, Player, PlayerBuilder, PlayerRuntime, StageAlign, StageScaleMode,
};
@ -339,46 +340,71 @@ impl RuffleInstanceBuilder {
impl RuffleInstanceBuilder {
pub fn setup_fonts(&self, player: &mut Player) {
for (font_name, bytes) in &self.custom_fonts {
if let Ok(swf_stream) = swf::decompress_swf(&bytes[..]) {
if let Ok(swf) = swf::parse_swf(&swf_stream) {
let encoding = swf::SwfStr::encoding_for_version(swf.header.version());
for tag in swf.tags {
match tag {
swf::Tag::DefineFont(_font) => {
tracing::warn!("DefineFont1 tag is not yet supported by Ruffle, inside font swf {font_name}");
}
swf::Tag::DefineFont2(font) => {
tracing::debug!(
"Loaded font {} from font swf {font_name}",
font.name.to_str_lossy(encoding)
);
player
.register_device_font(FontDefinition::SwfTag(*font, encoding));
}
swf::Tag::DefineFont4(font) => {
let name = font.name.to_str_lossy(encoding);
if let Some(data) = font.data {
tracing::debug!("Loaded font {name} from font swf {font_name}");
player.register_device_font(FontDefinition::FontFile {
name: name.to_string(),
is_bold: font.is_bold,
is_italic: font.is_italic,
data: data.to_vec(),
index: 0,
})
} else {
tracing::warn!(
"Font {name} from font swf {font_name} contains no data"
);
}
}
_ => {}
}
let bytes_slice = &bytes[..];
if let Ok(face) = ttf_parser::Face::parse(bytes_slice, 0) {
tracing::debug!("Loading font {font_name} as TTF/OTF/TTC/OTC font");
// Check if font collection
let number_of_fonts = ttf_parser::fonts_in_collection(bytes_slice).unwrap_or(1u32);
Self::register_ttf_face_by_name(font_name, bytes.clone(), face, 0, player);
// Register all remaining fonts in the collection if it is a collection
for i in 1u32..number_of_fonts {
if let Ok(face) = ttf_parser::Face::parse(bytes_slice, i) {
Self::register_ttf_face_by_name(font_name, bytes.clone(), face, i, player);
} else {
tracing::warn!(
"Failed to parse font {font_name} at index {i} in font collection"
);
}
continue;
}
} else {
tracing::debug!("Loading font {font_name} as SWF font");
if let Ok(swf_stream) = swf::decompress_swf(&bytes[..]) {
if let Ok(swf) = swf::parse_swf(&swf_stream) {
let encoding = swf::SwfStr::encoding_for_version(swf.header.version());
for tag in swf.tags {
match tag {
swf::Tag::DefineFont(_font) => {
tracing::warn!("DefineFont1 tag is not yet supported by Ruffle, inside font swf {font_name}");
}
swf::Tag::DefineFont2(font) => {
tracing::debug!(
"Loaded font {} from font swf {font_name}",
font.name.to_str_lossy(encoding)
);
player.register_device_font(FontDefinition::SwfTag(
*font, encoding,
));
}
swf::Tag::DefineFont4(font) => {
let name = font.name.to_str_lossy(encoding);
if let Some(data) = font.data {
tracing::debug!(
"Loaded font {name} from font swf {font_name}"
);
player.register_device_font(FontDefinition::FontFile {
name: name.to_string(),
is_bold: font.is_bold,
is_italic: font.is_italic,
data: data.to_vec(),
index: 0,
})
} else {
tracing::warn!(
"Font {name} from font swf {font_name} contains no data"
);
}
}
_ => {}
}
}
continue;
}
}
tracing::warn!("Font source {font_name} was not recognised (not a valid SWF?)");
}
tracing::warn!("Font source {font_name} was not recognised (not a valid SWF?)");
}
for (default, names) in &self.default_fonts {
@ -386,6 +412,36 @@ impl RuffleInstanceBuilder {
}
}
#[inline]
fn register_ttf_face_by_name(
url: &String,
bytes: Vec<u8>,
face: ttf_parser::Face<'_>,
index: u32,
player: &mut Player,
) {
let full_name = face
.names()
.into_iter()
.find(|name| name.name_id == ttf_parser::name_id::FULL_NAME)
.and_then(|name| name.to_string());
let name = if let Some(full_name) = full_name {
full_name
} else {
tracing::warn!("Font {url} at index {index} has no full name, using URL as font name");
url.to_string()
};
player.register_device_font(FontDefinition::FontFile {
name: name.to_string(),
is_bold: face.is_bold(),
is_italic: face.is_italic(),
data: bytes,
index,
});
}
pub fn create_log_subscriber(&self) -> Arc<Layered<WASMLayer, Registry>> {
let layer = WASMLayer::new(
WASMLayerConfigBuilder::new()