servo/components/gfx/platform/macos/font.rs
Patrick Walton 3addd775a5 gfx: Change the mapping from Mac weights to CSS weights so that 0.0 maps
to 400, not 500.

CSS `normal` font-weight is specified as 400, while Mac "Regular" font
weight is reported as 0.0. On the Mac, we need to center the two ranges
on the same value to avoid choosing "Light" fonts where "Regular" would
have been more appropriate.

Closes #9487.

fix for mac
2016-05-10 10:34:29 -07:00

218 lines
7.6 KiB
Rust

/* 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 http://mozilla.org/MPL/2.0/. */
/// Implementation of Quartz (CoreGraphics) fonts.
extern crate core_foundation;
extern crate core_graphics;
extern crate core_text;
use app_units::Au;
use core_foundation::base::CFIndex;
use core_foundation::data::CFData;
use core_foundation::string::UniChar;
use core_graphics::font::CGGlyph;
use core_graphics::geometry::CGRect;
use core_text::font::CTFont;
use core_text::font_descriptor::{SymbolicTraitAccessors, TraitAccessors};
use core_text::font_descriptor::{kCTFontDefaultOrientation};
use font::FontTableTag;
use font::FractionalPixel;
use font::{FontHandleMethods, FontMetrics, FontTableMethods};
use platform::font_template::FontTemplateData;
use platform::macos::font_context::FontContextHandle;
use std::ptr;
use std::sync::Arc;
use style::computed_values::{font_stretch, font_weight};
use text::glyph::GlyphId;
pub struct FontTable {
data: CFData,
}
// assumes 72 points per inch, and 96 px per inch
fn px_to_pt(px: f64) -> f64 {
px / 96. * 72.
}
// assumes 72 points per inch, and 96 px per inch
fn pt_to_px(pt: f64) -> f64 {
pt / 72. * 96.
}
fn au_from_pt(pt: f64) -> Au {
Au::from_f64_px(pt_to_px(pt))
}
impl FontTable {
pub fn wrap(data: CFData) -> FontTable {
FontTable { data: data }
}
}
impl FontTableMethods for FontTable {
fn with_buffer<F>(&self, blk: F) where F: FnOnce(*const u8, usize) {
blk(self.data.bytes().as_ptr(), self.data.len() as usize);
}
}
#[derive(Debug)]
pub struct FontHandle {
pub font_data: Arc<FontTemplateData>,
pub ctfont: CTFont,
}
impl FontHandleMethods for FontHandle {
fn new_from_template(_fctx: &FontContextHandle,
template: Arc<FontTemplateData>,
pt_size: Option<Au>)
-> Result<FontHandle, ()> {
let size = match pt_size {
Some(s) => s.to_f64_px(),
None => 0.0
};
match template.ctfont(size) {
Some(ref ctfont) => {
Ok(FontHandle {
font_data: template.clone(),
ctfont: ctfont.clone_with_font_size(size),
})
}
None => {
Err(())
}
}
}
fn template(&self) -> Arc<FontTemplateData> {
self.font_data.clone()
}
fn family_name(&self) -> String {
self.ctfont.family_name()
}
fn face_name(&self) -> String {
self.ctfont.face_name()
}
fn is_italic(&self) -> bool {
self.ctfont.symbolic_traits().is_italic()
}
fn boldness(&self) -> font_weight::T {
let normalized = self.ctfont.all_traits().normalized_weight(); // [-1.0, 1.0]
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,
}
}
fn stretchiness(&self) -> font_stretch::T {
let normalized = self.ctfont.all_traits().normalized_width(); // [-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_stretch::T::ultra_condensed,
v if v < 2.0 => font_stretch::T::extra_condensed,
v if v < 3.0 => font_stretch::T::condensed,
v if v < 4.0 => font_stretch::T::semi_condensed,
v if v < 5.0 => font_stretch::T::normal,
v if v < 6.0 => font_stretch::T::semi_expanded,
v if v < 7.0 => font_stretch::T::expanded,
v if v < 8.0 => font_stretch::T::extra_expanded,
_ => font_stretch::T::ultra_expanded,
}
}
fn glyph_index(&self, codepoint: char) -> Option<GlyphId> {
let characters: [UniChar; 1] = [codepoint as UniChar];
let mut glyphs: [CGGlyph; 1] = [0 as CGGlyph];
let count: CFIndex = 1;
let result = self.ctfont.get_glyphs_for_characters(&characters[0],
&mut glyphs[0],
count);
if !result {
// No glyph for this character
return None;
}
assert!(glyphs[0] != 0); // FIXME: error handling
return Some(glyphs[0] as GlyphId);
}
fn glyph_h_kerning(&self, _first_glyph: GlyphId, _second_glyph: GlyphId)
-> FractionalPixel {
// TODO: Implement on mac
0.0
}
fn glyph_h_advance(&self, glyph: GlyphId) -> Option<FractionalPixel> {
let glyphs = [glyph as CGGlyph];
let advance = self.ctfont.get_advances_for_glyphs(kCTFontDefaultOrientation,
&glyphs[0],
ptr::null_mut(),
1);
Some(advance as FractionalPixel)
}
fn metrics(&self) -> FontMetrics {
let bounding_rect: CGRect = self.ctfont.bounding_box();
let ascent = self.ctfont.ascent() as f64;
let descent = self.ctfont.descent() as f64;
let em_size = Au::from_f64_px(self.ctfont.pt_size() as f64);
let leading = self.ctfont.leading() as f64;
let scale = px_to_pt(self.ctfont.pt_size() as f64) / (ascent + descent);
let line_gap = (ascent + descent + leading + 0.5).floor();
let max_advance_width = au_from_pt(bounding_rect.size.width as f64);
let average_advance = self.glyph_index('0')
.and_then(|idx| self.glyph_h_advance(idx))
.map(Au::from_f64_px)
.unwrap_or(max_advance_width);
let metrics = FontMetrics {
underline_size: au_from_pt(self.ctfont.underline_thickness() as f64),
// TODO(Issue #201): underline metrics are not reliable. Have to pull out of font table
// directly.
//
// see also: https://bugs.webkit.org/show_bug.cgi?id=16768
// see also: https://bugreports.qt-project.org/browse/QTBUG-13364
underline_offset: au_from_pt(self.ctfont.underline_position() as f64),
strikeout_size: Au(0), // FIXME(Issue #942)
strikeout_offset: Au(0), // FIXME(Issue #942)
leading: au_from_pt(leading),
x_height: au_from_pt(self.ctfont.x_height() as f64),
em_size: em_size,
ascent: au_from_pt(ascent * scale),
descent: au_from_pt(descent * scale),
max_advance: max_advance_width,
average_advance: average_advance,
line_gap: Au::from_f64_px(line_gap),
};
debug!("Font metrics (@{} pt): {:?}", self.ctfont.pt_size() as f64, metrics);
metrics
}
fn table_for_tag(&self, tag: FontTableTag) -> Option<Box<FontTable>> {
let result: Option<CFData> = self.ctfont.get_font_table(tag);
result.and_then(|data| {
Some(box FontTable::wrap(data))
})
}
}