servo: Merge #11103 - gfx: Map CSS `normal` font weight to Regular font weight on the Mac (from pcwalton:mac-font-matching); r=metajack

This series of commits fixes #9487, and improves the look of nytimes.com among others.

r? @metajack

Source-Repo: https://github.com/servo/servo
Source-Revision: 1fd9c5583455b873fca1c95b2784f969870073bd
This commit is contained in:
Patrick Walton 2016-05-10 14:11:35 -07:00
Родитель abe21b5d60
Коммит b6bde70e16
3 изменённых файлов: 107 добавлений и 48 удалений

Просмотреть файл

@ -18,6 +18,7 @@ use std::borrow::ToOwned;
use std::collections::HashMap;
use std::mem;
use std::sync::{Arc, Mutex};
use std::u32;
use string_cache::Atom;
use style::font_face::Source;
use style::properties::longhands::font_family::computed_value::FontFamily;
@ -51,9 +52,6 @@ impl FontTemplates {
// TODO(Issue #189): optimize lookup for
// regular/bold/italic/bolditalic with fixed offsets and a
// static decision table for fallback between these values.
// TODO(Issue #190): if not in the fast path above, do
// expensive matching of weights, etc.
for template in &mut self.templates {
let maybe_template = template.data_for_descriptor(fctx, desc);
if maybe_template.is_some() {
@ -61,6 +59,22 @@ impl FontTemplates {
}
}
// We didn't find an exact match. Do more expensive fuzzy matching.
// TODO(#190): Do a better job.
let (mut best_template_data, mut best_distance) = (None, u32::MAX);
for template in &mut self.templates {
if let Some((template_data, distance)) =
template.data_for_approximate_descriptor(fctx, desc) {
if distance < best_distance {
best_template_data = Some(template_data);
best_distance = distance
}
}
}
if best_template_data.is_some() {
return best_template_data
}
// 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.
@ -81,8 +95,7 @@ impl FontTemplates {
}
}
let template = FontTemplate::new(identifier,
maybe_data);
let template = FontTemplate::new(identifier, maybe_data);
self.templates.push(template);
}
}

Просмотреть файл

