Skip to content

Commit

Permalink
DirectWrite font backend for Windows
Browse files Browse the repository at this point in the history
  • Loading branch information
vvuk committed Nov 10, 2016
1 parent d16f312 commit db357b0
Show file tree
Hide file tree
Showing 9 changed files with 569 additions and 176 deletions.
7 changes: 2 additions & 5 deletions components/gfx/Cargo.toml
Expand Up @@ -53,15 +53,12 @@ core-foundation = "0.2"
core-graphics = "0.4"
core-text = "2.0"

[target.'cfg(any(target_os = "linux", target_os = "android", target_os = "windows"))'.dependencies]
freetype = {git = "https://github.com/servo/rust-freetype"}

[target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies]
freetype = {git = "https://github.com/servo/rust-freetype"}
servo-fontconfig = "0.2.1"

[target.'cfg(any(target_arch = "x86_64", target_arch = "aarch64"))'.dependencies]
simd = {git = "https://github.com/huonw/simd"}

[target.'cfg(target_os = "windows")'.dependencies]
winapi = "0.2"
gdi32-sys = "0.2"
dwrote = {git = "https://github.com/vvuk/dwrote-rs"}
8 changes: 4 additions & 4 deletions components/gfx/lib.rs
Expand Up @@ -29,19 +29,19 @@ extern crate bitflags;
#[cfg(target_os = "macos")] extern crate core_text;

// Windows-specific library dependencies
#[cfg(target_os = "windows")] extern crate gdi32;
#[cfg(target_os = "windows")] extern crate winapi;
#[cfg(target_os = "windows")] extern crate dwrote;

extern crate euclid;
extern crate fnv;

// Platforms that use Freetype/Fontconfig library dependencies
#[cfg(any(target_os = "linux", target_os = "android"))]
extern crate fontconfig;
extern crate fontsan;
#[cfg(any(target_os = "linux", target_os = "android", target_os = "windows"))]
#[cfg(any(target_os = "linux", target_os = "android"))]
extern crate freetype;

extern crate fontsan;

extern crate gfx_traits;

// Eventually we would like the shaper to be pluggable, as many operating systems have their own
Expand Down
8 changes: 5 additions & 3 deletions components/gfx/platform/mod.rs
Expand Up @@ -2,19 +2,19 @@
* 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/. */

#[cfg(any(target_os = "linux", target_os = "android", target_os = "windows"))]
#[cfg(any(target_os = "linux", target_os = "android"))]
pub use platform::freetype::{font, font_context};

#[cfg(any(target_os = "linux", target_os = "android"))]
pub use platform::freetype::{font_list, font_template};

#[cfg(target_os = "windows")]
pub use platform::windows::{font_list, font_template};
pub use platform::windows::{font, font_context, font_list, font_template};

#[cfg(target_os = "macos")]
pub use platform::macos::{font, font_context, font_list, font_template};

#[cfg(any(target_os = "linux", target_os = "android", target_os = "windows"))]
#[cfg(any(target_os = "linux", target_os = "android"))]
mod freetype {
use libc::c_char;
use std::ffi::CStr;
Expand Down Expand Up @@ -46,6 +46,8 @@ mod macos {

#[cfg(target_os = "windows")]
mod windows {
pub mod font;
pub mod font_context;
pub mod font_list;
pub mod font_template;
}
204 changes: 204 additions & 0 deletions components/gfx/platform/windows/font.rs
@@ -0,0 +1,204 @@
/* 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/. */

// NOTE: https://www.chromium.org/directwrite-font-proxy has useful
// information for an approach that we'll likely need to take when the
// renderer moves to a sandboxed process.

use app_units::Au;
use font::{FontHandleMethods, FontMetrics, FontTableMethods};
use font::{FontTableTag, FractionalPixel};
use platform::font_template::{FontTemplateData};
use platform::windows::font_list::{font_from_atom};
use platform::windows::font_context::{FontContextHandle};
use std::sync::Arc;
use style::computed_values::{font_stretch, font_weight};
use text::glyph::GlyphId;
use dwrote::{Font, FontFace};
use dwrote::{FontWeight, FontStretch, FontStyle};

// 1em = 12pt = 16px, assuming 72 points per inch and 96 px per inch
fn pt_to_px(pt: f64) -> f64 { pt / 72. * 96. }
fn em_to_px(em: f64) -> f64 { em * 16. }
fn au_from_em(em: f64) -> Au { Au::from_f64_px(em_to_px(em)) }
fn au_from_pt(pt: f64) -> Au { Au::from_f64_px(pt_to_px(pt)) }

pub struct FontTable {
data: Vec<u8>,
}

impl FontTable {
pub fn wrap(data: &[u8]) -> FontTable {
FontTable { data: data.to_vec() }
}
}

impl FontTableMethods for FontTable {
fn buffer(&self) -> &[u8] {
&self.data
}
}

#[derive(Debug)]
pub struct FontHandle {
font_data: Arc<FontTemplateData>,
font: Font,
face: FontFace,
em_size: f32,
du_per_em: f32,
du_to_px: f32,
scaled_du_to_px: f32,
}

impl FontHandle {
}

impl FontHandleMethods for FontHandle {
fn new_from_template(_: &FontContextHandle, template: Arc<FontTemplateData>, pt_size: Option<Au>)
-> Result<Self, ()>
{
if let Some(_) = template.bytes {
// FIXME we should load from template.bytes
Err(())
} else {
let font = font_from_atom(&template.identifier);
let face = font.create_font_face();

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 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,
})
}
}

fn template(&self) -> Arc<FontTemplateData> {
self.font_data.clone()
}

fn family_name(&self) -> String {
self.font.family_name()
}

fn face_name(&self) -> String {
self.font.face_name()
}

fn is_italic(&self) -> bool {
match self.font.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,
}
}

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,
}
}

