diff options
author | Martin Robinson <mrobinson@igalia.com> | 2024-04-29 19:02:07 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-04-29 17:02:07 +0000 |
commit | 4732da347795c7a9e009a5125c20c1f5c3215209 (patch) | |
tree | 9244f043b9049c2e14d4984fa8012e3eb7c15f87 /components | |
parent | 628e33bfa95b286e1b8b974e426ffdad7850097e (diff) | |
download | servo-4732da347795c7a9e009a5125c20c1f5c3215209.tar.gz servo-4732da347795c7a9e009a5125c20c1f5c3215209.zip |
fonts: Add support for more @font-face features (#32164)
There are a couple major changes here:
1. Support is added for the `weight`, `style`, `stretch` and
`unicode-range` declarations in `@font-face`.
2. Font matching in the font cache can return templates and
`FontGroupFamily` can own mulitple templates. This is due to needing
support for "composite fonts". These are `@font-face` declarations
that only differ in their `unicode-range` definition.
This fixes a lot of non-determinism in font selection especially when
dealing with pages that define "composite faces." A notable example of
such a page is servo.org, which now consistently displays the correct
web font.
One test starts to fail due to an uncovered bug, but this will be fixed
in a followup change.
Fixes #20686.
Fixes #20684.
Co-authored-by: Mukilan Thiyagarajan <mukilan@igalia.com>
Diffstat (limited to 'components')
-rw-r--r-- | components/gfx/Cargo.toml | 1 | ||||
-rw-r--r-- | components/gfx/font.rs | 195 | ||||
-rw-r--r-- | components/gfx/font_cache_thread.rs | 450 | ||||
-rw-r--r-- | components/gfx/font_context.rs | 84 | ||||
-rw-r--r-- | components/gfx/font_template.rs | 133 | ||||
-rw-r--r-- | components/gfx/platform/freetype/android/font_list.rs | 6 | ||||
-rw-r--r-- | components/gfx/platform/freetype/font.rs | 6 | ||||
-rw-r--r-- | components/gfx/platform/freetype/font_list.rs | 6 | ||||
-rw-r--r-- | components/gfx/platform/macos/font.rs | 6 | ||||
-rw-r--r-- | components/gfx/platform/macos/font_list.rs | 7 | ||||
-rw-r--r-- | components/gfx/platform/windows/font.rs | 6 | ||||
-rw-r--r-- | components/gfx/tests/font_context.rs | 76 | ||||
-rw-r--r-- | components/gfx/tests/font_template.rs | 36 |
13 files changed, 598 insertions, 414 deletions
diff --git a/components/gfx/Cargo.toml b/components/gfx/Cargo.toml index 976687b953e..c9c0d388fcb 100644 --- a/components/gfx/Cargo.toml +++ b/components/gfx/Cargo.toml @@ -16,6 +16,7 @@ doctest = false [dependencies] app_units = { workspace = true } bitflags = { workspace = true } +cssparser = { workspace = true } euclid = { workspace = true } fnv = { workspace = true } fontsan = { git = "https://github.com/servo/fontsan" } diff --git a/components/gfx/font.rs b/components/gfx/font.rs index 273d7970ca0..862a11a05dc 100644 --- a/components/gfx/font.rs +++ b/components/gfx/font.rs @@ -21,6 +21,7 @@ use smallvec::SmallVec; use style::computed_values::font_variant_caps; use style::properties::style_structs::Font as FontStyleStruct; use style::values::computed::font::{GenericFontFamily, SingleFontFamily}; +use style::values::computed::{FontStretch, FontStyle, FontWeight}; use unicode_script::Script; use webrender_api::FontInstanceKey; @@ -58,7 +59,7 @@ pub trait PlatformFontMethods: Sized { pt_size: Option<Au>, ) -> Result<PlatformFont, &'static str> { let data = template.data(); - let face_index = template.borrow().identifier().index(); + let face_index = template.identifier().index(); let font_identifier = template.borrow().identifier.clone(); Self::new_from_data(font_identifier, data, face_index, pt_size) } @@ -150,17 +151,23 @@ impl FontMetrics { /// 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, Eq, Hash, PartialEq)] +#[derive(Clone, Debug, Deserialize, Hash, PartialEq, Serialize)] pub struct FontDescriptor { - pub template_descriptor: FontTemplateDescriptor, + pub weight: FontWeight, + pub stretch: FontStretch, + pub style: FontStyle, pub variant: font_variant_caps::T, pub pt_size: Au, } +impl Eq for FontDescriptor {} + impl<'a> From<&'a FontStyleStruct> for FontDescriptor { fn from(style: &'a FontStyleStruct) -> Self { FontDescriptor { - template_descriptor: FontTemplateDescriptor::from(style), + 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()), } @@ -209,7 +216,7 @@ impl Font { /// A unique identifier for the font, allowing comparison. pub fn identifier(&self) -> FontIdentifier { - self.template.borrow().identifier.clone() + self.template.identifier() } } @@ -405,7 +412,7 @@ impl FontGroup { .font_family .families .iter() - .map(|family| FontGroupFamily::new(descriptor.clone(), family)) + .map(|family| FontGroupFamily::new(family)) .collect(); FontGroup { @@ -436,19 +443,28 @@ impl FontGroup { Some(font) }; - let has_glyph = |font: &FontRef| font.borrow().has_glyph_for(codepoint); + let glyph_in_font = |font: &FontRef| font.borrow().has_glyph_for(codepoint); + let char_in_template = + |template: FontTemplateRef| template.char_in_unicode_range(codepoint); - if let Some(font) = self.find(font_context, has_glyph) { + if let Some(font) = self.find(font_context, char_in_template, glyph_in_font) { return font_or_synthesized_small_caps(font); } if let Some(ref last_matching_fallback) = self.last_matching_fallback { - if has_glyph(last_matching_fallback) { + if char_in_template(last_matching_fallback.borrow().template.clone()) && + glyph_in_font(last_matching_fallback) + { return font_or_synthesized_small_caps(last_matching_fallback.clone()); } } - if let Some(font) = self.find_fallback(font_context, Some(codepoint), has_glyph) { + if let Some(font) = self.find_fallback( + font_context, + Some(codepoint), + char_in_template, + glyph_in_font, + ) { self.last_matching_fallback = Some(font.clone()); return font_or_synthesized_small_caps(font); } @@ -458,80 +474,167 @@ impl FontGroup { /// Find the first available font in the group, or the first available fallback font. pub fn first<S: FontSource>(&mut self, font_context: &mut FontContext<S>) -> Option<FontRef> { - self.find(font_context, |_| true) - .or_else(|| self.find_fallback(font_context, None, |_| true)) + // From https://drafts.csswg.org/css-fonts/#first-available-font: + // > The first available font, used for example in the definition of font-relative lengths + // > such as ex or in the definition of the line-height property, is defined to be the first + // > font for which the character U+0020 (space) is not excluded by a unicode-range, given the + // > font families in the font-family list (or a user agent’s default font if none are + // > available). + // > Note: it does not matter whether that font actually has a glyph for the space character. + let space_in_template = |template: FontTemplateRef| template.char_in_unicode_range(' '); + let font_predicate = |_: &FontRef| true; + self.find(font_context, space_in_template, font_predicate) + .or_else(|| self.find_fallback(font_context, None, space_in_template, font_predicate)) } - /// Find a font which returns true for `predicate`. This method mutates because we may need to - /// load new font data in the process of finding a suitable font. - fn find<S, P>(&mut self, font_context: &mut FontContext<S>, predicate: P) -> Option<FontRef> + /// Attempts to find a font which matches the given `template_predicate` and `font_predicate`. + /// This method mutates because we may need to load new font data in the process of finding + /// a suitable font. + fn find<S, TemplatePredicate, FontPredicate>( + &mut self, + font_context: &mut FontContext<S>, + template_predicate: TemplatePredicate, + font_predicate: FontPredicate, + ) -> Option<FontRef> where S: FontSource, - P: FnMut(&FontRef) -> bool, + TemplatePredicate: Fn(FontTemplateRef) -> bool, + FontPredicate: Fn(&FontRef) -> bool, { + let font_descriptor = self.descriptor.clone(); self.families .iter_mut() - .filter_map(|family| family.font(font_context)) - .find(predicate) + .filter_map(|font_group_family| { + font_group_family.find( + &font_descriptor, + font_context, + &template_predicate, + &font_predicate, + ) + }) + .next() } - /// Attempts to find a suitable fallback font which matches the `predicate`. The default - /// family (i.e. "serif") will be tried first, followed by platform-specific family names. - /// If a `codepoint` is provided, then its Unicode block may be used to refine the list of - /// family names which will be tried. - fn find_fallback<S, P>( + /// Attempts to find a suitable fallback font which matches the given `template_predicate` and + /// `font_predicate`. The default family (i.e. "serif") will be tried first, followed by + /// platform-specific family names. If a `codepoint` is provided, then its Unicode block may be + /// used to refine the list of family names which will be tried. + fn find_fallback<S, TemplatePredicate, FontPredicate>( &mut self, font_context: &mut FontContext<S>, codepoint: Option<char>, - predicate: P, + template_predicate: TemplatePredicate, + font_predicate: FontPredicate, ) -> Option<FontRef> where S: FontSource, - P: FnMut(&FontRef) -> bool, + TemplatePredicate: Fn(FontTemplateRef) -> bool, + FontPredicate: Fn(&FontRef) -> bool, { - iter::once(FontFamilyDescriptor::default()) + iter::once(FontFamilyDescriptor::serif()) .chain(fallback_font_families(codepoint).into_iter().map(|family| { FontFamilyDescriptor::new(FontFamilyName::from(family), FontSearchScope::Local) })) - .filter_map(|family| font_context.font(&self.descriptor, &family)) - .find(predicate) + .into_iter() + .filter_map(|family_descriptor| { + FontGroupFamily { + family_descriptor, + members: None, + } + .find( + &self.descriptor, + font_context, + &template_predicate, + &font_predicate, + ) + }) + .next() } } +/// A [`FontGroupFamily`] can have multiple members if it is a "composite face", meaning +/// that it is defined by multiple `@font-face` declarations which vary only by their +/// `unicode-range` descriptors. In this case, font selection will select a single member +/// that contains the necessary unicode character. Unicode ranges are specified by the +/// [`FontGroupFamilyMember::template`] member. +#[derive(Debug)] +struct FontGroupFamilyMember { + template: FontTemplateRef, + font: Option<FontRef>, + loaded: bool, +} + /// A `FontGroupFamily` is a single font family in a `FontGroup`. It corresponds to one of the /// families listed in the `font-family` CSS property. The corresponding font data is lazy-loaded, -/// only if actually needed. +/// only if actually needed. A single `FontGroupFamily` can have multiple fonts, in the case that +/// individual fonts only cover part of the Unicode range. #[derive(Debug)] struct FontGroupFamily { - font_descriptor: FontDescriptor, family_descriptor: FontFamilyDescriptor, - loaded: bool, - font: Option<FontRef>, + members: Option<Vec<FontGroupFamilyMember>>, } impl FontGroupFamily { - fn new(font_descriptor: FontDescriptor, family: &SingleFontFamily) -> FontGroupFamily { + fn new(family: &SingleFontFamily) -> FontGroupFamily { let family_descriptor = FontFamilyDescriptor::new(FontFamilyName::from(family), FontSearchScope::Any); FontGroupFamily { - font_descriptor, family_descriptor, - loaded: false, - font: None, + members: None, } } - /// Returns the font within this family which matches the style. We'll fetch the data from the - /// `FontContext` the first time this method is called, and return a cached reference on - /// subsequent calls. - fn font<S: FontSource>(&mut self, font_context: &mut FontContext<S>) -> Option<FontRef> { - if !self.loaded { - self.font = font_context.font(&self.font_descriptor, &self.family_descriptor); - self.loaded = true; - } + fn find<S, TemplatePredicate, FontPredicate>( + &mut self, + font_descriptor: &FontDescriptor, + font_context: &mut FontContext<S>, + template_predicate: &TemplatePredicate, + font_predicate: &FontPredicate, + ) -> Option<FontRef> + where + S: FontSource, + TemplatePredicate: Fn(FontTemplateRef) -> bool, + FontPredicate: Fn(&FontRef) -> bool, + { + self.members(font_descriptor, font_context) + .filter_map(|member| { + if !template_predicate(member.template.clone()) { + return None; + } - self.font.clone() + if !member.loaded { + member.font = font_context.font(member.template.clone(), font_descriptor); + member.loaded = true; + } + if matches!(&member.font, Some(font) if font_predicate(font)) { + return member.font.clone(); + } + + None + }) + .next() + } + + fn members<'a, S: FontSource>( + &'a mut self, + font_descriptor: &FontDescriptor, + font_context: &mut FontContext<S>, + ) -> impl Iterator<Item = &mut FontGroupFamilyMember> + 'a { + let family_descriptor = &self.family_descriptor; + let members = self.members.get_or_insert_with(|| { + font_context + .matching_templates(font_descriptor, family_descriptor) + .into_iter() + .map(|template| FontGroupFamilyMember { + template, + loaded: false, + font: None, + }) + .collect() + }); + + members.iter_mut() } } @@ -636,7 +739,7 @@ impl FontFamilyDescriptor { FontFamilyDescriptor { name, scope } } - fn default() -> FontFamilyDescriptor { + fn serif() -> FontFamilyDescriptor { FontFamilyDescriptor { name: FontFamilyName::Generic(atom!("serif")), scope: FontSearchScope::Local, diff --git a/components/gfx/font_cache_thread.rs b/components/gfx/font_cache_thread.rs index 66f0708670d..c46c2b5dde3 100644 --- a/components/gfx/font_cache_thread.rs +++ b/components/gfx/font_cache_thread.rs @@ -5,32 +5,37 @@ use std::borrow::ToOwned; use std::cell::RefCell; use std::collections::HashMap; -use std::ops::Deref; +use std::ops::{Deref, RangeInclusive}; use std::rc::Rc; use std::sync::{Arc, Mutex}; use std::{f32, fmt, mem, thread}; use app_units::Au; use gfx_traits::WebrenderApi; -use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; +use ipc_channel::ipc::{self, IpcBytesSender, IpcReceiver, IpcSender}; use log::{debug, trace}; use net_traits::request::{Destination, Referrer, RequestBuilder}; use net_traits::{fetch_async, CoreResourceThread, FetchResponseMsg}; use serde::{Deserialize, Serialize}; use servo_atoms::Atom; use servo_url::ServoUrl; -use style::font_face::{FontFaceSourceFormat, FontFaceSourceFormatKeyword, Source}; +use style::font_face::{ + FontFaceRuleData, FontFaceSourceFormat, FontFaceSourceFormatKeyword, + FontStyle as FontFaceStyle, Source, +}; use style::media_queries::Device; use style::shared_lock::SharedRwLockReadGuard; use style::stylesheets::{Stylesheet, StylesheetInDocument}; +use style::values::computed::font::{FixedPoint, FontStyleFixedPoint}; +use style::values::computed::{FontStretch, FontWeight}; +use style::values::specified::FontStretch as SpecifiedFontStretch; use webrender_api::{FontInstanceKey, FontKey}; -use crate::font::{FontFamilyDescriptor, FontFamilyName, FontSearchScope, PlatformFontMethods}; +use crate::font::{FontDescriptor, FontFamilyDescriptor, FontFamilyName, FontSearchScope}; use crate::font_context::FontSource; use crate::font_template::{ FontTemplate, FontTemplateDescriptor, FontTemplateRef, FontTemplateRefMethods, }; -use crate::platform::font::PlatformFont; use crate::platform::font_list::{ for_each_available_family, for_each_variation, system_default_family, LocalFontIdentifier, SANS_SERIF_FONT_FAMILY, @@ -42,18 +47,6 @@ pub struct FontTemplates { templates: Vec<FontTemplateRef>, } -#[derive(Clone, Debug)] -pub struct FontTemplateAndWebRenderFontKey { - pub font_template: FontTemplateRef, - pub font_key: FontKey, -} - -#[derive(Debug, Deserialize, Serialize)] -pub struct SerializedFontTemplateAndWebRenderFontKey { - pub serialized_font_template: SerializedFontTemplate, - pub font_key: FontKey, -} - #[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] pub enum FontIdentifier { Local(LocalFontIdentifier), @@ -76,54 +69,51 @@ pub struct SerializedFontTemplate { bytes_receiver: ipc_channel::ipc::IpcBytesReceiver, } -impl SerializedFontTemplate { - pub fn to_font_template(&self) -> FontTemplate { - let font_data = self.bytes_receiver.recv().ok(); - FontTemplate { - identifier: self.identifier.clone(), - descriptor: self.descriptor, - data: font_data.map(Arc::new), - } - } -} - impl FontTemplates { /// Find a font in this family that matches a given descriptor. - pub fn find_font_for_style( + pub fn find_for_descriptor( &mut self, - desc: &FontTemplateDescriptor, - ) -> Option<FontTemplateRef> { + descriptor_to_match: &FontDescriptor, + ) -> Vec<FontTemplateRef> { // TODO(Issue #189): optimize lookup for // regular/bold/italic/bolditalic with fixed offsets and a // static decision table for fallback between these values. - for template in &mut self.templates { - if template.descriptor_matches(desc) { - return Some(template.clone()); - } + let matching_templates: Vec<FontTemplateRef> = self + .templates + .iter() + .filter(|template| template.matches_font_descriptor(descriptor_to_match)) + .cloned() + .collect(); + if !matching_templates.is_empty() { + return matching_templates; } // We didn't find an exact match. Do more expensive fuzzy matching. // TODO(#190): Do a better job. - let (mut best_template, mut best_distance) = (None, f32::MAX); + let mut best_templates = Vec::new(); + let mut best_distance = f32::MAX; for template in self.templates.iter() { - let distance = template.descriptor_distance(desc); + let distance = template.descriptor_distance(descriptor_to_match); if distance < best_distance { - best_template = Some(template); + best_templates = vec![template.clone()]; best_distance = distance + } else if distance == best_distance { + best_templates.push(template.clone()); } } - if best_template.is_some() { - return best_template.cloned(); + + if !best_templates.is_empty() { + return best_templates; } // If a request is made for a font family that exists, // pick the first valid font in the family if we failed // to find an exact match for the descriptor. for template in &mut self.templates.iter() { - return Some(template.clone()); + return vec![template.clone()]; } - None + Vec::new() } pub fn add_template(&mut self, new_template: FontTemplate) { @@ -142,30 +132,25 @@ impl FontTemplates { /// Commands that the FontContext sends to the font cache thread. #[derive(Debug, Deserialize, Serialize)] pub enum Command { - GetFontTemplate( - FontTemplateDescriptor, + GetFontTemplates( + FontDescriptor, FontFamilyDescriptor, - IpcSender<Reply>, + IpcSender<Vec<SerializedFontTemplate>>, ), - GetFontInstance(FontKey, Au, IpcSender<FontInstanceKey>), - AddWebFont(LowercaseString, Vec<Source>, IpcSender<()>), - AddDownloadedWebFont(LowercaseString, ServoUrl, Vec<u8>, IpcSender<()>), + GetFontInstance(FontIdentifier, Au, IpcSender<FontInstanceKey>), + AddWebFont(CSSFontFaceDescriptors, Vec<Source>, IpcSender<()>), + AddDownloadedWebFont(CSSFontFaceDescriptors, ServoUrl, Vec<u8>, IpcSender<()>), Exit(IpcSender<()>), Ping, } -/// Reply messages sent from the font cache thread to the FontContext caller. -#[derive(Debug, Deserialize, Serialize)] -pub enum Reply { - GetFontTemplateReply(Option<SerializedFontTemplateAndWebRenderFontKey>), -} - /// The font cache thread itself. It maintains a list of reference counted /// font templates that are currently in use. struct FontCache { port: IpcReceiver<Command>, channel_to_self: IpcSender<Command>, generic_fonts: HashMap<FontFamilyName, LowercaseString>, + font_data: HashMap<FontIdentifier, Arc<Vec<u8>>>, local_families: HashMap<LowercaseString, FontTemplates>, web_families: HashMap<LowercaseString, FontTemplates>, core_resource_thread: CoreResourceThread, @@ -207,61 +192,57 @@ impl FontCache { let msg = self.port.recv().unwrap(); match msg { - Command::GetFontTemplate(template_descriptor, family_descriptor, result) => { - let Some(font_template_info) = - self.find_font_template(&template_descriptor, &family_descriptor) - else { - let _ = result.send(Reply::GetFontTemplateReply(None)); - continue; - }; - - let (bytes_sender, bytes_receiver) = - ipc::bytes_channel().expect("failed to create IPC channel"); - let serialized_font_template = SerializedFontTemplate { - identifier: font_template_info.font_template.borrow().identifier.clone(), - descriptor: font_template_info.font_template.borrow().descriptor, - bytes_receiver, - }; - - let _ = result.send(Reply::GetFontTemplateReply(Some( - SerializedFontTemplateAndWebRenderFontKey { - serialized_font_template, - font_key: font_template_info.font_key, - }, - ))); + Command::GetFontTemplates(descriptor_to_match, family_descriptor, result) => { + let templates = + self.find_font_templates(&descriptor_to_match, &family_descriptor); + debug!("Found templates for descriptor {descriptor_to_match:?}: "); + debug!(" {templates:?}"); + + let (serialized_templates, senders): ( + Vec<SerializedFontTemplate>, + Vec<(FontTemplateRef, IpcBytesSender)>, + ) = templates + .into_iter() + .map(|template| { + let (bytes_sender, bytes_receiver) = + ipc::bytes_channel().expect("failed to create IPC channel"); + ( + SerializedFontTemplate { + identifier: template.identifier().clone(), + descriptor: template.descriptor().clone(), + bytes_receiver, + }, + (template.clone(), bytes_sender), + ) + }) + .unzip(); + + let _ = result.send(serialized_templates); // NB: This will load the font into memory if it hasn't been loaded already. - let _ = bytes_sender.send(&font_template_info.font_template.data()); + for (font_template, bytes_sender) in senders.iter() { + let identifier = font_template.identifier(); + let data = self + .font_data + .entry(identifier) + .or_insert_with(|| font_template.data()); + let _ = bytes_sender.send(&data); + } }, - Command::GetFontInstance(font_key, size, result) => { - let webrender_api = &self.webrender_api; - - let instance_key = - *self - .font_instances - .entry((font_key, size)) - .or_insert_with(|| { - webrender_api.add_font_instance(font_key, size.to_f32_px()) - }); - - let _ = result.send(instance_key); + Command::GetFontInstance(identifier, pt_size, result) => { + let _ = result.send(self.get_font_instance(identifier, pt_size)); }, - Command::AddWebFont(family_name, sources, result) => { - self.handle_add_web_font(family_name, sources, result); + Command::AddWebFont(css_font_face_descriptors, sources, result) => { + self.handle_add_web_font(css_font_face_descriptors, sources, result); }, - Command::AddDownloadedWebFont(family_name, url, bytes, result) => { + Command::AddDownloadedWebFont(css_font_face_descriptors, url, data, result) => { + let family_name = css_font_face_descriptors.family_name.clone(); let templates = &mut self.web_families.get_mut(&family_name).unwrap(); - - let data = Arc::new(bytes); - let identifier = FontIdentifier::Web(url.clone()); - let Ok(handle) = PlatformFont::new_from_data(identifier, data.clone(), 0, None) - else { - drop(result.send(())); - return; - }; - - let descriptor = handle.descriptor(); - templates.add_template(FontTemplate::new_web_font(url, descriptor, data)); + if let Ok(template) = + FontTemplate::new_web_font(url, Arc::new(data), css_font_face_descriptors) + { + templates.add_template(template); + } drop(result.send(())); }, Command::Ping => (), @@ -275,10 +256,11 @@ impl FontCache { fn handle_add_web_font( &mut self, - family_name: LowercaseString, + css_font_face_descriptors: CSSFontFaceDescriptors, mut sources: Vec<Source>, sender: IpcSender<()>, ) { + let family_name = css_font_face_descriptors.family_name.clone(); let src = if let Some(src) = sources.pop() { src } else { @@ -330,7 +312,7 @@ impl FontCache { trace!("@font-face {} EOF={:?}", family_name, response); if response.is_err() || !*response_valid.lock().unwrap() { let msg = Command::AddWebFont( - family_name.clone(), + css_font_face_descriptors.clone(), sources.clone(), sender.clone(), ); @@ -349,7 +331,7 @@ impl FontCache { family_name, url ); let msg = Command::AddWebFont( - family_name.clone(), + css_font_face_descriptors.clone(), sources.clone(), sender.clone(), ); @@ -358,7 +340,7 @@ impl FontCache { }, }; let command = Command::AddDownloadedWebFont( - family_name.clone(), + css_font_face_descriptors.clone(), url.clone().into(), bytes, sender.clone(), @@ -379,7 +361,8 @@ impl FontCache { if found { sender.send(()).unwrap(); } else { - let msg = Command::AddWebFont(family_name, sources, sender); + let msg = + Command::AddWebFont(css_font_face_descriptors.clone(), sources, sender); self.channel_to_self.send(msg).unwrap(); } }, @@ -401,58 +384,68 @@ impl FontCache { } } - fn find_font_in_local_family( + fn find_templates_in_local_family( &mut self, - template_descriptor: &FontTemplateDescriptor, + descriptor_to_match: &FontDescriptor, family_name: &FontFamilyName, - ) -> Option<FontTemplateRef> { - let family_name = self.transform_family(family_name); - + ) -> Vec<FontTemplateRef> { // TODO(Issue #188): look up localized font family names if canonical name not found // look up canonical name - if self.local_families.contains_key(&family_name) { - debug!("FontList: Found font family with name={}", &*family_name); - let font_templates = self.local_families.get_mut(&family_name).unwrap(); - - if font_templates.templates.is_empty() { - for_each_variation(&family_name, |font_template| { - font_templates.add_template(font_template); - }); - } - - // TODO(Issue #192: handle generic font families, like 'serif' and 'sans-serif'. - // if such family exists, try to match style to a font + // TODO(Issue #192: handle generic font families, like 'serif' and 'sans-serif'. + // if such family exists, try to match style to a font + let family_name = self.transform_family(family_name); + self.local_families + .get_mut(&family_name) + .map(|font_templates| { + if font_templates.templates.is_empty() { + for_each_variation(&family_name, |font_template| { + font_templates.add_template(font_template); + }); + } - font_templates.find_font_for_style(template_descriptor) - } else { - debug!( - "FontList: Couldn't find font family with name={}", - &*family_name - ); - None - } + font_templates.find_for_descriptor(descriptor_to_match) + }) + .unwrap_or_default() } - fn find_font_in_web_family( + fn find_templates_in_web_family( &mut self, - template_descriptor: &FontTemplateDescriptor, + descriptor_to_match: &FontDescriptor, family_name: &FontFamilyName, - ) -> Option<FontTemplateRef> { + ) -> Vec<FontTemplateRef> { let family_name = LowercaseString::from(family_name); + self.web_families + .get_mut(&family_name) + .map(|templates| templates.find_for_descriptor(descriptor_to_match)) + .unwrap_or_default() + } - if self.web_families.contains_key(&family_name) { - let templates = self.web_families.get_mut(&family_name).unwrap(); - templates.find_font_for_style(template_descriptor) - } else { - None + fn find_font_templates( + &mut self, + descriptor_to_match: &FontDescriptor, + family_descriptor: &FontFamilyDescriptor, + ) -> Vec<FontTemplateRef> { + if family_descriptor.scope == FontSearchScope::Any { + let templates = + self.find_templates_in_web_family(descriptor_to_match, &family_descriptor.name); + if !templates.is_empty() { + return templates; + } } + + self.find_templates_in_local_family(descriptor_to_match, &family_descriptor.name) } - fn get_font_key_for_template(&mut self, template: &FontTemplateRef) -> FontKey { + fn get_font_instance(&mut self, identifier: FontIdentifier, pt_size: Au) -> FontInstanceKey { let webrender_api = &self.webrender_api; let webrender_fonts = &mut self.webrender_fonts; - let identifier = template.borrow().identifier.clone(); - *webrender_fonts + let font_data = self + .font_data + .get(&identifier) + .expect("Got unexpected FontIdentifier") + .clone(); + + let font_key = *webrender_fonts .entry(identifier.clone()) .or_insert_with(|| { // CoreText cannot reliably create CoreTextFonts for system fonts stored @@ -466,31 +459,13 @@ impl FontCache { .add_system_font(local_font_identifier.native_font_handle()); } - let bytes = template.data(); - webrender_api.add_font(bytes, identifier.index()) - }) - } + webrender_api.add_font(font_data, identifier.index()) + }); - fn find_font_template( - &mut self, - template_descriptor: &FontTemplateDescriptor, - family_descriptor: &FontFamilyDescriptor, - ) -> Option<FontTemplateAndWebRenderFontKey> { - match family_descriptor.scope { - FontSearchScope::Any => self - .find_font_in_web_family(template_descriptor, &family_descriptor.name) - .or_else(|| { - self.find_font_in_local_family(template_descriptor, &family_descriptor.name) - }), - - FontSearchScope::Local => { - self.find_font_in_local_family(template_descriptor, &family_descriptor.name) - }, - } - .map(|font_template| FontTemplateAndWebRenderFontKey { - font_key: self.get_font_key_for_template(&font_template), - font_template, - }) + *self + .font_instances + .entry((font_key, pt_size)) + .or_insert_with(|| webrender_api.add_font_instance(font_key, pt_size.to_f32_px())) } } @@ -501,6 +476,94 @@ pub struct FontCacheThread { chan: IpcSender<Command>, } +/// 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 { + Normal, + 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: LowercaseString, + pub weight: Option<(FontWeight, FontWeight)>, + pub stretch: Option<(FontStretch, FontStretch)>, + pub style: Option<ComputedFontStyleDescriptor>, + pub unicode_range: Option<Vec<RangeInclusive<u32>>>, +} + +impl CSSFontFaceDescriptors { + pub fn new(family_name: &str) -> Self { + CSSFontFaceDescriptors { + family_name: LowercaseString::new(family_name), + ..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::Normal => ComputedFontStyleDescriptor::Normal, + 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| style_to_computed(style)); + let unicode_range = rule_data + .unicode_range + .as_ref() + .map(|ranges| ranges.iter().map(|range| range.start..=range.end).collect()); + + CSSFontFaceDescriptors { + family_name: LowercaseString::new(&family_name), + weight, + stretch, + style, + unicode_range, + } + } +} + impl FontCacheThread { pub fn new( core_resource_thread: CoreResourceThread, @@ -520,6 +583,7 @@ impl FontCacheThread { port, channel_to_self, generic_fonts, + font_data: HashMap::new(), local_families: HashMap::new(), web_families: HashMap::new(), core_resource_thread, @@ -572,11 +636,7 @@ impl FontCacheThread { let sender = sender.as_ref().unwrap_or(font_cache_sender).clone(); self.chan - .send(Command::AddWebFont( - LowercaseString::new(&font_face.family().name), - sources, - sender, - )) + .send(Command::AddWebFont(rule.into(), sources, sender)) .unwrap(); // Either increment the count of loading web fonts, or wait for a synchronous load. @@ -601,10 +661,10 @@ impl FontCacheThread { } impl FontSource for FontCacheThread { - fn get_font_instance(&mut self, key: FontKey, size: Au) -> FontInstanceKey { + fn get_font_instance(&mut self, identifier: FontIdentifier, size: Au) -> FontInstanceKey { let (response_chan, response_port) = ipc::channel().expect("failed to create IPC channel"); self.chan - .send(Command::GetFontInstance(key, size, response_chan)) + .send(Command::GetFontInstance(identifier, size, response_chan)) .expect("failed to send message to font cache thread"); let instance_key = response_port.recv(); @@ -619,22 +679,21 @@ impl FontSource for FontCacheThread { instance_key.unwrap() } - fn font_template( + fn find_matching_font_templates( &mut self, - template_descriptor: FontTemplateDescriptor, + descriptor_to_match: &FontDescriptor, family_descriptor: FontFamilyDescriptor, - ) -> Option<FontTemplateAndWebRenderFontKey> { + ) -> Vec<FontTemplateRef> { let (response_chan, response_port) = ipc::channel().expect("failed to create IPC channel"); self.chan - .send(Command::GetFontTemplate( - template_descriptor, + .send(Command::GetFontTemplates( + descriptor_to_match.clone(), family_descriptor, response_chan, )) .expect("failed to send message to font cache thread"); let reply = response_port.recv(); - if reply.is_err() { let font_thread_has_closed = self.chan.send(Command::Ping).is_err(); assert!( @@ -644,25 +703,22 @@ impl FontSource for FontCacheThread { panic!("Font cache thread has already exited."); } - match reply.unwrap() { - Reply::GetFontTemplateReply(maybe_serialized_font_template_info) => { - maybe_serialized_font_template_info.map(|serialized_font_template_info| { - let font_template = Rc::new(RefCell::new( - serialized_font_template_info - .serialized_font_template - .to_font_template(), - )); - FontTemplateAndWebRenderFontKey { - font_template, - font_key: serialized_font_template_info.font_key, - } - }) - }, - } + reply + .unwrap() + .into_iter() + .map(|serialized_font_template| { + let font_data = serialized_font_template.bytes_receiver.recv().ok(); + Rc::new(RefCell::new(FontTemplate { + identifier: serialized_font_template.identifier, + descriptor: serialized_font_template.descriptor.clone(), + data: font_data.map(Arc::new), + })) + }) + .collect() } } -#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct LowercaseString { inner: String, } diff --git a/components/gfx/font_context.rs b/components/gfx/font_context.rs index 3d652cd41d7..abe00db4977 100644 --- a/components/gfx/font_context.rs +++ b/components/gfx/font_context.rs @@ -15,11 +15,11 @@ use log::debug; use servo_arc::Arc; use style::computed_values::font_variant_caps::T as FontVariantCaps; use style::properties::style_structs::Font as FontStyleStruct; -use webrender_api::{FontInstanceKey, FontKey}; +use webrender_api::FontInstanceKey; use crate::font::{Font, FontDescriptor, FontFamilyDescriptor, FontGroup, FontRef}; -use crate::font_cache_thread::FontTemplateAndWebRenderFontKey; -use crate::font_template::FontTemplateDescriptor; +use crate::font_cache_thread::FontIdentifier; +use crate::font_template::{FontTemplateRef, FontTemplateRefMethods}; #[cfg(target_os = "macos")] use crate::platform::core_text_font_cache::CoreTextFontCache; @@ -30,13 +30,12 @@ static SMALL_CAPS_SCALE_FACTOR: f32 = 0.8; // Matches FireFox (see gfxFont.h) static FONT_CACHE_EPOCH: AtomicUsize = AtomicUsize::new(0); pub trait FontSource { - fn get_font_instance(&mut self, key: FontKey, size: Au) -> FontInstanceKey; - - fn font_template( + fn get_font_instance(&mut self, font_identifier: FontIdentifier, size: Au) -> FontInstanceKey; + fn find_matching_font_templates( &mut self, - template_descriptor: FontTemplateDescriptor, + descriptor_to_match: &FontDescriptor, family_descriptor: FontFamilyDescriptor, - ) -> Option<FontTemplateAndWebRenderFontKey>; + ) -> Vec<FontTemplateRef>; } /// The FontContext represents the per-thread/thread state necessary for @@ -51,7 +50,7 @@ pub struct FontContext<S: FontSource> { // so they will never be released. Find out a good time to drop them. // See bug https://github.com/servo/servo/issues/3300 font_cache: HashMap<FontCacheKey, Option<FontRef>>, - font_template_cache: HashMap<FontTemplateCacheKey, Option<FontTemplateAndWebRenderFontKey>>, + font_template_cache: HashMap<FontTemplateCacheKey, Vec<FontTemplateRef>>, font_group_cache: HashMap<FontGroupCacheKey, Rc<RefCell<FontGroup>>, BuildHasherDefault<FnvHasher>>, @@ -115,20 +114,20 @@ impl<S: FontSource> FontContext<S> { /// reference to the same underlying `Font`. pub fn font( &mut self, + font_template: FontTemplateRef, font_descriptor: &FontDescriptor, - family_descriptor: &FontFamilyDescriptor, ) -> Option<FontRef> { self.get_font_maybe_synthesizing_small_caps( + font_template, font_descriptor, - family_descriptor, true, /* synthesize_small_caps */ ) } fn get_font_maybe_synthesizing_small_caps( &mut self, + font_template: FontTemplateRef, font_descriptor: &FontDescriptor, - family_descriptor: &FontFamilyDescriptor, synthesize_small_caps: bool, ) -> Option<FontRef> { // TODO: (Bug #3463): Currently we only support fake small-caps @@ -140,8 +139,8 @@ impl<S: FontSource> FontContext<S> { small_caps_descriptor.pt_size = font_descriptor.pt_size.scale_by(SMALL_CAPS_SCALE_FACTOR); self.get_font_maybe_synthesizing_small_caps( + font_template.clone(), &small_caps_descriptor, - family_descriptor, false, /* synthesize_small_caps */ ) } else { @@ -149,52 +148,48 @@ impl<S: FontSource> FontContext<S> { }; let cache_key = FontCacheKey { + font_identifier: font_template.identifier(), font_descriptor: font_descriptor.clone(), - family_descriptor: family_descriptor.clone(), }; self.font_cache.get(&cache_key).cloned().unwrap_or_else(|| { debug!( - "FontContext::font cache miss for font_descriptor={:?} family_descriptor={:?}", - font_descriptor, family_descriptor + "FontContext::font cache miss for font_template={:?} font_descriptor={:?}", + font_template, font_descriptor ); let font = self - .font_template(&font_descriptor.template_descriptor, family_descriptor) - .and_then(|template_info| { - self.create_font( - template_info, - font_descriptor.to_owned(), - synthesized_small_caps_font, - ) - .ok() - }) - .map(|font| Rc::new(RefCell::new(font))); - + .create_font( + font_template, + font_descriptor.to_owned(), + synthesized_small_caps_font, + ) + .ok(); self.font_cache.insert(cache_key, font.clone()); + font }) } - fn font_template( + pub fn matching_templates( &mut self, - template_descriptor: &FontTemplateDescriptor, + descriptor_to_match: &FontDescriptor, family_descriptor: &FontFamilyDescriptor, - ) -> Option<FontTemplateAndWebRenderFontKey> { + ) -> Vec<FontTemplateRef> { let cache_key = FontTemplateCacheKey { - template_descriptor: *template_descriptor, + font_descriptor: descriptor_to_match.clone(), family_descriptor: family_descriptor.clone(), }; self.font_template_cache.get(&cache_key).cloned().unwrap_or_else(|| { debug!( "FontContext::font_template cache miss for template_descriptor={:?} family_descriptor={:?}", - template_descriptor, + descriptor_to_match, family_descriptor ); - let template_info = self.font_source.font_template( - *template_descriptor, + let template_info = self.font_source.find_matching_font_templates( + descriptor_to_match, family_descriptor.clone(), ); @@ -207,31 +202,32 @@ impl<S: FontSource> FontContext<S> { /// cache thread and a `FontDescriptor` which contains the styling parameters. fn create_font( &mut self, - info: FontTemplateAndWebRenderFontKey, - descriptor: FontDescriptor, + font_template: FontTemplateRef, + font_descriptor: FontDescriptor, synthesized_small_caps: Option<FontRef>, - ) -> Result<Font, &'static str> { + ) -> Result<FontRef, &'static str> { let font_instance_key = self .font_source - .get_font_instance(info.font_key, descriptor.pt_size); - Font::new( - info.font_template, - descriptor, + .get_font_instance(font_template.identifier(), font_descriptor.pt_size); + + Ok(Rc::new(RefCell::new(Font::new( + font_template, + font_descriptor, font_instance_key, synthesized_small_caps, - ) + )?))) } } #[derive(Debug, Eq, Hash, PartialEq)] struct FontCacheKey { + font_identifier: FontIdentifier, font_descriptor: FontDescriptor, - family_descriptor: FontFamilyDescriptor, } #[derive(Debug, Eq, Hash, PartialEq)] struct FontTemplateCacheKey { - template_descriptor: FontTemplateDescriptor, + font_descriptor: FontDescriptor, family_descriptor: FontFamilyDescriptor, } diff --git a/components/gfx/font_template.rs b/components/gfx/font_template.rs index 71318bba735..30a40608df5 100644 --- a/components/gfx/font_template.rs +++ b/components/gfx/font_template.rs @@ -4,6 +4,7 @@ use std::cell::RefCell; use std::fmt::{Debug, Error, Formatter}; +use std::ops::RangeInclusive; use std::rc::Rc; use std::sync::Arc; @@ -11,33 +12,33 @@ use serde::{Deserialize, Serialize}; use servo_url::ServoUrl; use style::computed_values::font_stretch::T as FontStretch; use style::computed_values::font_style::T as FontStyle; -use style::properties::style_structs::Font as FontStyleStruct; use style::values::computed::font::FontWeight; -use crate::font_cache_thread::FontIdentifier; +use crate::font::{FontDescriptor, PlatformFontMethods}; +use crate::font_cache_thread::{ + CSSFontFaceDescriptors, ComputedFontStyleDescriptor, FontIdentifier, +}; +use crate::platform::font::PlatformFont; use crate::platform::font_list::LocalFontIdentifier; /// A reference to a [`FontTemplate`] with shared ownership and mutability. -pub(crate) type FontTemplateRef = Rc<RefCell<FontTemplate>>; +pub type FontTemplateRef = Rc<RefCell<FontTemplate>>; /// Describes how to select a font from a given family. This is very basic at the moment and needs /// to be expanded or refactored when we support more of the font styling parameters. /// /// NB: If you change this, you will need to update `style::properties::compute_font_hash()`. -#[derive(Clone, Copy, Debug, Deserialize, Hash, PartialEq, Serialize)] +#[derive(Clone, Debug, Deserialize, Hash, PartialEq, Serialize)] pub struct FontTemplateDescriptor { - pub weight: FontWeight, - pub stretch: FontStretch, - pub style: FontStyle, + pub weight: (FontWeight, FontWeight), + pub stretch: (FontStretch, FontStretch), + pub style: (FontStyle, FontStyle), + pub unicode_range: Option<Vec<RangeInclusive<u32>>>, } impl Default for FontTemplateDescriptor { fn default() -> Self { - FontTemplateDescriptor { - weight: FontWeight::normal(), - stretch: FontStretch::NORMAL, - style: FontStyle::NORMAL, - } + Self::new(FontWeight::normal(), FontStretch::NORMAL, FontStyle::NORMAL) } } @@ -57,9 +58,10 @@ impl FontTemplateDescriptor { #[inline] pub fn new(weight: FontWeight, stretch: FontStretch, style: FontStyle) -> Self { Self { - weight, - stretch, - style, + weight: (weight, weight), + stretch: (stretch, stretch), + style: (style, style), + unicode_range: None, } } @@ -71,24 +73,51 @@ impl FontTemplateDescriptor { /// /// The policy is to care most about differences in italicness, then weight, then stretch #[inline] - fn distance_from(&self, other: &FontTemplateDescriptor) -> f32 { + fn distance_from(&self, other: &FontDescriptor) -> f32 { + let weight = self.weight.0; + let style = self.style.0; + let stretch = self.stretch.0; + // 0 <= style_part <= 180, since font-style obliqueness should be // between -90 and +90deg. - let style_part = (style_to_number(&self.style) - style_to_number(&other.style)).abs(); + let style_part = (style_to_number(&style) - style_to_number(&other.style)).abs(); // 0 <= weightPart <= 800 - let weight_part = (self.weight.value() - other.weight.value()).abs(); + let weight_part = (weight.value() - other.weight.value()).abs(); // 0 <= stretchPart <= 8 - let stretch_part = (self.stretch.to_percentage().0 - other.stretch.to_percentage().0).abs(); + let stretch_part = (stretch.to_percentage().0 - other.stretch.to_percentage().0).abs(); style_part + weight_part + stretch_part } -} -impl<'a> From<&'a FontStyleStruct> for FontTemplateDescriptor { - fn from(style: &'a FontStyleStruct) -> Self { - FontTemplateDescriptor { - weight: style.font_weight, - stretch: style.font_stretch, - style: style.font_style, + fn matches(&self, descriptor_to_match: &FontDescriptor) -> bool { + self.weight.0 <= descriptor_to_match.weight && + self.weight.1 >= descriptor_to_match.weight && + self.style.0 <= descriptor_to_match.style && + self.style.1 >= descriptor_to_match.style && + self.stretch.0 <= descriptor_to_match.stretch && + self.stretch.1 >= descriptor_to_match.stretch + } + + fn override_values_with_css_font_template_descriptors( + &mut self, + css_font_template_descriptors: CSSFontFaceDescriptors, + ) { + if let Some(weight) = css_font_template_descriptors.weight { + self.weight = weight; + } + self.style = match css_font_template_descriptors.style { + Some(ComputedFontStyleDescriptor::Italic) => (FontStyle::ITALIC, FontStyle::ITALIC), + Some(ComputedFontStyleDescriptor::Normal) => (FontStyle::NORMAL, FontStyle::NORMAL), + Some(ComputedFontStyleDescriptor::Oblique(angle_1, angle_2)) => ( + FontStyle::oblique(angle_1.to_float()), + FontStyle::oblique(angle_2.to_float()), + ), + None => self.style, + }; + if let Some(stretch) = css_font_template_descriptors.stretch { + self.stretch = stretch; + } + if let Some(unicode_range) = css_font_template_descriptors.unicode_range { + self.unicode_range = Some(unicode_range); } } } @@ -129,14 +158,22 @@ impl FontTemplate { pub fn new_web_font( url: ServoUrl, - descriptor: FontTemplateDescriptor, data: Arc<Vec<u8>>, - ) -> FontTemplate { - FontTemplate { + css_font_template_descriptors: CSSFontFaceDescriptors, + ) -> Result<FontTemplate, &'static str> { + let identifier = FontIdentifier::Web(url.clone()); + let Ok(handle) = PlatformFont::new_from_data(identifier, data.clone(), 0, None) else { + return Err("Could not initialize platform font data for: {url:?}"); + }; + + let mut descriptor = handle.descriptor(); + descriptor + .override_values_with_css_font_template_descriptors(css_font_template_descriptors); + Ok(FontTemplate { identifier: FontIdentifier::Web(url), descriptor, data: Some(data), - } + }) } pub fn identifier(&self) -> &FontIdentifier { @@ -155,26 +192,35 @@ pub trait FontTemplateRefMethods { /// operation (depending on the platform) which performs synchronous disk I/O /// and should never be done lightly. fn data(&self) -> Arc<Vec<u8>>; - /// Get the descriptor. Returns `None` when instantiating the data fails. + /// Get the descriptor. fn descriptor(&self) -> FontTemplateDescriptor; + /// Get the [`FontIdentifier`] for this template. + fn identifier(&self) -> FontIdentifier; /// Returns true if the given descriptor matches the one in this [`FontTemplate`]. - fn descriptor_matches(&self, requested_desc: &FontTemplateDescriptor) -> bool; + fn matches_font_descriptor(&self, descriptor_to_match: &FontDescriptor) -> bool; /// Calculate the distance from this [`FontTemplate`]s descriptor and return it /// or None if this is not a valid [`FontTemplate`]. - fn descriptor_distance(&self, requested_descriptor: &FontTemplateDescriptor) -> f32; + fn descriptor_distance(&self, descriptor_to_match: &FontDescriptor) -> f32; + /// Whether or not this character is in the unicode ranges specified in + /// this temlates `@font-face` definition, if any. + fn char_in_unicode_range(&self, character: char) -> bool; } impl FontTemplateRefMethods for FontTemplateRef { fn descriptor(&self) -> FontTemplateDescriptor { - self.borrow().descriptor + self.borrow().descriptor.clone() + } + + fn identifier(&self) -> FontIdentifier { + self.borrow().identifier.clone() } - fn descriptor_matches(&self, requested_descriptor: &FontTemplateDescriptor) -> bool { - self.descriptor() == *requested_descriptor + fn matches_font_descriptor(&self, descriptor_to_match: &FontDescriptor) -> bool { + self.descriptor().matches(descriptor_to_match) } - fn descriptor_distance(&self, requested_descriptor: &FontTemplateDescriptor) -> f32 { - self.descriptor().distance_from(requested_descriptor) + fn descriptor_distance(&self, descriptor_to_match: &FontDescriptor) -> f32 { + self.descriptor().distance_from(descriptor_to_match) } fn data(&self) -> Arc<Vec<u8>> { @@ -190,4 +236,15 @@ impl FontTemplateRefMethods for FontTemplateRef { }) .clone() } + + fn char_in_unicode_range(&self, character: char) -> bool { + let character = character as u32; + self.borrow() + .descriptor + .unicode_range + .as_ref() + .map_or(true, |ranges| { + ranges.iter().any(|range| range.contains(&character)) + }) + } } diff --git a/components/gfx/platform/freetype/android/font_list.rs b/components/gfx/platform/freetype/android/font_list.rs index 2a069320eb8..c29069156c5 100644 --- a/components/gfx/platform/freetype/android/font_list.rs +++ b/components/gfx/platform/freetype/android/font_list.rs @@ -485,11 +485,7 @@ where }, None => StyleFontStyle::NORMAL, }; - let descriptor = FontTemplateDescriptor { - weight, - stretch, - style, - }; + let descriptor = FontTemplateDescriptor::new(weight, stretch, style); callback(FontTemplate::new_local(local_font_identifier, descriptor)); }; diff --git a/components/gfx/platform/freetype/font.rs b/components/gfx/platform/freetype/font.rs index 0115374301b..82e451a17f7 100644 --- a/components/gfx/platform/freetype/font.rs +++ b/components/gfx/platform/freetype/font.rs @@ -173,11 +173,7 @@ impl PlatformFontMethods for PlatformFont { }) .unwrap_or(FontStretch::NORMAL); - FontTemplateDescriptor { - weight, - stretch, - style, - } + FontTemplateDescriptor::new(weight, stretch, style) } fn glyph_index(&self, codepoint: char) -> Option<GlyphId> { diff --git a/components/gfx/platform/freetype/font_list.rs b/components/gfx/platform/freetype/font_list.rs index 0741fb61ce2..65a6457b25d 100644 --- a/components/gfx/platform/freetype/font_list.rs +++ b/components/gfx/platform/freetype/font_list.rs @@ -148,11 +148,7 @@ where path: Atom::from(c_str_to_string(path as *const c_char)), variation_index: index as i32, }; - let descriptor = FontTemplateDescriptor { - weight, - stretch, - style, - }; + let descriptor = FontTemplateDescriptor::new(weight, stretch, style); callback(FontTemplate::new_local(local_font_identifier, descriptor)) } diff --git a/components/gfx/platform/macos/font.rs b/components/gfx/platform/macos/font.rs index abbf15e0212..dcb5d08f6a9 100644 --- a/components/gfx/platform/macos/font.rs +++ b/components/gfx/platform/macos/font.rs @@ -190,11 +190,7 @@ impl PlatformFontMethods for PlatformFont { fn descriptor(&self) -> FontTemplateDescriptor { let traits = self.ctfont.all_traits(); - FontTemplateDescriptor { - weight: traits.weight(), - stretch: traits.stretch(), - style: traits.style(), - } + FontTemplateDescriptor::new(traits.weight(), traits.stretch(), traits.style()) } fn glyph_index(&self, codepoint: char) -> Option<GlyphId> { diff --git a/components/gfx/platform/macos/font_list.rs b/components/gfx/platform/macos/font_list.rs index 1792fb7a593..d4063576316 100644 --- a/components/gfx/platform/macos/font_list.rs +++ b/components/gfx/platform/macos/font_list.rs @@ -74,11 +74,8 @@ where }; let traits = family_descriptor.traits(); - let descriptor = FontTemplateDescriptor { - weight: traits.weight(), - stretch: traits.stretch(), - style: traits.style(), - }; + let descriptor = + FontTemplateDescriptor::new(traits.weight(), traits.stretch(), traits.style()); let identifier = LocalFontIdentifier { postscript_name: Atom::from(family_descriptor.font_name()), path: Atom::from(path), diff --git a/components/gfx/platform/windows/font.rs b/components/gfx/platform/windows/font.rs index 4930e9f7132..44300da9d5f 100644 --- a/components/gfx/platform/windows/font.rs +++ b/components/gfx/platform/windows/font.rs @@ -186,11 +186,7 @@ impl PlatformFontMethods for PlatformFont { StyleFontStyle::NORMAL }; - FontTemplateDescriptor { - weight, - stretch, - style, - } + FontTemplateDescriptor::new(weight, stretch, style) } fn glyph_index(&self, codepoint: char) -> Option<GlyphId> { diff --git a/components/gfx/tests/font_context.rs b/components/gfx/tests/font_context.rs index f65642f6360..c16f26339dc 100644 --- a/components/gfx/tests/font_context.rs +++ b/components/gfx/tests/font_context.rs @@ -12,12 +12,10 @@ use std::rc::Rc; use app_units::Au; use gfx::font::{ fallback_font_families, FontDescriptor, FontFamilyDescriptor, FontFamilyName, FontSearchScope, - PlatformFontMethods, }; -use gfx::font_cache_thread::{FontIdentifier, FontTemplateAndWebRenderFontKey, FontTemplates}; +use gfx::font_cache_thread::{CSSFontFaceDescriptors, FontIdentifier, FontTemplates}; use gfx::font_context::{FontContext, FontSource}; -use gfx::font_template::{FontTemplate, FontTemplateDescriptor}; -use gfx::platform::font::PlatformFont; +use gfx::font_template::{FontTemplate, FontTemplateRef}; use servo_arc::Arc; use servo_atoms::Atom; use servo_url::ServoUrl; @@ -29,7 +27,7 @@ use style::values::computed::font::{ }; use style::values::computed::{FontLanguageOverride, XLang}; use style::values::generics::font::LineHeight; -use webrender_api::{FontInstanceKey, FontKey, IdNamespace}; +use webrender_api::{FontInstanceKey, IdNamespace}; struct TestFontSource { families: HashMap<String, FontTemplates>, @@ -78,40 +76,36 @@ impl TestFontSource { let file = File::open(path).unwrap(); let data: Vec<u8> = file.bytes().map(|b| b.unwrap()).collect(); - let data = std::sync::Arc::new(data); - let handle = PlatformFont::new_from_data( - Self::identifier_for_font_name(name), - data.clone(), - 0, - None, - ) - .unwrap(); - family.add_template(FontTemplate::new_web_font( - Self::url_for_font_name(name), - handle.descriptor(), - data, - )); + family.add_template( + FontTemplate::new_web_font( + Self::url_for_font_name(name), + std::sync::Arc::new(data), + CSSFontFaceDescriptors::new(name), + ) + .unwrap(), + ); } } impl FontSource for TestFontSource { - fn get_font_instance(&mut self, _key: FontKey, _size: Au) -> FontInstanceKey { + fn get_font_instance( + &mut self, + _font_identifier: FontIdentifier, + _size: Au, + ) -> FontInstanceKey { FontInstanceKey(IdNamespace(0), 0) } - fn font_template( + fn find_matching_font_templates( &mut self, - template_descriptor: FontTemplateDescriptor, + descriptor_to_match: &FontDescriptor, family_descriptor: FontFamilyDescriptor, - ) -> Option<FontTemplateAndWebRenderFontKey> { + ) -> Vec<FontTemplateRef> { self.find_font_count.set(self.find_font_count.get() + 1); self.families .get_mut(family_descriptor.name()) - .and_then(|family| family.find_font_for_style(&template_descriptor)) - .map(|template| FontTemplateAndWebRenderFontKey { - font_template: template, - font_key: FontKey(IdNamespace(0), 0), - }) + .map(|family| family.find_for_descriptor(descriptor_to_match)) + .unwrap_or_default() } } @@ -191,7 +185,7 @@ fn test_font_group_find_by_codepoint() { .find_by_codepoint(&mut context, 'a') .unwrap(); assert_eq!( - font.borrow().template.borrow().identifier, + font.borrow().identifier(), TestFontSource::identifier_for_font_name("csstest-ascii") ); assert_eq!( @@ -205,7 +199,7 @@ fn test_font_group_find_by_codepoint() { .find_by_codepoint(&mut context, 'a') .unwrap(); assert_eq!( - font.borrow().template.borrow().identifier, + font.borrow().identifier(), TestFontSource::identifier_for_font_name("csstest-ascii") ); assert_eq!( @@ -219,7 +213,7 @@ fn test_font_group_find_by_codepoint() { .find_by_codepoint(&mut context, 'á') .unwrap(); assert_eq!( - font.borrow().template.borrow().identifier, + font.borrow().identifier(), TestFontSource::identifier_for_font_name("csstest-basic-regular") ); assert_eq!(count.get(), 2, "both fonts should now have been loaded"); @@ -240,7 +234,7 @@ fn test_font_fallback() { .find_by_codepoint(&mut context, 'a') .unwrap(); assert_eq!( - font.borrow().template.borrow().identifier, + font.borrow().identifier(), TestFontSource::identifier_for_font_name("csstest-ascii"), "a family in the group should be used if there is a matching glyph" ); @@ -250,7 +244,7 @@ fn test_font_fallback() { .find_by_codepoint(&mut context, 'á') .unwrap(); assert_eq!( - font.borrow().template.borrow().identifier, + font.borrow().identifier(), TestFontSource::identifier_for_font_name("csstest-basic-regular"), "a fallback font should be used if there is no matching glyph in the group" ); @@ -263,11 +257,9 @@ fn test_font_template_is_cached() { let mut context = FontContext::new(source); let mut font_descriptor = FontDescriptor { - template_descriptor: FontTemplateDescriptor { - weight: FontWeight::normal(), - stretch: FontStretch::hundred(), - style: FontStyle::normal(), - }, + weight: FontWeight::normal(), + stretch: FontStretch::hundred(), + style: FontStyle::normal(), variant: FontVariantCaps::Normal, pt_size: Au(10), }; @@ -275,10 +267,16 @@ fn test_font_template_is_cached() { let family_descriptor = FontFamilyDescriptor::new(FontFamilyName::from("CSSTest Basic"), FontSearchScope::Any); - let font1 = context.font(&font_descriptor, &family_descriptor).unwrap(); + let font_template = context.matching_templates(&font_descriptor, &family_descriptor)[0].clone(); + + let font1 = context + .font(font_template.clone(), &font_descriptor) + .unwrap(); font_descriptor.pt_size = Au(20); - let font2 = context.font(&font_descriptor, &family_descriptor).unwrap(); + let font2 = context + .font(font_template.clone(), &font_descriptor) + .unwrap(); assert_ne!( font1.borrow().descriptor.pt_size, diff --git a/components/gfx/tests/font_template.rs b/components/gfx/tests/font_template.rs index f7b29b7aba1..8ae6caf7a62 100644 --- a/components/gfx/tests/font_template.rs +++ b/components/gfx/tests/font_template.rs @@ -39,37 +39,33 @@ fn test_font_template_descriptor() { assert_eq!( descriptor("DejaVuSans"), - FontTemplateDescriptor { - weight: FontWeight::NORMAL, - stretch: FontStretch::hundred(), - style: FontStyle::NORMAL, - } + FontTemplateDescriptor::new( + FontWeight::NORMAL, + FontStretch::hundred(), + FontStyle::NORMAL, + ) ); assert_eq!( descriptor("DejaVuSans-Bold"), - FontTemplateDescriptor { - weight: FontWeight::BOLD, - stretch: FontStretch::hundred(), - style: FontStyle::NORMAL, - } + FontTemplateDescriptor::new(FontWeight::BOLD, FontStretch::hundred(), FontStyle::NORMAL,) ); assert_eq!( descriptor("DejaVuSans-Oblique"), - FontTemplateDescriptor { - weight: FontWeight::NORMAL, - stretch: FontStretch::hundred(), - style: FontStyle::ITALIC, - } + FontTemplateDescriptor::new( + FontWeight::NORMAL, + FontStretch::hundred(), + FontStyle::ITALIC, + ) ); assert_eq!( descriptor("DejaVuSansCondensed-BoldOblique"), - FontTemplateDescriptor { - weight: FontWeight::BOLD, - stretch: FontStretch::from_percentage(0.875), - style: FontStyle::ITALIC, - } + FontTemplateDescriptor::new( + FontWeight::BOLD, + FontStretch::from_percentage(0.875), + FontStyle::ITALIC, + ) ); } |