fonts: Add more types to fonts_traits (#38898)

This is necessary so that `constellation_traits` can get these types via
a
dependency on `fonts_traits`. This will allow sending IPC channels to
shared
workers so that they can have access to a shared `FontContext` from
`script`.

Testing: This just moves code between crates, so is covered by existing
tests.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
Martin Robinson 2025-08-24 12:36:59 -07:00 committed by GitHub
parent 0b5bcfbf17
commit 3a97d4eed2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 495 additions and 446 deletions

View file

@ -18,7 +18,6 @@ tracing = ["dep:tracing"]
[dependencies]
app_units = { workspace = true }
atomic_refcell = { workspace = true }
base = { workspace = true }
bitflags = { workspace = true }
compositing_traits = { workspace = true }
@ -73,7 +72,7 @@ fontconfig_sys = { package = "yeslogic-fontconfig-sys", version = "6" }
xml-rs = "0.8"
[target.'cfg(target_os = "windows")'.dependencies]
dwrote = "0.11.4"
dwrote = { workspace = true }
winapi = { workspace = true }
[lints.rust]

View file

@ -15,6 +15,7 @@ use app_units::Au;
use bitflags::bitflags;
use euclid::default::{Point2D, Rect, Size2D};
use euclid::num::Zero;
use fonts_traits::FontDescriptor;
use log::debug;
use malloc_size_of_derive::MallocSizeOf;
use parking_lot::RwLock;
@ -190,44 +191,6 @@ impl FontMetrics {
}
}
/// `FontDescriptor` describes the parameters of a `Font`. It represents rendering a given font
/// template at a particular size, with a particular font-variant-caps applied, etc. This contrasts
/// with `FontTemplateDescriptor` in that the latter represents only the parameters inherent in the
/// font data (weight, stretch, etc.).
#[derive(Clone, Debug, Deserialize, Hash, MallocSizeOf, PartialEq, Serialize)]
pub struct FontDescriptor {
pub weight: FontWeight,
pub stretch: FontStretch,
pub style: FontStyle,
pub variant: font_variant_caps::T,
pub pt_size: Au,
pub variation_settings: Vec<FontVariation>,
}
impl Eq for FontDescriptor {}
impl<'a> From<&'a FontStyleStruct> for FontDescriptor {
fn from(style: &'a FontStyleStruct) -> Self {
let variation_settings = style
.clone_font_variation_settings()
.0
.into_iter()
.map(|setting| FontVariation {
tag: setting.tag.0,
value: setting.value,
})
.collect();
FontDescriptor {
weight: style.font_weight,
stretch: style.font_stretch,
style: style.font_style,
variant: style.font_variant_caps,
pt_size: Au::from_f32_px(style.font_size.computed_size().px()),
variation_settings,
}
}
}
#[derive(Debug, Default)]
struct CachedShapeData {
glyph_advances: HashMap<GlyphId, FractionalPixel>,

View file

@ -13,7 +13,10 @@ use app_units::Au;
use base::id::WebViewId;
use compositing_traits::CrossProcessCompositorApi;
use fnv::FnvHasher;
use fonts_traits::StylesheetWebFontLoadFinishedCallback;
use fonts_traits::{
CSSFontFaceDescriptors, FontDescriptor, FontIdentifier, FontTemplate, FontTemplateRef,
FontTemplateRefMethods, StylesheetWebFontLoadFinishedCallback,
};
use log::{debug, trace};
use malloc_size_of_derive::MallocSizeOf;
use net_traits::request::{Destination, Referrer, RequestBuilder};
@ -35,13 +38,9 @@ use style::values::computed::font::{FamilyName, FontFamilyNameSyntax, SingleFont
use url::Url;
use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey, FontVariation};
use crate::font::{
Font, FontDescriptor, FontFamilyDescriptor, FontGroup, FontRef, FontSearchScope,
};
use crate::font::{Font, FontFamilyDescriptor, FontGroup, FontRef, FontSearchScope};
use crate::font_store::CrossThreadFontStore;
use crate::font_template::{FontTemplate, FontTemplateRef, FontTemplateRefMethods};
use crate::platform::font::PlatformFont;
use crate::system_font_service::{CSSFontFaceDescriptors, FontIdentifier};
use crate::{FontData, LowercaseFontFamilyName, PlatformFontMethods, SystemFontServiceProxy};
static SMALL_CAPS_SCALE_FACTOR: f32 = 0.8; // Matches FireFox (see gfxFont.h)

View file

@ -6,16 +6,16 @@ use std::collections::HashMap;
use std::ops::Deref;
use std::sync::Arc;
use fonts_traits::{
FontDescriptor, FontIdentifier, FontTemplate, FontTemplateRef, FontTemplateRefMethods,
IsOblique, LowercaseFontFamilyName,
};
use log::warn;
use malloc_size_of_derive::MallocSizeOf;
use parking_lot::RwLock;
use style::stylesheets::DocumentStyleSheet;
use style::values::computed::{FontStyle, FontWeight};
use crate::font::FontDescriptor;
use crate::font_template::{FontTemplate, FontTemplateRef, FontTemplateRefMethods, IsOblique};
use crate::system_font_service::{FontIdentifier, LowercaseFontFamilyName};
#[derive(Default, MallocSizeOf)]
pub struct FontStore {
pub(crate) families: HashMap<LowercaseFontFamilyName, FontTemplates>,

View file

@ -1,523 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::fmt::{Debug, Error, Formatter};
use std::ops::{Deref, RangeInclusive};
use std::sync::Arc;
use atomic_refcell::AtomicRefCell;
use malloc_size_of_derive::MallocSizeOf;
use serde::{Deserialize, Serialize};
use style::computed_values::font_stretch::T as FontStretch;
use style::computed_values::font_style::T as FontStyle;
use style::stylesheets::DocumentStyleSheet;
use style::values::computed::font::FontWeight;
use crate::font::FontDescriptor;
use crate::system_font_service::{
CSSFontFaceDescriptors, ComputedFontStyleDescriptor, FontIdentifier,
};
/// A reference to a [`FontTemplate`] with shared ownership and mutability.
#[derive(Clone, Debug, MallocSizeOf)]
pub struct FontTemplateRef(#[conditional_malloc_size_of] Arc<AtomicRefCell<FontTemplate>>);
impl FontTemplateRef {
pub fn new(template: FontTemplate) -> Self {
Self(Arc::new(AtomicRefCell::new(template)))
}
}
impl Deref for FontTemplateRef {
type Target = Arc<AtomicRefCell<FontTemplate>>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
/// 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, Debug, Deserialize, Hash, MallocSizeOf, PartialEq, Serialize)]
pub struct FontTemplateDescriptor {
pub weight: (FontWeight, FontWeight),
pub stretch: (FontStretch, FontStretch),
pub style: (FontStyle, FontStyle),
#[ignore_malloc_size_of = "MallocSizeOf does not yet support RangeInclusive"]
pub unicode_range: Option<Vec<RangeInclusive<u32>>>,
}
impl Default for FontTemplateDescriptor {
fn default() -> Self {
Self::new(FontWeight::normal(), FontStretch::NORMAL, FontStyle::NORMAL)
}
}
/// FontTemplateDescriptor contains floats, which are not Eq because of NaN. However,
/// we know they will never be NaN, so we can manually implement Eq.
impl Eq for FontTemplateDescriptor {}
impl FontTemplateDescriptor {
#[inline]
pub fn new(weight: FontWeight, stretch: FontStretch, style: FontStyle) -> Self {
Self {
weight: (weight, weight),
stretch: (stretch, stretch),
style: (style, style),
unicode_range: None,
}
}
pub fn is_variation_font(&self) -> bool {
self.weight.0 != self.weight.1 ||
self.stretch.0 != self.stretch.1 ||
self.style.0 != self.style.1
}
/// Returns a score indicating how far apart visually the two font descriptors are. This is
/// used for implmenting the CSS Font Matching algorithm:
/// <https://drafts.csswg.org/css-fonts/#font-matching-algorithm>.
///
/// The smaller the score, the better the fonts match. 0 indicates an exact match. This must
/// be commutative (distance(A, B) == distance(B, A)).
#[inline]
fn distance_from(&self, target: &FontDescriptor) -> f32 {
let stretch_distance = target.stretch.match_distance(&self.stretch);
let style_distance = target.style.match_distance(&self.style);
let weight_distance = target.weight.match_distance(&self.weight);
// Sanity-check that the distances are within the expected range
// (update if implementation of the distance functions is changed).
assert!((0.0..=2000.0).contains(&stretch_distance));
assert!((0.0..=500.0).contains(&style_distance));
assert!((0.0..=1600.0).contains(&weight_distance));
// Factors used to weight the distances between the available and target font
// properties during font-matching. These ensure that we respect the CSS-fonts
// requirement that font-stretch >> font-style >> font-weight; and in addition,
// a mismatch between the desired and actual glyph presentation (emoji vs text)
// will take precedence over any of the style attributes.
//
// Also relevant for font selection is the emoji presentation preference, but this
// is handled later when filtering fonts based on the glyphs they contain.
const STRETCH_FACTOR: f32 = 1.0e8;
const STYLE_FACTOR: f32 = 1.0e4;
const WEIGHT_FACTOR: f32 = 1.0e0;
stretch_distance * STRETCH_FACTOR +
style_distance * STYLE_FACTOR +
weight_distance * WEIGHT_FACTOR
}
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
}
pub(crate) 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::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(ref unicode_range) = css_font_template_descriptors.unicode_range {
self.unicode_range = Some(unicode_range.clone());
}
}
}
/// This describes all the information needed to create
/// font instance handles. It contains a unique
/// FontTemplateData structure that is platform specific.
#[derive(Clone, Deserialize, MallocSizeOf, Serialize)]
pub struct FontTemplate {
pub identifier: FontIdentifier,
pub descriptor: FontTemplateDescriptor,
/// If this font is a web font, this is a reference to the stylesheet that
/// created it. This will be used to remove this font from caches, when the
/// stylesheet is removed.
///
/// This is not serialized, as it's only useful in the [`super::FontContext`]
/// that it is created in.
#[serde(skip)]
pub stylesheet: Option<DocumentStyleSheet>,
}
impl Debug for FontTemplate {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
self.identifier.fmt(f)
}
}
/// Holds all of the template information for a font that
/// is common, regardless of the number of instances of
/// this font handle per thread.
impl FontTemplate {
/// Create a new [`FontTemplate`].
pub fn new(
identifier: FontIdentifier,
descriptor: FontTemplateDescriptor,
stylesheet: Option<DocumentStyleSheet>,
) -> FontTemplate {
FontTemplate {
identifier,
descriptor,
stylesheet,
}
}
/// Create a new [`FontTemplate`] for a `@font-family` with a `local(...)` `src`. This takes in
/// the template of the local font and creates a new one that reflects the properties specified
/// by `@font-family` in the stylesheet.
pub fn new_for_local_web_font(
local_template: FontTemplateRef,
css_font_template_descriptors: &CSSFontFaceDescriptors,
stylesheet: Option<DocumentStyleSheet>,
) -> Result<FontTemplate, &'static str> {
let mut alias_template = local_template.borrow().clone();
alias_template
.descriptor
.override_values_with_css_font_template_descriptors(css_font_template_descriptors);
alias_template.stylesheet = stylesheet;
Ok(alias_template)
}
pub fn identifier(&self) -> &FontIdentifier {
&self.identifier
}
}
pub trait FontTemplateRefMethods {
/// 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 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, 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.clone()
}
fn identifier(&self) -> FontIdentifier {
self.borrow().identifier.clone()
}
fn matches_font_descriptor(&self, descriptor_to_match: &FontDescriptor) -> bool {
self.descriptor().matches(descriptor_to_match)
}
fn descriptor_distance(&self, descriptor_to_match: &FontDescriptor) -> f32 {
self.descriptor().distance_from(descriptor_to_match)
}
fn char_in_unicode_range(&self, character: char) -> bool {
let character = character as u32;
self.borrow()
.descriptor
.unicode_range
.as_ref()
.is_none_or(|ranges| ranges.iter().any(|range| range.contains(&character)))
}
}
/// A trait for implementing the CSS font matching algorithm against various font features.
/// See <https://drafts.csswg.org/css-fonts/#font-matching-algorithm>.
///
/// This implementation is ported from Gecko at:
/// <https://searchfox.org/mozilla-central/rev/0529464f0d2981347ef581f7521ace8b7af7f7ac/gfx/thebes/gfxFontUtils.h#1217>.
trait FontMatchDistanceMethod: Sized {
fn match_distance(&self, range: &(Self, Self)) -> f32;
fn to_float(&self) -> f32;
}
impl FontMatchDistanceMethod for FontStretch {
fn match_distance(&self, range: &(Self, Self)) -> f32 {
// stretch distance ==> [0,2000]
const REVERSE_DISTANCE: f32 = 1000.0;
let min_stretch = range.0;
let max_stretch = range.1;
// The stretch value is a (non-negative) percentage; currently we support
// values in the range 0 .. 1000. (If the upper limit is ever increased,
// the kReverseDistance value used here may need to be adjusted.)
// If aTargetStretch is >100, we prefer larger values if available;
// if <=100, we prefer smaller values if available.
if *self < min_stretch {
if *self > FontStretch::NORMAL {
return min_stretch.to_float() - self.to_float();
}
return (min_stretch.to_float() - self.to_float()) + REVERSE_DISTANCE;
}
if *self > max_stretch {
if *self <= FontStretch::NORMAL {
return self.to_float() - max_stretch.to_float();
}
return (self.to_float() - max_stretch.to_float()) + REVERSE_DISTANCE;
}
0.0
}
fn to_float(&self) -> f32 {
self.0.to_float()
}
}
impl FontMatchDistanceMethod for FontWeight {
// Calculate weight distance with values in the range (0..1000). In general,
// heavier weights match towards even heavier weights while lighter weights
// match towards even lighter weights. Target weight values in the range
// [400..500] are special, since they will first match up to 500, then down
// towards 0, then up again towards 999.
//
// Example: with target 600 and font weight 800, distance will be 200. With
// target 300 and font weight 600, distance will be 900, since heavier
// weights are farther away than lighter weights. If the target is 5 and the
// font weight 995, the distance would be 1590 for the same reason.
fn match_distance(&self, range: &(Self, Self)) -> f32 {
// weight distance ==> [0,1600]
const NOT_WITHIN_CENTRAL_RANGE: f32 = 100.0;
const REVERSE_DISTANCE: f32 = 600.0;
let min_weight = range.0;
let max_weight = range.1;
if *self >= min_weight && *self <= max_weight {
// Target is within the face's range, so it's a perfect match
return 0.0;
}
if *self < FontWeight::NORMAL {
// Requested a lighter-than-400 weight
if max_weight < *self {
return self.to_float() - max_weight.to_float();
}
// Add reverse-search penalty for bolder faces
return (min_weight.to_float() - self.to_float()) + REVERSE_DISTANCE;
}
if *self > FontWeight::from_float(500.) {
// Requested a bolder-than-500 weight
if min_weight > *self {
return min_weight.to_float() - self.to_float();
}
// Add reverse-search penalty for lighter faces
return (self.to_float() - max_weight.to_float()) + REVERSE_DISTANCE;
}
// Special case for requested weight in the [400..500] range
if min_weight > *self {
if min_weight <= FontWeight::from_float(500.) {
// Bolder weight up to 500 is first choice
return min_weight.to_float() - self.to_float();
}
// Other bolder weights get a reverse-search penalty
return (min_weight.to_float() - self.to_float()) + REVERSE_DISTANCE;
}
// Lighter weights are not as good as bolder ones within [400..500]
(self.to_float() - max_weight.to_float()) + NOT_WITHIN_CENTRAL_RANGE
}
fn to_float(&self) -> f32 {
self.value()
}
}
impl FontMatchDistanceMethod for FontStyle {
fn match_distance(&self, range: &(Self, Self)) -> f32 {
// style distance ==> [0,500]
let min_style = range.0;
if *self == min_style {
return 0.0; // styles match exactly ==> 0
}
// bias added to angle difference when searching in the non-preferred
// direction from a target angle
const REVERSE: f32 = 100.0;
// bias added when we've crossed from positive to negative angles or
// vice versa
const NEGATE: f32 = 200.0;
if *self == FontStyle::NORMAL {
if min_style.is_oblique() {
// to distinguish oblique 0deg from normal, we add 1.0 to the angle
let min_angle = min_style.oblique_degrees();
if min_angle >= 0.0 {
return 1.0 + min_angle;
}
let max_style = range.1;
let max_angle = max_style.oblique_degrees();
if max_angle >= 0.0 {
// [min,max] range includes 0.0, so just return our minimum
return 1.0;
}
// negative oblique is even worse than italic
return NEGATE - max_angle;
}
// must be italic, which is worse than any non-negative oblique;
// treat as a match in the wrong search direction
assert!(min_style == FontStyle::ITALIC);
return REVERSE;
}
let default_oblique_angle = FontStyle::OBLIQUE.oblique_degrees();
if *self == FontStyle::ITALIC {
if min_style.is_oblique() {
let min_angle = min_style.oblique_degrees();
if min_angle >= default_oblique_angle {
return 1.0 + (min_angle - default_oblique_angle);
}
let max_style = range.1;
let max_angle = max_style.oblique_degrees();
if max_angle >= default_oblique_angle {
return 1.0;
}
if max_angle > 0.0 {
// wrong direction but still > 0, add bias of 100
return REVERSE + (default_oblique_angle - max_angle);
}
// negative oblique angle, add bias of 300
return REVERSE + NEGATE + (default_oblique_angle - max_angle);
}
// normal is worse than oblique > 0, but better than oblique <= 0
assert!(min_style == FontStyle::NORMAL);
return NEGATE;
}
// target is oblique <angle>: four different cases depending on
// the value of the <angle>, which determines the preferred direction
// of search
let target_angle = self.oblique_degrees();
if target_angle >= default_oblique_angle {
if min_style.is_oblique() {
let min_angle = min_style.oblique_degrees();
if min_angle >= target_angle {
return min_angle - target_angle;
}
let max_style = range.1;
let max_angle = max_style.oblique_degrees();
if max_angle >= target_angle {
return 0.0;
}
if max_angle > 0.0 {
return REVERSE + (target_angle - max_angle);
}
return REVERSE + NEGATE + (target_angle - max_angle);
}
if min_style == FontStyle::ITALIC {
return REVERSE + NEGATE;
}
return REVERSE + NEGATE + 1.0;
}
if target_angle <= -default_oblique_angle {
if min_style.is_oblique() {
let max_style = range.1;
let max_angle = max_style.oblique_degrees();
if max_angle <= target_angle {
return target_angle - max_angle;
}
let min_angle = min_style.oblique_degrees();
if min_angle <= target_angle {
return 0.0;
}
if min_angle < 0.0 {
return REVERSE + (min_angle - target_angle);
}
return REVERSE + NEGATE + (min_angle - target_angle);
}
if min_style == FontStyle::ITALIC {
return REVERSE + NEGATE;
}
return REVERSE + NEGATE + 1.0;
}
if target_angle >= 0.0 {
if min_style.is_oblique() {
let min_angle = min_style.oblique_degrees();
if min_angle > target_angle {
return REVERSE + (min_angle - target_angle);
}
let max_style = range.1;
let max_angle = max_style.oblique_degrees();
if max_angle >= target_angle {
return 0.0;
}
if max_angle > 0.0 {
return target_angle - max_angle;
}
return REVERSE + NEGATE + (target_angle - max_angle);
}
if min_style == FontStyle::ITALIC {
return REVERSE + NEGATE - 2.0;
}
return REVERSE + NEGATE - 1.0;
}
// last case: (targetAngle < 0.0 && targetAngle > kDefaultAngle)
if min_style.is_oblique() {
let max_style = range.1;
let max_angle = max_style.oblique_degrees();
if max_angle < target_angle {
return REVERSE + (target_angle - max_angle);
}
let min_angle = min_style.oblique_degrees();
if min_angle <= target_angle {
return 0.0;
}
if min_angle < 0.0 {
return min_angle - target_angle;
}
return REVERSE + NEGATE + (min_angle - target_angle);
}
if min_style == FontStyle::ITALIC {
return REVERSE + NEGATE - 2.0;
}
REVERSE + NEGATE - 1.0
}
fn to_float(&self) -> f32 {
unimplemented!("Don't know how to convert FontStyle to float.");
}
}
pub(crate) trait IsOblique {
fn is_oblique(&self) -> bool;
}
impl IsOblique for FontStyle {
fn is_oblique(&self) -> bool {
*self != FontStyle::NORMAL && *self != FontStyle::ITALIC
}
}

