├── .gitignore ├── README.md ├── Cargo.toml └── src ├── rendering_params.rs ├── gdi_interop.rs ├── helpers.rs ├── font_family.rs ├── types.rs ├── font.rs ├── comptr.rs ├── glyph_run_analysis.rs ├── com_helpers.rs ├── font_file.rs ├── font_collection.rs ├── test.rs ├── bitmap_render_target.rs ├── lib.rs ├── font_file_loader_impl.rs └── font_face.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | *~ 4 | #~# 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dwrote 2 | A thin wrapper around Windows DirectWrite for Rust 3 | 4 | Dwrote provides access to pieces of DirectWrite needed by WebRender 5 | and Servo. It can be easily extended to other parts of DirectWrite, 6 | but full mapping of the DirectWrite API is not a goal (pull requests 7 | accepted, of course). 8 | 9 | There are a few additional helper functions on individual types that 10 | don't exist in DirectWrite, and a few have had their signatures changed, 11 | but for the most part this library attempts to replicate the DirectWrite 12 | API. 13 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dwrote" 3 | description = "Lightweight binding to DirectWrite." 4 | repository = "https://github.com/vvuk/dwrote-rs" 5 | license = "MPL-2.0" 6 | version = "0.2.0" 7 | authors = ["Vladimir Vukicevic "] 8 | build = "build.rs" 9 | 10 | [lib] 11 | name = "dwrote" 12 | 13 | [features] 14 | default = ["codegen"] 15 | nightly = ["serde/unstable"] 16 | codegen = ["serde_codegen", "serde_codegen/with-syntex"] 17 | 18 | [dependencies] 19 | libc = "0.2" 20 | lazy_static = "0.2" 21 | winapi = "0.2" 22 | kernel32-sys = "0.2" 23 | gdi32-sys = "0.2" 24 | serde = "0.9" 25 | serde_derive = {version = "0.9", optional = true} 26 | 27 | [build-dependencies.serde_codegen] 28 | version = "0.9" 29 | default_features = false 30 | optional = true 31 | -------------------------------------------------------------------------------- /src/rendering_params.rs: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | use std::cell::UnsafeCell; 6 | 7 | use comptr::ComPtr; 8 | use winapi; 9 | use super::DWriteFactory; 10 | 11 | #[derive(Debug)] 12 | pub struct RenderingParams { 13 | native: UnsafeCell>, 14 | } 15 | 16 | impl RenderingParams { 17 | pub fn create_for_primary_monitor() -> RenderingParams { 18 | unsafe { 19 | let mut native: ComPtr = ComPtr::new(); 20 | let hr = (*DWriteFactory()).CreateRenderingParams(native.getter_addrefs()); 21 | assert!(hr == 0); 22 | RenderingParams::take(native) 23 | } 24 | } 25 | 26 | pub fn take(native: ComPtr) -> RenderingParams { 27 | RenderingParams { 28 | native: UnsafeCell::new(native), 29 | } 30 | } 31 | 32 | pub unsafe fn as_ptr(&self) -> *mut winapi::IDWriteRenderingParams { 33 | (*self.native.get()).as_ptr() 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/gdi_interop.rs: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | use std::ptr; 6 | use std::cell::UnsafeCell; 7 | 8 | use comptr::ComPtr; 9 | use winapi; 10 | use super::{DWriteFactory, BitmapRenderTarget}; 11 | 12 | #[derive(Debug)] 13 | pub struct GdiInterop { 14 | native: UnsafeCell>, 15 | } 16 | 17 | impl GdiInterop { 18 | pub fn create() -> GdiInterop { 19 | unsafe { 20 | let mut native: ComPtr = ComPtr::new(); 21 | let hr = (*DWriteFactory()).GetGdiInterop(native.getter_addrefs()); 22 | assert!(hr == 0); 23 | GdiInterop::take(native) 24 | } 25 | } 26 | 27 | pub fn take(native: ComPtr) -> GdiInterop { 28 | GdiInterop { 29 | native: UnsafeCell::new(native), 30 | } 31 | } 32 | 33 | pub fn create_bitmap_render_target(&self, width: u32, height: u32) -> BitmapRenderTarget { 34 | unsafe { 35 | let mut native: ComPtr = ComPtr::new(); 36 | let hr = (*self.native.get()).CreateBitmapRenderTarget(ptr::null_mut(), 37 | width, height, 38 | native.getter_addrefs()); 39 | assert!(hr == 0); 40 | BitmapRenderTarget::take(native) 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/helpers.rs: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | use winapi::IDWriteLocalizedStrings; 6 | use winapi::wchar_t; 7 | use comptr::ComPtr; 8 | use winapi::winerror::S_OK; 9 | use winapi::minwindef::{BOOL, FALSE}; 10 | use kernel32; 11 | use std::ffi::{OsStr}; 12 | use std::os::windows::ffi::{OsStrExt}; 13 | 14 | lazy_static! { 15 | static ref SYSTEM_LOCALE: Vec = { 16 | unsafe { 17 | let mut locale: Vec = vec![0; 85]; 18 | kernel32::GetUserDefaultLocaleName(locale.as_mut_ptr(), locale.len() as i32 - 1); 19 | locale 20 | } 21 | }; 22 | static ref EN_US_LOCALE: Vec = { 23 | OsStr::new("en-us").encode_wide().collect() 24 | }; 25 | } 26 | 27 | pub fn get_locale_string(strings: &mut ComPtr) -> String { 28 | unsafe { 29 | let mut index: u32 = 0; 30 | let mut exists: BOOL = FALSE; 31 | let hr = strings.FindLocaleName((*SYSTEM_LOCALE).as_ptr(), &mut index, &mut exists); 32 | if hr != S_OK || exists == FALSE { 33 | let hr = strings.FindLocaleName((*EN_US_LOCALE).as_ptr(), &mut index, &mut exists); 34 | if hr != S_OK || exists == FALSE { 35 | // Ultimately fall back to first locale on list 36 | index = 0; 37 | } 38 | } 39 | 40 | let mut length: u32 = 0; 41 | let hr = strings.GetStringLength(index, &mut length); 42 | assert!(hr == 0); 43 | 44 | let mut name: Vec = Vec::with_capacity(length as usize + 1); 45 | let hr = strings.GetString(index, name.as_mut_ptr(), length + 1); 46 | assert!(hr == 0); 47 | name.set_len(length as usize); 48 | 49 | String::from_utf16(&name).ok().unwrap() 50 | } 51 | } 52 | 53 | // ToWide from https://github.com/retep998/wio-rs/blob/master/src/wide.rs 54 | 55 | pub trait ToWide { 56 | fn to_wide(&self) -> Vec; 57 | fn to_wide_null(&self) -> Vec; 58 | } 59 | 60 | impl ToWide for T where T: AsRef { 61 | fn to_wide(&self) -> Vec { 62 | self.as_ref().encode_wide().collect() 63 | } 64 | fn to_wide_null(&self) -> Vec { 65 | self.as_ref().encode_wide().chain(Some(0)).collect() 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/font_family.rs: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | use std::cell::UnsafeCell; 6 | 7 | use comptr::ComPtr; 8 | use winapi; 9 | 10 | use super::*; 11 | use helpers::*; 12 | 13 | #[derive(Debug)] 14 | pub struct FontFamily { 15 | native: UnsafeCell>, 16 | } 17 | 18 | impl FontFamily { 19 | pub fn take(native: ComPtr) -> FontFamily { 20 | FontFamily { 21 | native: UnsafeCell::new(native) 22 | } 23 | } 24 | 25 | pub unsafe fn as_ptr(&self) -> *mut winapi::IDWriteFontFamily { 26 | (*self.native.get()).as_ptr() 27 | } 28 | 29 | pub fn name(&self) -> String { 30 | unsafe { 31 | let mut family_names: ComPtr = ComPtr::new(); 32 | let hr = (*self.native.get()).GetFamilyNames(family_names.getter_addrefs()); 33 | assert!(hr == 0); 34 | 35 | get_locale_string(&mut family_names) 36 | } 37 | } 38 | 39 | pub fn get_first_matching_font(&self, 40 | weight: FontWeight, 41 | stretch: FontStretch, 42 | style: FontStyle) 43 | -> Font 44 | { 45 | unsafe { 46 | let mut font: ComPtr = ComPtr::new(); 47 | let hr = (*self.native.get()).GetFirstMatchingFont(weight.t(), stretch.t(), style.t(), font.getter_addrefs()); 48 | assert!(hr == 0); 49 | Font::take(font) 50 | } 51 | } 52 | 53 | pub fn get_font_collection(&self) -> FontCollection { 54 | unsafe { 55 | let mut collection: ComPtr = ComPtr::new(); 56 | let hr = (*self.native.get()).GetFontCollection(collection.getter_addrefs()); 57 | assert!(hr == 0); 58 | FontCollection::take(collection) 59 | } 60 | } 61 | 62 | pub fn get_font_count(&self) -> u32 { 63 | unsafe { 64 | (*self.native.get()).GetFontCount() 65 | } 66 | } 67 | 68 | pub fn get_font(&self, index: u32) -> Font { 69 | unsafe { 70 | let mut font: ComPtr = ComPtr::new(); 71 | let hr = (*self.native.get()).GetFont(index, font.getter_addrefs()); 72 | assert!(hr == 0); 73 | Font::take(font) 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/types.rs: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | /* this is include!()'d in lib.rs */ 6 | use std::mem; 7 | 8 | // mirrors DWRITE_FONT_WEIGHT 9 | #[repr(u32)] 10 | #[derive(Deserialize, Serialize, PartialEq, Debug, Clone, Copy)] 11 | pub enum FontWeight { 12 | Thin = 100, 13 | ExtraLight = 200, 14 | Light = 300, 15 | SemiLight = 350, 16 | Regular = 400, 17 | Medium = 500, 18 | SemiBold = 600, 19 | Bold = 700, 20 | ExtraBold = 800, 21 | Black = 900, 22 | ExtraBlack = 950, 23 | } 24 | 25 | impl FontWeight { 26 | fn t(&self) -> winapi::DWRITE_FONT_WEIGHT { 27 | unsafe { mem::transmute::(*self) } 28 | } 29 | pub fn to_u32(&self) -> u32 { unsafe { mem::transmute::(*self) } } 30 | pub fn from_u32(v: u32) -> FontWeight { unsafe { mem::transmute::(v) } } 31 | } 32 | 33 | // mirrors DWRITE_FONT_STRETCH 34 | #[repr(u32)] 35 | #[derive(Deserialize, Serialize, PartialEq, Debug, Clone, Copy)] 36 | pub enum FontStretch { 37 | Undefined = 0, 38 | UltraCondensed = 1, 39 | ExtraCondensed = 2, 40 | Condensed = 3, 41 | SemiCondensed = 4, 42 | Normal = 5, 43 | SemiExpanded = 6, 44 | Expanded = 7, 45 | ExtraExpanded = 8, 46 | UltraExpanded = 9, 47 | } 48 | 49 | impl FontStretch { 50 | fn t(&self) -> winapi::DWRITE_FONT_STRETCH { 51 | unsafe { mem::transmute::(*self) } 52 | } 53 | pub fn to_u32(&self) -> u32 { unsafe { mem::transmute::(*self) } } 54 | pub fn from_u32(v: u32) -> FontStretch { unsafe { mem::transmute::(v) } } 55 | } 56 | 57 | // mirrors DWRITE_FONT_STYLE 58 | #[repr(u32)] 59 | #[derive(Deserialize, Serialize, PartialEq, Debug, Clone, Copy)] 60 | pub enum FontStyle { 61 | Normal = 0, 62 | Oblique = 1, 63 | Italic = 2, 64 | } 65 | 66 | impl FontStyle { 67 | fn t(&self) -> winapi::DWRITE_FONT_STYLE { 68 | unsafe { mem::transmute::(*self) } 69 | } 70 | pub fn to_u32(&self) -> u32 { unsafe { mem::transmute::(*self) } } 71 | pub fn from_u32(v: u32) -> FontStyle { unsafe { mem::transmute::(v) } } 72 | } 73 | 74 | #[derive(Deserialize, Serialize, PartialEq, Debug, Clone)] 75 | pub struct FontDescriptor { 76 | pub family_name: String, 77 | pub weight: FontWeight, 78 | pub stretch: FontStretch, 79 | pub style: FontStyle, 80 | } 81 | -------------------------------------------------------------------------------- /src/font.rs: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | use std::cell::UnsafeCell; 6 | 7 | use comptr::ComPtr; 8 | use winapi; 9 | use std::mem; 10 | 11 | use super::*; 12 | use helpers::*; 13 | 14 | #[derive(Debug)] 15 | pub struct Font { 16 | native: UnsafeCell>, 17 | } 18 | 19 | impl Font { 20 | pub fn take(native: ComPtr) -> Font { 21 | Font { 22 | native: UnsafeCell::new(native), 23 | } 24 | } 25 | 26 | pub unsafe fn as_ptr(&self) -> *mut winapi::IDWriteFont { 27 | (*self.native.get()).as_ptr() 28 | } 29 | 30 | pub fn to_descriptor(&self) -> FontDescriptor { 31 | FontDescriptor { 32 | family_name: self.family_name(), 33 | stretch: self.stretch(), 34 | style: self.style(), 35 | weight: self.weight(), 36 | } 37 | } 38 | 39 | pub fn stretch(&self) -> FontStretch { 40 | unsafe { 41 | mem::transmute::((*self.native.get()).GetStretch().0) 42 | } 43 | } 44 | 45 | pub fn style(&self) -> FontStyle { 46 | unsafe { 47 | mem::transmute::((*self.native.get()).GetStyle().0) 48 | } 49 | } 50 | 51 | pub fn weight(&self) -> FontWeight { 52 | unsafe { 53 | mem::transmute::((*self.native.get()).GetWeight().0) 54 | } 55 | } 56 | 57 | pub fn family_name(&self) -> String { 58 | unsafe { 59 | let mut family: ComPtr = ComPtr::new(); 60 | let hr = (*self.native.get()).GetFontFamily(family.getter_addrefs()); 61 | assert!(hr == 0); 62 | 63 | FontFamily::take(family).name() 64 | } 65 | } 66 | 67 | pub fn face_name(&self) -> String { 68 | unsafe { 69 | let mut names: ComPtr = ComPtr::new(); 70 | let hr = (*self.native.get()).GetFaceNames(names.getter_addrefs()); 71 | assert!(hr == 0); 72 | 73 | get_locale_string(&mut names) 74 | } 75 | } 76 | 77 | pub fn create_font_face(&self) -> FontFace { 78 | // FIXME create_font_face should cache the FontFace and return it, 79 | // there's a 1:1 relationship 80 | unsafe { 81 | let mut face: ComPtr = ComPtr::new(); 82 | let hr = (*self.native.get()).CreateFontFace(face.getter_addrefs()); 83 | assert!(hr == 0); 84 | FontFace::take(face) 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/comptr.rs: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | use std::ops::{Deref, DerefMut}; 6 | use std::ptr; 7 | use winapi::{IUnknown, REFIID, S_OK, E_NOINTERFACE}; 8 | 9 | #[derive(Debug)] 10 | pub struct ComPtr { 11 | ptr: *mut T, 12 | } 13 | 14 | impl ComPtr { 15 | pub fn new() -> Self { 16 | ComPtr { ptr: ptr::null_mut() } 17 | } 18 | 19 | pub fn from_ptr(ptr: *mut T) -> Self { 20 | unsafe { 21 | if !ptr.is_null() { 22 | (*(ptr as *mut IUnknown)).AddRef(); 23 | } 24 | } 25 | ComPtr { ptr: ptr } 26 | } 27 | 28 | pub fn already_addrefed(ptr: *mut T) -> Self { 29 | ComPtr { ptr: ptr } 30 | } 31 | 32 | pub fn getter_addrefs(&mut self) -> *mut *mut Q { 33 | self.release(); 34 | return &mut self.ptr as *mut *mut _ as *mut *mut Q; 35 | } 36 | 37 | pub fn as_ptr(&self) -> *mut T { 38 | self.ptr 39 | } 40 | 41 | pub fn query_interface(&self, iid: REFIID) -> Option> { 42 | if self.ptr.is_null() { 43 | return None; 44 | } 45 | 46 | unsafe { 47 | let mut p = ComPtr::::new(); 48 | let hr = (*(self.ptr as *mut IUnknown)).QueryInterface(iid, p.getter_addrefs()); 49 | if hr == S_OK { 50 | return Some(p); 51 | } 52 | assert!(hr == E_NOINTERFACE); 53 | return None; 54 | } 55 | } 56 | 57 | pub fn addref(&self) { 58 | unsafe { 59 | assert!(!self.ptr.is_null()); 60 | (*(self.ptr as *mut IUnknown)).AddRef(); 61 | } 62 | } 63 | 64 | pub fn release(&self) { 65 | unsafe { 66 | if !self.ptr.is_null() { 67 | (*(self.ptr as *mut IUnknown)).Release(); 68 | } 69 | } 70 | } 71 | 72 | pub fn forget(&mut self) -> *mut T { 73 | let ptr = self.ptr; 74 | self.ptr = ptr::null_mut(); 75 | ptr 76 | } 77 | } 78 | 79 | impl Clone for ComPtr { 80 | fn clone(&self) -> Self { 81 | if !self.ptr.is_null() { 82 | self.addref(); 83 | } 84 | ComPtr { ptr: self.ptr } 85 | } 86 | } 87 | 88 | impl Deref for ComPtr { 89 | type Target = T; 90 | fn deref(&self) -> &T { 91 | assert!(!self.ptr.is_null()); 92 | unsafe { &mut *self.ptr } 93 | } 94 | } 95 | 96 | impl DerefMut for ComPtr { 97 | fn deref_mut(&mut self) -> &mut T { 98 | assert!(!self.ptr.is_null()); 99 | unsafe { &mut *self.ptr } 100 | } 101 | } 102 | 103 | impl PartialEq for ComPtr { 104 | fn eq(&self, other: &ComPtr) -> bool { 105 | self.ptr == other.ptr 106 | } 107 | } 108 | 109 | impl Drop for ComPtr { 110 | fn drop(&mut self) { 111 | self.release(); 112 | } 113 | } 114 | 115 | unsafe impl Send for ComPtr {} 116 | -------------------------------------------------------------------------------- /src/glyph_run_analysis.rs: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | use std::ptr; 6 | use std::cell::UnsafeCell; 7 | 8 | use comptr::ComPtr; 9 | use winapi; 10 | use std::mem; 11 | use super::DWriteFactory; 12 | 13 | #[derive(Debug)] 14 | pub struct GlyphRunAnalysis { 15 | native: UnsafeCell>, 16 | } 17 | 18 | impl GlyphRunAnalysis { 19 | pub fn create(glyph_run: &winapi::DWRITE_GLYPH_RUN, 20 | pixels_per_dip: f32, 21 | transform: Option, 22 | rendering_mode: winapi::DWRITE_RENDERING_MODE, 23 | measuring_mode: winapi::DWRITE_MEASURING_MODE, 24 | baseline_x: f32, 25 | baseline_y: f32) -> GlyphRunAnalysis 26 | { 27 | unsafe { 28 | let mut native: ComPtr = ComPtr::new(); 29 | let hr = (*DWriteFactory()).CreateGlyphRunAnalysis(glyph_run as *const winapi::DWRITE_GLYPH_RUN, 30 | pixels_per_dip, 31 | transform.as_ref().map(|x| x as *const _).unwrap_or(ptr::null()), 32 | rendering_mode, measuring_mode, 33 | baseline_x, baseline_y, 34 | native.getter_addrefs()); 35 | assert!(hr == 0); 36 | GlyphRunAnalysis::take(native) 37 | } 38 | } 39 | 40 | pub fn take(native: ComPtr) -> GlyphRunAnalysis { 41 | GlyphRunAnalysis { 42 | native: UnsafeCell::new(native), 43 | } 44 | } 45 | 46 | pub fn get_alpha_texture_bounds(&self, texture_type: winapi::DWRITE_TEXTURE_TYPE) -> winapi::RECT { 47 | unsafe { 48 | let mut rect: winapi::RECT = mem::zeroed(); 49 | rect.left = 1234; 50 | rect.top = 1234; 51 | let hr = (*self.native.get()).GetAlphaTextureBounds(texture_type, &mut rect); 52 | assert!(hr == 0); 53 | rect 54 | } 55 | } 56 | 57 | pub fn create_alpha_texture(&self, texture_type: winapi::DWRITE_TEXTURE_TYPE, rect: winapi::RECT) -> Vec { 58 | unsafe { 59 | let rect_pixels = (rect.right - rect.left) * (rect.bottom - rect.top); 60 | let rect_bytes = rect_pixels * match texture_type { 61 | winapi::DWRITE_TEXTURE_ALIASED_1x1 => 1, 62 | winapi::DWRITE_TEXTURE_CLEARTYPE_3x1 => 3, 63 | _ => panic!("bad texture type specified"), 64 | }; 65 | 66 | let mut out_bytes: Vec = vec![0; rect_bytes as usize]; 67 | let hr = (*self.native.get()).CreateAlphaTexture(texture_type, &rect, out_bytes.as_mut_ptr(), out_bytes.len() as u32); 68 | assert!(hr == 0); 69 | out_bytes 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/com_helpers.rs: -------------------------------------------------------------------------------- 1 | 2 | // This is only handy for implementing a single-interface-implementing IUnknown. 3 | // 4 | // it assumes that there's a UuidOf$interface GUID globally defined 5 | 6 | DEFINE_GUID!{UuidOfIUnknown, 0x00000000, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46} 7 | 8 | macro_rules! guid_equals { 9 | ($left:expr, $right:expr) => { 10 | $left.Data1 == $right.Data1 && 11 | $left.Data2 == $right.Data2 && 12 | $left.Data3 == $right.Data3 && 13 | $left.Data4 == $right.Data4 14 | } 15 | } 16 | 17 | macro_rules! implement_iunknown { 18 | ($interface:ident, $iuud:ident, $typ:ident) => { 19 | IUnknownVtbl { 20 | QueryInterface: { 21 | unsafe extern "system" fn QueryInterface(This: *mut IUnknown, 22 | riid: REFIID, 23 | ppvObject: *mut *mut c_void) -> HRESULT { 24 | let this = if guid_equals!(*riid, $iuud) { 25 | mem::transmute(This) 26 | } else if guid_equals!(*riid, UuidOfIUnknown) { 27 | mem::transmute(This) 28 | } else { 29 | return $crate::winapi::E_NOINTERFACE; 30 | }; 31 | 32 | (*This).AddRef(); 33 | *ppvObject = this; 34 | return S_OK; 35 | } 36 | QueryInterface 37 | }, 38 | AddRef: { 39 | unsafe extern "system" fn AddRef(This: *mut IUnknown) -> ULONG { 40 | let this = $typ::from_interface(This); 41 | let count = this.refcount.fetch_add(1, atomic::Ordering::Relaxed) + 1; 42 | count as ULONG 43 | } 44 | AddRef 45 | }, 46 | Release: { 47 | unsafe extern "system" fn Release(This: *mut IUnknown) -> ULONG { 48 | let this = $typ::from_interface(This); 49 | let count = this.refcount.fetch_sub(1, atomic::Ordering::Release) - 1; 50 | if count == 0 { 51 | FontFileStream::destroy(This); 52 | } 53 | count as ULONG 54 | } 55 | Release 56 | }, 57 | } 58 | }; 59 | (static $interface:ident, $iuud:ident, $typ:ident) => { 60 | IUnknownVtbl { 61 | QueryInterface: { 62 | unsafe extern "system" fn QueryInterface(This: *mut IUnknown, 63 | riid: REFIID, 64 | ppvObject: *mut *mut c_void) -> HRESULT { 65 | let this = if guid_equals!(*riid, $iuud) { 66 | mem::transmute(This) 67 | } else if guid_equals!(*riid, UuidOfIUnknown) { 68 | mem::transmute(This) 69 | } else { 70 | return $crate::winapi::E_NOINTERFACE; 71 | }; 72 | 73 | (*This).AddRef(); 74 | *ppvObject = this; 75 | return S_OK; 76 | } 77 | QueryInterface 78 | }, 79 | AddRef: { 80 | unsafe extern "system" fn AddRef(_This: *mut IUnknown) -> ULONG { 81 | 1 82 | } 83 | AddRef 84 | }, 85 | Release: { 86 | unsafe extern "system" fn Release(_This: *mut IUnknown) -> ULONG { 87 | 1 88 | } 89 | Release 90 | }, 91 | } 92 | } 93 | } 94 | 95 | #[repr(C)] 96 | pub struct ComRepr(*const Vtbl, Type); 97 | 98 | pub trait Com where Self: Sized { 99 | type Vtbl: 'static; 100 | 101 | fn vtbl() -> &'static Self::Vtbl; 102 | 103 | fn into_interface(self) -> *mut Interface { 104 | let com = Box::new(ComRepr(Self::vtbl(), self)); 105 | Box::into_raw(com) as *mut Interface 106 | } 107 | 108 | unsafe fn from_interface<'a>(thing: *mut Interface) -> &'a mut Self { 109 | &mut (*(thing as *mut ComRepr)).1 110 | } 111 | 112 | unsafe fn destroy(thing: *mut Interface) { 113 | Box::from_raw(thing as *mut ComRepr); 114 | } 115 | } 116 | 117 | -------------------------------------------------------------------------------- /src/font_file.rs: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | use std::slice; 6 | use std::ptr; 7 | use std::cell::UnsafeCell; 8 | 9 | use comptr::ComPtr; 10 | 11 | use winapi; 12 | 13 | use font_file_loader_impl::DataFontHelper; 14 | use font_face::FontFace; 15 | use super::DWriteFactory; 16 | 17 | #[derive(Debug)] 18 | pub struct FontFile { 19 | native: UnsafeCell>, 20 | data_key: usize, 21 | face_type: winapi::DWRITE_FONT_FACE_TYPE, 22 | } 23 | 24 | impl FontFile { 25 | pub fn new_from_data(data: &[u8]) -> Option { 26 | let (font_file, key) = DataFontHelper::register_font_data(data); 27 | 28 | let mut ff = FontFile { 29 | native: UnsafeCell::new(font_file), 30 | data_key: key, 31 | face_type: winapi::DWRITE_FONT_FACE_TYPE_UNKNOWN, 32 | }; 33 | 34 | if ff.analyze() == false { 35 | DataFontHelper::unregister_font_data(key); 36 | return None; 37 | } 38 | 39 | Some(ff) 40 | } 41 | 42 | fn analyze(&mut self) -> bool { 43 | let mut face_type = winapi::DWRITE_FONT_FACE_TYPE_UNKNOWN; 44 | unsafe { 45 | let mut supported = 0; 46 | let mut _file_type = winapi::DWRITE_FONT_FILE_TYPE_UNKNOWN; 47 | let mut _num_faces = 0; 48 | 49 | let hr = (*self.as_ptr()).Analyze(&mut supported, &mut _file_type, &mut face_type, &mut _num_faces); 50 | if hr != 0 || supported == 0 { 51 | return false; 52 | } 53 | } 54 | self.face_type = face_type; 55 | true 56 | } 57 | 58 | pub fn take(native: ComPtr) -> FontFile { 59 | let mut ff = FontFile { 60 | native: UnsafeCell::new(native), 61 | data_key: 0, 62 | face_type: winapi::DWRITE_FONT_FACE_TYPE_UNKNOWN, 63 | }; 64 | ff.analyze(); 65 | ff 66 | } 67 | 68 | pub fn data_key(&self) -> Option { 69 | if self.data_key != 0 { 70 | Some(self.data_key) 71 | } else { 72 | None 73 | } 74 | } 75 | 76 | pub unsafe fn as_ptr(&self) -> *mut winapi::IDWriteFontFile { 77 | (*self.native.get()).as_ptr() 78 | } 79 | 80 | // This is a helper to read the contents of this FontFile, 81 | // without requiring callers to deal with loaders, keys, 82 | // or streams. 83 | pub fn get_font_file_bytes(&self) -> Vec { 84 | unsafe { 85 | let mut ref_key: *const winapi::c_void = ptr::null(); 86 | let mut ref_key_size: u32 = 0; 87 | let hr = (*self.native.get()).GetReferenceKey(&mut ref_key, &mut ref_key_size); 88 | assert!(hr == 0); 89 | 90 | let mut loader: ComPtr = ComPtr::new(); 91 | let hr = (*self.native.get()).GetLoader(loader.getter_addrefs()); 92 | assert!(hr == 0); 93 | 94 | let mut stream: ComPtr = ComPtr::new(); 95 | let hr = loader.CreateStreamFromKey(ref_key, ref_key_size, stream.getter_addrefs()); 96 | assert!(hr == 0); 97 | 98 | let mut file_size: u64 = 0; 99 | let hr = stream.GetFileSize(&mut file_size); 100 | assert!(hr == 0); 101 | 102 | let mut fragment_start: *const winapi::c_void = ptr::null(); 103 | let mut fragment_context: *mut winapi::c_void = ptr::null_mut(); 104 | let hr = stream.ReadFileFragment(&mut fragment_start, 0, file_size, &mut fragment_context); 105 | assert!(hr == 0); 106 | 107 | let in_ptr = slice::from_raw_parts(fragment_start as *const u8, file_size as usize); 108 | let bytes = in_ptr.to_vec(); 109 | 110 | stream.ReleaseFileFragment(fragment_context); 111 | 112 | bytes 113 | } 114 | } 115 | 116 | pub fn create_face(&self, face_index: u32, simulations: winapi::DWRITE_FONT_SIMULATIONS) -> FontFace { 117 | unsafe { 118 | let mut face: ComPtr = ComPtr::new(); 119 | let ptr = self.as_ptr(); 120 | let hr = (*DWriteFactory()).CreateFontFace(self.face_type, 1, &ptr, 121 | face_index, simulations, face.getter_addrefs()); 122 | assert!(hr == 0); 123 | FontFace::take(face) 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/font_collection.rs: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | use comptr::ComPtr; 6 | use winapi; 7 | use winapi::FALSE; 8 | use std::cell::UnsafeCell; 9 | 10 | use super::{DWriteFactory, FontFamily, Font, FontFace, FontDescriptor}; 11 | use helpers::*; 12 | 13 | #[derive(Debug)] 14 | pub struct FontCollectionFamilyIterator { 15 | collection: ComPtr, 16 | curr: u32, 17 | count: u32, 18 | } 19 | 20 | impl Iterator for FontCollectionFamilyIterator { 21 | type Item = FontFamily; 22 | fn next(&mut self) -> Option { 23 | if self.curr == self.count { 24 | return None; 25 | } 26 | 27 | unsafe { 28 | let mut family: ComPtr = ComPtr::new(); 29 | let hr = self.collection.GetFontFamily(self.curr, family.getter_addrefs()); 30 | assert!(hr == 0); 31 | self.curr += 1; 32 | Some(FontFamily::take(family)) 33 | } 34 | } 35 | } 36 | 37 | pub struct FontCollection { 38 | native: UnsafeCell>, 39 | } 40 | 41 | impl FontCollection { 42 | pub fn system() -> FontCollection { 43 | unsafe { 44 | let mut native: ComPtr = ComPtr::new(); 45 | let hr = (*DWriteFactory()).GetSystemFontCollection(native.getter_addrefs(), FALSE); 46 | assert!(hr == 0); 47 | 48 | FontCollection { 49 | native: UnsafeCell::new(native) 50 | } 51 | } 52 | } 53 | 54 | pub fn take(native: ComPtr) -> FontCollection { 55 | FontCollection { 56 | native: UnsafeCell::new(native) 57 | } 58 | } 59 | 60 | pub unsafe fn as_ptr(&self) -> *mut winapi::IDWriteFontCollection { 61 | (*self.native.get()).as_ptr() 62 | } 63 | 64 | 65 | pub fn families_iter(&self) -> FontCollectionFamilyIterator { 66 | unsafe { 67 | FontCollectionFamilyIterator { 68 | collection: (*self.native.get()).clone(), 69 | curr: 0, 70 | count: (*self.native.get()).GetFontFamilyCount(), 71 | } 72 | } 73 | } 74 | 75 | pub fn get_font_family_count(&self) -> u32 { 76 | unsafe { 77 | (*self.native.get()).GetFontFamilyCount() 78 | } 79 | } 80 | 81 | pub fn get_font_family(&self, index: u32) -> FontFamily { 82 | unsafe { 83 | let mut family: ComPtr = ComPtr::new(); 84 | let hr = (*self.native.get()).GetFontFamily(index, family.getter_addrefs()); 85 | assert!(hr == 0); 86 | FontFamily::take(family) 87 | } 88 | } 89 | 90 | // Find a font matching the given font descriptor in this 91 | // font collection. 92 | pub fn get_font_from_descriptor(&self, desc: &FontDescriptor) -> Option { 93 | if let Some(family) = self.get_font_family_by_name(&desc.family_name) { 94 | let font = family.get_first_matching_font(desc.weight, desc.stretch, desc.style); 95 | // Exact matches only here 96 | if font.weight() == desc.weight && 97 | font.stretch() == desc.stretch && 98 | font.style() == desc.style 99 | { 100 | return Some(font); 101 | } 102 | } 103 | 104 | None 105 | } 106 | 107 | pub fn get_font_from_face(&self, face: &FontFace) -> Option { 108 | unsafe { 109 | let mut font: ComPtr = ComPtr::new(); 110 | let hr = (*self.native.get()).GetFontFromFontFace(face.as_ptr(), font.getter_addrefs()); 111 | if hr != 0 { 112 | return None; 113 | } 114 | Some(Font::take(font)) 115 | } 116 | } 117 | 118 | pub fn get_font_family_by_name(&self, family_name: &str) -> Option { 119 | unsafe { 120 | let mut index: u32 = 0; 121 | let mut exists: winapi::BOOL = winapi::FALSE; 122 | let hr = (*self.native.get()).FindFamilyName(family_name.to_wide_null().as_ptr(), &mut index, &mut exists); 123 | assert!(hr == 0); 124 | if exists == winapi::FALSE { 125 | return None; 126 | } 127 | 128 | let mut family: ComPtr = ComPtr::new(); 129 | let hr = (*self.native.get()).GetFontFamily(index, family.getter_addrefs()); 130 | assert!(hr == 0); 131 | 132 | Some(FontFamily::take(family)) 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/test.rs: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | use super::*; 6 | 7 | #[test] 8 | fn test_system_family_iter() { 9 | let system_fc = FontCollection::system(); 10 | let count = system_fc.families_iter().count(); 11 | assert!(count > 0); 12 | assert!(system_fc.families_iter().find(|f| f.name() == "Arial").is_some()); 13 | } 14 | 15 | #[test] 16 | fn test_descriptor_round_trip() { 17 | let system_fc = FontCollection::system(); 18 | 19 | let arial_family = system_fc.get_font_family_by_name("Arial").unwrap(); 20 | let arial_font = arial_family.get_first_matching_font(FontWeight::Regular, 21 | FontStretch::Normal, 22 | FontStyle::Normal); 23 | 24 | let descriptor = arial_font.to_descriptor(); 25 | assert!(descriptor.family_name == "Arial"); 26 | 27 | let arial_font_2 = system_fc.get_font_from_descriptor(&descriptor).unwrap(); 28 | let descriptor2 = arial_font_2.to_descriptor(); 29 | assert_eq!(descriptor, descriptor2); 30 | } 31 | 32 | #[test] 33 | fn test_get_font_file_bytes() { 34 | let system_fc = FontCollection::system(); 35 | 36 | let arial_family = system_fc.get_font_family_by_name("Arial").unwrap(); 37 | let arial_font = arial_family.get_first_matching_font(FontWeight::Regular, 38 | FontStretch::Normal, 39 | FontStyle::Normal); 40 | let face = arial_font.create_font_face(); 41 | let files = face.get_files(); 42 | assert!(files.len() > 0); 43 | 44 | let bytes = files[0].get_font_file_bytes(); 45 | assert!(bytes.len() > 0); 46 | } 47 | 48 | #[test] 49 | fn test_create_font_file_from_bytes() { 50 | let system_fc = FontCollection::system(); 51 | 52 | let arial_family = system_fc.get_font_family_by_name("Arial").unwrap(); 53 | let arial_font = arial_family.get_first_matching_font(FontWeight::Regular, 54 | FontStretch::Normal, 55 | FontStyle::Normal); 56 | let face = arial_font.create_font_face(); 57 | let files = face.get_files(); 58 | assert!(files.len() > 0); 59 | 60 | let bytes = files[0].get_font_file_bytes(); 61 | assert!(bytes.len() > 0); 62 | 63 | // now go back 64 | let new_font = FontFile::new_from_data(&bytes); 65 | assert!(new_font.is_some()); 66 | 67 | let new_font = new_font.unwrap(); 68 | } 69 | 70 | #[test] 71 | fn test_glyph_image() { 72 | let system_fc = FontCollection::system(); 73 | let arial_family = system_fc.get_font_family_by_name("Arial").unwrap(); 74 | let arial_font = arial_family.get_first_matching_font(FontWeight::Regular, 75 | FontStretch::Normal, 76 | FontStyle::Normal); 77 | 78 | let face = arial_font.create_font_face(); 79 | let a_index = face.get_glyph_indices(&['A' as u32])[0]; 80 | 81 | let metrics = face.get_metrics(); 82 | println!("Metrics:\n======\n{:?}\n======", metrics); 83 | 84 | let gm = face.get_design_glyph_metrics(&[a_index], false)[0]; 85 | println!("Glyph metrics:\n======\n{:?}\n======", gm); 86 | 87 | let device_pixel_ratio = 1.0f32; 88 | let em_size = 10.0f32; 89 | 90 | let design_units_per_pixel = face.metrics().designUnitsPerEm as f32 / 16. as f32; 91 | let scaled_design_units_to_pixels = (em_size * device_pixel_ratio) / design_units_per_pixel; 92 | 93 | let width = (gm.advanceWidth as i32 - (gm.leftSideBearing + gm.rightSideBearing)) as f32 * scaled_design_units_to_pixels; 94 | let height = (gm.advanceHeight as i32 - (gm.topSideBearing + gm.bottomSideBearing)) as f32 * scaled_design_units_to_pixels; 95 | let x = (-gm.leftSideBearing) as f32 * scaled_design_units_to_pixels; 96 | let y = (gm.verticalOriginY - gm.topSideBearing) as f32 * scaled_design_units_to_pixels; 97 | 98 | // FIXME I'm pretty sure we need to do a proper RoundOut type 99 | // operation on this rect to properly handle any aliasing 100 | let left_i = x.floor() as i32; 101 | let top_i = (height - y).floor() as i32; 102 | let width_u = width.ceil() as u32; 103 | let height_u = height.ceil() as u32; 104 | 105 | println!("GlyphDimensions: {} {} {} {}", left_i, top_i, width_u, height_u); 106 | 107 | let gdi_interop = GdiInterop::create(); 108 | let rt = gdi_interop.create_bitmap_render_target(width_u, height_u); 109 | let rp = RenderingParams::create_for_primary_monitor(); 110 | rt.set_pixels_per_dip(device_pixel_ratio); 111 | rt.draw_glyph_run(x as f32, y as f32, 112 | DWRITE_MEASURING_MODE_NATURAL, 113 | &face, 114 | em_size, 115 | &[a_index], 116 | &[0f32], 117 | &[GlyphOffset { advanceOffset: 0., ascenderOffset: 0. }], 118 | &rp, 119 | &(255.0f32, 255.0f32, 255.0f32)); 120 | let bytes = rt.get_opaque_values_as_mask(); 121 | println!("bytes length: {}", bytes.len()); 122 | } 123 | -------------------------------------------------------------------------------- /src/bitmap_render_target.rs: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | use std::slice; 6 | use std::cell::UnsafeCell; 7 | use std::mem::{zeroed, size_of}; 8 | 9 | use comptr::ComPtr; 10 | use winapi; 11 | use gdi32; 12 | use super::{FontFace, RenderingParams}; 13 | 14 | #[derive(Debug)] 15 | pub struct BitmapRenderTarget { 16 | native: UnsafeCell>, 17 | } 18 | 19 | impl BitmapRenderTarget { 20 | pub fn take(native: ComPtr) -> BitmapRenderTarget { 21 | BitmapRenderTarget { 22 | native: UnsafeCell::new(native), 23 | } 24 | } 25 | 26 | pub unsafe fn as_ptr(&self) -> *mut winapi::IDWriteBitmapRenderTarget { 27 | (*self.native.get()).as_ptr() 28 | } 29 | 30 | // A dip is 1/96th of an inch, so this value is the number of pixels per inch divided by 96. 31 | pub fn set_pixels_per_dip(&self, ppd: f32) { 32 | unsafe { 33 | (*self.native.get()).SetPixelsPerDip(ppd); 34 | } 35 | } 36 | 37 | pub fn get_memory_dc(&self) -> winapi::HDC { 38 | unsafe { 39 | (*self.native.get()).GetMemoryDC() 40 | } 41 | } 42 | 43 | pub fn draw_glyph_run(&self, 44 | baseline_origin_x: f32, 45 | baseline_origin_y: f32, 46 | measuring_mode: winapi::DWRITE_MEASURING_MODE, 47 | font_face: &FontFace, 48 | em_size: f32, 49 | glyph_indices: &[u16], 50 | glyph_advances: &[f32], 51 | glyph_offsets: &[winapi::DWRITE_GLYPH_OFFSET], 52 | rendering_params: &RenderingParams, 53 | color: &(f32, f32, f32)) 54 | -> winapi::RECT 55 | { 56 | unsafe { 57 | assert!(glyph_indices.len() == glyph_advances.len()); 58 | assert!(glyph_indices.len() == glyph_offsets.len()); 59 | 60 | let r = (color.0 * 255.0) as u8; 61 | let g = (color.1 * 255.0) as u8; 62 | let b = (color.2 * 255.0) as u8; 63 | 64 | let mut glyph_run: winapi::DWRITE_GLYPH_RUN = zeroed(); 65 | glyph_run.fontFace = font_face.as_ptr(); 66 | glyph_run.fontEmSize = em_size; 67 | glyph_run.glyphCount = glyph_indices.len() as u32; 68 | glyph_run.glyphIndices = glyph_indices.as_ptr(); 69 | glyph_run.glyphAdvances = glyph_advances.as_ptr(); 70 | glyph_run.glyphOffsets = glyph_offsets.as_ptr(); 71 | glyph_run.isSideways = 0; 72 | glyph_run.bidiLevel = 0; 73 | 74 | let mut rect: winapi::RECT = zeroed(); 75 | let hr = (*self.native.get()).DrawGlyphRun(baseline_origin_x, 76 | baseline_origin_y, 77 | measuring_mode, 78 | &glyph_run, 79 | rendering_params.as_ptr(), 80 | winapi::RGB(r,g,b), 81 | &mut rect); 82 | assert!(hr == 0); 83 | rect 84 | } 85 | } 86 | 87 | // This function expects to have glyphs rendered in WHITE, 88 | // and pulls out a u8 vector of width*height*4 size with 89 | // the coverage value (we pull out R) broadcast to the alpha 90 | // channel, with the color white. That is, it performs: 91 | // RGBX -> xxxR, where xxx = 0xff 92 | pub fn get_opaque_values_as_mask(&self) -> Vec { 93 | // Now grossness to pull out the pixels 94 | unsafe { 95 | let memory_dc = self.get_memory_dc(); 96 | let mut bitmap: winapi::BITMAP = zeroed(); 97 | let ret = gdi32::GetObjectW(gdi32::GetCurrentObject(memory_dc, winapi::OBJ_BITMAP), 98 | size_of::() as i32, 99 | &mut bitmap as *mut _ as *mut winapi::c_void); 100 | assert!(ret == size_of::() as i32); 101 | assert!(bitmap.bmBitsPixel == 32); 102 | 103 | let width = bitmap.bmWidth as usize; 104 | let stride = bitmap.bmWidthBytes as usize; 105 | let height = bitmap.bmHeight as usize; 106 | 107 | let mut out_bytes: Vec = vec![0; width * height * 4]; 108 | let mut out_u32 = slice::from_raw_parts_mut(out_bytes.as_mut_ptr() as *mut u32, 109 | width * height); 110 | 111 | for row in 0..height { 112 | let in_offset = (row * stride) as isize; 113 | let in_u32 = slice::from_raw_parts(bitmap.bmBits.offset(in_offset) as *const u32, 114 | width); 115 | for col in 0..width { 116 | let r = in_u32[col] & 0xff; 117 | out_u32[width*row + col] = (r << 24) | (0x00ffffffu32); 118 | } 119 | } 120 | 121 | out_bytes 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #![allow(non_upper_case_globals)] 6 | 7 | #[cfg(feature = "serde_derive")] 8 | #[macro_use] 9 | extern crate serde_derive; 10 | 11 | #[macro_use] 12 | extern crate lazy_static; 13 | #[macro_use(DEFINE_GUID)] 14 | extern crate winapi; 15 | extern crate gdi32; 16 | extern crate kernel32; 17 | extern crate libc; 18 | extern crate serde; 19 | 20 | #[cfg(feature = "serde_codegen")] 21 | include!(concat!(env!("OUT_DIR"), "/types.rs")); 22 | 23 | #[cfg(feature = "serde_derive")] 24 | include!("types.rs"); 25 | 26 | use winapi::DWRITE_FACTORY_TYPE_SHARED; 27 | use winapi::IDWriteFactory; 28 | use winapi::IDWriteRenderingParams; 29 | use std::ffi::CString; 30 | 31 | use comptr::ComPtr; 32 | use winapi::S_OK; 33 | 34 | mod comptr; 35 | mod helpers; 36 | use helpers::ToWide; 37 | use std::os::raw::c_void; 38 | 39 | #[cfg(test)] 40 | mod test; 41 | 42 | // We still use the DWrite structs for things like metrics; re-export them 43 | // here 44 | pub use winapi::DWRITE_FONT_METRICS as FontMetrics; 45 | pub use winapi::DWRITE_GLYPH_OFFSET as GlyphOffset; 46 | pub use winapi::{DWRITE_MATRIX, DWRITE_GLYPH_RUN}; 47 | pub use winapi::{DWRITE_RENDERING_MODE_DEFAULT, 48 | DWRITE_RENDERING_MODE_ALIASED, 49 | DWRITE_RENDERING_MODE_GDI_CLASSIC, 50 | DWRITE_RENDERING_MODE_GDI_NATURAL, 51 | DWRITE_RENDERING_MODE_NATURAL, 52 | DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC, 53 | DWRITE_RENDERING_MODE_OUTLINE, 54 | DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC, 55 | DWRITE_RENDERING_MODE_CLEARTYPE_GDI_NATURAL, 56 | DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL, 57 | DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC}; 58 | pub use winapi::{DWRITE_MEASURING_MODE_NATURAL, 59 | DWRITE_MEASURING_MODE_GDI_CLASSIC, 60 | DWRITE_MEASURING_MODE_GDI_NATURAL}; 61 | pub use winapi::{DWRITE_FONT_SIMULATIONS_NONE, 62 | DWRITE_FONT_SIMULATIONS_BOLD, 63 | DWRITE_FONT_SIMULATIONS_OBLIQUE}; 64 | pub use winapi::{DWRITE_TEXTURE_ALIASED_1x1, DWRITE_TEXTURE_CLEARTYPE_3x1}; 65 | pub use winapi::{DWRITE_RENDERING_MODE}; 66 | pub use winapi::{DWRITE_MEASURING_MODE}; 67 | pub use winapi::{DWRITE_TEXTURE_TYPE}; 68 | 69 | #[macro_use] mod com_helpers; 70 | 71 | mod bitmap_render_target; pub use bitmap_render_target::BitmapRenderTarget; 72 | mod font; pub use font::Font; 73 | mod font_collection; pub use font_collection::FontCollection; 74 | mod font_face; pub use font_face::FontFace; 75 | mod font_family; pub use font_family::FontFamily; 76 | mod font_file; pub use font_file::FontFile; 77 | mod gdi_interop; pub use gdi_interop::GdiInterop; 78 | mod rendering_params; pub use rendering_params::RenderingParams; 79 | mod glyph_run_analysis; pub use glyph_run_analysis::GlyphRunAnalysis; 80 | 81 | // This is an internal implementation of FontFileLoader, for our utility 82 | // functions. We don't wrap the DWriteFontFileLoader interface and 83 | // related things. 84 | mod font_file_loader_impl; 85 | 86 | DEFINE_GUID!{UuidOfIDWriteFactory, 0xb859ee5a, 0xd838, 0x4b5b, 0xa2, 0xe8, 0x1a, 0xdc, 0x7d, 0x93, 0xdb, 0x48} 87 | 88 | unsafe impl Sync for ComPtr { } 89 | unsafe impl Sync for ComPtr {} 90 | 91 | lazy_static! { 92 | static ref DWRITE_FACTORY_RAW_PTR: usize = { 93 | unsafe { 94 | type DWriteCreateFactoryType = extern "system" fn(winapi::DWRITE_FACTORY_TYPE, winapi::REFIID, *mut *mut winapi::IUnknown) -> winapi::HRESULT; 95 | 96 | let dwrite_dll = kernel32::LoadLibraryW("dwrite.dll".to_wide_null().as_ptr()); 97 | assert!(!dwrite_dll.is_null()); 98 | let create_factory_name = CString::new("DWriteCreateFactory").unwrap(); 99 | let dwrite_create_factory_ptr = 100 | kernel32::GetProcAddress(dwrite_dll, create_factory_name.as_ptr() as winapi::LPCSTR); 101 | assert!(!dwrite_create_factory_ptr.is_null()); 102 | 103 | let dwrite_create_factory = 104 | mem::transmute::<*const c_void, DWriteCreateFactoryType>(dwrite_create_factory_ptr); 105 | 106 | let mut factory: ComPtr = ComPtr::new(); 107 | let hr = dwrite_create_factory( 108 | DWRITE_FACTORY_TYPE_SHARED, 109 | &UuidOfIDWriteFactory, 110 | factory.getter_addrefs()); 111 | assert!(hr == S_OK); 112 | factory.forget() as usize 113 | } 114 | }; 115 | 116 | static ref DEFAULT_DWRITE_RENDERING_PARAMS_RAW_PTR: usize = { 117 | unsafe { 118 | let mut default_rendering_params: ComPtr = ComPtr::new(); 119 | let hr = (*DWriteFactory()).CreateRenderingParams(default_rendering_params.getter_addrefs()); 120 | assert!(hr == S_OK); 121 | 122 | default_rendering_params.forget() as usize 123 | } 124 | }; 125 | 126 | } // end lazy static 127 | 128 | // FIXME vlad would be nice to return, say, FactoryPtr 129 | // that has a DerefMut impl, so that we can write 130 | // DWriteFactory().SomeOperation() as opposed to 131 | // (*DWriteFactory()).SomeOperation() 132 | #[allow(non_snake_case)] 133 | fn DWriteFactory() -> *mut IDWriteFactory { 134 | (*DWRITE_FACTORY_RAW_PTR) as *mut IDWriteFactory 135 | } 136 | 137 | #[allow(non_snake_case)] 138 | fn DefaultDWriteRenderParams() -> *mut IDWriteRenderingParams { 139 | (*DEFAULT_DWRITE_RENDERING_PARAMS_RAW_PTR) as *mut IDWriteRenderingParams 140 | } 141 | -------------------------------------------------------------------------------- /src/font_file_loader_impl.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case, non_upper_case_globals)] 2 | 3 | use std::{mem, ptr}; 4 | use std::collections::HashMap; 5 | use std::sync::{Mutex, atomic}; 6 | use std::marker::Send; 7 | use winapi::{IUnknown, IUnknownVtbl}; 8 | use winapi::{IDWriteFontFileStream, IDWriteFontFileStreamVtbl}; 9 | use winapi::{IDWriteFontFileLoader, IDWriteFontFileLoaderVtbl}; 10 | use winapi::IDWriteFontFile; 11 | use winapi::{E_FAIL, E_INVALIDARG, E_NOTIMPL, S_OK}; 12 | use winapi::{c_void, UINT32, UINT64, ULONG, HRESULT, REFIID}; 13 | 14 | use super::DWriteFactory; 15 | use comptr::ComPtr; 16 | use com_helpers::*; 17 | 18 | struct FontFileLoader; 19 | 20 | DEFINE_GUID!{UuidOfIDWriteFontFileLoader, 0x727cad4e, 0xd6af, 0x4c9e, 0x8a, 0x08, 0xd6, 0x95, 0xb1, 0x1c, 0xaa, 0x49} 21 | DEFINE_GUID!{UuidOfIDWriteFontFileStream, 0x6d4865fe, 0x0ab8, 0x4d91, 0x8f, 0x62, 0x5d, 0xd6, 0xbe, 0x34, 0xa3, 0xe0} 22 | 23 | const FontFileLoaderVtbl: &'static IDWriteFontFileLoaderVtbl = &IDWriteFontFileLoaderVtbl { 24 | parent: implement_iunknown!(static IDWriteFontFileLoader, UuidOfIDWriteFontFileLoader, FontFileLoader), 25 | CreateStreamFromKey: { 26 | unsafe extern "system" fn CreateStreamFromKey( 27 | _This: *mut IDWriteFontFileLoader, 28 | fontFileReferenceKey: *const c_void, 29 | fontFileReferenceKeySize: UINT32, 30 | fontFileStream: *mut *mut IDWriteFontFileStream) -> HRESULT 31 | { 32 | if fontFileReferenceKey.is_null() || fontFileStream.is_null() { 33 | return E_INVALIDARG 34 | } 35 | assert!(fontFileReferenceKeySize == mem::size_of::() as UINT32); 36 | let key = *(fontFileReferenceKey as *const usize); 37 | let stream = match FONT_FILE_STREAM_MAP.lock().unwrap().get_mut(&key) { 38 | None => { 39 | *fontFileStream = ptr::null_mut(); 40 | return E_FAIL 41 | } 42 | Some(file_stream) => { 43 | file_stream.as_ptr() 44 | } 45 | }; 46 | 47 | *fontFileStream = stream; 48 | S_OK 49 | } 50 | CreateStreamFromKey 51 | } 52 | }; 53 | 54 | impl Com for FontFileLoader { 55 | type Vtbl = IDWriteFontFileLoaderVtbl; 56 | fn vtbl() -> &'static IDWriteFontFileLoaderVtbl { FontFileLoaderVtbl } 57 | } 58 | 59 | impl Com for FontFileLoader { 60 | type Vtbl = IUnknownVtbl; 61 | fn vtbl() -> &'static IUnknownVtbl { &FontFileLoaderVtbl.parent } 62 | } 63 | 64 | impl FontFileLoader { 65 | pub fn new() -> FontFileLoader { 66 | FontFileLoader 67 | } 68 | } 69 | 70 | unsafe impl Send for FontFileLoader {} 71 | unsafe impl Sync for FontFileLoader {} 72 | 73 | struct FontFileStream { 74 | refcount: atomic::AtomicUsize, 75 | data: Vec, 76 | } 77 | 78 | const FontFileStreamVtbl: &'static IDWriteFontFileStreamVtbl = &IDWriteFontFileStreamVtbl { 79 | parent: implement_iunknown!(IDWriteFontFileStream, UuidOfIDWriteFontFileStream, FontFileStream), 80 | ReadFileFragment: { 81 | unsafe extern "system" fn ReadFileFragment( 82 | This: *mut IDWriteFontFileStream, 83 | fragmentStart: *mut *const c_void, 84 | fileOffset: UINT64, 85 | fragmentSize: UINT64, 86 | fragmentContext: *mut *mut c_void) -> HRESULT 87 | { 88 | let this = FontFileStream::from_interface(This); 89 | *fragmentContext = ptr::null_mut(); 90 | if (fileOffset + fragmentSize) as usize > this.data.len() { 91 | return E_INVALIDARG 92 | } 93 | let index = fileOffset as usize; 94 | *fragmentStart = this.data[index..].as_mut_ptr() as *const c_void; 95 | S_OK 96 | } 97 | ReadFileFragment 98 | }, 99 | ReleaseFileFragment: { 100 | unsafe extern "system" fn ReleaseFileFragment( 101 | _This: *mut IDWriteFontFileStream, 102 | _fragmentContext: *mut c_void) 103 | { 104 | } 105 | ReleaseFileFragment 106 | }, 107 | GetFileSize: { 108 | unsafe extern "system" fn GetFileSize( 109 | This: *mut IDWriteFontFileStream, 110 | fileSize: *mut UINT64) -> HRESULT 111 | { 112 | let this = FontFileStream::from_interface(This); 113 | *fileSize = this.data.len() as UINT64; 114 | S_OK 115 | } 116 | GetFileSize 117 | }, 118 | GetLastWriteTime: { 119 | unsafe extern "system" fn GetLastWriteTime( 120 | _This: *mut IDWriteFontFileStream, 121 | _lastWriteTime: *mut UINT64) -> HRESULT 122 | { 123 | E_NOTIMPL 124 | } 125 | GetLastWriteTime 126 | }, 127 | }; 128 | 129 | impl FontFileStream { 130 | pub fn new(data: &[u8]) -> FontFileStream { 131 | FontFileStream { 132 | refcount: atomic::ATOMIC_USIZE_INIT, 133 | data: data.to_vec(), 134 | } 135 | } 136 | } 137 | 138 | impl Com for FontFileStream { 139 | type Vtbl = IDWriteFontFileStreamVtbl; 140 | fn vtbl() -> &'static IDWriteFontFileStreamVtbl { FontFileStreamVtbl } 141 | } 142 | 143 | impl Com for FontFileStream { 144 | type Vtbl = IUnknownVtbl; 145 | fn vtbl() -> &'static IUnknownVtbl { &FontFileStreamVtbl.parent } 146 | } 147 | 148 | static mut FONT_FILE_KEY: atomic::AtomicUsize = atomic::ATOMIC_USIZE_INIT; 149 | 150 | lazy_static! { 151 | static ref FONT_FILE_STREAM_MAP: Mutex>> = { 152 | Mutex::new(HashMap::new()) 153 | }; 154 | 155 | static ref FONT_FILE_LOADER: Mutex> = { 156 | let ffl_native = FontFileLoader::new(); 157 | let ffl = ComPtr::::from_ptr(ffl_native.into_interface()); 158 | unsafe { 159 | let hr = (*DWriteFactory()).RegisterFontFileLoader(ffl.as_ptr()); 160 | assert!(hr == 0); 161 | } 162 | Mutex::new(ffl) 163 | }; 164 | } 165 | 166 | pub struct DataFontHelper; 167 | 168 | impl DataFontHelper { 169 | pub fn register_font_data(font_data: &[u8]) -> (ComPtr, usize) { 170 | unsafe { 171 | let key = FONT_FILE_KEY.fetch_add(1, atomic::Ordering::Relaxed); 172 | let font_file_stream_native = FontFileStream::new(font_data); 173 | let font_file_stream = ComPtr::from_ptr(font_file_stream_native.into_interface()); 174 | { 175 | let mut map = FONT_FILE_STREAM_MAP.lock().unwrap(); 176 | map.insert(key, font_file_stream); 177 | } 178 | 179 | let mut font_file: ComPtr = ComPtr::new(); 180 | { 181 | let loader = FONT_FILE_LOADER.lock().unwrap(); 182 | let hr = (*DWriteFactory()).CreateCustomFontFileReference( 183 | mem::transmute(&key), 184 | mem::size_of::() as UINT32, 185 | loader.as_ptr(), 186 | font_file.getter_addrefs()); 187 | assert!(hr == S_OK); 188 | } 189 | 190 | (font_file, key) 191 | } 192 | } 193 | 194 | pub fn unregister_font_data(key: usize) { 195 | let mut map = FONT_FILE_STREAM_MAP.lock().unwrap(); 196 | if map.remove(&key).is_none() { 197 | panic!("unregister_font_data: trying to unregister key that is no longer registered"); 198 | } 199 | } 200 | } -------------------------------------------------------------------------------- /src/font_face.rs: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | use std::slice; 6 | use std::ptr; 7 | use std::cell::UnsafeCell; 8 | use std::mem::zeroed; 9 | 10 | use comptr::ComPtr; 11 | use super::{FontMetrics, FontFile, DefaultDWriteRenderParams}; 12 | 13 | use winapi; 14 | 15 | #[derive(Debug)] 16 | pub struct FontFace { 17 | native: UnsafeCell>, 18 | metrics: FontMetrics, 19 | } 20 | 21 | impl FontFace { 22 | pub fn take(native: ComPtr) -> FontFace { 23 | unsafe { 24 | let mut metrics: FontMetrics = zeroed(); 25 | let cell = UnsafeCell::new(native); 26 | (*cell.get()).GetMetrics(&mut metrics); 27 | FontFace { 28 | native: cell, 29 | metrics: metrics, 30 | } 31 | } 32 | } 33 | 34 | pub unsafe fn as_ptr(&self) -> *mut winapi::IDWriteFontFace { 35 | (*self.native.get()).as_ptr() 36 | } 37 | 38 | pub fn get_files(&self) -> Vec { 39 | unsafe { 40 | let mut number_of_files: u32 = 0; 41 | let hr = (*self.native.get()).GetFiles(&mut number_of_files, ptr::null_mut()); 42 | assert!(hr == 0); 43 | 44 | let mut file_ptrs: Vec<*mut winapi::IDWriteFontFile> = 45 | vec![ptr::null_mut(); number_of_files as usize]; 46 | let hr = (*self.native.get()).GetFiles(&mut number_of_files, file_ptrs.as_mut_ptr()); 47 | assert!(hr == 0); 48 | 49 | file_ptrs.iter().map(|p| FontFile::take(ComPtr::already_addrefed(*p))).collect() 50 | } 51 | } 52 | 53 | pub fn get_glyph_count(&self) -> u16 { 54 | unsafe { 55 | (*self.native.get()).GetGlyphCount() 56 | } 57 | } 58 | 59 | pub fn metrics(&self) -> &FontMetrics { 60 | &self.metrics 61 | } 62 | 63 | pub fn get_metrics(&self) -> FontMetrics { 64 | unsafe { 65 | let mut metrics: winapi::DWRITE_FONT_METRICS = zeroed(); 66 | (*self.native.get()).GetMetrics(&mut metrics); 67 | metrics 68 | } 69 | } 70 | 71 | pub fn get_glyph_indices(&self, code_points: &[u32]) -> Vec { 72 | unsafe { 73 | let mut glyph_indices: Vec = vec![0; code_points.len()]; 74 | let hr = (*self.native.get()).GetGlyphIndices(code_points.as_ptr(), 75 | code_points.len() as u32, 76 | glyph_indices.as_mut_ptr()); 77 | assert!(hr == 0); 78 | glyph_indices 79 | } 80 | } 81 | 82 | pub fn get_design_glyph_metrics(&self, glyph_indices: &[u16], is_sideways: bool) -> Vec { 83 | unsafe { 84 | let mut metrics: Vec = vec![zeroed(); glyph_indices.len()]; 85 | let hr = (*self.native.get()).GetDesignGlyphMetrics(glyph_indices.as_ptr(), 86 | glyph_indices.len() as u32, 87 | metrics.as_mut_ptr(), 88 | is_sideways as winapi::BOOL); 89 | assert!(hr == 0); 90 | metrics 91 | } 92 | } 93 | 94 | pub fn get_gdi_compatible_glyph_metrics(&self, em_size: f32, pixels_per_dip: f32, transform: *const winapi::DWRITE_MATRIX, 95 | use_gdi_natural: bool, glyph_indices: &[u16], is_sideways: bool) 96 | -> Vec 97 | { 98 | unsafe { 99 | let mut metrics: Vec = vec![zeroed(); glyph_indices.len()]; 100 | let hr = (*self.native.get()).GetGdiCompatibleGlyphMetrics(em_size, pixels_per_dip, 101 | transform, 102 | use_gdi_natural as winapi::BOOL, 103 | glyph_indices.as_ptr(), 104 | glyph_indices.len() as u32, 105 | metrics.as_mut_ptr(), 106 | is_sideways as winapi::BOOL); 107 | assert!(hr == 0); 108 | metrics 109 | } 110 | } 111 | 112 | pub fn get_font_table(&self, opentype_table_tag: u32) -> Option> { 113 | unsafe { 114 | let mut table_data_ptr: *const u8 = ptr::null_mut(); 115 | let mut table_size: u32 = 0; 116 | let mut table_context: *mut winapi::c_void = ptr::null_mut(); 117 | let mut exists: winapi::BOOL = winapi::FALSE; 118 | 119 | let hr = (*self.native.get()).TryGetFontTable(opentype_table_tag, 120 | &mut table_data_ptr as *mut *const _ as *mut *const winapi::c_void, 121 | &mut table_size, 122 | &mut table_context, 123 | &mut exists); 124 | assert!(hr == 0); 125 | 126 | if exists == winapi::FALSE { 127 | return None; 128 | } 129 | 130 | let table_bytes = slice::from_raw_parts(table_data_ptr, table_size as usize).to_vec(); 131 | 132 | (*self.native.get()).ReleaseFontTable(table_context); 133 | 134 | Some(table_bytes) 135 | } 136 | } 137 | 138 | pub fn get_recommended_rendering_mode(&self, 139 | em_size: f32, 140 | pixels_per_dip: f32, 141 | measure_mode: winapi::DWRITE_MEASURING_MODE, 142 | rendering_params: *mut winapi::IDWriteRenderingParams) -> 143 | winapi::DWRITE_RENDERING_MODE { 144 | unsafe { 145 | let mut render_mode : winapi::DWRITE_RENDERING_MODE = winapi::DWRITE_RENDERING_MODE_DEFAULT; 146 | let hr = (*self.native.get()).GetRecommendedRenderingMode(em_size, 147 | pixels_per_dip, 148 | measure_mode, 149 | rendering_params, 150 | &mut render_mode); 151 | 152 | if !(hr != 0) { 153 | return winapi::DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC; 154 | } 155 | 156 | render_mode 157 | } 158 | } 159 | 160 | pub fn get_recommended_rendering_mode_default_params(&self, 161 | em_size: f32, 162 | pixels_per_dip: f32, 163 | measure_mode: winapi::DWRITE_MEASURING_MODE) -> 164 | winapi::DWRITE_RENDERING_MODE { 165 | self.get_recommended_rendering_mode(em_size, 166 | pixels_per_dip, 167 | measure_mode, 168 | DefaultDWriteRenderParams()) 169 | } 170 | } 171 | --------------------------------------------------------------------------------