aboutsummaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
authorMartin Robinson <mrobinson@igalia.com>2024-04-29 19:02:07 +0200
committerGitHub <noreply@github.com>2024-04-29 17:02:07 +0000
commit4732da347795c7a9e009a5125c20c1f5c3215209 (patch)
tree9244f043b9049c2e14d4984fa8012e3eb7c15f87 /components
parent628e33bfa95b286e1b8b974e426ffdad7850097e (diff)
downloadservo-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.toml1
-rw-r--r--components/gfx/font.rs195
-rw-r--r--components/gfx/font_cache_thread.rs450
-rw-r--r--components/gfx/font_context.rs84
-rw-r--r--components/gfx/font_template.rs133
-rw-r--r--components/gfx/platform/freetype/android/font_list.rs6
-rw-r--r--components/gfx/platform/freetype/font.rs6
-rw-r--r--components/gfx/platform/freetype/font_list.rs6
-rw-r--r--components/gfx/platform/macos/font.rs6
-rw-r--r--components/gfx/platform/macos/font_list.rs7
-rw-r--r--components/gfx/platform/windows/font.rs6
-rw-r--r--components/gfx/tests/font_context.rs76
-rw-r--r--components/gfx/tests/font_template.rs36
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,
+ )
);
}