diff --git a/packages/dom/Cargo.toml b/packages/dom/Cargo.toml index 2bb81b93..8cfc0482 100644 --- a/packages/dom/Cargo.toml +++ b/packages/dom/Cargo.toml @@ -30,3 +30,4 @@ data-url = "0.3.1" image = "0.25.2" winit = { version = "0.30.4", default-features = false } usvg = "0.42.0" +woff = "0.3.3" \ No newline at end of file diff --git a/packages/dom/src/util.rs b/packages/dom/src/util.rs index b4448c33..8bbbdf21 100644 --- a/packages/dom/src/util.rs +++ b/packages/dom/src/util.rs @@ -62,9 +62,40 @@ impl RequestHandler for CssHandler { _ => None, }) .for_each(|url_source| { + let mut format = match &url_source.format_hint { + Some(FontFaceSourceFormat::Keyword(fmt)) => *fmt, + Some(FontFaceSourceFormat::String(str)) => match str.as_str() { + "woff2" => FontFaceSourceFormatKeyword::Woff2, + "ttf" => FontFaceSourceFormatKeyword::Truetype, + "otf" => FontFaceSourceFormatKeyword::Opentype, + _ => FontFaceSourceFormatKeyword::None, + }, + _ => FontFaceSourceFormatKeyword::None, + }; + if format == FontFaceSourceFormatKeyword::None { + let Some((_, end)) = url_source.url.as_str().rsplit_once('.') else { + return; + }; + format = match end { + "woff2" => FontFaceSourceFormatKeyword::Woff2, + "woff" => FontFaceSourceFormatKeyword::Woff, + "ttf" => FontFaceSourceFormatKeyword::Truetype, + "otf" => FontFaceSourceFormatKeyword::Opentype, + "svg" => FontFaceSourceFormatKeyword::Svg, + "eot" => FontFaceSourceFormatKeyword::EmbeddedOpentype, + _ => FontFaceSourceFormatKeyword::None, + } + } + if let font_format @ (FontFaceSourceFormatKeyword::Svg + | FontFaceSourceFormatKeyword::EmbeddedOpentype + | FontFaceSourceFormatKeyword::Woff) = format + { + tracing::warn!("Skipping unsupported font of type {:?}", font_format); + return; + } self.provider.fetch( Url::from_str(url_source.url.as_str()).unwrap(), - Box::new(FontFaceHandler), + Box::new(FontFaceHandler(format)), ) }); @@ -74,10 +105,39 @@ impl RequestHandler for CssHandler { )) } } -struct FontFaceHandler; +struct FontFaceHandler(FontFaceSourceFormatKeyword); impl RequestHandler for FontFaceHandler { type Data = Resource; - fn bytes(self: Box, bytes: Bytes, callback: SharedCallback) { + fn bytes(mut self: Box, mut bytes: Bytes, callback: SharedCallback) { + if self.0 == FontFaceSourceFormatKeyword::None { + self.0 = match bytes.as_ref() { + // https://w3c.github.io/woff/woff2/#woff20Header + [0x77, 0x4F, 0x46, 0x32, ..] => FontFaceSourceFormatKeyword::Woff2, + // https://learn.microsoft.com/en-us/typography/opentype/spec/otff#organization-of-an-opentype-font + [0x4F, 0x54, 0x54, 0x4F, ..] => FontFaceSourceFormatKeyword::Opentype, + // https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6.html#ScalerTypeNote + [0x00, 0x01, 0x00, 0x00, ..] | [0x74, 0x72, 0x75, 0x65, ..] => { + FontFaceSourceFormatKeyword::Truetype + } + _ => FontFaceSourceFormatKeyword::None, + } + } + match self.0 { + FontFaceSourceFormatKeyword::Woff2 => { + tracing::info!("Decompressing woff2 font"); + let decompressed = woff::version2::decompress(&bytes); + if let Some(decompressed) = decompressed { + bytes = Bytes::from(decompressed); + } else { + tracing::warn!("Failed to decompress woff2 font"); + } + } + FontFaceSourceFormatKeyword::None => { + return; + } + _ => {} + } + callback.call(Resource::Font(bytes)) } } @@ -188,7 +248,7 @@ pub fn walk_tree(indent: usize, node: &Node) { use blitz_traits::net::{Bytes, RequestHandler, SharedCallback, SharedProvider}; use peniko::Color as PenikoColor; -use style::font_face::Source; +use style::font_face::{FontFaceSourceFormat, FontFaceSourceFormatKeyword, Source}; use style::stylesheets::{CssRule, StylesheetInDocument}; pub trait ToPenikoColor {