@ -6,7 +6,9 @@ use font::FontHandleMethods;
use platform::font::FontHandle;
use platform::font_context::FontContextHandle;
use platform::font_template::FontTemplateData;
use std::fmt::{Debug, Error, Formatter};
use std::sync::{Arc, Weak};
use std::u32;
use string_cache::Atom;
use style::computed_values::{font_stretch, font_weight};
@ -31,13 +33,25 @@ impl FontTemplateDescriptor {
italic: italic,
}
}
/// Returns a score indicating how far apart visually the two font descriptors are. This is
/// used for fuzzy font selection.
///
/// 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, other: &FontTemplateDescriptor) -> u32 {
if self.stretch != other.stretch || self.italic != other.italic {
// A value higher than all weights.
return 1000
}
((self.weight as i16) - (other.weight as i16)).abs() as u32
}
}
impl PartialEq for FontTemplateDescriptor {
fn eq(&self, other: &FontTemplateDescriptor) -> bool {
self.weight.is_bold() == other.weight.is_bold() &&
self.stretch == other.stretch &&
self.italic == other.italic
self.weight == other.weight && self.stretch == other.stretch && self.italic == other.italic
}
}
@ -53,6 +67,12 @@ pub struct FontTemplate {
is_valid: bool,
}
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.
@ -88,52 +108,74 @@ impl FontTemplate {
/// Get the data for creating a font if it matches a given descriptor.
pub fn data_for_descriptor(&mut self,
fctx: &FontContextHandle,
requested_desc: &FontTemplateDescriptor)
-> Option<Arc<FontTemplateData>> {
fctx: &FontContextHandle,
requested_desc: &FontTemplateDescriptor)
-> Option<Arc<FontTemplateData>> {
// The font template data can be unloaded when nothing is referencing
// it (via the Weak reference to the Arc above). However, if we have
// already loaded a font, store the style information about it separately,
// so that we can do font matching against it again in the future
// without having to reload the font (unless it is an actual match).
match self.descriptor {
Some(actual_desc) => {
if *requested_desc == actual_desc {
Some(actual_desc) if *requested_desc == actual_desc => Some(self.data()),
Some(_) => None,
None => {
if self.instantiate(fctx).is_err() {
return None
}
if self.descriptor
.as_ref()
.expect("Instantiation succeeded but no descriptor?") == requested_desc {
Some(self.data())
} else {
None
}
},
None if self.is_valid => {
let data = self.data();
let handle: Result<FontHandle, ()> =
FontHandleMethods::new_from_template(fctx, data.clone(), None);
match handle {
Ok(handle) => {
let actual_desc = FontTemplateDescriptor::new(handle.boldness(),
handle.stretchiness(),
handle.is_italic());
let desc_match = actual_desc == *requested_desc;
}
}
}
self.descriptor = Some(actual_desc);
self.is_valid = true;
if desc_match {
Some(data)
} else {
None
}
}
Err(()) => {
self.is_valid = false;
debug!("Unable to create a font from template {}", self.identifier);
None
}
/// Returns the font data along with the distance between this font's descriptor and the given
/// descriptor, if the font can be loaded.
pub fn data_for_approximate_descriptor(&mut self,
font_context: &FontContextHandle,
requested_descriptor: &FontTemplateDescriptor)
-> Option<(Arc<FontTemplateData>, u32)> {
match self.descriptor {
Some(actual_descriptor) => {
Some((self.data(), actual_descriptor.distance_from(requested_descriptor)))
}
None => {
if self.instantiate(font_context).is_ok() {
let distance = self.descriptor
.as_ref()
.expect("Instantiation successful but no descriptor?")
.distance_from(requested_descriptor);
Some((self.data(), distance))
} else {
None
}
}
None => None,
}
}
fn instantiate(&mut self, font_context: &FontContextHandle) -> Result<(), ()> {
if !self.is_valid {
return Err(())
}
let data = self.data();
let handle: Result<FontHandle, ()> = FontHandleMethods::new_from_template(font_context,
data,
None);
self.is_valid = handle.is_ok();
let handle = try!(handle);
self.descriptor = Some(FontTemplateDescriptor::new(handle.boldness(),
handle.stretchiness(),
handle.is_italic()));
Ok(())
}
/// Get the data for creating a font.
pub fn get(&mut self) -> Option<Arc<FontTemplateData>> {
if self.is_valid {

Просмотреть файл

@ -103,16 +103,20 @@ impl FontHandleMethods for FontHandle {
fn boldness(&self) -> font_weight::T {
let normalized = self.ctfont.all_traits().normalized_weight(); // [-1.0, 1.0]
let normalized = (normalized + 1.0) / 2.0 * 9.0; // [0.0, 9.0]
match normalized {
v if v < 1.0 => font_weight::T::Weight100,
v if v < 2.0 => font_weight::T::Weight200,
v if v < 3.0 => font_weight::T::Weight300,
v if v < 4.0 => font_weight::T::Weight400,
v if v < 5.0 => font_weight::T::Weight500,
v if v < 6.0 => font_weight::T::Weight600,
v if v < 7.0 => font_weight::T::Weight700,
v if v < 8.0 => font_weight::T::Weight800,
let normalized = if normalized <= 0.0 {
4.0 + normalized * 3.0 // [1.0, 4.0]
} else {
4.0 + normalized * 5.0 // [4.0, 9.0]
}; // [1.0, 9.0], centered on 4.0
match normalized.round() as u32 {
1 => font_weight::T::Weight100,
2 => font_weight::T::Weight200,
3 => font_weight::T::Weight300,
4 => font_weight::T::Weight400,
5 => font_weight::T::Weight500,
6 => font_weight::T::Weight600,
7 => font_weight::T::Weight700,
8 => font_weight::T::Weight800,
_ => font_weight::T::Weight900,
}
}