diff --git a/Cargo.toml b/Cargo.toml index 3cfa44f..8eee890 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "dwrote" description = "Lightweight binding to DirectWrite." repository = "https://github.com/servo/dwrote-rs" license = "MPL-2.0" -version = "0.4.2" +version = "0.5.0" authors = ["Vladimir Vukicevic "] [lib] diff --git a/src/com_helpers.rs b/src/com_helpers.rs index 609cbe6..a948f2d 100644 --- a/src/com_helpers.rs +++ b/src/com_helpers.rs @@ -48,7 +48,7 @@ macro_rules! implement_iunknown { let this = $typ::from_interface(This); let count = this.refcount.fetch_sub(1, atomic::Ordering::Release) - 1; if count == 0 { - FontFileStream::destroy(This); + <$typ as Com<$interface>>::destroy(This as *mut $interface); } count as ULONG } @@ -61,7 +61,7 @@ macro_rules! implement_iunknown { QueryInterface: { unsafe extern "system" fn QueryInterface(This: *mut IUnknown, riid: REFIID, - ppvObject: *mut *mut c_void) -> HRESULT { + ppvObject: *mut *mut $crate::winapi::ctypes::c_void) -> HRESULT { let this = if guid_equals!(*riid, $iuud) { mem::transmute(This) } else if guid_equals!(*riid, UuidOfIUnknown) { diff --git a/src/comptr.rs b/src/comptr.rs index 11a948e..6ddeae9 100644 --- a/src/comptr.rs +++ b/src/comptr.rs @@ -27,11 +27,11 @@ impl ComPtr { ComPtr { ptr: ptr } } - pub fn already_addrefed(ptr: *mut T) -> Self { + pub unsafe fn already_addrefed(ptr: *mut T) -> Self { ComPtr { ptr: ptr } } - pub fn getter_addrefs(&mut self) -> *mut *mut Q { + pub unsafe fn getter_addrefs(&mut self) -> *mut *mut Q { self.release(); return &mut self.ptr as *mut *mut _ as *mut *mut Q; } @@ -63,11 +63,9 @@ impl ComPtr { } } - pub fn release(&self) { - unsafe { - if !self.ptr.is_null() { - (*(self.ptr as *mut IUnknown)).Release(); - } + pub unsafe fn release(&self) { + if !self.ptr.is_null() { + (*(self.ptr as *mut IUnknown)).Release(); } } @@ -110,7 +108,9 @@ impl PartialEq for ComPtr { impl Drop for ComPtr { fn drop(&mut self) { - self.release(); + unsafe { + self.release(); + } } } diff --git a/src/font.rs b/src/font.rs index 77dd076..fa2c0a3 100644 --- a/src/font.rs +++ b/src/font.rs @@ -5,8 +5,12 @@ use std::cell::UnsafeCell; use comptr::ComPtr; -use winapi::um::dwrite::{IDWriteFontFace, IDWriteLocalizedStrings, IDWriteFont}; -use winapi::um::dwrite::IDWriteFontFamily; +use winapi::shared::minwindef::{BOOL, FALSE, TRUE}; +use winapi::shared::winerror::S_OK; +use winapi::um::dwrite::{DWRITE_FONT_METRICS, DWRITE_INFORMATIONAL_STRING_FULL_NAME, DWRITE_INFORMATIONAL_STRING_ID}; +use winapi::um::dwrite::{DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_CID_NAME}; +use winapi::um::dwrite::{DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_NAME, IDWriteFontFace}; +use winapi::um::dwrite::{IDWriteLocalizedStrings, IDWriteFont, IDWriteFontFamily}; use std::mem; use super::*; @@ -54,6 +58,12 @@ impl Font { } } + pub fn simulations(&self) -> FontSimulations { + unsafe { + mem::transmute::((*self.native.get()).GetSimulations()) + } + } + pub fn family_name(&self) -> String { unsafe { let mut family: ComPtr = ComPtr::new(); @@ -74,6 +84,23 @@ impl Font { } } + pub fn informational_string(&self, id: InformationalStringId) -> Option { + unsafe { + let mut names: ComPtr = ComPtr::new(); + let mut exists = FALSE; + let id = id as DWRITE_INFORMATIONAL_STRING_ID; + let hr = (*self.native.get()).GetInformationalStrings(id, + names.getter_addrefs(), + &mut exists); + assert!(hr == S_OK); + if exists == TRUE { + Some(get_locale_string(&mut names)) + } else { + None + } + } + } + pub fn create_font_face(&self) -> FontFace { // FIXME create_font_face should cache the FontFace and return it, // there's a 1:1 relationship @@ -84,4 +111,30 @@ impl Font { FontFace::take(face) } } + + pub fn metrics(&self) -> DWRITE_FONT_METRICS { + unsafe { + let mut metrics = mem::zeroed(); + (*self.native.get()).GetMetrics(&mut metrics); + metrics + } + } +} + +impl Clone for Font { + fn clone(&self) -> Font { + unsafe { + Font { + native: UnsafeCell::new((*self.native.get()).clone()), + } + } + } +} + +#[repr(u32)] +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum InformationalStringId { + FullName = DWRITE_INFORMATIONAL_STRING_FULL_NAME, + PostscriptName = DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_NAME, + PostscriptCidName = DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_CID_NAME, } diff --git a/src/font_collection.rs b/src/font_collection.rs index 463c45b..580ae05 100644 --- a/src/font_collection.rs +++ b/src/font_collection.rs @@ -4,11 +4,20 @@ use comptr::ComPtr; use winapi::um::dwrite::{IDWriteFontFamily, IDWriteFont, IDWriteFontCollection}; +use winapi::um::dwrite::{IDWriteFontCollectionLoader}; use winapi::shared::minwindef::{BOOL, FALSE}; +use winapi::shared::winerror::S_OK; use std::cell::UnsafeCell; +use std::mem; +use std::ptr; +use std::sync::atomic::{ATOMIC_USIZE_INIT, AtomicUsize, Ordering}; -use super::{DWriteFactory, FontFamily, Font, FontFace, FontDescriptor}; +use super::{CustomFontCollectionLoaderImpl, DWriteFactory, FontFamily, Font}; +use super::{FontFace, FontDescriptor}; use helpers::*; +use com_helpers::Com; + +static NEXT_ID: AtomicUsize = ATOMIC_USIZE_INIT; pub struct FontCollectionFamilyIterator { collection: ComPtr, @@ -56,6 +65,23 @@ impl FontCollection { } } + pub fn from_loader(collection_loader: ComPtr) -> FontCollection { + unsafe { + let factory = DWriteFactory(); + assert_eq!((*factory).RegisterFontCollectionLoader(collection_loader.clone().forget()), + S_OK); + let mut collection: ComPtr = ComPtr::new(); + let id = NEXT_ID.fetch_add(1, Ordering::SeqCst); + assert_eq!((*factory).CreateCustomFontCollection( + collection_loader.clone().forget(), + &id as *const usize as *const _, + mem::size_of::() as u32, + collection.getter_addrefs()), + S_OK); + FontCollection::take(collection) + } + } + pub unsafe fn as_ptr(&self) -> *mut IDWriteFontCollection { (*self.native.get()).as_ptr() } diff --git a/src/font_collection_impl.rs b/src/font_collection_impl.rs new file mode 100644 index 0000000..08efe2a --- /dev/null +++ b/src/font_collection_impl.rs @@ -0,0 +1,156 @@ +/* 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/. */ + +// A temporary custom font collection that exists solely for the face-to-font mapping to work. + +use std::mem; +use std::sync::atomic::AtomicUsize; +use winapi::ctypes::c_void; +use winapi::shared::guiddef::REFIID; +use winapi::shared::minwindef::{BOOL, FALSE, TRUE, ULONG}; +use winapi::shared::winerror::{E_INVALIDARG, S_OK}; +use winapi::um::dwrite::{IDWriteFactory, IDWriteFontCollectionLoader}; +use winapi::um::dwrite::{IDWriteFontCollectionLoaderVtbl, IDWriteFontFile, IDWriteFontFileEnumerator}; +use winapi::um::dwrite::{IDWriteFontFileEnumeratorVtbl}; +use winapi::um::unknwnbase::{IUnknown, IUnknownVtbl}; +use winapi::um::winnt::HRESULT; + +use com_helpers::{Com, UuidOfIUnknown}; +use comptr::ComPtr; +use FontFile; + +DEFINE_GUID! { + DWRITE_FONT_COLLECTION_LOADER_UUID, + 0x12345678, 0x1234, 0x5678, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0 +} +DEFINE_GUID! { + DWRITE_FONT_FILE_ENUMERATOR_UUID, + 0x12345678, 0x1234, 0x5678, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0 +} + +static FONT_COLLECTION_LOADER_VTBL: IDWriteFontCollectionLoaderVtbl = + IDWriteFontCollectionLoaderVtbl { + parent: implement_iunknown!(static IDWriteFontCollectionLoader, + DWRITE_FONT_COLLECTION_LOADER_UUID, + CustomFontCollectionLoaderImpl), + CreateEnumeratorFromKey: CustomFontCollectionLoaderImpl_CreateEnumeratorFromKey, +}; + +pub struct CustomFontCollectionLoaderImpl { + refcount: AtomicUsize, + font_files: Vec>, +} + +impl Com for CustomFontCollectionLoaderImpl { + type Vtbl = IDWriteFontCollectionLoaderVtbl; + #[inline] + fn vtbl() -> &'static IDWriteFontCollectionLoaderVtbl { + &FONT_COLLECTION_LOADER_VTBL + } +} + +impl Com for CustomFontCollectionLoaderImpl { + type Vtbl = IUnknownVtbl; + #[inline] + fn vtbl() -> &'static IUnknownVtbl { + &FONT_COLLECTION_LOADER_VTBL.parent + } +} + +impl CustomFontCollectionLoaderImpl { + pub fn new(font_files: &[FontFile]) -> ComPtr { + unsafe { + ComPtr::already_addrefed(CustomFontCollectionLoaderImpl { + refcount: AtomicUsize::new(1), + font_files: font_files.iter().map(|file| file.as_com_ptr()).collect(), + }.into_interface()) + } + } +} + +unsafe extern "system" fn CustomFontCollectionLoaderImpl_CreateEnumeratorFromKey( + this: *mut IDWriteFontCollectionLoader, + _: *mut IDWriteFactory, + _: *const c_void, + _: u32, + out_enumerator: *mut *mut IDWriteFontFileEnumerator) + -> HRESULT { + let this = CustomFontCollectionLoaderImpl::from_interface(this); + let enumerator = CustomFontFileEnumeratorImpl::new((*this).font_files.clone()); + let enumerator = ComPtr::::from_ptr(enumerator.into_interface()); + *out_enumerator = enumerator.as_ptr(); + mem::forget(enumerator); + S_OK +} + +struct CustomFontFileEnumeratorImpl { + refcount: AtomicUsize, + font_files: Vec>, + index: isize, +} + +impl Com for CustomFontFileEnumeratorImpl { + type Vtbl = IDWriteFontFileEnumeratorVtbl; + #[inline] + fn vtbl() -> &'static IDWriteFontFileEnumeratorVtbl { + &FONT_FILE_ENUMERATOR_VTBL + } +} + +impl Com for CustomFontFileEnumeratorImpl { + type Vtbl = IUnknownVtbl; + #[inline] + fn vtbl() -> &'static IUnknownVtbl { + &FONT_FILE_ENUMERATOR_VTBL.parent + } +} + +static FONT_FILE_ENUMERATOR_VTBL: IDWriteFontFileEnumeratorVtbl = IDWriteFontFileEnumeratorVtbl { + parent: implement_iunknown!(static IDWriteFontFileEnumerator, + DWRITE_FONT_FILE_ENUMERATOR_UUID, + CustomFontFileEnumeratorImpl), + GetCurrentFontFile: CustomFontFileEnumeratorImpl_GetCurrentFontFile, + MoveNext: CustomFontFileEnumeratorImpl_MoveNext, +}; + +impl CustomFontFileEnumeratorImpl { + pub fn new(font_files: Vec>) -> CustomFontFileEnumeratorImpl { + CustomFontFileEnumeratorImpl { + refcount: AtomicUsize::new(1), + font_files, + index: -1, + } + } +} + +unsafe extern "system" fn CustomFontFileEnumeratorImpl_GetCurrentFontFile( + this: *mut IDWriteFontFileEnumerator, + out_font_file: *mut *mut IDWriteFontFile) + -> HRESULT { + let this = CustomFontFileEnumeratorImpl::from_interface(this); + if (*this).index < 0 || (*this).index >= (*this).font_files.len() as isize { + return E_INVALIDARG + } + let new_font_file = (*this).font_files[(*this).index as usize].clone(); + *out_font_file = new_font_file.as_ptr(); + mem::forget(new_font_file); + S_OK +} + +unsafe extern "system" fn CustomFontFileEnumeratorImpl_MoveNext( + this: *mut IDWriteFontFileEnumerator, + has_current_file: *mut BOOL) + -> HRESULT { + let this = CustomFontFileEnumeratorImpl::from_interface(this); + let font_file_count = (*this).font_files.len() as isize; + if (*this).index < font_file_count { + (*this).index += 1 + } + *has_current_file = if (*this).index >= 0 && (*this).index < font_file_count { + TRUE + } else { + FALSE + }; + S_OK +} diff --git a/src/font_face.rs b/src/font_face.rs index 8e83d44..cfba7da 100644 --- a/src/font_face.rs +++ b/src/font_face.rs @@ -5,18 +5,24 @@ use std::slice; use std::ptr; use std::cell::UnsafeCell; -use std::mem::zeroed; +use std::mem::{self, zeroed}; +use com_helpers::Com; use comptr::ComPtr; +use font::Font; +use geometry_sink_impl::GeometrySinkImpl; +use outline_builder::OutlineBuilder; use super::{FontMetrics, FontFile, DefaultDWriteRenderParams, DWriteFactory}; -use winapi::um::dwrite::{DWRITE_RENDERING_MODE, DWRITE_RENDERING_MODE_DEFAULT}; -use winapi::um::dwrite::{DWRITE_FONT_METRICS, DWRITE_FONT_SIMULATIONS, DWRITE_MATRIX}; -use winapi::um::dwrite::{DWRITE_GLYPH_METRICS, DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC}; -use winapi::um::dwrite::{IDWriteRenderingParams, IDWriteFontFace, IDWriteFontFile}; -use winapi::shared::minwindef::{BOOL, FALSE}; use winapi::ctypes::c_void; +use winapi::shared::minwindef::{BOOL, FALSE, TRUE}; +use winapi::shared::winerror::S_OK; use winapi::um::dcommon::DWRITE_MEASURING_MODE; +use winapi::um::dwrite::{DWRITE_GLYPH_OFFSET, DWRITE_RENDERING_MODE, DWRITE_RENDERING_MODE_DEFAULT}; +use winapi::um::dwrite::{DWRITE_FONT_METRICS, DWRITE_FONT_SIMULATIONS, DWRITE_MATRIX}; +use winapi::um::dwrite::{DWRITE_GLYPH_METRICS, DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC}; +use winapi::um::dwrite::{IDWriteFontCollection, IDWriteFont, IDWriteFontFace, IDWriteFontFile}; +use winapi::um::dwrite::{IDWriteRenderingParams}; pub struct FontFace { native: UnsafeCell>, @@ -55,7 +61,7 @@ impl FontFace { pub fn get_files(&self) -> Vec { unsafe { let file_ptrs = self.get_raw_files(); - file_ptrs.iter().map(|p| FontFile::take(ComPtr::already_addrefed(*p))).collect() + file_ptrs.iter().map(|p| FontFile::take(ComPtr::from_ptr(*p))).collect() } } @@ -198,4 +204,61 @@ impl FontFace { measure_mode, DefaultDWriteRenderParams()) } + + pub fn get_glyph_run_outline(&self, + em_size: f32, + glyph_indices: &[u16], + glyph_advances: Option<&[f32]>, + glyph_offsets: Option<&[DWRITE_GLYPH_OFFSET]>, + is_sideways: bool, + is_right_to_left: bool, + outline_builder: Box) { + unsafe { + let glyph_advances = match glyph_advances { + None => ptr::null(), + Some(glyph_advances) => { + assert_eq!(glyph_advances.len(), glyph_indices.len()); + glyph_advances.as_ptr() + } + }; + let glyph_offsets = match glyph_offsets { + None => ptr::null(), + Some(glyph_offsets) => { + assert_eq!(glyph_offsets.len(), glyph_indices.len()); + glyph_offsets.as_ptr() + } + }; + let is_sideways = if is_sideways { TRUE } else { FALSE }; + let is_right_to_left = if is_right_to_left { TRUE } else { FALSE }; + let geometry_sink = GeometrySinkImpl::new(outline_builder); + let geometry_sink = geometry_sink.into_interface(); + let hr = (*self.native.get()).GetGlyphRunOutline(em_size, + glyph_indices.as_ptr(), + glyph_advances, + glyph_offsets, + glyph_indices.len() as u32, + is_sideways, + is_right_to_left, + geometry_sink); + assert_eq!(hr, S_OK); + } + } + + #[inline] + pub fn get_index(&self) -> u32 { + unsafe { + (*self.native.get()).GetIndex() + } + } +} + +impl Clone for FontFace { + fn clone(&self) -> FontFace { + unsafe { + FontFace { + native: UnsafeCell::new((*self.native.get()).clone()), + metrics: self.metrics, + } + } + } } diff --git a/src/font_family.rs b/src/font_family.rs index c6d8d28..1166f99 100644 --- a/src/font_family.rs +++ b/src/font_family.rs @@ -70,6 +70,7 @@ impl FontFamily { let mut font: ComPtr = ComPtr::new(); let hr = (*self.native.get()).GetFont(index, font.getter_addrefs()); assert!(hr == 0); + assert!(!font.as_ptr().is_null()); Font::take(font) } } diff --git a/src/font_file.rs b/src/font_file.rs index 859dade..86b3862 100644 --- a/src/font_file.rs +++ b/src/font_file.rs @@ -5,19 +5,25 @@ use std::slice; use std::ptr; use std::cell::UnsafeCell; +use std::path::PathBuf; +use std::ffi::OsString; +use std::os::windows::ffi::OsStringExt; use comptr::ComPtr; use winapi::ctypes::c_void; use winapi::um::dwrite::{IDWriteFontFace, IDWriteFontFile, IDWriteFontFileStream}; -use winapi::um::dwrite::{IDWriteFontFileLoader, DWRITE_FONT_SIMULATIONS}; -use winapi::um::dwrite::{DWRITE_FONT_FACE_TYPE_UNKNOWN, DWRITE_FONT_FACE_TYPE}; -use winapi::um::dwrite::DWRITE_FONT_FILE_TYPE_UNKNOWN; +use winapi::um::dwrite::{IDWriteFontFileLoader, IDWriteLocalFontFileLoader}; +use winapi::um::dwrite::{DWRITE_FONT_SIMULATIONS, DWRITE_FONT_FACE_TYPE_UNKNOWN}; +use winapi::um::dwrite::{DWRITE_FONT_FACE_TYPE, DWRITE_FONT_FILE_TYPE_UNKNOWN}; use font_file_loader_impl::DataFontHelper; use font_face::FontFace; use super::DWriteFactory; +DEFINE_GUID!{UuidOfIDWriteFontFileLoader, 0x727cad4e, 0xd6af, 0x4c9e, 0x8a, 0x08, 0xd6, 0x95, 0xb1, 0x1c, 0xaa, 0x49} +DEFINE_GUID!{UuidOfIDWriteLocalFontFileLoader, 0xb2d9f3ec, 0xc9fe, 0x4a11, 0xa2, 0xec, 0xd8, 0x62, 0x08, 0xf7, 0xc0, 0xa2} + pub struct FontFile { native: UnsafeCell>, data_key: usize, @@ -34,7 +40,7 @@ impl FontFile { face_type: DWRITE_FONT_FACE_TYPE_UNKNOWN, }; - if ff.analyze() == false { + if ff.analyze() == 0 { DataFontHelper::unregister_font_data(key); return None; } @@ -42,20 +48,35 @@ impl FontFile { Some(ff) } - fn analyze(&mut self) -> bool { + pub fn analyze_data(data: &[u8]) -> u32 { + let (font_file, key) = DataFontHelper::register_font_data(data); + + let mut ff = FontFile { + native: UnsafeCell::new(font_file), + data_key: key, + face_type: DWRITE_FONT_FACE_TYPE_UNKNOWN, + }; + + ff.analyze() + } + + fn analyze(&mut self) -> u32 { let mut face_type = DWRITE_FONT_FACE_TYPE_UNKNOWN; + let mut num_faces = 0; unsafe { let mut supported = 0; let mut _file_type = DWRITE_FONT_FILE_TYPE_UNKNOWN; - let mut _num_faces = 0; - let hr = (*self.as_ptr()).Analyze(&mut supported, &mut _file_type, &mut face_type, &mut _num_faces); + let hr = (*self.native.get()).Analyze(&mut supported, + &mut _file_type, + &mut face_type, + &mut num_faces); if hr != 0 || supported == 0 { - return false; + return 0 } } self.face_type = face_type; - true + num_faces as u32 } pub fn take(native: ComPtr) -> FontFile { @@ -76,8 +97,8 @@ impl FontFile { } } - pub unsafe fn as_ptr(&self) -> *mut IDWriteFontFile { - (*self.native.get()).as_ptr() + pub(crate) unsafe fn as_com_ptr(&self) -> ComPtr { + (*self.native.get()).clone() } // This is a helper to read the contents of this FontFile, @@ -116,14 +137,66 @@ impl FontFile { } } + // This is a helper to get the path of a font file, + // without requiring callers to deal with loaders. + pub fn get_font_file_path(&self) -> Option { + unsafe { + let mut ref_key: *const c_void = ptr::null(); + let mut ref_key_size: u32 = 0; + let hr = (*self.native.get()).GetReferenceKey(&mut ref_key, &mut ref_key_size); + assert!(hr == 0); + + let mut loader: ComPtr = ComPtr::new(); + let hr = (*self.native.get()).GetLoader(loader.getter_addrefs()); + assert!(hr == 0); + + let mut local_loader: ComPtr = + match loader.query_interface(&UuidOfIDWriteLocalFontFileLoader) { + Some(local_loader) => local_loader, + None => return None, + }; + + let mut file_path_len = 0; + let hr = local_loader.GetFilePathLengthFromKey(ref_key, + ref_key_size, + &mut file_path_len); + assert_eq!(hr, 0); + + let mut file_path_buf = vec![0; file_path_len as usize + 1]; + let hr = local_loader.GetFilePathFromKey(ref_key, + ref_key_size, + file_path_buf.as_mut_ptr(), + file_path_len + 1); + assert_eq!(hr, 0); + + if let Some(&0) = file_path_buf.last() { + file_path_buf.pop(); + } + + Some(PathBuf::from(OsString::from_wide(&file_path_buf))) + } + } + pub fn create_face(&self, face_index: u32, simulations: DWRITE_FONT_SIMULATIONS) -> FontFace { unsafe { let mut face: ComPtr = ComPtr::new(); - let ptr = self.as_ptr(); - let hr = (*DWriteFactory()).CreateFontFace(self.face_type, 1, &ptr, + let ptr = self.as_com_ptr(); + let hr = (*DWriteFactory()).CreateFontFace(self.face_type, 1, &ptr.as_ptr(), face_index, simulations, face.getter_addrefs()); assert!(hr == 0); FontFace::take(face) } } } + +impl Clone for FontFile { + fn clone(&self) -> FontFile { + unsafe { + FontFile { + native: UnsafeCell::new((*self.native.get()).clone()), + data_key: self.data_key, + face_type: self.face_type, + } + } + } +} diff --git a/src/font_file_loader_impl.rs b/src/font_file_loader_impl.rs index 9b1ed2e..d917dd4 100644 --- a/src/font_file_loader_impl.rs +++ b/src/font_file_loader_impl.rs @@ -2,6 +2,7 @@ use std::{mem, ptr}; use std::collections::HashMap; +use std::sync::atomic::AtomicUsize; use std::sync::{Mutex, atomic}; use std::marker::Send; use winapi::ctypes::c_void; @@ -48,6 +49,9 @@ const FontFileLoaderVtbl: &'static IDWriteFontFileLoaderVtbl = &IDWriteFontFileL } }; + // This is an addref getter, so make sure to do that! + (*stream).AddRef(); + *fontFileStream = stream; S_OK } @@ -133,7 +137,7 @@ const FontFileStreamVtbl: &'static IDWriteFontFileStreamVtbl = &IDWriteFontFileS impl FontFileStream { pub fn new(data: &[u8]) -> FontFileStream { FontFileStream { - refcount: atomic::ATOMIC_USIZE_INIT, + refcount: AtomicUsize::new(1), data: data.to_vec(), } } @@ -174,7 +178,9 @@ impl DataFontHelper { unsafe { let key = FONT_FILE_KEY.fetch_add(1, atomic::Ordering::Relaxed); let font_file_stream_native = FontFileStream::new(font_data); - let font_file_stream = ComPtr::from_ptr(font_file_stream_native.into_interface()); + let font_file_stream: ComPtr = + ComPtr::from_ptr(font_file_stream_native.into_interface()); + { let mut map = FONT_FILE_STREAM_MAP.lock().unwrap(); map.insert(key, font_file_stream); diff --git a/src/geometry_sink_impl.rs b/src/geometry_sink_impl.rs new file mode 100644 index 0000000..6ae917b --- /dev/null +++ b/src/geometry_sink_impl.rs @@ -0,0 +1,113 @@ +#![allow(non_snake_case, non_upper_case_globals)] + +use std::mem; +use std::os::raw::c_void; +use std::slice; +use std::sync::atomic::AtomicUsize; +use winapi::shared::guiddef::REFIID; +use winapi::shared::minwindef::{UINT, ULONG}; +use winapi::shared::winerror::S_OK; +use winapi::um::d2d1::{D2D1_BEZIER_SEGMENT, D2D1_FIGURE_BEGIN, D2D1_FIGURE_END, D2D1_FIGURE_END_CLOSED}; +use winapi::um::d2d1::{D2D1_FILL_MODE, D2D1_PATH_SEGMENT, D2D1_POINT_2F}; +use winapi::um::d2d1::{ID2D1SimplifiedGeometrySink, ID2D1SimplifiedGeometrySinkVtbl}; +use winapi::um::unknwnbase::{IUnknown, IUnknownVtbl}; +use winapi::um::winnt::HRESULT; + +use com_helpers::{Com, UuidOfIUnknown}; +use comptr::ComPtr; +use outline_builder::OutlineBuilder; + +DEFINE_GUID!{ + D2D1_SIMPLIFIED_GEOMETRY_SINK_UUID, + 0x2cd9069e, 0x12e2, 0x11dc, 0x9f, 0xed, 0x00, 0x11, 0x43, 0xa0, 0x55, 0xf9 +} + +static GEOMETRY_SINK_VTBL: ID2D1SimplifiedGeometrySinkVtbl = ID2D1SimplifiedGeometrySinkVtbl { + parent: implement_iunknown!(static ID2D1SimplifiedGeometrySink, + D2D1_SIMPLIFIED_GEOMETRY_SINK_UUID, + GeometrySinkImpl), + BeginFigure: GeometrySinkImpl_BeginFigure, + EndFigure: GeometrySinkImpl_EndFigure, + AddLines: GeometrySinkImpl_AddLines, + AddBeziers: GeometrySinkImpl_AddBeziers, + Close: GeometrySinkImpl_Close, + SetFillMode: GeometrySinkImpl_SetFillMode, + SetSegmentFlags: GeometrySinkImpl_SetSegmentFlags, +}; + +pub struct GeometrySinkImpl { + refcount: AtomicUsize, + outline_builder: Box, +} + +impl Com for GeometrySinkImpl { + type Vtbl = ID2D1SimplifiedGeometrySinkVtbl; + #[inline] + fn vtbl() -> &'static ID2D1SimplifiedGeometrySinkVtbl { + &GEOMETRY_SINK_VTBL + } +} + +impl Com for GeometrySinkImpl { + type Vtbl = IUnknownVtbl; + #[inline] + fn vtbl() -> &'static IUnknownVtbl { + &GEOMETRY_SINK_VTBL.parent + } +} + +impl GeometrySinkImpl { + pub fn new(outline_builder: Box) -> GeometrySinkImpl { + GeometrySinkImpl { + refcount: AtomicUsize::new(1), + outline_builder, + } + } +} + +unsafe extern "system" fn GeometrySinkImpl_BeginFigure(this: *mut ID2D1SimplifiedGeometrySink, + start_point: D2D1_POINT_2F, + _: D2D1_FIGURE_BEGIN) { + let this = GeometrySinkImpl::from_interface(this); + (*this).outline_builder.move_to(start_point.x, start_point.y) +} + +unsafe extern "system" fn GeometrySinkImpl_EndFigure(this: *mut ID2D1SimplifiedGeometrySink, + figure_end: D2D1_FIGURE_END) { + let this = GeometrySinkImpl::from_interface(this); + if figure_end == D2D1_FIGURE_END_CLOSED { + (*this).outline_builder.close() + } +} + +unsafe extern "system" fn GeometrySinkImpl_AddLines(this: *mut ID2D1SimplifiedGeometrySink, + points: *const D2D1_POINT_2F, + points_count: UINT) { + let this = GeometrySinkImpl::from_interface(this); + let points = slice::from_raw_parts(points, points_count as usize); + for point in points { + (*this).outline_builder.line_to(point.x, point.y) + } +} + +unsafe extern "system" fn GeometrySinkImpl_AddBeziers(this: *mut ID2D1SimplifiedGeometrySink, + beziers: *const D2D1_BEZIER_SEGMENT, + beziers_count: UINT) { + let this = GeometrySinkImpl::from_interface(this); + let beziers = slice::from_raw_parts(beziers, beziers_count as usize); + for bezier in beziers { + (*this).outline_builder.curve_to(bezier.point1.x, bezier.point1.y, + bezier.point2.x, bezier.point2.y, + bezier.point3.x, bezier.point3.y) + } +} + +unsafe extern "system" fn GeometrySinkImpl_Close(_: *mut ID2D1SimplifiedGeometrySink) -> HRESULT { + S_OK +} + +unsafe extern "system" fn GeometrySinkImpl_SetFillMode(_: *mut ID2D1SimplifiedGeometrySink, + _: D2D1_FILL_MODE) {} + +unsafe extern "system" fn GeometrySinkImpl_SetSegmentFlags(_: *mut ID2D1SimplifiedGeometrySink, + _: D2D1_PATH_SEGMENT) {} diff --git a/src/lib.rs b/src/lib.rs index 1d8554e..9e392b8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -68,12 +68,13 @@ use winapi::um::libloaderapi::{GetProcAddress, LoadLibraryW}; #[macro_use] mod com_helpers; mod bitmap_render_target; pub use bitmap_render_target::BitmapRenderTarget; -mod font; pub use font::Font; +mod font; pub use font::{Font, InformationalStringId}; mod font_collection; pub use font_collection::FontCollection; mod font_face; pub use font_face::FontFace; mod font_family; pub use font_family::FontFamily; mod font_file; pub use font_file::FontFile; mod gdi_interop; pub use gdi_interop::GdiInterop; +mod outline_builder; pub use outline_builder::OutlineBuilder; mod rendering_params; pub use rendering_params::RenderingParams; mod glyph_run_analysis; pub use glyph_run_analysis::GlyphRunAnalysis; @@ -82,6 +83,13 @@ mod glyph_run_analysis; pub use glyph_run_analysis::GlyphRunAnalysis; // related things. mod font_file_loader_impl; +// This is an implementation of `FontCollectionLoader` for client code. +mod font_collection_impl; pub use font_collection_impl::CustomFontCollectionLoaderImpl; + +// This is an internal implementation of `GeometrySink` so that we can +// expose `IDWriteGeometrySink` in an idiomatic way. +mod geometry_sink_impl; + DEFINE_GUID!{UuidOfIDWriteFactory, 0xb859ee5a, 0xd838, 0x4b5b, 0xa2, 0xe8, 0x1a, 0xdc, 0x7d, 0x93, 0xdb, 0x48} unsafe impl Sync for ComPtr { } diff --git a/src/outline_builder.rs b/src/outline_builder.rs new file mode 100644 index 0000000..7e7716b --- /dev/null +++ b/src/outline_builder.rs @@ -0,0 +1,6 @@ +pub trait OutlineBuilder { + fn move_to(&mut self, x: f32, y: f32); + fn line_to(&mut self, x: f32, y: f32); + fn curve_to(&mut self, cp0x: f32, cp0y: f32, cp1x: f32, cp1y: f32, x: f32, y: f32); + fn close(&mut self); +} diff --git a/src/types.rs b/src/types.rs index 5d5e8d3..050eed5 100644 --- a/src/types.rs +++ b/src/types.rs @@ -102,6 +102,17 @@ impl FontStyle { pub fn from_u32(v: u32) -> FontStyle { unsafe { mem::transmute::(v) } } } +// mirrors DWRITE_FONT_SIMULATIONS +#[repr(u32)] +#[derive(PartialEq, Debug, Clone, Copy)] +pub enum FontSimulations { + None = winapi::um::dwrite::DWRITE_FONT_SIMULATIONS_NONE, + Bold = winapi::um::dwrite::DWRITE_FONT_SIMULATIONS_BOLD, + Oblique = winapi::um::dwrite::DWRITE_FONT_SIMULATIONS_OBLIQUE, + BoldOblique = winapi::um::dwrite::DWRITE_FONT_SIMULATIONS_BOLD | + winapi::um::dwrite::DWRITE_FONT_SIMULATIONS_OBLIQUE, +} + #[derive(Deserialize, Serialize, PartialEq, Debug, Clone)] pub struct FontDescriptor { pub family_name: String,