fn glyph_index(&self, codepoint: char) -> Option<GlyphId> {
let glyph = self.face.get_glyph_indices(&[codepoint as u32])[0];
if glyph == 0 {
return None;
}
Some(glyph as GlyphId)
}

fn glyph_h_advance(&self, glyph: GlyphId) -> Option<FractionalPixel> {
if glyph == 0 {
return None;
}

let gm = self.face.get_design_glyph_metrics(&[glyph as u16], false)[0];
let f = (gm.advanceWidth as f32 * self.scaled_du_to_px) as FractionalPixel;

Some(f)
}

/// Can this font do basic horizontal LTR shaping without Harfbuzz?
fn can_do_fast_shaping(&self) -> bool {
// TODO copy CachedKernTable from the MacOS X implementation to
// somehwere global and use it here. We could also implement the
// IDirectWriteFontFace1 interface and use the glyph kerning pair
// methods there.
false
}

fn glyph_h_kerning(&self, _: GlyphId, _: GlyphId) -> FractionalPixel {
0.0
}

fn metrics(&self) -> FontMetrics {
let dm = self.face.metrics();

let au_from_du = |du| -> Au { Au::from_f32_px(du as f32 * self.du_to_px) };
let au_from_du_s = |du| -> Au { Au:: from_f32_px(du as f32 * self.scaled_du_to_px) };

// anything that we calculate and don't just pull out of self.face.metrics
// is pulled out here for clarity
let leading = dm.ascent - dm.capHeight;

let metrics = FontMetrics {
underline_size: au_from_du(dm.underlineThickness as i32),
underline_offset: au_from_du_s(dm.underlinePosition as i32),
strikeout_size: au_from_du(dm.strikethroughThickness as i32),
strikeout_offset: au_from_du_s(dm.strikethroughPosition as i32),
leading: au_from_du_s(leading as i32),
x_height: au_from_du_s(dm.xHeight as i32),
em_size: au_from_em(self.em_size as f64),
ascent: au_from_du_s(dm.ascent as i32),
descent: au_from_du_s(dm.descent as i32),
max_advance: au_from_pt(0.0), // FIXME
average_advance: au_from_pt(0.0), // FIXME
line_gap: au_from_du(dm.lineGap as i32),
};
debug!("Font metrics (@{} pt): {:?}", self.em_size * 12., metrics);
metrics
}

fn table_for_tag(&self, tag: FontTableTag) -> Option<FontTable> {
self.face.get_font_table(tag).map(|bytes| FontTable { data: bytes })
}
}
21 changes: 21 additions & 0 deletions components/gfx/platform/windows/font_context.rs
@@ -0,0 +1,21 @@
/* 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/. */

use heapsize::HeapSizeOf;

#[derive(Clone, Debug)]
pub struct FontContextHandle;

impl FontContextHandle {
// *shrug*
pub fn new() -> FontContextHandle {
FontContextHandle {}
}
}

impl HeapSizeOf for FontContextHandle {
fn heap_size_of_children(&self) -> usize {
0
}
}

0 comments on commit db357b0

Please sign in to comment.