mirror of
https://github.com/servo/servo.git
synced 2025-06-13 02:44:29 +00:00
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
218 lines
7.6 KiB
Rust
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))
|
|
})
|
|
}
|
|
}
|