View file

@ -7,7 +7,6 @@
mod font;
mod font_context;
mod font_store;
mod font_template;
mod glyph;
#[allow(unsafe_code)]
pub mod platform;
@ -17,10 +16,8 @@ mod system_font_service;
pub use font::*;
pub use font_context::*;
pub use font_store::*;
pub use font_template::*;
pub use fonts_traits::*;
pub use fonts_traits::{LocalFontIdentifier, *};
pub use glyph::*;
pub use platform::LocalFontIdentifier;
pub use shapers::*;
pub use system_font_service::*;
use unicode_properties::{EmojiStatus, UnicodeEmoji, emoji};

View file

@ -7,6 +7,7 @@ use std::fs::File;
use app_units::Au;
use euclid::default::{Point2D, Rect, Size2D};
use fonts_traits::{FontIdentifier, FontTemplateDescriptor, LocalFontIdentifier};
use freetype_sys::{
FT_F26Dot6, FT_Get_Char_Index, FT_Get_Kerning, FT_GlyphSlot, FT_KERNING_DEFAULT,
FT_LOAD_DEFAULT, FT_LOAD_NO_HINTING, FT_Load_Glyph, FT_Size_Metrics, FT_SizeRec, FT_UInt,
@ -21,14 +22,11 @@ use servo_arc::Arc;
use style::Zero;
use webrender_api::{FontInstanceFlags, FontVariation};
use super::LocalFontIdentifier;
use super::library_handle::FreeTypeLibraryHandle;
use crate::FontData;
use crate::font::{FontMetrics, FontTableMethods, FractionalPixel, PlatformFontMethods};
use crate::font_template::FontTemplateDescriptor;
use crate::glyph::GlyphId;
use crate::platform::freetype::freetype_face::FreeTypeFace;
use crate::system_font_service::FontIdentifier;
/// Convert FreeType-style 26.6 fixed point to an [`f64`].
fn fixed_26_dot_6_to_float(fixed: FT_F26Dot6) -> f64 {

View file

@ -19,6 +19,7 @@ use fontconfig_sys::{
FcObjectSetCreate, FcObjectSetDestroy, FcPattern, FcPatternAddString, FcPatternCreate,
FcPatternDestroy, FcPatternGetInteger, FcPatternGetString, FcResultMatch, FcSetSystem,
};
use fonts_traits::{FontTemplate, FontTemplateDescriptor, LocalFontIdentifier};
use libc::{c_char, c_int};
use log::debug;
use style::Atom;
@ -26,9 +27,7 @@ use style::values::computed::font::GenericFontFamily;
use style::values::computed::{FontStretch, FontStyle, FontWeight};
use unicode_script::Script;
use super::LocalFontIdentifier;
use crate::font::map_platform_values_to_style_values;
use crate::font_template::{FontTemplate, FontTemplateDescriptor};
use crate::platform::add_noto_fallback_families;
use crate::{
EmojiPresentationPreference, FallbackFontSelectionOptions, FontIdentifier,

View file

@ -2,19 +2,6 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::convert::TryInto;
use std::fs::File;
use std::path::{Path, PathBuf};
use std::str;
use malloc_size_of_derive::MallocSizeOf;
use memmap2::Mmap;
use serde::{Deserialize, Serialize};
use style::Atom;
use webrender_api::NativeFontHandle;
use crate::{FontData, FontDataAndIndex};
pub mod font;
mod freetype_face;
@ -37,36 +24,3 @@ mod ohos {
pub use self::ohos::font_list;
mod library_handle;
/// An identifier for a local font on systems using Freetype.
#[derive(Clone, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
pub struct LocalFontIdentifier {
/// The path to the font.
pub path: Atom,
/// The variation index within the font.
pub variation_index: i32,
}
impl LocalFontIdentifier {
pub(crate) fn index(&self) -> u32 {
self.variation_index.try_into().unwrap()
}
pub(crate) fn native_font_handle(&self) -> NativeFontHandle {
NativeFontHandle {
path: PathBuf::from(&*self.path),
index: self.variation_index as u32,
}
}
pub(crate) fn font_data_and_index(&self) -> Option<FontDataAndIndex> {
let file = File::open(Path::new(&*self.path)).ok()?;
let mmap = unsafe { Mmap::map(&file).ok()? };
let data = FontData::from_bytes(&mmap);
Some(FontDataAndIndex {
data,
index: self.variation_index as u32,
})
}
}

View file

@ -13,12 +13,12 @@ use core_foundation::string::{CFString, CFStringRef};
use core_foundation::url::{CFURL, kCFURLPOSIXPathStyle};
use core_graphics::display::CFDictionary;
use core_text::font_descriptor::{kCTFontURLAttribute, kCTFontVariationAttribute};
use fonts_traits::FontIdentifier;
use parking_lot::RwLock;
use webrender_api::FontVariation;
use crate::FontData;
use crate::platform::font::PlatformFont;
use crate::system_font_service::FontIdentifier;
/// A cache of `CTFont` to avoid having to create `CTFont` instances over and over. It is
/// always possible to create a `CTFont` using a `FontTemplate` even if it isn't in this

View file

@ -17,16 +17,16 @@ use core_text::font_descriptor::{
CTFontTraits, SymbolicTraitAccessors, TraitAccessors, kCTFontDefaultOrientation,
};
use euclid::default::{Point2D, Rect, Size2D};
use fonts_traits::{FontIdentifier, LocalFontIdentifier};
use log::debug;
use skrifa::Tag;
use style::values::computed::font::{FontStretch, FontStyle, FontWeight};
use webrender_api::{FontInstanceFlags, FontVariation};
use super::core_text_font_cache::CoreTextFontCache;
use super::font_list::LocalFontIdentifier;
use crate::{
CBDT, COLR, FontData, FontIdentifier, FontMetrics, FontTableMethods, FontTemplateDescriptor,
FractionalPixel, GlyphId, KERN, PlatformFontMethods, SBIX, map_platform_values_to_style_values,
CBDT, COLR, FontData, FontMetrics, FontTableMethods, FontTemplateDescriptor, FractionalPixel,
GlyphId, KERN, PlatformFontMethods, SBIX, map_platform_values_to_style_values,
};
const KERN_PAIR_LEN: usize = 6;

View file

@ -2,107 +2,20 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::fs::File;
use std::path::Path;
use base::text::{UnicodeBlock, UnicodeBlockMethod, unicode_plane};
use log::{debug, warn};
use malloc_size_of_derive::MallocSizeOf;
use memmap2::Mmap;
use read_fonts::types::NameId;
use read_fonts::{FileRef, TableProvider as _};
use serde::{Deserialize, Serialize};
use fonts_traits::LocalFontIdentifier;
use log::debug;
use style::Atom;
use style::values::computed::font::GenericFontFamily;
use unicode_script::Script;
use webrender_api::NativeFontHandle;
use crate::platform::add_noto_fallback_families;
use crate::platform::font::CoreTextFontTraitsMapping;
use crate::{
EmojiPresentationPreference, FallbackFontSelectionOptions, FontData, FontDataAndIndex,
FontIdentifier, FontTemplate, FontTemplateDescriptor, LowercaseFontFamilyName,
EmojiPresentationPreference, FallbackFontSelectionOptions, FontIdentifier, FontTemplate,
FontTemplateDescriptor, LowercaseFontFamilyName,
};
/// An identifier for a local font on a MacOS system. These values comes from the CoreText
/// CTFontCollection. Note that `path` here is required. We do not load fonts that do not
/// have paths.
#[derive(Clone, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
pub struct LocalFontIdentifier {
pub postscript_name: Atom,
pub path: Atom,
}
impl LocalFontIdentifier {
pub(crate) fn native_font_handle(&self) -> NativeFontHandle {
NativeFontHandle {
name: self.postscript_name.to_string(),
path: self.path.to_string(),
}
}
pub(crate) fn index(&self) -> u32 {
0
}
pub(crate) fn font_data_and_index(&self) -> Option<FontDataAndIndex> {
let file = File::open(Path::new(&*self.path)).ok()?;
let mmap = unsafe { Mmap::map(&file).ok()? };
// Determine index
let file_ref = FileRef::new(mmap.as_ref()).ok()?;
let index = ttc_index_from_postscript_name(file_ref, &self.postscript_name);
Some(FontDataAndIndex {
data: FontData::from_bytes(&mmap),
index,
})
}
}
/// CoreText font enumeration gives us a Postscript name rather than an index.
/// This functions maps from a Postscript name to an index.
///
/// This mapping works for single-font files and for simple TTC files, but may not work in all cases.
/// We are not 100% sure which cases (if any) will not work. But we suspect that variable fonts may cause
/// issues due to the Postscript names corresponding to instances not being straightforward, and the possibility
/// that CoreText may return a non-standard in that scenerio.
fn ttc_index_from_postscript_name(font_file: FileRef<'_>, postscript_name: &str) -> u32 {
match font_file {
// File only contains one font: simply return 0
FileRef::Font(_) => 0,
// File is a collection: iterate through each font in the collection and check
// whether the name matches
FileRef::Collection(collection) => {
for i in 0..collection.len() {
let font = collection.get(i).unwrap();
let name_table = font.name().unwrap();
if name_table
.name_record()
.iter()
.filter(|record| record.name_id() == NameId::POSTSCRIPT_NAME)
.any(|record| {
record
.string(name_table.string_data())
.unwrap()
.chars()
.eq(postscript_name.chars())
})
{
return i;
}
}
// If we fail to find a font, just use the first font in the file.
warn!(
"Font with postscript_name {} not found in collection",
postscript_name
);
0
},
}
}
pub fn for_each_available_family<F>(mut callback: F)
where
F: FnMut(String),

View file

@ -22,13 +22,11 @@ use unicode_script::Script;
))]
use crate::FallbackFontSelectionOptions;
#[cfg(any(target_os = "linux", target_os = "android"))]
pub use crate::platform::freetype::{LocalFontIdentifier, font, font_list};
pub use crate::platform::freetype::{font, font_list};
#[cfg(target_os = "macos")]
pub use crate::platform::macos::{
core_text_font_cache, font, font_list, font_list::LocalFontIdentifier,
};
pub use crate::platform::macos::{core_text_font_cache, font, font_list};
#[cfg(target_os = "windows")]
pub use crate::platform::windows::{font, font_list, font_list::LocalFontIdentifier};
pub use crate::platform::windows::{font, font_list};
#[cfg(any(target_os = "linux", target_os = "android"))]
pub mod freetype;

View file

@ -17,6 +17,7 @@ use dwrote::{
DWRITE_FONT_AXIS_VALUE, DWRITE_FONT_SIMULATIONS_NONE, FontCollection, FontFace, FontFile,
};
use euclid::default::{Point2D, Rect, Size2D};
use fonts_traits::LocalFontIdentifier;
use log::debug;
use read_fonts::TableProvider;
use skrifa::Tag;
@ -24,7 +25,6 @@ use style::Zero;
use webrender_api::{FontInstanceFlags, FontVariation};
use winapi::shared::minwindef::{BOOL, FALSE};
use super::font_list::LocalFontIdentifier;
use crate::{
FontData, FontIdentifier, FontMetrics, FontTableMethods, FontTemplateDescriptor,
FractionalPixel, GlyphId, PlatformFontMethods,

View file

@ -2,21 +2,18 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::hash::Hash;
use std::sync::Arc;
use base::text::{UnicodeBlock, UnicodeBlockMethod, unicode_plane};
use dwrote::{Font, FontCollection, FontDescriptor, FontStretch, FontStyle};
use malloc_size_of_derive::MallocSizeOf;
use serde::{Deserialize, Serialize};
use dwrote::{Font, FontCollection, FontStretch, FontStyle};
use fonts_traits::LocalFontIdentifier;
use style::values::computed::font::GenericFontFamily;
use style::values::computed::{FontStyle as StyleFontStyle, FontWeight as StyleFontWeight};
use style::values::specified::font::FontStretchKeyword;
use webrender_api::NativeFontHandle;
use crate::{
EmojiPresentationPreference, FallbackFontSelectionOptions, FontData, FontDataAndIndex,
FontIdentifier, FontTemplate, FontTemplateDescriptor, LowercaseFontFamilyName,
EmojiPresentationPreference, FallbackFontSelectionOptions, FontIdentifier, FontTemplate,
FontTemplateDescriptor, LowercaseFontFamilyName,
};
pub fn for_each_available_family<F>(mut callback: F)
@ -31,71 +28,6 @@ where
}
}
/// An identifier for a local font on a Windows system.
#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
pub struct LocalFontIdentifier {
/// The FontDescriptor of this font.
#[ignore_malloc_size_of = "dwrote does not support MallocSizeOf"]
pub font_descriptor: Arc<FontDescriptor>,
}
impl LocalFontIdentifier {
pub fn index(&self) -> u32 {
FontCollection::system()
.font_from_descriptor(&self.font_descriptor)
.ok()
.flatten()
.map_or(0, |font| font.create_font_face().get_index())
}
pub(crate) fn native_font_handle(&self) -> NativeFontHandle {
let face = FontCollection::system()
.font_from_descriptor(&self.font_descriptor)
.ok()
.flatten()
.expect("Could not create Font from FontDescriptor")
.create_font_face();
let path = face
.files()
.ok()
.and_then(|files| files.first().cloned())
.expect("Could not get FontFace files")
.font_file_path()
.ok()
.expect("Could not get FontFace files path");
NativeFontHandle {
path,
index: face.get_index(),
}
}
pub(crate) fn font_data_and_index(&self) -> Option<FontDataAndIndex> {
let font = FontCollection::system()
.font_from_descriptor(&self.font_descriptor)
.ok()??;
let face = font.create_font_face();
let index = face.get_index();
let files = face.files().ok()?;
assert!(!files.is_empty());
let data = files[0].font_file_bytes().ok()?;
let data = FontData::from_bytes(&data);
Some(FontDataAndIndex { data, index })
}
}
impl Eq for LocalFontIdentifier {}
impl Hash for LocalFontIdentifier {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.font_descriptor.family_name.hash(state);
self.font_descriptor.weight.to_u32().hash(state);
self.font_descriptor.stretch.to_u32().hash(state);
self.font_descriptor.style.to_u32().hash(state);
}
}
pub fn for_each_variation<F>(family_name: &str, mut callback: F)
where
F: FnMut(FontTemplate),
@ -107,7 +39,7 @@ where
let Ok(font) = family.font(i) else {
continue;
};
let template_descriptor = (&font).into();
let template_descriptor = font_template_descriptor_from_font(&font);
let local_font_identifier = LocalFontIdentifier {
font_descriptor: Arc::new(font.to_descriptor()),
};
@ -383,29 +315,27 @@ pub fn fallback_font_families(options: FallbackFontSelectionOptions) -> Vec<&'st
families
}
impl From<&Font> for FontTemplateDescriptor {
fn from(font: &Font) -> Self {
let style = match font.style() {
FontStyle::Normal => StyleFontStyle::NORMAL,
FontStyle::Oblique => StyleFontStyle::OBLIQUE,
FontStyle::Italic => StyleFontStyle::ITALIC,
};
let weight = StyleFontWeight::from_float(font.weight().to_u32() as f32);
let stretch = match font.stretch() {
FontStretch::Undefined => FontStretchKeyword::Normal,
FontStretch::UltraCondensed => FontStretchKeyword::UltraCondensed,
FontStretch::ExtraCondensed => FontStretchKeyword::ExtraCondensed,
FontStretch::Condensed => FontStretchKeyword::Condensed,
FontStretch::SemiCondensed => FontStretchKeyword::SemiCondensed,
FontStretch::Normal => FontStretchKeyword::Normal,
FontStretch::SemiExpanded => FontStretchKeyword::SemiExpanded,
FontStretch::Expanded => FontStretchKeyword::Expanded,
FontStretch::ExtraExpanded => FontStretchKeyword::ExtraExpanded,
FontStretch::UltraExpanded => FontStretchKeyword::UltraExpanded,
}
.compute();
FontTemplateDescriptor::new(weight, stretch, style)
fn font_template_descriptor_from_font(font: &Font) -> FontTemplateDescriptor {
let style = match font.style() {
FontStyle::Normal => StyleFontStyle::NORMAL,
FontStyle::Oblique => StyleFontStyle::OBLIQUE,
FontStyle::Italic => StyleFontStyle::ITALIC,
};
let weight = StyleFontWeight::from_float(font.weight().to_u32() as f32);
let stretch = match font.stretch() {
FontStretch::Undefined => FontStretchKeyword::Normal,
FontStretch::UltraCondensed => FontStretchKeyword::UltraCondensed,
FontStretch::ExtraCondensed => FontStretchKeyword::ExtraCondensed,
FontStretch::Condensed => FontStretchKeyword::Condensed,
FontStretch::SemiCondensed => FontStretchKeyword::SemiCondensed,
FontStretch::Normal => FontStretchKeyword::Normal,
FontStretch::SemiExpanded => FontStretchKeyword::SemiExpanded,
FontStretch::Expanded => FontStretchKeyword::Expanded,
FontStretch::ExtraExpanded => FontStretchKeyword::ExtraExpanded,
FontStretch::UltraExpanded => FontStretchKeyword::UltraExpanded,
}
.compute();
FontTemplateDescriptor::new(weight, stretch, style)
}
pub fn default_system_generic_font_family(generic: GenericFontFamily) -> LowercaseFontFamilyName {

View file

@ -5,11 +5,13 @@
use std::borrow::ToOwned;
use std::cell::OnceCell;
use std::collections::HashMap;
use std::ops::{Deref, RangeInclusive};
use std::{fmt, thread};
use std::thread;
use app_units::Au;
use compositing_traits::CrossProcessCompositorApi;
use fonts_traits::{
FontDescriptor, FontIdentifier, FontTemplate, FontTemplateRef, LowercaseFontFamilyName,
};
use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
use log::debug;
use malloc_size_of::MallocSizeOf as MallocSizeOfTrait;
@ -21,38 +23,14 @@ use profile_traits::mem::{
use profile_traits::path;
use serde::{Deserialize, Serialize};
use servo_config::pref;
use servo_url::ServoUrl;
use style::font_face::{FontFaceRuleData, FontStyle as FontFaceStyle};
use style::values::computed::font::{
FixedPoint, FontStyleFixedPoint, GenericFontFamily, SingleFontFamily,
};
use style::values::computed::{FontStretch, FontWeight};
use style::values::specified::FontStretch as SpecifiedFontStretch;
use style::values::computed::font::{GenericFontFamily, SingleFontFamily};
use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey, FontVariation};
use crate::font::FontDescriptor;
use crate::font_store::FontStore;
use crate::font_template::{FontTemplate, FontTemplateRef};
use crate::platform::LocalFontIdentifier;
use crate::platform::font_list::{
default_system_generic_font_family, for_each_available_family, for_each_variation,
};
#[derive(Clone, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
pub enum FontIdentifier {
Local(LocalFontIdentifier),
Web(ServoUrl),
}
impl FontIdentifier {
pub fn index(&self) -> u32 {
match *self {
Self::Local(ref local_font_identifier) => local_font_identifier.index(),
Self::Web(_) => 0,
}
}
}
/// Commands that the `FontContext` sends to the `SystemFontService`.
#[derive(Debug, Deserialize, Serialize)]
pub enum SystemFontServiceMessage {
@ -379,89 +357,6 @@ pub struct SystemFontServiceProxy {
templates: RwLock<HashMap<FontTemplateCacheKey, Vec<FontTemplateRef>>>,
}
/// A version of `FontStyle` from Stylo that is serializable. Normally this is not
/// because the specified version of `FontStyle` contains floats.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum ComputedFontStyleDescriptor {
Italic,
Oblique(FontStyleFixedPoint, FontStyleFixedPoint),
}
/// This data structure represents the various optional descriptors that can be
/// applied to a `@font-face` rule in CSS. These are used to create a [`FontTemplate`]
/// from the given font data used as the source of the `@font-face` rule. If values
/// like weight, stretch, and style are not specified they are initialized based
/// on the contents of the font itself.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct CSSFontFaceDescriptors {
pub family_name: LowercaseFontFamilyName,
pub weight: Option<(FontWeight, FontWeight)>,
pub stretch: Option<(FontStretch, FontStretch)>,
pub style: Option<ComputedFontStyleDescriptor>,
pub unicode_range: Option<Vec<RangeInclusive<u32>>>,
}
impl CSSFontFaceDescriptors {
pub fn new(family_name: &str) -> Self {
CSSFontFaceDescriptors {
family_name: family_name.into(),
..Default::default()
}
}
}
impl From<&FontFaceRuleData> for CSSFontFaceDescriptors {
fn from(rule_data: &FontFaceRuleData) -> Self {
let family_name = rule_data
.family
.as_ref()
.expect("Expected rule to contain a font family.")
.name
.clone();
let weight = rule_data
.weight
.as_ref()
.map(|weight_range| (weight_range.0.compute(), weight_range.1.compute()));
let stretch_to_computed = |specified: SpecifiedFontStretch| match specified {
SpecifiedFontStretch::Stretch(percentage) => {
FontStretch::from_percentage(percentage.compute().0)
},
SpecifiedFontStretch::Keyword(keyword) => keyword.compute(),
SpecifiedFontStretch::System(_) => FontStretch::NORMAL,
};
let stretch = rule_data.stretch.as_ref().map(|stretch_range| {
(
stretch_to_computed(stretch_range.0),
stretch_to_computed(stretch_range.1),
)
});
fn style_to_computed(specified: &FontFaceStyle) -> ComputedFontStyleDescriptor {
match specified {
FontFaceStyle::Italic => ComputedFontStyleDescriptor::Italic,
FontFaceStyle::Oblique(angle_a, angle_b) => ComputedFontStyleDescriptor::Oblique(
FixedPoint::from_float(angle_a.degrees()),
FixedPoint::from_float(angle_b.degrees()),
),
}
}
let style = rule_data.style.as_ref().map(style_to_computed);
let unicode_range = rule_data
.unicode_range
.as_ref()
.map(|ranges| ranges.iter().map(|range| range.start..=range.end).collect());
CSSFontFaceDescriptors {
family_name: family_name.into(),
weight,
stretch,
style,
unicode_range,
}
}
}
impl SystemFontServiceProxy {
pub fn exit(&self) {
let (response_chan, response_port) = ipc::channel().unwrap();
@ -584,31 +479,3 @@ impl SystemFontServiceProxy {
.expect("Failed to communicate with system font service.")
}
}
#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
pub struct LowercaseFontFamilyName {
inner: String,
}
impl<T: AsRef<str>> From<T> for LowercaseFontFamilyName {
fn from(value: T) -> Self {
LowercaseFontFamilyName {
inner: value.as_ref().to_lowercase(),
}
}
}
impl Deref for LowercaseFontFamilyName {
type Target = str;
#[inline]
fn deref(&self) -> &str {
&self.inner
}
}
impl fmt::Display for LowercaseFontFamilyName {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.inner.fmt(f)
}
}