Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

web: Add defaultFonts and fontSources config options #13604

Merged
merged 2 commits into from
Oct 30, 2023
Merged
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
2 changes: 2 additions & 0 deletions web/packages/core/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,6 @@ export const DEFAULT_CONFIG: Required<BaseLoadOptions> = {
allowNetworking: NetworkingAccessMode.All,
openInNewTab: null,
socketProxy: [],
fontSources: [],
defaultFonts: {},
};
44 changes: 44 additions & 0 deletions web/packages/core/src/load-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,30 @@ export interface SocketProxy {
proxyUrl: string;
}

/**
* Defines the names of the fonts to use for each "default" Flash device font.
*
* The name of each font provided will be used, in priority order.
*
* For example, defining `sans: ["Helvetica", "Arial"]` would use Helvetica if present, before trying Arial.
*/
export interface DefaultFonts {
/**
* `_sans`, a Sans-Serif font (similar to Helvetica or Arial)
*/
sans?: Array<string>;

/**
* `_serif`, a Serif font (similar to Times Roman)
*/
serif?: Array<string>;

/**
* `_typewriter`, a Monospace font (similar to Courier)
*/
typewriter?: Array<string>;
}

/**
* Any options used for loading a movie.
*/
Expand Down Expand Up @@ -561,6 +585,26 @@ export interface BaseLoadOptions {
* @default []
*/
socketProxy?: Array<SocketProxy>;

/**
* An array of font URLs to eagerly load and provide to Ruffle.
*
* These will be fetched by the browser as part of the loading of Flash content, which may slow down load times.
*
* Currently only SWFs are supported, and each font embedded within that SWF will be used as device font by Flash content.
*
* If any URL fails to load (either it's an invalid file, or a network error occurs), Ruffle will log an error but continue without it.
*
* @default []
*/
fontSources?: Array<string>;

/**
* The font names to use for each "default" Flash device font.
*
* @default {}
*/
defaultFonts?: DefaultFonts;
}

/**
Expand Down
36 changes: 36 additions & 0 deletions web/packages/core/src/ruffle-player.ts
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,42 @@ export class RufflePlayer extends HTMLElement {
throw e;
});

if (this.loadedConfig?.fontSources) {
for (const url of this.loadedConfig.fontSources) {
try {
const response = await fetch(url);
this.instance!.add_font(
url,
new Uint8Array(await response.arrayBuffer()),
);
} catch (error) {
console.warn(
`Couldn't download font source from ${url}`,
error,
);
}
}
}

if (this.loadedConfig?.defaultFonts?.sans) {
this.instance!.set_default_font(
"sans",
this.loadedConfig?.defaultFonts.sans,
);
}
if (this.loadedConfig?.defaultFonts?.serif) {
this.instance!.set_default_font(
"serif",
this.loadedConfig?.defaultFonts.serif,
);
}
if (this.loadedConfig?.defaultFonts?.typewriter) {
this.instance!.set_default_font(
"typewriter",
this.loadedConfig?.defaultFonts.typewriter,
);
}

this.instance!.set_volume(this.volumeSettings.get_volume());

this.rendererDebugInfo = this.instance!.renderer_debug_info();
Expand Down
55 changes: 55 additions & 0 deletions web/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ mod ui;
use generational_arena::{Arena, Index};
use js_sys::{Array, Error as JsError, Function, Object, Promise, Uint8Array};
use ruffle_core::backend::navigator::OpenURLMode;
use ruffle_core::backend::ui::FontDefinition;
use ruffle_core::compatibility_rules::CompatibilityRules;
use ruffle_core::config::{Letterbox, NetworkingAccessMode};
use ruffle_core::context::UpdateContext;
Expand All @@ -19,6 +20,7 @@ use ruffle_core::external::{
Value,
};
use ruffle_core::tag_utils::SwfMovie;
use ruffle_core::{swf, DefaultFont};
use ruffle_core::{
Color, Player, PlayerBuilder, PlayerEvent, SandboxType, StageAlign, StageScaleMode,
StaticCallstack, ViewportDimensions,
Expand Down Expand Up @@ -460,6 +462,59 @@ impl Ruffle {
// Instance is dropped at this point.
}

pub fn add_font(&mut self, font_name: &str, data: Uint8Array) {
let _ = self.with_core_mut(|core| {
let bytes: Vec<u8> = data.to_vec();
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)
);
core.register_device_font(FontDefinition::SwfTag(*font, encoding));
}
swf::Tag::DefineFont4(_font) => {
tracing::warn!("DefineFont4 tag is not yet supported by Ruffle, inside font swf {font_name}");
}
_ => {}
}
}
return;
}
}

tracing::warn!("Font source {font_name} was not recognised (not a valid SWF?)");
});
}

pub fn set_default_font(&mut self, default_name: &str, fonts: Vec<JsValue>) {
let _ = self.with_core_mut(|core| {
let default = match default_name {
"sans" => DefaultFont::Sans,
"serif" => DefaultFont::Serif,
"typewriter" => DefaultFont::Typewriter,
name => {
tracing::error!("Unknown default font name '{name}'");
return;
}
};
core.set_default_font(
default,
fonts
.into_iter()
.flat_map(|value| value.as_string())
.collect(),
);
});
}

#[allow(clippy::boxed_local)] // for js_bind
pub fn call_exposed_callback(&self, name: &str, args: Box<[JsValue]>) -> JsValue {
let args: Vec<ExternalValue> = args.iter().map(js_to_external_value).collect();
Expand Down
5 changes: 4 additions & 1 deletion web/src/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,5 +145,8 @@ impl UiBackend for WebUiBackend {
self.js_player.display_unsupported_video(url.as_str());
}

fn load_device_font(&self, _name: &str, _register: &dyn FnMut(FontDefinition)) {}
fn load_device_font(&self, _name: &str, _register: &dyn FnMut(FontDefinition)) {
// Because fonts must be loaded instantly (no async),
// we actually just provide them all upfront at time of Player creation.
}
}