diff --git a/Cargo.lock b/Cargo.lock index d135c23970f..346deea4a61 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2669,7 +2669,6 @@ name = "fonts" version = "0.0.1" dependencies = [ "app_units", - "atomic_refcell", "base", "bitflags 2.9.3", "byteorder", @@ -2721,11 +2720,19 @@ dependencies = [ name = "fonts_traits" version = "0.0.1" dependencies = [ + "atomic_refcell", + "dwrote", "ipc-channel", + "log", "malloc_size_of_derive", + "memmap2", "range", + "read-fonts", "serde", "servo_malloc_size_of", + "servo_url", + "stylo", + "webrender_api", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 695467df73e..8eeb6629953 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,6 +55,7 @@ ctr = "0.9.2" data-url = "0.3" devtools_traits = { path = "components/shared/devtools" } dpi = "0.1" +dwrote = "0.11.4" embedder_traits = { path = "components/shared/embedder" } encoding_rs = "0.8" env_logger = "0.11" diff --git a/components/fonts/Cargo.toml b/components/fonts/Cargo.toml index 1495728f505..08bbdca3880 100644 --- a/components/fonts/Cargo.toml +++ b/components/fonts/Cargo.toml @@ -18,7 +18,6 @@ tracing = ["dep:tracing"] [dependencies] app_units = { workspace = true } -atomic_refcell = { workspace = true } base = { workspace = true } bitflags = { workspace = true } compositing_traits = { workspace = true } @@ -73,7 +72,7 @@ fontconfig_sys = { package = "yeslogic-fontconfig-sys", version = "6" } xml-rs = "0.8" [target.'cfg(target_os = "windows")'.dependencies] -dwrote = "0.11.4" +dwrote = { workspace = true } winapi = { workspace = true } [lints.rust] diff --git a/components/fonts/font.rs b/components/fonts/font.rs index 61f883d356c..2677e5ef9cb 100644 --- a/components/fonts/font.rs +++ b/components/fonts/font.rs @@ -15,6 +15,7 @@ use app_units::Au; use bitflags::bitflags; use euclid::default::{Point2D, Rect, Size2D}; use euclid::num::Zero; +use fonts_traits::FontDescriptor; use log::debug; use malloc_size_of_derive::MallocSizeOf; use parking_lot::RwLock; @@ -190,44 +191,6 @@ impl FontMetrics { } } -/// `FontDescriptor` describes the parameters of a `Font`. It represents rendering a given font -/// template at a particular size, with a particular font-variant-caps applied, etc. This contrasts -/// with `FontTemplateDescriptor` in that the latter represents only the parameters inherent in the -/// font data (weight, stretch, etc.). -#[derive(Clone, Debug, Deserialize, Hash, MallocSizeOf, PartialEq, Serialize)] -pub struct FontDescriptor { - pub weight: FontWeight, - pub stretch: FontStretch, - pub style: FontStyle, - pub variant: font_variant_caps::T, - pub pt_size: Au, - pub variation_settings: Vec, -} - -impl Eq for FontDescriptor {} - -impl<'a> From<&'a FontStyleStruct> for FontDescriptor { - fn from(style: &'a FontStyleStruct) -> Self { - let variation_settings = style - .clone_font_variation_settings() - .0 - .into_iter() - .map(|setting| FontVariation { - tag: setting.tag.0, - value: setting.value, - }) - .collect(); - FontDescriptor { - weight: style.font_weight, - stretch: style.font_stretch, - style: style.font_style, - variant: style.font_variant_caps, - pt_size: Au::from_f32_px(style.font_size.computed_size().px()), - variation_settings, - } - } -} - #[derive(Debug, Default)] struct CachedShapeData { glyph_advances: HashMap, diff --git a/components/fonts/font_context.rs b/components/fonts/font_context.rs index c4a795bbe24..01e79e8eb7a 100644 --- a/components/fonts/font_context.rs +++ b/components/fonts/font_context.rs @@ -13,7 +13,10 @@ use app_units::Au; use base::id::WebViewId; use compositing_traits::CrossProcessCompositorApi; use fnv::FnvHasher; -use fonts_traits::StylesheetWebFontLoadFinishedCallback; +use fonts_traits::{ + CSSFontFaceDescriptors, FontDescriptor, FontIdentifier, FontTemplate, FontTemplateRef, + FontTemplateRefMethods, StylesheetWebFontLoadFinishedCallback, +}; use log::{debug, trace}; use malloc_size_of_derive::MallocSizeOf; use net_traits::request::{Destination, Referrer, RequestBuilder}; @@ -35,13 +38,9 @@ use style::values::computed::font::{FamilyName, FontFamilyNameSyntax, SingleFont use url::Url; use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey, FontVariation}; -use crate::font::{ - Font, FontDescriptor, FontFamilyDescriptor, FontGroup, FontRef, FontSearchScope, -}; +use crate::font::{Font, FontFamilyDescriptor, FontGroup, FontRef, FontSearchScope}; use crate::font_store::CrossThreadFontStore; -use crate::font_template::{FontTemplate, FontTemplateRef, FontTemplateRefMethods}; use crate::platform::font::PlatformFont; -use crate::system_font_service::{CSSFontFaceDescriptors, FontIdentifier}; use crate::{FontData, LowercaseFontFamilyName, PlatformFontMethods, SystemFontServiceProxy}; static SMALL_CAPS_SCALE_FACTOR: f32 = 0.8; // Matches FireFox (see gfxFont.h) diff --git a/components/fonts/font_store.rs b/components/fonts/font_store.rs index 16440982e98..869a6def9ca 100644 --- a/components/fonts/font_store.rs +++ b/components/fonts/font_store.rs @@ -6,16 +6,16 @@ use std::collections::HashMap; use std::ops::Deref; use std::sync::Arc; +use fonts_traits::{ + FontDescriptor, FontIdentifier, FontTemplate, FontTemplateRef, FontTemplateRefMethods, + IsOblique, LowercaseFontFamilyName, +}; use log::warn; use malloc_size_of_derive::MallocSizeOf; use parking_lot::RwLock; use style::stylesheets::DocumentStyleSheet; use style::values::computed::{FontStyle, FontWeight}; -use crate::font::FontDescriptor; -use crate::font_template::{FontTemplate, FontTemplateRef, FontTemplateRefMethods, IsOblique}; -use crate::system_font_service::{FontIdentifier, LowercaseFontFamilyName}; - #[derive(Default, MallocSizeOf)] pub struct FontStore { pub(crate) families: HashMap, diff --git a/components/fonts/lib.rs b/components/fonts/lib.rs index bfd9b9cf448..19080c456f5 100644 --- a/components/fonts/lib.rs +++ b/components/fonts/lib.rs @@ -7,7 +7,6 @@ mod font; mod font_context; mod font_store; -mod font_template; mod glyph; #[allow(unsafe_code)] pub mod platform; @@ -17,10 +16,8 @@ mod system_font_service; pub use font::*; pub use font_context::*; pub use font_store::*; -pub use font_template::*; -pub use fonts_traits::*; +pub use fonts_traits::{LocalFontIdentifier, *}; pub use glyph::*; -pub use platform::LocalFontIdentifier; pub use shapers::*; pub use system_font_service::*; use unicode_properties::{EmojiStatus, UnicodeEmoji, emoji}; diff --git a/components/fonts/platform/freetype/font.rs b/components/fonts/platform/freetype/font.rs index 0ca904ce61e..92998e8ae94 100644 --- a/components/fonts/platform/freetype/font.rs +++ b/components/fonts/platform/freetype/font.rs @@ -7,6 +7,7 @@ use std::fs::File; use app_units::Au; use euclid::default::{Point2D, Rect, Size2D}; +use fonts_traits::{FontIdentifier, FontTemplateDescriptor, LocalFontIdentifier}; use freetype_sys::{ FT_F26Dot6, FT_Get_Char_Index, FT_Get_Kerning, FT_GlyphSlot, FT_KERNING_DEFAULT, FT_LOAD_DEFAULT, FT_LOAD_NO_HINTING, FT_Load_Glyph, FT_Size_Metrics, FT_SizeRec, FT_UInt, @@ -21,14 +22,11 @@ use servo_arc::Arc; use style::Zero; use webrender_api::{FontInstanceFlags, FontVariation}; -use super::LocalFontIdentifier; use super::library_handle::FreeTypeLibraryHandle; use crate::FontData; use crate::font::{FontMetrics, FontTableMethods, FractionalPixel, PlatformFontMethods}; -use crate::font_template::FontTemplateDescriptor; use crate::glyph::GlyphId; use crate::platform::freetype::freetype_face::FreeTypeFace; -use crate::system_font_service::FontIdentifier; /// Convert FreeType-style 26.6 fixed point to an [`f64`]. fn fixed_26_dot_6_to_float(fixed: FT_F26Dot6) -> f64 { diff --git a/components/fonts/platform/freetype/font_list.rs b/components/fonts/platform/freetype/font_list.rs index 68b1aa99e9d..030b786fb27 100644 --- a/components/fonts/platform/freetype/font_list.rs +++ b/components/fonts/platform/freetype/font_list.rs @@ -19,6 +19,7 @@ use fontconfig_sys::{ FcObjectSetCreate, FcObjectSetDestroy, FcPattern, FcPatternAddString, FcPatternCreate, FcPatternDestroy, FcPatternGetInteger, FcPatternGetString, FcResultMatch, FcSetSystem, }; +use fonts_traits::{FontTemplate, FontTemplateDescriptor, LocalFontIdentifier}; use libc::{c_char, c_int}; use log::debug; use style::Atom; @@ -26,9 +27,7 @@ use style::values::computed::font::GenericFontFamily; use style::values::computed::{FontStretch, FontStyle, FontWeight}; use unicode_script::Script; -use super::LocalFontIdentifier; use crate::font::map_platform_values_to_style_values; -use crate::font_template::{FontTemplate, FontTemplateDescriptor}; use crate::platform::add_noto_fallback_families; use crate::{ EmojiPresentationPreference, FallbackFontSelectionOptions, FontIdentifier, diff --git a/components/fonts/platform/freetype/mod.rs b/components/fonts/platform/freetype/mod.rs index 09bad600c95..3698087927a 100644 --- a/components/fonts/platform/freetype/mod.rs +++ b/components/fonts/platform/freetype/mod.rs @@ -2,19 +2,6 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use std::convert::TryInto; -use std::fs::File; -use std::path::{Path, PathBuf}; -use std::str; - -use malloc_size_of_derive::MallocSizeOf; -use memmap2::Mmap; -use serde::{Deserialize, Serialize}; -use style::Atom; -use webrender_api::NativeFontHandle; - -use crate::{FontData, FontDataAndIndex}; - pub mod font; mod freetype_face; @@ -37,36 +24,3 @@ mod ohos { pub use self::ohos::font_list; mod library_handle; - -/// An identifier for a local font on systems using Freetype. -#[derive(Clone, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)] -pub struct LocalFontIdentifier { - /// The path to the font. - pub path: Atom, - /// The variation index within the font. - pub variation_index: i32, -} - -impl LocalFontIdentifier { - pub(crate) fn index(&self) -> u32 { - self.variation_index.try_into().unwrap() - } - - pub(crate) fn native_font_handle(&self) -> NativeFontHandle { - NativeFontHandle { - path: PathBuf::from(&*self.path), - index: self.variation_index as u32, - } - } - - pub(crate) fn font_data_and_index(&self) -> Option { - let file = File::open(Path::new(&*self.path)).ok()?; - let mmap = unsafe { Mmap::map(&file).ok()? }; - let data = FontData::from_bytes(&mmap); - - Some(FontDataAndIndex { - data, - index: self.variation_index as u32, - }) - } -} diff --git a/components/fonts/platform/macos/core_text_font_cache.rs b/components/fonts/platform/macos/core_text_font_cache.rs index 734a8ce7ff0..852932adb48 100644 --- a/components/fonts/platform/macos/core_text_font_cache.rs +++ b/components/fonts/platform/macos/core_text_font_cache.rs @@ -13,12 +13,12 @@ use core_foundation::string::{CFString, CFStringRef}; use core_foundation::url::{CFURL, kCFURLPOSIXPathStyle}; use core_graphics::display::CFDictionary; use core_text::font_descriptor::{kCTFontURLAttribute, kCTFontVariationAttribute}; +use fonts_traits::FontIdentifier; use parking_lot::RwLock; use webrender_api::FontVariation; use crate::FontData; use crate::platform::font::PlatformFont; -use crate::system_font_service::FontIdentifier; /// A cache of `CTFont` to avoid having to create `CTFont` instances over and over. It is /// always possible to create a `CTFont` using a `FontTemplate` even if it isn't in this diff --git a/components/fonts/platform/macos/font.rs b/components/fonts/platform/macos/font.rs index 9b711ffbfba..4d7ef164b7e 100644 --- a/components/fonts/platform/macos/font.rs +++ b/components/fonts/platform/macos/font.rs @@ -17,16 +17,16 @@ use core_text::font_descriptor::{ CTFontTraits, SymbolicTraitAccessors, TraitAccessors, kCTFontDefaultOrientation, }; use euclid::default::{Point2D, Rect, Size2D}; +use fonts_traits::{FontIdentifier, LocalFontIdentifier}; use log::debug; use skrifa::Tag; use style::values::computed::font::{FontStretch, FontStyle, FontWeight}; use webrender_api::{FontInstanceFlags, FontVariation}; use super::core_text_font_cache::CoreTextFontCache; -use super::font_list::LocalFontIdentifier; use crate::{ - CBDT, COLR, FontData, FontIdentifier, FontMetrics, FontTableMethods, FontTemplateDescriptor, - FractionalPixel, GlyphId, KERN, PlatformFontMethods, SBIX, map_platform_values_to_style_values, + CBDT, COLR, FontData, FontMetrics, FontTableMethods, FontTemplateDescriptor, FractionalPixel, + GlyphId, KERN, PlatformFontMethods, SBIX, map_platform_values_to_style_values, }; const KERN_PAIR_LEN: usize = 6; diff --git a/components/fonts/platform/macos/font_list.rs b/components/fonts/platform/macos/font_list.rs index 922b449737b..a5d9b6d936a 100644 --- a/components/fonts/platform/macos/font_list.rs +++ b/components/fonts/platform/macos/font_list.rs @@ -2,107 +2,20 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use std::fs::File; -use std::path::Path; - use base::text::{UnicodeBlock, UnicodeBlockMethod, unicode_plane}; -use log::{debug, warn}; -use malloc_size_of_derive::MallocSizeOf; -use memmap2::Mmap; -use read_fonts::types::NameId; -use read_fonts::{FileRef, TableProvider as _}; -use serde::{Deserialize, Serialize}; +use fonts_traits::LocalFontIdentifier; +use log::debug; use style::Atom; use style::values::computed::font::GenericFontFamily; use unicode_script::Script; -use webrender_api::NativeFontHandle; use crate::platform::add_noto_fallback_families; use crate::platform::font::CoreTextFontTraitsMapping; use crate::{ - EmojiPresentationPreference, FallbackFontSelectionOptions, FontData, FontDataAndIndex, - FontIdentifier, FontTemplate, FontTemplateDescriptor, LowercaseFontFamilyName, + EmojiPresentationPreference, FallbackFontSelectionOptions, FontIdentifier, FontTemplate, + FontTemplateDescriptor, LowercaseFontFamilyName, }; -/// An identifier for a local font on a MacOS system. These values comes from the CoreText -/// CTFontCollection. Note that `path` here is required. We do not load fonts that do not -/// have paths. -#[derive(Clone, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)] -pub struct LocalFontIdentifier { - pub postscript_name: Atom, - pub path: Atom, -} - -impl LocalFontIdentifier { - pub(crate) fn native_font_handle(&self) -> NativeFontHandle { - NativeFontHandle { - name: self.postscript_name.to_string(), - path: self.path.to_string(), - } - } - - pub(crate) fn index(&self) -> u32 { - 0 - } - - pub(crate) fn font_data_and_index(&self) -> Option { - let file = File::open(Path::new(&*self.path)).ok()?; - let mmap = unsafe { Mmap::map(&file).ok()? }; - - // Determine index - let file_ref = FileRef::new(mmap.as_ref()).ok()?; - let index = ttc_index_from_postscript_name(file_ref, &self.postscript_name); - - Some(FontDataAndIndex { - data: FontData::from_bytes(&mmap), - index, - }) - } -} - -/// CoreText font enumeration gives us a Postscript name rather than an index. -/// This functions maps from a Postscript name to an index. -/// -/// This mapping works for single-font files and for simple TTC files, but may not work in all cases. -/// We are not 100% sure which cases (if any) will not work. But we suspect that variable fonts may cause -/// issues due to the Postscript names corresponding to instances not being straightforward, and the possibility -/// that CoreText may return a non-standard in that scenerio. -fn ttc_index_from_postscript_name(font_file: FileRef<'_>, postscript_name: &str) -> u32 { - match font_file { - // File only contains one font: simply return 0 - FileRef::Font(_) => 0, - // File is a collection: iterate through each font in the collection and check - // whether the name matches - FileRef::Collection(collection) => { - for i in 0..collection.len() { - let font = collection.get(i).unwrap(); - let name_table = font.name().unwrap(); - if name_table - .name_record() - .iter() - .filter(|record| record.name_id() == NameId::POSTSCRIPT_NAME) - .any(|record| { - record - .string(name_table.string_data()) - .unwrap() - .chars() - .eq(postscript_name.chars()) - }) - { - return i; - } - } - - // If we fail to find a font, just use the first font in the file. - warn!( - "Font with postscript_name {} not found in collection", - postscript_name - ); - 0 - }, - } -} - pub fn for_each_available_family(mut callback: F) where F: FnMut(String), diff --git a/components/fonts/platform/mod.rs b/components/fonts/platform/mod.rs index 2c77d17d5d5..c8e1dc14800 100644 --- a/components/fonts/platform/mod.rs +++ b/components/fonts/platform/mod.rs @@ -22,13 +22,11 @@ use unicode_script::Script; ))] use crate::FallbackFontSelectionOptions; #[cfg(any(target_os = "linux", target_os = "android"))] -pub use crate::platform::freetype::{LocalFontIdentifier, font, font_list}; +pub use crate::platform::freetype::{font, font_list}; #[cfg(target_os = "macos")] -pub use crate::platform::macos::{ - core_text_font_cache, font, font_list, font_list::LocalFontIdentifier, -}; +pub use crate::platform::macos::{core_text_font_cache, font, font_list}; #[cfg(target_os = "windows")] -pub use crate::platform::windows::{font, font_list, font_list::LocalFontIdentifier}; +pub use crate::platform::windows::{font, font_list}; #[cfg(any(target_os = "linux", target_os = "android"))] pub mod freetype; diff --git a/components/fonts/platform/windows/font.rs b/components/fonts/platform/windows/font.rs index bd673b8a58f..9e2cc277835 100644 --- a/components/fonts/platform/windows/font.rs +++ b/components/fonts/platform/windows/font.rs @@ -17,6 +17,7 @@ use dwrote::{ DWRITE_FONT_AXIS_VALUE, DWRITE_FONT_SIMULATIONS_NONE, FontCollection, FontFace, FontFile, }; use euclid::default::{Point2D, Rect, Size2D}; +use fonts_traits::LocalFontIdentifier; use log::debug; use read_fonts::TableProvider; use skrifa::Tag; @@ -24,7 +25,6 @@ use style::Zero; use webrender_api::{FontInstanceFlags, FontVariation}; use winapi::shared::minwindef::{BOOL, FALSE}; -use super::font_list::LocalFontIdentifier; use crate::{ FontData, FontIdentifier, FontMetrics, FontTableMethods, FontTemplateDescriptor, FractionalPixel, GlyphId, PlatformFontMethods, diff --git a/components/fonts/platform/windows/font_list.rs b/components/fonts/platform/windows/font_list.rs index 72346c618b6..cbf6560af47 100644 --- a/components/fonts/platform/windows/font_list.rs +++ b/components/fonts/platform/windows/font_list.rs @@ -2,21 +2,18 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use std::hash::Hash; use std::sync::Arc; use base::text::{UnicodeBlock, UnicodeBlockMethod, unicode_plane}; -use dwrote::{Font, FontCollection, FontDescriptor, FontStretch, FontStyle}; -use malloc_size_of_derive::MallocSizeOf; -use serde::{Deserialize, Serialize}; +use dwrote::{Font, FontCollection, FontStretch, FontStyle}; +use fonts_traits::LocalFontIdentifier; use style::values::computed::font::GenericFontFamily; use style::values::computed::{FontStyle as StyleFontStyle, FontWeight as StyleFontWeight}; use style::values::specified::font::FontStretchKeyword; -use webrender_api::NativeFontHandle; use crate::{ - EmojiPresentationPreference, FallbackFontSelectionOptions, FontData, FontDataAndIndex, - FontIdentifier, FontTemplate, FontTemplateDescriptor, LowercaseFontFamilyName, + EmojiPresentationPreference, FallbackFontSelectionOptions, FontIdentifier, FontTemplate, + FontTemplateDescriptor, LowercaseFontFamilyName, }; pub fn for_each_available_family(mut callback: F) @@ -31,71 +28,6 @@ where } } -/// An identifier for a local font on a Windows system. -#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)] -pub struct LocalFontIdentifier { - /// The FontDescriptor of this font. - #[ignore_malloc_size_of = "dwrote does not support MallocSizeOf"] - pub font_descriptor: Arc, -} - -impl LocalFontIdentifier { - pub fn index(&self) -> u32 { - FontCollection::system() - .font_from_descriptor(&self.font_descriptor) - .ok() - .flatten() - .map_or(0, |font| font.create_font_face().get_index()) - } - - pub(crate) fn native_font_handle(&self) -> NativeFontHandle { - let face = FontCollection::system() - .font_from_descriptor(&self.font_descriptor) - .ok() - .flatten() - .expect("Could not create Font from FontDescriptor") - .create_font_face(); - let path = face - .files() - .ok() - .and_then(|files| files.first().cloned()) - .expect("Could not get FontFace files") - .font_file_path() - .ok() - .expect("Could not get FontFace files path"); - NativeFontHandle { - path, - index: face.get_index(), - } - } - - pub(crate) fn font_data_and_index(&self) -> Option { - let font = FontCollection::system() - .font_from_descriptor(&self.font_descriptor) - .ok()??; - let face = font.create_font_face(); - let index = face.get_index(); - let files = face.files().ok()?; - assert!(!files.is_empty()); - - let data = files[0].font_file_bytes().ok()?; - let data = FontData::from_bytes(&data); - - Some(FontDataAndIndex { data, index }) - } -} - -impl Eq for LocalFontIdentifier {} - -impl Hash for LocalFontIdentifier { - fn hash(&self, state: &mut H) { - self.font_descriptor.family_name.hash(state); - self.font_descriptor.weight.to_u32().hash(state); - self.font_descriptor.stretch.to_u32().hash(state); - self.font_descriptor.style.to_u32().hash(state); - } -} - pub fn for_each_variation(family_name: &str, mut callback: F) where F: FnMut(FontTemplate), @@ -107,7 +39,7 @@ where let Ok(font) = family.font(i) else { continue; }; - let template_descriptor = (&font).into(); + let template_descriptor = font_template_descriptor_from_font(&font); let local_font_identifier = LocalFontIdentifier { font_descriptor: Arc::new(font.to_descriptor()), }; @@ -383,29 +315,27 @@ pub fn fallback_font_families(options: FallbackFontSelectionOptions) -> Vec<&'st families } -impl From<&Font> for FontTemplateDescriptor { - fn from(font: &Font) -> Self { - let style = match font.style() { - FontStyle::Normal => StyleFontStyle::NORMAL, - FontStyle::Oblique => StyleFontStyle::OBLIQUE, - FontStyle::Italic => StyleFontStyle::ITALIC, - }; - let weight = StyleFontWeight::from_float(font.weight().to_u32() as f32); - let stretch = match font.stretch() { - FontStretch::Undefined => FontStretchKeyword::Normal, - FontStretch::UltraCondensed => FontStretchKeyword::UltraCondensed, - FontStretch::ExtraCondensed => FontStretchKeyword::ExtraCondensed, - FontStretch::Condensed => FontStretchKeyword::Condensed, - FontStretch::SemiCondensed => FontStretchKeyword::SemiCondensed, - FontStretch::Normal => FontStretchKeyword::Normal, - FontStretch::SemiExpanded => FontStretchKeyword::SemiExpanded, - FontStretch::Expanded => FontStretchKeyword::Expanded, - FontStretch::ExtraExpanded => FontStretchKeyword::ExtraExpanded, - FontStretch::UltraExpanded => FontStretchKeyword::UltraExpanded, - } - .compute(); - FontTemplateDescriptor::new(weight, stretch, style) +fn font_template_descriptor_from_font(font: &Font) -> FontTemplateDescriptor { + let style = match font.style() { + FontStyle::Normal => StyleFontStyle::NORMAL, + FontStyle::Oblique => StyleFontStyle::OBLIQUE, + FontStyle::Italic => StyleFontStyle::ITALIC, + }; + let weight = StyleFontWeight::from_float(font.weight().to_u32() as f32); + let stretch = match font.stretch() { + FontStretch::Undefined => FontStretchKeyword::Normal, + FontStretch::UltraCondensed => FontStretchKeyword::UltraCondensed, + FontStretch::ExtraCondensed => FontStretchKeyword::ExtraCondensed, + FontStretch::Condensed => FontStretchKeyword::Condensed, + FontStretch::SemiCondensed => FontStretchKeyword::SemiCondensed, + FontStretch::Normal => FontStretchKeyword::Normal, + FontStretch::SemiExpanded => FontStretchKeyword::SemiExpanded, + FontStretch::Expanded => FontStretchKeyword::Expanded, + FontStretch::ExtraExpanded => FontStretchKeyword::ExtraExpanded, + FontStretch::UltraExpanded => FontStretchKeyword::UltraExpanded, } + .compute(); + FontTemplateDescriptor::new(weight, stretch, style) } pub fn default_system_generic_font_family(generic: GenericFontFamily) -> LowercaseFontFamilyName { diff --git a/components/fonts/system_font_service.rs b/components/fonts/system_font_service.rs index dd2de815596..8f88d761648 100644 --- a/components/fonts/system_font_service.rs +++ b/components/fonts/system_font_service.rs @@ -5,11 +5,13 @@ use std::borrow::ToOwned; use std::cell::OnceCell; use std::collections::HashMap; -use std::ops::{Deref, RangeInclusive}; -use std::{fmt, thread}; +use std::thread; use app_units::Au; use compositing_traits::CrossProcessCompositorApi; +use fonts_traits::{ + FontDescriptor, FontIdentifier, FontTemplate, FontTemplateRef, LowercaseFontFamilyName, +}; use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use log::debug; use malloc_size_of::MallocSizeOf as MallocSizeOfTrait; @@ -21,38 +23,14 @@ use profile_traits::mem::{ use profile_traits::path; use serde::{Deserialize, Serialize}; use servo_config::pref; -use servo_url::ServoUrl; -use style::font_face::{FontFaceRuleData, FontStyle as FontFaceStyle}; -use style::values::computed::font::{ - FixedPoint, FontStyleFixedPoint, GenericFontFamily, SingleFontFamily, -}; -use style::values::computed::{FontStretch, FontWeight}; -use style::values::specified::FontStretch as SpecifiedFontStretch; +use style::values::computed::font::{GenericFontFamily, SingleFontFamily}; use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey, FontVariation}; -use crate::font::FontDescriptor; use crate::font_store::FontStore; -use crate::font_template::{FontTemplate, FontTemplateRef}; -use crate::platform::LocalFontIdentifier; use crate::platform::font_list::{ default_system_generic_font_family, for_each_available_family, for_each_variation, }; -#[derive(Clone, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)] -pub enum FontIdentifier { - Local(LocalFontIdentifier), - Web(ServoUrl), -} - -impl FontIdentifier { - pub fn index(&self) -> u32 { - match *self { - Self::Local(ref local_font_identifier) => local_font_identifier.index(), - Self::Web(_) => 0, - } - } -} - /// Commands that the `FontContext` sends to the `SystemFontService`. #[derive(Debug, Deserialize, Serialize)] pub enum SystemFontServiceMessage { @@ -379,89 +357,6 @@ pub struct SystemFontServiceProxy { templates: RwLock>>, } -/// A version of `FontStyle` from Stylo that is serializable. Normally this is not -/// because the specified version of `FontStyle` contains floats. -#[derive(Clone, Debug, Deserialize, Serialize)] -pub enum ComputedFontStyleDescriptor { - Italic, - Oblique(FontStyleFixedPoint, FontStyleFixedPoint), -} - -/// This data structure represents the various optional descriptors that can be -/// applied to a `@font-face` rule in CSS. These are used to create a [`FontTemplate`] -/// from the given font data used as the source of the `@font-face` rule. If values -/// like weight, stretch, and style are not specified they are initialized based -/// on the contents of the font itself. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -pub struct CSSFontFaceDescriptors { - pub family_name: LowercaseFontFamilyName, - pub weight: Option<(FontWeight, FontWeight)>, - pub stretch: Option<(FontStretch, FontStretch)>, - pub style: Option, - pub unicode_range: Option>>, -} - -impl CSSFontFaceDescriptors { - pub fn new(family_name: &str) -> Self { - CSSFontFaceDescriptors { - family_name: family_name.into(), - ..Default::default() - } - } -} - -impl From<&FontFaceRuleData> for CSSFontFaceDescriptors { - fn from(rule_data: &FontFaceRuleData) -> Self { - let family_name = rule_data - .family - .as_ref() - .expect("Expected rule to contain a font family.") - .name - .clone(); - let weight = rule_data - .weight - .as_ref() - .map(|weight_range| (weight_range.0.compute(), weight_range.1.compute())); - - let stretch_to_computed = |specified: SpecifiedFontStretch| match specified { - SpecifiedFontStretch::Stretch(percentage) => { - FontStretch::from_percentage(percentage.compute().0) - }, - SpecifiedFontStretch::Keyword(keyword) => keyword.compute(), - SpecifiedFontStretch::System(_) => FontStretch::NORMAL, - }; - let stretch = rule_data.stretch.as_ref().map(|stretch_range| { - ( - stretch_to_computed(stretch_range.0), - stretch_to_computed(stretch_range.1), - ) - }); - - fn style_to_computed(specified: &FontFaceStyle) -> ComputedFontStyleDescriptor { - match specified { - FontFaceStyle::Italic => ComputedFontStyleDescriptor::Italic, - FontFaceStyle::Oblique(angle_a, angle_b) => ComputedFontStyleDescriptor::Oblique( - FixedPoint::from_float(angle_a.degrees()), - FixedPoint::from_float(angle_b.degrees()), - ), - } - } - let style = rule_data.style.as_ref().map(style_to_computed); - let unicode_range = rule_data - .unicode_range - .as_ref() - .map(|ranges| ranges.iter().map(|range| range.start..=range.end).collect()); - - CSSFontFaceDescriptors { - family_name: family_name.into(), - weight, - stretch, - style, - unicode_range, - } - } -} - impl SystemFontServiceProxy { pub fn exit(&self) { let (response_chan, response_port) = ipc::channel().unwrap(); @@ -584,31 +479,3 @@ impl SystemFontServiceProxy { .expect("Failed to communicate with system font service.") } } - -#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)] -pub struct LowercaseFontFamilyName { - inner: String, -} - -impl> From for LowercaseFontFamilyName { - fn from(value: T) -> Self { - LowercaseFontFamilyName { - inner: value.as_ref().to_lowercase(), - } - } -} - -impl Deref for LowercaseFontFamilyName { - type Target = str; - - #[inline] - fn deref(&self) -> &str { - &self.inner - } -} - -impl fmt::Display for LowercaseFontFamilyName { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.inner.fmt(f) - } -} diff --git a/components/shared/fonts/Cargo.toml b/components/shared/fonts/Cargo.toml index d2fda9441da..3e51d3ce200 100644 --- a/components/shared/fonts/Cargo.toml +++ b/components/shared/fonts/Cargo.toml @@ -12,8 +12,18 @@ name = "fonts_traits" path = "lib.rs" [dependencies] +atomic_refcell = { workspace = true } ipc-channel = { workspace = true } +log = { workspace = true } malloc_size_of = { workspace = true } malloc_size_of_derive = { workspace = true } +memmap2 = { workspace = true } range = { path = "../../range" } +read-fonts = { workspace = true } serde = { workspace = true } +servo_url = { path = "../../url" } +stylo = { workspace = true } +webrender_api = { workspace = true } + +[target.'cfg(target_os = "windows")'.dependencies] +dwrote = { workspace = true } diff --git a/components/shared/fonts/font_descriptor.rs b/components/shared/fonts/font_descriptor.rs new file mode 100644 index 00000000000..d5d3b05ceb3 --- /dev/null +++ b/components/shared/fonts/font_descriptor.rs @@ -0,0 +1,164 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use std::ops::{Deref, RangeInclusive}; + +use malloc_size_of_derive::MallocSizeOf; +use serde::{Deserialize, Serialize}; +use style::computed_values::font_variant_caps; +use style::font_face::{FontFaceRuleData, FontStyle as FontFaceStyle}; +use style::properties::style_structs::Font as FontStyleStruct; +use style::values::computed::font::{FixedPoint, FontStyleFixedPoint}; +use style::values::computed::{Au, FontStretch, FontStyle, FontWeight}; +use style::values::specified::FontStretch as SpecifiedFontStretch; +use webrender_api::FontVariation; + +/// `FontDescriptor` describes the parameters of a `Font`. It represents rendering a given font +/// template at a particular size, with a particular font-variant-caps applied, etc. This contrasts +/// with `FontTemplateDescriptor` in that the latter represents only the parameters inherent in the +/// font data (weight, stretch, etc.). +#[derive(Clone, Debug, Deserialize, Hash, MallocSizeOf, PartialEq, Serialize)] +pub struct FontDescriptor { + pub weight: FontWeight, + pub stretch: FontStretch, + pub style: FontStyle, + pub variant: font_variant_caps::T, + pub pt_size: Au, + pub variation_settings: Vec, +} + +impl Eq for FontDescriptor {} + +impl<'a> From<&'a FontStyleStruct> for FontDescriptor { + fn from(style: &'a FontStyleStruct) -> Self { + let variation_settings = style + .clone_font_variation_settings() + .0 + .into_iter() + .map(|setting| FontVariation { + tag: setting.tag.0, + value: setting.value, + }) + .collect(); + FontDescriptor { + weight: style.font_weight, + stretch: style.font_stretch, + style: style.font_style, + variant: style.font_variant_caps, + pt_size: Au::from_f32_px(style.font_size.computed_size().px()), + variation_settings, + } + } +} + +/// A version of `FontStyle` from Stylo that is serializable. Normally this is not +/// because the specified version of `FontStyle` contains floats. +#[derive(Clone, Debug, Deserialize, Serialize)] +pub enum ComputedFontStyleDescriptor { + Italic, + Oblique(FontStyleFixedPoint, FontStyleFixedPoint), +} + +/// This data structure represents the various optional descriptors that can be +/// applied to a `@font-face` rule in CSS. These are used to create a [`FontTemplate`] +/// from the given font data used as the source of the `@font-face` rule. If values +/// like weight, stretch, and style are not specified they are initialized based +/// on the contents of the font itself. +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +pub struct CSSFontFaceDescriptors { + pub family_name: LowercaseFontFamilyName, + pub weight: Option<(FontWeight, FontWeight)>, + pub stretch: Option<(FontStretch, FontStretch)>, + pub style: Option, + pub unicode_range: Option>>, +} + +impl CSSFontFaceDescriptors { + pub fn new(family_name: &str) -> Self { + CSSFontFaceDescriptors { + family_name: family_name.into(), + ..Default::default() + } + } +} + +impl From<&FontFaceRuleData> for CSSFontFaceDescriptors { + fn from(rule_data: &FontFaceRuleData) -> Self { + let family_name = rule_data + .family + .as_ref() + .expect("Expected rule to contain a font family.") + .name + .clone(); + let weight = rule_data + .weight + .as_ref() + .map(|weight_range| (weight_range.0.compute(), weight_range.1.compute())); + + let stretch_to_computed = |specified: SpecifiedFontStretch| match specified { + SpecifiedFontStretch::Stretch(percentage) => { + FontStretch::from_percentage(percentage.compute().0) + }, + SpecifiedFontStretch::Keyword(keyword) => keyword.compute(), + SpecifiedFontStretch::System(_) => FontStretch::NORMAL, + }; + let stretch = rule_data.stretch.as_ref().map(|stretch_range| { + ( + stretch_to_computed(stretch_range.0), + stretch_to_computed(stretch_range.1), + ) + }); + + fn style_to_computed(specified: &FontFaceStyle) -> ComputedFontStyleDescriptor { + match specified { + FontFaceStyle::Italic => ComputedFontStyleDescriptor::Italic, + FontFaceStyle::Oblique(angle_a, angle_b) => ComputedFontStyleDescriptor::Oblique( + FixedPoint::from_float(angle_a.degrees()), + FixedPoint::from_float(angle_b.degrees()), + ), + } + } + let style = rule_data.style.as_ref().map(style_to_computed); + let unicode_range = rule_data + .unicode_range + .as_ref() + .map(|ranges| ranges.iter().map(|range| range.start..=range.end).collect()); + + CSSFontFaceDescriptors { + family_name: family_name.into(), + weight, + stretch, + style, + unicode_range, + } + } +} + +#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)] +pub struct LowercaseFontFamilyName { + inner: String, +} + +impl> From for LowercaseFontFamilyName { + fn from(value: T) -> Self { + LowercaseFontFamilyName { + inner: value.as_ref().to_lowercase(), + } + } +} + +impl Deref for LowercaseFontFamilyName { + type Target = str; + + #[inline] + fn deref(&self) -> &str { + &self.inner + } +} + +impl std::fmt::Display for LowercaseFontFamilyName { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + self.inner.fmt(f) + } +} diff --git a/components/shared/fonts/font_identifier.rs b/components/shared/fonts/font_identifier.rs new file mode 100644 index 00000000000..00050c968c1 --- /dev/null +++ b/components/shared/fonts/font_identifier.rs @@ -0,0 +1,246 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use malloc_size_of_derive::MallocSizeOf; +pub use platform::LocalFontIdentifier; +use serde::{Deserialize, Serialize}; +use servo_url::ServoUrl; + +#[derive(Clone, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)] +pub enum FontIdentifier { + Local(LocalFontIdentifier), + Web(ServoUrl), +} + +impl FontIdentifier { + pub fn index(&self) -> u32 { + match *self { + Self::Local(ref local_font_identifier) => local_font_identifier.index(), + Self::Web(_) => 0, + } + } +} + +#[cfg(any(target_os = "linux", target_os = "android"))] +mod platform { + use std::fs::File; + use std::path::{Path, PathBuf}; + + use malloc_size_of_derive::MallocSizeOf; + use memmap2::Mmap; + use serde::{Deserialize, Serialize}; + use style::Atom; + use webrender_api::NativeFontHandle; + + use crate::{FontData, FontDataAndIndex}; + + /// An identifier for a local font on systems using Freetype. + #[derive(Clone, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)] + pub struct LocalFontIdentifier { + /// The path to the font. + pub path: Atom, + /// The variation index within the font. + pub variation_index: i32, + } + + impl LocalFontIdentifier { + pub fn index(&self) -> u32 { + self.variation_index.try_into().unwrap() + } + + pub fn native_font_handle(&self) -> NativeFontHandle { + NativeFontHandle { + path: PathBuf::from(&*self.path), + index: self.variation_index as u32, + } + } + + #[allow(unsafe_code)] + pub fn font_data_and_index(&self) -> Option { + let file = File::open(Path::new(&*self.path)).ok()?; + let mmap = unsafe { Mmap::map(&file).ok()? }; + let data = FontData::from_bytes(&mmap); + + Some(FontDataAndIndex { + data, + index: self.variation_index as u32, + }) + } + } +} + +#[cfg(target_os = "macos")] +mod platform { + use std::fs::File; + use std::path::Path; + + use log::warn; + use malloc_size_of_derive::MallocSizeOf; + use memmap2::Mmap; + use read_fonts::types::NameId; + use read_fonts::{FileRef, TableProvider}; + use serde::{Deserialize, Serialize}; + use style::Atom; + use webrender_api::NativeFontHandle; + + use crate::{FontData, FontDataAndIndex}; + + /// An identifier for a local font on a MacOS system. These values comes from the CoreText + /// CTFontCollection. Note that `path` here is required. We do not load fonts that do not + /// have paths. + #[derive(Clone, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)] + pub struct LocalFontIdentifier { + pub postscript_name: Atom, + pub path: Atom, + } + + impl LocalFontIdentifier { + pub fn native_font_handle(&self) -> NativeFontHandle { + NativeFontHandle { + name: self.postscript_name.to_string(), + path: self.path.to_string(), + } + } + + pub(crate) fn index(&self) -> u32 { + 0 + } + + #[allow(unsafe_code)] + pub fn font_data_and_index(&self) -> Option { + let file = File::open(Path::new(&*self.path)).ok()?; + let mmap = unsafe { Mmap::map(&file).ok()? }; + + // Determine index + let file_ref = FileRef::new(mmap.as_ref()).ok()?; + let index = ttc_index_from_postscript_name(file_ref, &self.postscript_name); + + Some(FontDataAndIndex { + data: FontData::from_bytes(&mmap), + index, + }) + } + } + + /// CoreText font enumeration gives us a Postscript name rather than an index. + /// This functions maps from a Postscript name to an index. + /// + /// This mapping works for single-font files and for simple TTC files, but may not work in all cases. + /// We are not 100% sure which cases (if any) will not work. But we suspect that variable fonts may cause + /// issues due to the Postscript names corresponding to instances not being straightforward, and the possibility + /// that CoreText may return a non-standard in that scenerio. + fn ttc_index_from_postscript_name(font_file: FileRef<'_>, postscript_name: &str) -> u32 { + match font_file { + // File only contains one font: simply return 0 + FileRef::Font(_) => 0, + // File is a collection: iterate through each font in the collection and check + // whether the name matches + FileRef::Collection(collection) => { + for i in 0..collection.len() { + let font = collection.get(i).unwrap(); + let name_table = font.name().unwrap(); + if name_table + .name_record() + .iter() + .filter(|record| record.name_id() == NameId::POSTSCRIPT_NAME) + .any(|record| { + record + .string(name_table.string_data()) + .unwrap() + .chars() + .eq(postscript_name.chars()) + }) + { + return i; + } + } + + // If we fail to find a font, just use the first font in the file. + warn!( + "Font with postscript_name {} not found in collection", + postscript_name + ); + 0 + }, + } + } +} + +#[cfg(target_os = "windows")] +mod platform { + use std::hash::Hash; + use std::sync::Arc; + + use dwrote::{FontCollection, FontDescriptor}; + use malloc_size_of_derive::MallocSizeOf; + use serde::{Deserialize, Serialize}; + use webrender_api::NativeFontHandle; + + use crate::{FontData, FontDataAndIndex}; + + /// An identifier for a local font on a Windows system. + #[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)] + pub struct LocalFontIdentifier { + /// The FontDescriptor of this font. + #[ignore_malloc_size_of = "dwrote does not support MallocSizeOf"] + pub font_descriptor: Arc, + } + + impl LocalFontIdentifier { + pub fn index(&self) -> u32 { + FontCollection::system() + .font_from_descriptor(&self.font_descriptor) + .ok() + .flatten() + .map_or(0, |font| font.create_font_face().get_index()) + } + + pub fn native_font_handle(&self) -> NativeFontHandle { + let face = FontCollection::system() + .font_from_descriptor(&self.font_descriptor) + .ok() + .flatten() + .expect("Could not create Font from FontDescriptor") + .create_font_face(); + let path = face + .files() + .ok() + .and_then(|files| files.first().cloned()) + .expect("Could not get FontFace files") + .font_file_path() + .ok() + .expect("Could not get FontFace files path"); + NativeFontHandle { + path, + index: face.get_index(), + } + } + + pub fn font_data_and_index(&self) -> Option { + let font = FontCollection::system() + .font_from_descriptor(&self.font_descriptor) + .ok()??; + let face = font.create_font_face(); + let index = face.get_index(); + let files = face.files().ok()?; + assert!(!files.is_empty()); + + let data = files[0].font_file_bytes().ok()?; + let data = FontData::from_bytes(&data); + + Some(FontDataAndIndex { data, index }) + } + } + + impl Eq for LocalFontIdentifier {} + + impl Hash for LocalFontIdentifier { + fn hash(&self, state: &mut H) { + self.font_descriptor.family_name.hash(state); + self.font_descriptor.weight.to_u32().hash(state); + self.font_descriptor.stretch.to_u32().hash(state); + self.font_descriptor.style.to_u32().hash(state); + } + } +} diff --git a/components/fonts/font_template.rs b/components/shared/fonts/font_template.rs similarity index 98% rename from components/fonts/font_template.rs rename to components/shared/fonts/font_template.rs index b8173ee0317..3c16f051f69 100644 --- a/components/fonts/font_template.rs +++ b/components/shared/fonts/font_template.rs @@ -14,10 +14,7 @@ use style::computed_values::font_style::T as FontStyle; use style::stylesheets::DocumentStyleSheet; use style::values::computed::font::FontWeight; -use crate::font::FontDescriptor; -use crate::system_font_service::{ - CSSFontFaceDescriptors, ComputedFontStyleDescriptor, FontIdentifier, -}; +use crate::{CSSFontFaceDescriptors, ComputedFontStyleDescriptor, FontDescriptor, FontIdentifier}; /// A reference to a [`FontTemplate`] with shared ownership and mutability. #[derive(Clone, Debug, MallocSizeOf)] @@ -120,7 +117,7 @@ impl FontTemplateDescriptor { self.stretch.1 >= descriptor_to_match.stretch } - pub(crate) fn override_values_with_css_font_template_descriptors( + pub fn override_values_with_css_font_template_descriptors( &mut self, css_font_template_descriptors: &CSSFontFaceDescriptors, ) { @@ -512,7 +509,7 @@ impl FontMatchDistanceMethod for FontStyle { } } -pub(crate) trait IsOblique { +pub trait IsOblique { fn is_oblique(&self) -> bool; } diff --git a/components/shared/fonts/lib.rs b/components/shared/fonts/lib.rs index d56c63dbde1..9370a3eddda 100644 --- a/components/shared/fonts/lib.rs +++ b/components/shared/fonts/lib.rs @@ -4,8 +4,15 @@ #![deny(unsafe_code)] +mod font_descriptor; +mod font_identifier; +mod font_template; + use std::sync::Arc; +pub use font_descriptor::*; +pub use font_identifier::*; +pub use font_template::*; use ipc_channel::ipc::IpcSharedMemory; use malloc_size_of_derive::MallocSizeOf; use range::{RangeIndex, int_range_index};