Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gfx: Map CSS `normal` font weight to Regular font weight on the Mac. #11103

Merged
merged 4 commits into from May 10, 2016
Merged
Changes from 1 commit
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

Next

gfx: Perform fuzzy matching on font weights if an exact match wasn't

found.

Partially addresses #190.
Partially addresses #9487.
  • Loading branch information
pcwalton committed May 10, 2016
commit 479c6c9c424e8e36ecd8c1458b2818f14ea229df
@@ -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,16 +52,29 @@ 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() {
return maybe_template;
}
}

// 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);
}
}
@@ -7,6 +7,7 @@ use platform::font::FontHandle;
use platform::font_context::FontContextHandle;
use platform::font_template::FontTemplateData;
use std::sync::{Arc, Weak};
use std::u32;
use string_cache::Atom;
use style::computed_values::{font_stretch, font_weight};

@@ -31,13 +32,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
}
}

@@ -88,52 +101,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 {
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.