diff --git a/Cargo.lock b/Cargo.lock index 11084f178f1..a89c2e1d3c6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -955,6 +955,7 @@ dependencies = [ "style 0.0.1", "style_traits 0.0.1", "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", + "truetype 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-script 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "webrender_traits 0.11.0 (git+https://github.com/servo/webrender)", "xi-unicode 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3022,6 +3023,11 @@ name = "traitobject" version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "truetype" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "typeable" version = "0.1.2" @@ -3607,6 +3613,7 @@ dependencies = [ "checksum tinyfiledialogs 2.5.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1d401358cd71aca93d5f4fccd3db5b87d970ae70fe457911929d99f4a87f7531" "checksum toml 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)" = "0590d72182e50e879c4da3b11c6488dae18fccb1ae0c7a3eda18e16795844796" "checksum traitobject 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "07eaeb7689bb7fca7ce15628319635758eda769fed481ecfe6686ddef2600616" +"checksum truetype 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)" = "87167c7d6d14725f614d16761de2bb71c01ee374989936b49797d3db1a02bcdd" "checksum typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887" "checksum unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "13a5906ca2b98c799f4b1ab4557b76367ebd6ae5ef14930ec841c74aed5f3764" "checksum unicode-bidi 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b61814f3e7fd0e0f15370f767c7c943e08bc2e3214233ae8f88522b334ceb778" diff --git a/components/gfx/Cargo.toml b/components/gfx/Cargo.toml index b8fe39cda04..3f6ff4f7104 100644 --- a/components/gfx/Cargo.toml +++ b/components/gfx/Cargo.toml @@ -60,4 +60,5 @@ servo-fontconfig = "0.2.1" simd = {git = "https://github.com/huonw/simd"} [target.'cfg(target_os = "windows")'.dependencies] -dwrote = "0.1.1" +dwrote = "0.1.5" +truetype = "0.24" diff --git a/components/gfx/lib.rs b/components/gfx/lib.rs index e02793c3a32..ccde43b2b73 100644 --- a/components/gfx/lib.rs +++ b/components/gfx/lib.rs @@ -29,6 +29,7 @@ extern crate bitflags; // Windows-specific library dependencies #[cfg(target_os = "windows")] extern crate dwrote; +#[cfg(target_os = "windows")] extern crate truetype; extern crate euclid; extern crate fnv; diff --git a/components/gfx/platform/windows/font.rs b/components/gfx/platform/windows/font.rs index 7397d216599..d45ac25edd3 100644 --- a/components/gfx/platform/windows/font.rs +++ b/components/gfx/platform/windows/font.rs @@ -7,7 +7,8 @@ // renderer moves to a sandboxed process. use app_units::Au; -use dwrote::{Font, FontFace}; +use dwrote; +use dwrote::{Font, FontFace, FontFile}; use dwrote::{FontWeight, FontStretch, FontStyle}; use font::{FontHandleMethods, FontMetrics, FontTableMethods}; use font::{FontTableTag, FractionalPixel}; @@ -17,6 +18,7 @@ use platform::windows::font_list::font_from_atom; use std::sync::Arc; use style::computed_values::{font_stretch, font_weight}; use text::glyph::GlyphId; +use truetype; // 1em = 12pt = 16px, assuming 72 points per inch and 96 px per inch fn pt_to_px(pt: f64) -> f64 { pt / 72. * 96. } @@ -40,11 +42,205 @@ impl FontTableMethods for FontTable { } } +fn make_tag(tag_bytes: &[u8]) -> FontTableTag { + assert_eq!(tag_bytes.len(), 4); + unsafe { *(tag_bytes.as_ptr() as *const FontTableTag) } +} + +macro_rules! try_lossy(($result:expr) => (try!($result.map_err(|_| (()))))); + +// Given a set of records, figure out the string indices for the family and face +// names. We want name_id 1 and 2, and we need to use platform_id == 1 and +// language_id == 0 to avoid limitations in the truetype crate. We *could* just +// do our own parsing here, and use the offset/length data and pull the values out +// ourselves. +fn get_family_face_indices(records: &[truetype::naming_table::Record]) -> Option<(usize, usize)> { + let mut family_name_index = None; + let mut face_name_index = None; + + for i in 0..records.len() { + // the truetype crate can only decode mac platform format names + if records[i].platform_id != 1 { + continue; + } + + if records[i].language_id != 0 { + continue; + } + + if records[i].name_id == 1 { + family_name_index = Some(i); + } else if records[i].name_id == 2 { + face_name_index = Some(i); + } + } + + if family_name_index.is_some() && face_name_index.is_some() { + Some((family_name_index.unwrap(), face_name_index.unwrap())) + } else { + None + } +} + +// We need the font (DWriteFont) in order to be able to query things like +// the family name, face name, weight, etc. On Windows 10, the +// DWriteFontFace3 interface provides this on the FontFace, but that's only +// available on Win10+. +// +// Instead, we do the parsing work using the truetype crate for raw fonts. +// We're just extracting basic info, so this is sufficient for now. + +#[derive(Debug)] +struct FontInfo { + family_name: String, + face_name: String, + weight: font_weight::T, + stretch: font_stretch::T, + style: FontStyle, +} + +impl FontInfo { + fn new_from_face(face: &FontFace) -> Result { + use std::cmp::{min, max}; + use std::io::Cursor; + use truetype::{NamingTable, Value, WindowsMetrics}; + + let name_table_bytes = face.get_font_table(make_tag(b"name")); + let os2_table_bytes = face.get_font_table(make_tag(b"OS/2")); + if name_table_bytes.is_none() || os2_table_bytes.is_none() { + return Err(()); + } + + let mut name_table_cursor = Cursor::new(name_table_bytes.as_ref().unwrap()); + let names = try_lossy!(NamingTable::read(&mut name_table_cursor)); + let (family, face) = match names { + NamingTable::Format0(ref table) => { + if let Some((family_index, face_index)) = get_family_face_indices(&table.records) { + let strings = table.strings().unwrap(); + let family = strings[family_index].clone(); + let face = strings[face_index].clone(); + ((family, face)) + } else { + return Err(()); + } + }, + NamingTable::Format1(ref table) => { + if let Some((family_index, face_index)) = get_family_face_indices(&table.records) { + let strings = table.strings().unwrap(); + let family = strings[family_index].clone(); + let face = strings[face_index].clone(); + ((family, face)) + } else { + return Err(()); + } + } + }; + + let mut os2_table_cursor = Cursor::new(os2_table_bytes.as_ref().unwrap()); + let metrics = try_lossy!(WindowsMetrics::read(&mut os2_table_cursor)); + let (weight_val, width_val, italic_bool) = match metrics { + WindowsMetrics::Version0(ref m) => { + (m.weight_class, m.width_class, m.selection_flags.0 & 1 == 1) + }, + WindowsMetrics::Version1(ref m) => { + (m.weight_class, m.width_class, m.selection_flags.0 & 1 == 1) + }, + WindowsMetrics::Version2(ref m) | + WindowsMetrics::Version3(ref m) | + WindowsMetrics::Version4(ref m) => { + (m.weight_class, m.width_class, m.selection_flags.0 & 1 == 1) + }, + WindowsMetrics::Version5(ref m) => { + (m.weight_class, m.width_class, m.selection_flags.0 & 1 == 1) + }, + }; + + let weight = match min(9, max(1, weight_val / 100)) { + 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, + 9 => font_weight::T::Weight900, + _ => return Err(()), + }; + + let stretch = match min(9, max(1, width_val)) { + 1 => font_stretch::T::ultra_condensed, + 2 => font_stretch::T::extra_condensed, + 3 => font_stretch::T::condensed, + 4 => font_stretch::T::semi_condensed, + 5 => font_stretch::T::normal, + 6 => font_stretch::T::semi_expanded, + 7 => font_stretch::T::expanded, + 8 => font_stretch::T::extra_expanded, + 9 => font_stretch::T::ultra_expanded, + _ => return Err(()), + }; + + let style = if italic_bool { + FontStyle::Italic + } else { + FontStyle::Normal + }; + + Ok(FontInfo { + family_name: family, + face_name: face, + weight: weight, + stretch: stretch, + style: style, + }) + } + + fn new_from_font(font: &Font) -> Result { + let style = font.style(); + let weight = match font.weight() { + FontWeight::Thin => font_weight::T::Weight100, + FontWeight::ExtraLight => font_weight::T::Weight200, + FontWeight::Light => font_weight::T::Weight300, + // slightly grayer gray + FontWeight::SemiLight => font_weight::T::Weight300, + FontWeight::Regular => font_weight::T::Weight400, + FontWeight::Medium => font_weight::T::Weight500, + FontWeight::SemiBold => font_weight::T::Weight600, + FontWeight::Bold => font_weight::T::Weight700, + FontWeight::ExtraBold => font_weight::T::Weight800, + FontWeight::Black => font_weight::T::Weight900, + // slightly blacker black + FontWeight::ExtraBlack => font_weight::T::Weight900, + }; + let stretch = match font.stretch() { + FontStretch::Undefined => font_stretch::T::normal, + FontStretch::UltraCondensed => font_stretch::T::ultra_condensed, + FontStretch::ExtraCondensed => font_stretch::T::extra_condensed, + FontStretch::Condensed => font_stretch::T::condensed, + FontStretch::SemiCondensed => font_stretch::T::semi_condensed, + FontStretch::Normal => font_stretch::T::normal, + FontStretch::SemiExpanded => font_stretch::T::semi_expanded, + FontStretch::Expanded => font_stretch::T::expanded, + FontStretch::ExtraExpanded => font_stretch::T::extra_expanded, + FontStretch::UltraExpanded => font_stretch::T::ultra_expanded, + }; + + Ok(FontInfo { + family_name: font.family_name(), + face_name: font.face_name(), + style: style, + weight: weight, + stretch: stretch, + }) + } +} + #[derive(Debug)] pub struct FontHandle { font_data: Arc, - font: Font, face: FontFace, + info: FontInfo, em_size: f32, du_per_em: f32, du_to_px: f32, @@ -58,32 +254,41 @@ impl FontHandleMethods for FontHandle { fn new_from_template(_: &FontContextHandle, template: Arc, pt_size: Option) -> Result { - if let Some(_) = template.bytes { - // FIXME we should load from template.bytes - Err(()) + let (info, face) = if let Some(ref raw_font) = template.bytes { + let font_file = FontFile::new_from_data(&raw_font); + if font_file.is_none() { + // failed to load raw font + return Err(()); + } + + let face = font_file.unwrap().create_face(0, dwrote::DWRITE_FONT_SIMULATIONS_NONE); + let info = try!(FontInfo::new_from_face(&face)); + (info, face) } else { let font = font_from_atom(&template.identifier); let face = font.create_font_face(); + let info = try!(FontInfo::new_from_font(&font)); + (info, face) + }; - let pt_size = pt_size.unwrap_or(au_from_pt(12.)); - let du_per_em = face.metrics().designUnitsPerEm as f32; + let pt_size = pt_size.unwrap_or(au_from_pt(12.)); + let du_per_em = face.metrics().designUnitsPerEm as f32; - let em_size = pt_size.to_f32_px() / 16.; - let design_units_per_pixel = du_per_em / 16.; + let em_size = pt_size.to_f32_px() / 16.; + let design_units_per_pixel = du_per_em / 16.; - let design_units_to_pixels = 1. / design_units_per_pixel; - let scaled_design_units_to_pixels = em_size / design_units_per_pixel; + let design_units_to_pixels = 1. / design_units_per_pixel; + let scaled_design_units_to_pixels = em_size / design_units_per_pixel; - Ok(FontHandle { - font_data: template.clone(), - font: font, - face: face, - em_size: em_size, - du_per_em: du_per_em, - du_to_px: design_units_to_pixels, - scaled_du_to_px: scaled_design_units_to_pixels, - }) - } + Ok(FontHandle { + font_data: template.clone(), + face: face, + info: info, + em_size: em_size, + du_per_em: du_per_em, + du_to_px: design_units_to_pixels, + scaled_du_to_px: scaled_design_units_to_pixels, + }) } fn template(&self) -> Arc { @@ -91,51 +296,26 @@ impl FontHandleMethods for FontHandle { } fn family_name(&self) -> String { - self.font.family_name() + self.info.family_name.clone() } fn face_name(&self) -> String { - self.font.face_name() + self.info.face_name.clone() } fn is_italic(&self) -> bool { - match self.font.style() { + match self.info.style { FontStyle::Normal => false, FontStyle::Oblique | FontStyle::Italic => true, } } fn boldness(&self) -> font_weight::T { - match self.font.weight() { - FontWeight::Thin => font_weight::T::Weight100, - FontWeight::ExtraLight => font_weight::T::Weight200, - FontWeight::Light => font_weight::T::Weight300, - // slightly lighter gray - FontWeight::SemiLight => font_weight::T::Weight300, - FontWeight::Regular => font_weight::T::Weight400, - FontWeight::Medium => font_weight::T::Weight500, - FontWeight::SemiBold => font_weight::T::Weight600, - FontWeight::Bold => font_weight::T::Weight700, - FontWeight::ExtraBold => font_weight::T::Weight800, - FontWeight::Black => font_weight::T::Weight900, - // slightly blacker black - FontWeight::ExtraBlack => font_weight::T::Weight900, - } + self.info.weight } fn stretchiness(&self) -> font_stretch::T { - match self.font.stretch() { - FontStretch::Undefined => font_stretch::T::normal, - FontStretch::UltraCondensed => font_stretch::T::ultra_condensed, - FontStretch::ExtraCondensed => font_stretch::T::extra_condensed, - FontStretch::Condensed => font_stretch::T::condensed, - FontStretch::SemiCondensed => font_stretch::T::semi_condensed, - FontStretch::Normal => font_stretch::T::normal, - FontStretch::SemiExpanded => font_stretch::T::semi_expanded, - FontStretch::Expanded => font_stretch::T::expanded, - FontStretch::ExtraExpanded => font_stretch::T::extra_expanded, - FontStretch::UltraExpanded => font_stretch::T::ultra_expanded, - } + self.info.stretch } fn glyph_index(&self, codepoint: char) -> Option { diff --git a/components/gfx/platform/windows/font_template.rs b/components/gfx/platform/windows/font_template.rs index 5a4709610c4..30d6a640cf1 100644 --- a/components/gfx/platform/windows/font_template.rs +++ b/components/gfx/platform/windows/font_template.rs @@ -40,11 +40,10 @@ impl FontTemplateData { } pub fn native_font(&self) -> Option { - if self.bytes.is_some() { - panic!("Can't create fonts yet"); + if self.bytes.is_none() { + Some(descriptor_from_atom(&self.identifier)) + } else { + None } - - let descriptor = descriptor_from_atom(&self.identifier); - Some(descriptor) } }