├── .gitignore ├── src ├── outline_builder.rs ├── rendering_params.rs ├── gdi_interop.rs ├── helpers.rs ├── text_analysis_source.rs ├── font_fallback.rs ├── font_family.rs ├── glyph_run_analysis.rs ├── geometry_sink_impl.rs ├── types.rs ├── com_helpers.rs ├── bitmap_render_target.rs ├── font_collection_impl.rs ├── test.rs ├── lib.rs ├── font_collection.rs ├── font_file_loader_impl.rs ├── font.rs ├── text_analysis_source_impl.rs ├── font_file.rs └── font_face.rs ├── README.md ├── Cargo.toml └── .github └── workflows └── main.yml /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | *~ 4 | #~# 5 | 6 | -------------------------------------------------------------------------------- /src/outline_builder.rs: -------------------------------------------------------------------------------- 1 | pub trait OutlineBuilder { 2 | fn move_to(&mut self, x: f32, y: f32); 3 | fn line_to(&mut self, x: f32, y: f32); 4 | fn curve_to(&mut self, cp0x: f32, cp0y: f32, cp1x: f32, cp1y: f32, x: f32, y: f32); 5 | fn close(&mut self); 6 | } 7 | -------------------------------------------------------------------------------- /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/servo/dwrote-rs" 5 | license = "MPL-2.0" 6 | version = "0.11.5" 7 | authors = ["The Servo Project Developers", "Vladimir Vukicevic "] 8 | edition = "2018" 9 | 10 | [lib] 11 | name = "dwrote" 12 | 13 | [features] 14 | default = ["serde_serialization"] 15 | serde_serialization = ["serde", "serde_derive"] 16 | 17 | [dependencies] 18 | libc = "0.2" 19 | lazy_static = "1" 20 | winapi = { version = "0.3.6", features = ["dwrite", "dwrite_1", "dwrite_3", "winnt", "unknwnbase", "libloaderapi", "winnls"] } 21 | serde = { version = "1.0", optional = true } 22 | serde_derive = { version = "1.0", optional = true } 23 | wio = "0.2" 24 | 25 | [package.metadata.docs.rs] 26 | targets = ["x86_64-pc-windows-msvc"] 27 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | workflow_dispatch: 8 | 9 | jobs: 10 | windows-ci: 11 | name: Windows 12 | runs-on: windows-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v4 16 | 17 | - name: Install stable toolchain 18 | uses: dtolnay/rust-toolchain@stable 19 | 20 | - name: Cargo build 21 | run: cargo build --no-default-features 22 | 23 | - name: Cargo test 24 | run: cargo test --verbose 25 | env: 26 | RUST_BACKTRACE: 1 27 | 28 | build_result: 29 | name: homu build finished 30 | runs-on: ubuntu-latest 31 | needs: 32 | - "windows-ci" 33 | 34 | steps: 35 | - name: Mark the job as successful 36 | run: exit 0 37 | if: success() 38 | - name: Mark the job as unsuccessful 39 | run: exit 1 40 | if: "!success()" 41 | -------------------------------------------------------------------------------- /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 | use std::ptr; 7 | use winapi::um::dwrite::IDWriteRenderingParams; 8 | use wio::com::ComPtr; 9 | 10 | use super::DWriteFactory; 11 | 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: *mut IDWriteRenderingParams = ptr::null_mut(); 20 | let hr = (*DWriteFactory()).CreateRenderingParams(&mut native); 21 | assert!(hr == 0); 22 | RenderingParams::take(ComPtr::from_raw(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 IDWriteRenderingParams { 33 | (*self.native.get()).as_raw() 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::cell::UnsafeCell; 6 | use std::ptr; 7 | use winapi::um::dwrite::IDWriteBitmapRenderTarget; 8 | use winapi::um::dwrite::IDWriteGdiInterop; 9 | use wio::com::ComPtr; 10 | 11 | use super::{BitmapRenderTarget, DWriteFactory}; 12 | 13 | pub struct GdiInterop { 14 | native: UnsafeCell>, 15 | } 16 | 17 | impl GdiInterop { 18 | pub fn create() -> GdiInterop { 19 | unsafe { 20 | let mut native: *mut IDWriteGdiInterop = ptr::null_mut(); 21 | let hr = (*DWriteFactory()).GetGdiInterop(&mut native); 22 | assert!(hr == 0); 23 | GdiInterop::take(ComPtr::from_raw(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: *mut IDWriteBitmapRenderTarget = ptr::null_mut(); 36 | let hr = (*self.native.get()).CreateBitmapRenderTarget( 37 | ptr::null_mut(), 38 | width, 39 | height, 40 | &mut native, 41 | ); 42 | assert!(hr == 0); 43 | BitmapRenderTarget::take(ComPtr::from_raw(native)) 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /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 std::ffi::OsStr; 6 | use std::os::windows::ffi::OsStrExt; 7 | use winapi::ctypes::wchar_t; 8 | use winapi::shared::minwindef::{BOOL, FALSE}; 9 | use winapi::shared::winerror::S_OK; 10 | use winapi::um::dwrite::IDWriteLocalizedStrings; 11 | use winapi::um::winnls::GetUserDefaultLocaleName; 12 | use wio::com::ComPtr; 13 | 14 | lazy_static! { 15 | static ref SYSTEM_LOCALE: Vec = { 16 | unsafe { 17 | let mut locale: Vec = vec![0; 85]; 18 | GetUserDefaultLocaleName(locale.as_mut_ptr(), locale.len() as i32 - 1); 19 | locale 20 | } 21 | }; 22 | static ref EN_US_LOCALE: Vec = OsStr::new("en-us").to_wide_null(); 23 | } 24 | 25 | pub fn get_locale_string(strings: &mut ComPtr) -> String { 26 | unsafe { 27 | let mut index: u32 = 0; 28 | let mut exists: BOOL = FALSE; 29 | let hr = strings.FindLocaleName((*SYSTEM_LOCALE).as_ptr(), &mut index, &mut exists); 30 | if hr != S_OK || exists == FALSE { 31 | let hr = strings.FindLocaleName((*EN_US_LOCALE).as_ptr(), &mut index, &mut exists); 32 | if hr != S_OK || exists == FALSE { 33 | // Ultimately fall back to first locale on list 34 | index = 0; 35 | } 36 | } 37 | 38 | let mut length: u32 = 0; 39 | let hr = strings.GetStringLength(index, &mut length); 40 | assert!(hr == 0); 41 | 42 | let mut name: Vec = Vec::with_capacity(length as usize + 1); 43 | let hr = strings.GetString(index, name.as_mut_ptr(), length + 1); 44 | assert!(hr == 0); 45 | name.set_len(length as usize); 46 | 47 | String::from_utf16(&name).ok().unwrap() 48 | } 49 | } 50 | 51 | // ToWide from https://github.com/retep998/wio-rs/blob/master/src/wide.rs 52 | 53 | pub trait ToWide { 54 | fn to_wide_null(&self) -> Vec; 55 | } 56 | 57 | impl ToWide for T 58 | where 59 | T: AsRef, 60 | { 61 | fn to_wide_null(&self) -> Vec { 62 | self.as_ref().encode_wide().chain(Some(0)).collect() 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/text_analysis_source.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::borrow::Cow; 6 | use std::marker::PhantomData; 7 | use winapi::ctypes::wchar_t; 8 | use winapi::um::dwrite::IDWriteTextAnalysisSource; 9 | use wio::com::ComPtr; 10 | 11 | use super::*; 12 | use crate::com_helpers::Com; 13 | 14 | pub struct TextAnalysisSource<'a> { 15 | native: ComPtr, 16 | phantom: PhantomData>, 17 | } 18 | 19 | impl<'a> TextAnalysisSource<'a> { 20 | /// Create a new custom TextAnalysisSource for the given text and a trait 21 | /// implementation. 22 | /// 23 | /// Note: this method has no NumberSubsitution specified. See 24 | /// `from_text_and_number_subst` if you need number substitution. 25 | pub fn from_text( 26 | inner: Box, 27 | text: Cow<'a, [wchar_t]>, 28 | ) -> TextAnalysisSource<'a> { 29 | let native = unsafe { 30 | ComPtr::from_raw( 31 | CustomTextAnalysisSourceImpl::from_text_native(inner, text).into_interface(), 32 | ) 33 | }; 34 | TextAnalysisSource { 35 | native, 36 | phantom: PhantomData, 37 | } 38 | } 39 | 40 | /// Create a new custom TextAnalysisSource for the given text and a trait 41 | /// implementation. 42 | /// 43 | /// Note: this method only supports a single `NumberSubstitution` for the 44 | /// entire string. 45 | pub fn from_text_and_number_subst( 46 | inner: Box, 47 | text: Cow<'a, [wchar_t]>, 48 | number_subst: NumberSubstitution, 49 | ) -> TextAnalysisSource<'a> { 50 | let native = unsafe { 51 | ComPtr::from_raw( 52 | CustomTextAnalysisSourceImpl::from_text_and_number_subst_native( 53 | inner, 54 | text, 55 | number_subst, 56 | ) 57 | .into_interface(), 58 | ) 59 | }; 60 | TextAnalysisSource { 61 | native, 62 | phantom: PhantomData, 63 | } 64 | } 65 | 66 | pub fn as_ptr(&self) -> *mut IDWriteTextAnalysisSource { 67 | self.native.as_raw() 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/font_fallback.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 | use std::ptr::null_mut; 7 | use winapi::um::dwrite_2::{IDWriteFactory2, IDWriteFontFallback}; 8 | use wio::com::ComPtr; 9 | 10 | use super::*; 11 | 12 | pub struct FontFallback { 13 | native: UnsafeCell>, 14 | } 15 | 16 | pub struct FallbackResult { 17 | /// Length of mapped substring, in utf-16 code units. 18 | pub mapped_length: usize, 19 | /// The font that should be used to render the substring. 20 | pub mapped_font: Option, 21 | /// The scale factor to apply. 22 | pub scale: f32, 23 | } 24 | 25 | impl FontFallback { 26 | pub fn get_system_fallback() -> Option { 27 | unsafe { 28 | let factory = ComPtr::from_raw(DWriteFactory()); 29 | let factory2: Option> = factory.cast().ok(); 30 | std::mem::forget(factory); 31 | let factory2 = factory2?; 32 | let mut native = null_mut(); 33 | let hr = factory2.GetSystemFontFallback(&mut native); 34 | assert_eq!(hr, 0); 35 | Some(Self::take(ComPtr::from_raw(native))) 36 | } 37 | } 38 | 39 | pub fn take(native: ComPtr) -> FontFallback { 40 | FontFallback { 41 | native: UnsafeCell::new(native), 42 | } 43 | } 44 | 45 | // TODO: I'm following crate conventions for unsafe, but it's bullshit 46 | pub unsafe fn as_ptr(&self) -> *mut IDWriteFontFallback { 47 | (*self.native.get()).as_raw() 48 | } 49 | 50 | // TODO: map_characters (main function) 51 | pub fn map_characters( 52 | &self, 53 | text_analysis_source: &TextAnalysisSource, 54 | text_position: u32, 55 | text_length: u32, 56 | base_font: &FontCollection, 57 | base_family: Option<&str>, 58 | base_weight: FontWeight, 59 | base_style: FontStyle, 60 | base_stretch: FontStretch, 61 | ) -> FallbackResult { 62 | unsafe { 63 | let mut font = null_mut(); 64 | let mut mapped_length = 0; 65 | let mut scale = 0.0; 66 | let hr = (*self.as_ptr()).MapCharacters( 67 | text_analysis_source.as_ptr(), 68 | text_position, 69 | text_length, 70 | base_font.as_ptr(), 71 | base_family 72 | .map(|s| s.to_wide_null().as_mut_ptr()) 73 | .unwrap_or(null_mut()), 74 | base_weight.t(), 75 | base_style.t(), 76 | base_stretch.t(), 77 | &mut mapped_length, 78 | &mut font, 79 | &mut scale, 80 | ); 81 | assert_eq!(hr, 0); 82 | let mapped_font = if font.is_null() { 83 | None 84 | } else { 85 | Some(Font::take(ComPtr::from_raw(font))) 86 | }; 87 | FallbackResult { 88 | mapped_length: mapped_length as usize, 89 | mapped_font, 90 | scale, 91 | } 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /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 | use std::ptr; 7 | use winapi::um::dwrite::IDWriteLocalizedStrings; 8 | use winapi::um::dwrite::{IDWriteFont, IDWriteFontCollection, IDWriteFontFamily}; 9 | use wio::com::ComPtr; 10 | 11 | use super::*; 12 | use helpers::*; 13 | 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 IDWriteFontFamily { 26 | (*self.native.get()).as_raw() 27 | } 28 | 29 | #[deprecated(note = "Use `family_name` instead.")] 30 | pub fn name(&self) -> String { 31 | self.family_name().unwrap() 32 | } 33 | 34 | pub fn family_name(&self) -> Result { 35 | let mut family_names: *mut IDWriteLocalizedStrings = ptr::null_mut(); 36 | unsafe { 37 | let hr = (*self.native.get()).GetFamilyNames(&mut family_names); 38 | if hr != 0 { 39 | return Err(hr); 40 | } 41 | Ok(get_locale_string(&mut ComPtr::from_raw(family_names))) 42 | } 43 | } 44 | 45 | #[deprecated(note = "Use `first_matching_font` instead.")] 46 | pub fn get_first_matching_font( 47 | &self, 48 | weight: FontWeight, 49 | stretch: FontStretch, 50 | style: FontStyle, 51 | ) -> Font { 52 | self.first_matching_font(weight, stretch, style).unwrap() 53 | } 54 | 55 | pub fn first_matching_font( 56 | &self, 57 | weight: FontWeight, 58 | stretch: FontStretch, 59 | style: FontStyle, 60 | ) -> Result { 61 | let mut font: *mut IDWriteFont = ptr::null_mut(); 62 | unsafe { 63 | let hr = (*self.native.get()).GetFirstMatchingFont( 64 | weight.t(), 65 | stretch.t(), 66 | style.t(), 67 | &mut font, 68 | ); 69 | if hr != 0 { 70 | return Err(hr); 71 | } 72 | Ok(Font::take(ComPtr::from_raw(font))) 73 | } 74 | } 75 | 76 | #[deprecated(note = "Use `font_collection` instead.")] 77 | pub fn get_font_collection(&self) -> FontCollection { 78 | self.font_collection().unwrap() 79 | } 80 | 81 | pub fn font_collection(&self) -> Result { 82 | let mut collection: *mut IDWriteFontCollection = ptr::null_mut(); 83 | unsafe { 84 | let hr = (*self.native.get()).GetFontCollection(&mut collection); 85 | if hr != 0 { 86 | return Err(hr); 87 | } 88 | Ok(FontCollection::take(ComPtr::from_raw(collection))) 89 | } 90 | } 91 | 92 | pub fn get_font_count(&self) -> u32 { 93 | unsafe { (*self.native.get()).GetFontCount() } 94 | } 95 | 96 | #[deprecated(note = "Use `font` instead.")] 97 | pub fn get_font(&self, index: u32) -> Font { 98 | self.font(index).unwrap() 99 | } 100 | 101 | pub fn font(&self, index: u32) -> Result { 102 | let mut font: *mut IDWriteFont = ptr::null_mut(); 103 | unsafe { 104 | let hr = (*self.native.get()).GetFont(index, &mut font); 105 | if hr != 0 { 106 | return Err(hr); 107 | } 108 | Ok(Font::take(ComPtr::from_raw(font))) 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /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::cell::UnsafeCell; 6 | use std::mem; 7 | use std::ptr; 8 | use winapi::shared::windef::RECT; 9 | use winapi::um::dcommon::DWRITE_MEASURING_MODE; 10 | use winapi::um::dwrite::DWRITE_TEXTURE_CLEARTYPE_3x1; 11 | use winapi::um::dwrite::IDWriteGlyphRunAnalysis; 12 | use winapi::um::dwrite::{DWRITE_TEXTURE_ALIASED_1x1, DWRITE_GLYPH_RUN, DWRITE_TEXTURE_TYPE}; 13 | use winapi::um::dwrite::{DWRITE_MATRIX, DWRITE_RENDERING_MODE}; 14 | use winapi::um::winnt::HRESULT; 15 | use wio::com::ComPtr; 16 | 17 | use super::DWriteFactory; 18 | 19 | pub struct GlyphRunAnalysis { 20 | native: UnsafeCell>, 21 | } 22 | 23 | impl GlyphRunAnalysis { 24 | pub fn create( 25 | glyph_run: &DWRITE_GLYPH_RUN, 26 | pixels_per_dip: f32, 27 | transform: Option, 28 | rendering_mode: DWRITE_RENDERING_MODE, 29 | measuring_mode: DWRITE_MEASURING_MODE, 30 | baseline_x: f32, 31 | baseline_y: f32, 32 | ) -> Result { 33 | unsafe { 34 | let mut native: *mut IDWriteGlyphRunAnalysis = ptr::null_mut(); 35 | let hr = (*DWriteFactory()).CreateGlyphRunAnalysis( 36 | glyph_run as *const DWRITE_GLYPH_RUN, 37 | pixels_per_dip, 38 | transform 39 | .as_ref() 40 | .map(|x| x as *const _) 41 | .unwrap_or(ptr::null()), 42 | rendering_mode, 43 | measuring_mode, 44 | baseline_x, 45 | baseline_y, 46 | &mut native, 47 | ); 48 | if hr != 0 { 49 | Err(hr) 50 | } else { 51 | Ok(GlyphRunAnalysis::take(ComPtr::from_raw(native))) 52 | } 53 | } 54 | } 55 | 56 | pub fn take(native: ComPtr) -> GlyphRunAnalysis { 57 | GlyphRunAnalysis { 58 | native: UnsafeCell::new(native), 59 | } 60 | } 61 | 62 | pub fn get_alpha_texture_bounds( 63 | &self, 64 | texture_type: DWRITE_TEXTURE_TYPE, 65 | ) -> Result { 66 | unsafe { 67 | let mut rect: RECT = mem::zeroed(); 68 | rect.left = 1234; 69 | rect.top = 1234; 70 | let hr = (*self.native.get()).GetAlphaTextureBounds(texture_type, &mut rect); 71 | if hr != 0 { 72 | Err(hr) 73 | } else { 74 | Ok(rect) 75 | } 76 | } 77 | } 78 | 79 | pub fn create_alpha_texture( 80 | &self, 81 | texture_type: DWRITE_TEXTURE_TYPE, 82 | rect: RECT, 83 | ) -> Result, HRESULT> { 84 | unsafe { 85 | let rect_pixels = (rect.right - rect.left) * (rect.bottom - rect.top); 86 | let rect_bytes = rect_pixels 87 | * match texture_type { 88 | DWRITE_TEXTURE_ALIASED_1x1 => 1, 89 | DWRITE_TEXTURE_CLEARTYPE_3x1 => 3, 90 | _ => panic!("bad texture type specified"), 91 | }; 92 | 93 | let mut out_bytes: Vec = vec![0; rect_bytes as usize]; 94 | let hr = (*self.native.get()).CreateAlphaTexture( 95 | texture_type, 96 | &rect, 97 | out_bytes.as_mut_ptr(), 98 | out_bytes.len() as u32, 99 | ); 100 | if hr != 0 { 101 | Err(hr) 102 | } else { 103 | Ok(out_bytes) 104 | } 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/geometry_sink_impl.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case, non_upper_case_globals)] 2 | 3 | use std::mem; 4 | use std::slice; 5 | use std::sync::atomic::AtomicUsize; 6 | use winapi::shared::guiddef::REFIID; 7 | use winapi::shared::minwindef::{UINT, ULONG}; 8 | use winapi::shared::winerror::S_OK; 9 | use winapi::um::d2d1::{ID2D1SimplifiedGeometrySink, ID2D1SimplifiedGeometrySinkVtbl}; 10 | use winapi::um::d2d1::{D2D1_BEZIER_SEGMENT, D2D1_FIGURE_BEGIN, D2D1_FIGURE_END}; 11 | use winapi::um::d2d1::{D2D1_FIGURE_END_CLOSED, D2D1_FILL_MODE, D2D1_PATH_SEGMENT, D2D1_POINT_2F}; 12 | use winapi::um::unknwnbase::{IUnknown, IUnknownVtbl}; 13 | use winapi::um::winnt::HRESULT; 14 | 15 | use crate::com_helpers::Com; 16 | use crate::outline_builder::OutlineBuilder; 17 | 18 | static GEOMETRY_SINK_VTBL: ID2D1SimplifiedGeometrySinkVtbl = ID2D1SimplifiedGeometrySinkVtbl { 19 | parent: implement_iunknown!(static ID2D1SimplifiedGeometrySink, GeometrySinkImpl), 20 | BeginFigure: GeometrySinkImpl_BeginFigure, 21 | EndFigure: GeometrySinkImpl_EndFigure, 22 | AddLines: GeometrySinkImpl_AddLines, 23 | AddBeziers: GeometrySinkImpl_AddBeziers, 24 | Close: GeometrySinkImpl_Close, 25 | SetFillMode: GeometrySinkImpl_SetFillMode, 26 | SetSegmentFlags: GeometrySinkImpl_SetSegmentFlags, 27 | }; 28 | 29 | #[repr(C)] 30 | pub struct GeometrySinkImpl { 31 | // NB: This must be the first field. 32 | _refcount: AtomicUsize, 33 | outline_builder: Box, 34 | } 35 | 36 | impl Com for GeometrySinkImpl { 37 | type Vtbl = ID2D1SimplifiedGeometrySinkVtbl; 38 | #[inline] 39 | fn vtbl() -> &'static ID2D1SimplifiedGeometrySinkVtbl { 40 | &GEOMETRY_SINK_VTBL 41 | } 42 | } 43 | 44 | impl Com for GeometrySinkImpl { 45 | type Vtbl = IUnknownVtbl; 46 | #[inline] 47 | fn vtbl() -> &'static IUnknownVtbl { 48 | &GEOMETRY_SINK_VTBL.parent 49 | } 50 | } 51 | 52 | impl GeometrySinkImpl { 53 | pub fn new(outline_builder: Box) -> GeometrySinkImpl { 54 | GeometrySinkImpl { 55 | _refcount: AtomicUsize::new(1), 56 | outline_builder, 57 | } 58 | } 59 | } 60 | 61 | unsafe extern "system" fn GeometrySinkImpl_BeginFigure( 62 | this: *mut ID2D1SimplifiedGeometrySink, 63 | start_point: D2D1_POINT_2F, 64 | _: D2D1_FIGURE_BEGIN, 65 | ) { 66 | let this = GeometrySinkImpl::from_interface(this); 67 | this 68 | .outline_builder 69 | .move_to(start_point.x, start_point.y) 70 | } 71 | 72 | unsafe extern "system" fn GeometrySinkImpl_EndFigure( 73 | this: *mut ID2D1SimplifiedGeometrySink, 74 | figure_end: D2D1_FIGURE_END, 75 | ) { 76 | let this = GeometrySinkImpl::from_interface(this); 77 | if figure_end == D2D1_FIGURE_END_CLOSED { 78 | this.outline_builder.close() 79 | } 80 | } 81 | 82 | unsafe extern "system" fn GeometrySinkImpl_AddLines( 83 | this: *mut ID2D1SimplifiedGeometrySink, 84 | points: *const D2D1_POINT_2F, 85 | points_count: UINT, 86 | ) { 87 | let this = GeometrySinkImpl::from_interface(this); 88 | let points = slice::from_raw_parts(points, points_count as usize); 89 | for point in points { 90 | this.outline_builder.line_to(point.x, point.y) 91 | } 92 | } 93 | 94 | unsafe extern "system" fn GeometrySinkImpl_AddBeziers( 95 | this: *mut ID2D1SimplifiedGeometrySink, 96 | beziers: *const D2D1_BEZIER_SEGMENT, 97 | beziers_count: UINT, 98 | ) { 99 | let this = GeometrySinkImpl::from_interface(this); 100 | let beziers = slice::from_raw_parts(beziers, beziers_count as usize); 101 | for bezier in beziers { 102 | this.outline_builder.curve_to( 103 | bezier.point1.x, 104 | bezier.point1.y, 105 | bezier.point2.x, 106 | bezier.point2.y, 107 | bezier.point3.x, 108 | bezier.point3.y, 109 | ) 110 | } 111 | } 112 | 113 | unsafe extern "system" fn GeometrySinkImpl_Close(_: *mut ID2D1SimplifiedGeometrySink) -> HRESULT { 114 | S_OK 115 | } 116 | 117 | unsafe extern "system" fn GeometrySinkImpl_SetFillMode( 118 | _: *mut ID2D1SimplifiedGeometrySink, 119 | _: D2D1_FILL_MODE, 120 | ) { 121 | } 122 | 123 | unsafe extern "system" fn GeometrySinkImpl_SetSegmentFlags( 124 | _: *mut ID2D1SimplifiedGeometrySink, 125 | _: D2D1_PATH_SEGMENT, 126 | ) { 127 | } 128 | -------------------------------------------------------------------------------- /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 | use winapi::um::dwrite::{DWRITE_FONT_STYLE, DWRITE_FONT_WEIGHT, DWRITE_FONT_STRETCH}; 8 | 9 | // mirrors DWRITE_FONT_WEIGHT 10 | #[cfg_attr(feature = "serde_serialization", derive(Deserialize, Serialize))] 11 | #[derive(PartialEq, Debug, Clone, Copy)] 12 | pub enum FontWeight { 13 | Thin, 14 | ExtraLight, 15 | Light, 16 | SemiLight, 17 | Regular, 18 | Medium, 19 | SemiBold, 20 | Bold, 21 | ExtraBold, 22 | Black, 23 | ExtraBlack, 24 | Unknown(u32) 25 | } 26 | 27 | impl FontWeight { 28 | fn t(&self) -> DWRITE_FONT_WEIGHT { 29 | unsafe { mem::transmute::(self.to_u32()) } 30 | } 31 | pub fn to_u32(&self) -> u32 { 32 | match self { 33 | FontWeight::Thin=> 100, 34 | FontWeight::ExtraLight=> 200, 35 | FontWeight::Light=> 300, 36 | FontWeight::SemiLight=> 350, 37 | FontWeight::Regular=> 400, 38 | FontWeight::Medium=> 500, 39 | FontWeight::SemiBold=> 600, 40 | FontWeight::Bold=> 700, 41 | FontWeight::ExtraBold=> 800, 42 | FontWeight::Black=> 900, 43 | FontWeight::ExtraBlack=> 950, 44 | FontWeight::Unknown(v) => { *v } 45 | } 46 | } 47 | pub fn from_u32(v: u32) -> FontWeight { 48 | match v { 49 | 100 => FontWeight::Thin, 50 | 200 => FontWeight::ExtraLight, 51 | 300 => FontWeight::Light, 52 | 350 => FontWeight::SemiLight, 53 | 400 => FontWeight::Regular, 54 | 500 => FontWeight::Medium, 55 | 600 => FontWeight::SemiBold, 56 | 700 => FontWeight::Bold, 57 | 800 => FontWeight::ExtraBold, 58 | 900 => FontWeight::Black, 59 | 950 => FontWeight::ExtraBlack, 60 | _ => FontWeight::Unknown(v) 61 | } 62 | } 63 | } 64 | 65 | // mirrors DWRITE_FONT_STRETCH 66 | #[repr(u32)] 67 | #[cfg_attr(feature = "serde_serialization", derive(Deserialize, Serialize))] 68 | #[derive(PartialEq, Debug, Clone, Copy)] 69 | pub enum FontStretch { 70 | Undefined = 0, 71 | UltraCondensed = 1, 72 | ExtraCondensed = 2, 73 | Condensed = 3, 74 | SemiCondensed = 4, 75 | Normal = 5, 76 | SemiExpanded = 6, 77 | Expanded = 7, 78 | ExtraExpanded = 8, 79 | UltraExpanded = 9, 80 | } 81 | 82 | impl FontStretch { 83 | fn t(&self) -> DWRITE_FONT_STRETCH { 84 | unsafe { mem::transmute::(*self) } 85 | } 86 | pub fn to_u32(&self) -> u32 { unsafe { mem::transmute::(*self) } } 87 | pub fn from_u32(v: u32) -> FontStretch { unsafe { mem::transmute::(v) } } 88 | } 89 | 90 | // mirrors DWRITE_FONT_STYLE 91 | #[repr(u32)] 92 | #[cfg_attr(feature = "serde_serialization", derive(Deserialize, Serialize))] 93 | #[derive(PartialEq, Debug, Clone, Copy)] 94 | pub enum FontStyle { 95 | Normal = 0, 96 | Oblique = 1, 97 | Italic = 2, 98 | } 99 | 100 | impl FontStyle { 101 | fn t(&self) -> DWRITE_FONT_STYLE { 102 | unsafe { mem::transmute::(*self) } 103 | } 104 | pub fn to_u32(&self) -> u32 { unsafe { mem::transmute::(*self) } } 105 | pub fn from_u32(v: u32) -> FontStyle { unsafe { mem::transmute::(v) } } 106 | } 107 | 108 | // mirrors DWRITE_FONT_SIMULATIONS 109 | #[repr(u32)] 110 | #[derive(PartialEq, Debug, Clone, Copy)] 111 | pub enum FontSimulations { 112 | None = winapi::um::dwrite::DWRITE_FONT_SIMULATIONS_NONE, 113 | Bold = winapi::um::dwrite::DWRITE_FONT_SIMULATIONS_BOLD, 114 | Oblique = winapi::um::dwrite::DWRITE_FONT_SIMULATIONS_OBLIQUE, 115 | BoldOblique = winapi::um::dwrite::DWRITE_FONT_SIMULATIONS_BOLD | 116 | winapi::um::dwrite::DWRITE_FONT_SIMULATIONS_OBLIQUE, 117 | } 118 | 119 | #[cfg_attr(feature = "serde_serialization", derive(Deserialize, Serialize))] 120 | #[derive(PartialEq, Debug, Clone)] 121 | pub struct FontDescriptor { 122 | pub family_name: String, 123 | pub weight: FontWeight, 124 | pub stretch: FontStretch, 125 | pub style: FontStyle, 126 | } 127 | -------------------------------------------------------------------------------- /src/com_helpers.rs: -------------------------------------------------------------------------------- 1 | // This is only handy for implementing a single-interface-implementing IUnknown. 2 | 3 | macro_rules! implement_iunknown { 4 | ($interface:ident, $typ:ident) => { 5 | IUnknownVtbl { 6 | QueryInterface: { 7 | #[allow(non_snake_case)] 8 | unsafe extern "system" fn QueryInterface( 9 | unknown_this: *mut IUnknown, 10 | riid: REFIID, 11 | ppv_object: *mut *mut c_void, 12 | ) -> HRESULT { 13 | use $crate::winapi::Interface; 14 | let this = if $crate::winapi::shared::guiddef::IsEqualGUID( 15 | &*riid, 16 | &$interface::uuidof(), 17 | ) { 18 | mem::transmute(unknown_this) 19 | } else if $crate::winapi::shared::guiddef::IsEqualGUID( 20 | &*riid, 21 | &IUnknown::uuidof(), 22 | ) { 23 | mem::transmute(unknown_this) 24 | } else { 25 | return $crate::winapi::shared::winerror::E_NOINTERFACE; 26 | }; 27 | 28 | (*unknown_this).AddRef(); 29 | *ppv_object = this; 30 | return S_OK; 31 | } 32 | QueryInterface 33 | }, 34 | AddRef: { 35 | unsafe extern "system" fn AddRef(unknown_this: *mut IUnknown) -> ULONG { 36 | let this = $typ::from_interface(unknown_this); 37 | let count = this.refcount.fetch_add(1, atomic::Ordering::Relaxed) + 1; 38 | count as ULONG 39 | } 40 | AddRef 41 | }, 42 | Release: { 43 | unsafe extern "system" fn Release(unknown_this: *mut IUnknown) -> ULONG { 44 | let this = $typ::from_interface(unknown_this); 45 | let count = this.refcount.fetch_sub(1, atomic::Ordering::Release) - 1; 46 | if count == 0 { 47 | <$typ as Com<$interface>>::destroy(unknown_this as *mut $interface); 48 | } 49 | count as ULONG 50 | } 51 | Release 52 | }, 53 | } 54 | }; 55 | (static $interface:ident, $typ:ident) => { 56 | IUnknownVtbl { 57 | QueryInterface: { 58 | #[allow(non_snake_case)] 59 | unsafe extern "system" fn QueryInterface( 60 | unknown_this: *mut IUnknown, 61 | riid: REFIID, 62 | ppvObject: *mut *mut $crate::winapi::ctypes::c_void, 63 | ) -> HRESULT { 64 | use $crate::winapi::Interface; 65 | let this = if $crate::winapi::shared::guiddef::IsEqualGUID( 66 | &*riid, 67 | &$interface::uuidof(), 68 | ) { 69 | mem::transmute(unknown_this) 70 | } else if $crate::winapi::shared::guiddef::IsEqualGUID( 71 | &*riid, 72 | &IUnknown::uuidof(), 73 | ) { 74 | mem::transmute(unknown_this) 75 | } else { 76 | return $crate::winapi::shared::winerror::E_NOINTERFACE; 77 | }; 78 | 79 | (*unknown_this).AddRef(); 80 | *ppvObject = this; 81 | return S_OK; 82 | } 83 | QueryInterface 84 | }, 85 | AddRef: { 86 | // FIXME(pcwalton): Uh? Maybe we should actually reference count? 87 | #[allow(non_snake_case)] 88 | unsafe extern "system" fn AddRef(_: *mut IUnknown) -> ULONG { 89 | 1 90 | } 91 | AddRef 92 | }, 93 | Release: { 94 | #[allow(non_snake_case)] 95 | unsafe extern "system" fn Release(_: *mut IUnknown) -> ULONG { 96 | 1 97 | } 98 | Release 99 | }, 100 | } 101 | }; 102 | } 103 | 104 | #[repr(C)] 105 | pub struct ComRepr(*const Vtbl, Type); 106 | 107 | pub trait Com 108 | where 109 | Self: Sized, 110 | { 111 | type Vtbl: 'static; 112 | 113 | fn vtbl() -> &'static Self::Vtbl; 114 | 115 | fn into_interface(self) -> *mut Interface { 116 | let com = Box::new(ComRepr(Self::vtbl(), self)); 117 | Box::into_raw(com) as *mut Interface 118 | } 119 | 120 | unsafe fn from_interface<'a>(thing: *mut Interface) -> &'a mut Self { 121 | &mut (*(thing as *mut ComRepr)).1 122 | } 123 | 124 | unsafe fn destroy(thing: *mut Interface) { 125 | let _ = Box::from_raw(thing as *mut ComRepr); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /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::cell::UnsafeCell; 6 | use std::mem::{size_of, zeroed}; 7 | use std::slice; 8 | use winapi::ctypes::c_void; 9 | use winapi::shared::windef::{HDC, RECT}; 10 | use winapi::um::dcommon::DWRITE_MEASURING_MODE; 11 | use winapi::um::dwrite::IDWriteBitmapRenderTarget; 12 | use winapi::um::dwrite::{DWRITE_GLYPH_OFFSET, DWRITE_GLYPH_RUN}; 13 | use winapi::um::wingdi::{GetCurrentObject, GetObjectW, BITMAP, OBJ_BITMAP, RGB}; 14 | use wio::com::ComPtr; 15 | 16 | use super::{FontFace, RenderingParams}; 17 | 18 | pub struct BitmapRenderTarget { 19 | native: UnsafeCell>, 20 | } 21 | 22 | impl BitmapRenderTarget { 23 | pub fn take(native: ComPtr) -> BitmapRenderTarget { 24 | BitmapRenderTarget { 25 | native: UnsafeCell::new(native), 26 | } 27 | } 28 | 29 | pub unsafe fn as_ptr(&self) -> *mut IDWriteBitmapRenderTarget { 30 | (*self.native.get()).as_raw() 31 | } 32 | 33 | // A dip is 1/96th of an inch, so this value is the number of pixels per inch divided by 96. 34 | pub fn set_pixels_per_dip(&self, ppd: f32) { 35 | unsafe { 36 | (*self.native.get()).SetPixelsPerDip(ppd); 37 | } 38 | } 39 | 40 | pub fn get_memory_dc(&self) -> HDC { 41 | unsafe { (*self.native.get()).GetMemoryDC() } 42 | } 43 | 44 | pub fn draw_glyph_run( 45 | &self, 46 | baseline_origin_x: f32, 47 | baseline_origin_y: f32, 48 | measuring_mode: DWRITE_MEASURING_MODE, 49 | font_face: &FontFace, 50 | em_size: f32, 51 | glyph_indices: &[u16], 52 | glyph_advances: &[f32], 53 | glyph_offsets: &[DWRITE_GLYPH_OFFSET], 54 | rendering_params: &RenderingParams, 55 | color: &(f32, f32, f32), 56 | ) -> RECT { 57 | unsafe { 58 | assert!(glyph_indices.len() == glyph_advances.len()); 59 | assert!(glyph_indices.len() == glyph_offsets.len()); 60 | 61 | let r = (color.0 * 255.0) as u8; 62 | let g = (color.1 * 255.0) as u8; 63 | let b = (color.2 * 255.0) as u8; 64 | 65 | let mut glyph_run: DWRITE_GLYPH_RUN = zeroed(); 66 | glyph_run.fontFace = font_face.as_ptr(); 67 | glyph_run.fontEmSize = em_size; 68 | glyph_run.glyphCount = glyph_indices.len() as u32; 69 | glyph_run.glyphIndices = glyph_indices.as_ptr(); 70 | glyph_run.glyphAdvances = glyph_advances.as_ptr(); 71 | glyph_run.glyphOffsets = glyph_offsets.as_ptr(); 72 | glyph_run.isSideways = 0; 73 | glyph_run.bidiLevel = 0; 74 | 75 | let mut rect: RECT = zeroed(); 76 | let hr = (*self.native.get()).DrawGlyphRun( 77 | baseline_origin_x, 78 | baseline_origin_y, 79 | measuring_mode, 80 | &glyph_run, 81 | rendering_params.as_ptr(), 82 | RGB(r, g, b), 83 | &mut rect, 84 | ); 85 | assert!(hr == 0); 86 | rect 87 | } 88 | } 89 | 90 | // This function expects to have glyphs rendered in WHITE, 91 | // and pulls out a u8 vector of width*height*4 size with 92 | // the coverage value (we pull out R) broadcast to the alpha 93 | // channel, with the color white. That is, it performs: 94 | // RGBX -> xxxR, where xxx = 0xff 95 | pub fn get_opaque_values_as_mask(&self) -> Vec { 96 | // Now grossness to pull out the pixels 97 | unsafe { 98 | let memory_dc = self.get_memory_dc(); 99 | let mut bitmap: BITMAP = zeroed(); 100 | let ret = GetObjectW( 101 | GetCurrentObject(memory_dc, OBJ_BITMAP), 102 | size_of::() as i32, 103 | &mut bitmap as *mut _ as *mut c_void, 104 | ); 105 | assert!(ret == size_of::() as i32); 106 | assert!(bitmap.bmBitsPixel == 32); 107 | 108 | let width = bitmap.bmWidth as usize; 109 | let stride = bitmap.bmWidthBytes as usize; 110 | let height = bitmap.bmHeight as usize; 111 | 112 | let mut out_bytes: Vec = vec![0; width * height * 4]; 113 | let out_u32 = 114 | slice::from_raw_parts_mut(out_bytes.as_mut_ptr() as *mut u32, width * height); 115 | 116 | for row in 0..height { 117 | let in_offset = (row * stride) as isize; 118 | let in_u32 = 119 | slice::from_raw_parts(bitmap.bmBits.offset(in_offset) as *const u32, width); 120 | for col in 0..width { 121 | let r = in_u32[col] & 0xff; 122 | out_u32[width * row + col] = (r << 24) | (0x00ffffffu32); 123 | } 124 | } 125 | 126 | out_bytes 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/font_collection_impl.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 | // A temporary custom font collection that exists solely for the face-to-font mapping to work. 6 | 7 | use std::mem; 8 | use std::sync::atomic::AtomicUsize; 9 | use winapi::ctypes::c_void; 10 | use winapi::shared::guiddef::REFIID; 11 | use winapi::shared::minwindef::{BOOL, FALSE, TRUE, ULONG}; 12 | use winapi::shared::winerror::{E_INVALIDARG, S_OK}; 13 | use winapi::um::dwrite::IDWriteFactory; 14 | use winapi::um::dwrite::IDWriteFontCollectionLoader; 15 | use winapi::um::dwrite::IDWriteFontCollectionLoaderVtbl; 16 | use winapi::um::dwrite::IDWriteFontFile; 17 | use winapi::um::dwrite::IDWriteFontFileEnumerator; 18 | use winapi::um::dwrite::IDWriteFontFileEnumeratorVtbl; 19 | use winapi::um::unknwnbase::{IUnknown, IUnknownVtbl}; 20 | use winapi::um::winnt::HRESULT; 21 | use wio::com::ComPtr; 22 | 23 | use crate::com_helpers::Com; 24 | use crate::FontFile; 25 | 26 | static FONT_COLLECTION_LOADER_VTBL: IDWriteFontCollectionLoaderVtbl = 27 | IDWriteFontCollectionLoaderVtbl { 28 | parent: implement_iunknown!(static IDWriteFontCollectionLoader, 29 | CustomFontCollectionLoaderImpl), 30 | CreateEnumeratorFromKey: CustomFontCollectionLoaderImpl_CreateEnumeratorFromKey, 31 | }; 32 | 33 | #[repr(C)] 34 | pub struct CustomFontCollectionLoaderImpl { 35 | // NB: This must be the first field. 36 | _refcount: AtomicUsize, 37 | font_files: Vec>, 38 | } 39 | 40 | impl Com for CustomFontCollectionLoaderImpl { 41 | type Vtbl = IDWriteFontCollectionLoaderVtbl; 42 | #[inline] 43 | fn vtbl() -> &'static IDWriteFontCollectionLoaderVtbl { 44 | &FONT_COLLECTION_LOADER_VTBL 45 | } 46 | } 47 | 48 | impl Com for CustomFontCollectionLoaderImpl { 49 | type Vtbl = IUnknownVtbl; 50 | #[inline] 51 | fn vtbl() -> &'static IUnknownVtbl { 52 | &FONT_COLLECTION_LOADER_VTBL.parent 53 | } 54 | } 55 | 56 | impl CustomFontCollectionLoaderImpl { 57 | pub fn new(font_files: &[FontFile]) -> ComPtr { 58 | unsafe { 59 | ComPtr::from_raw( 60 | CustomFontCollectionLoaderImpl { 61 | _refcount: AtomicUsize::new(1), 62 | font_files: font_files.iter().map(|file| file.as_com_ptr()).collect(), 63 | } 64 | .into_interface(), 65 | ) 66 | } 67 | } 68 | } 69 | 70 | #[allow(non_snake_case)] 71 | unsafe extern "system" fn CustomFontCollectionLoaderImpl_CreateEnumeratorFromKey( 72 | this: *mut IDWriteFontCollectionLoader, 73 | _: *mut IDWriteFactory, 74 | _: *const c_void, 75 | _: u32, 76 | out_enumerator: *mut *mut IDWriteFontFileEnumerator, 77 | ) -> HRESULT { 78 | let this = CustomFontCollectionLoaderImpl::from_interface(this); 79 | let enumerator = CustomFontFileEnumeratorImpl::new(this.font_files.clone()); 80 | let enumerator = ComPtr::::from_raw(enumerator.into_interface()); 81 | *out_enumerator = enumerator.as_raw(); 82 | mem::forget(enumerator); 83 | S_OK 84 | } 85 | 86 | #[repr(C)] 87 | struct CustomFontFileEnumeratorImpl { 88 | // NB(pcwalton): This must be the first field. 89 | _refcount: AtomicUsize, 90 | font_files: Vec>, 91 | index: isize, 92 | } 93 | 94 | impl Com for CustomFontFileEnumeratorImpl { 95 | type Vtbl = IDWriteFontFileEnumeratorVtbl; 96 | #[inline] 97 | fn vtbl() -> &'static IDWriteFontFileEnumeratorVtbl { 98 | &FONT_FILE_ENUMERATOR_VTBL 99 | } 100 | } 101 | 102 | impl Com for CustomFontFileEnumeratorImpl { 103 | type Vtbl = IUnknownVtbl; 104 | #[inline] 105 | fn vtbl() -> &'static IUnknownVtbl { 106 | &FONT_FILE_ENUMERATOR_VTBL.parent 107 | } 108 | } 109 | 110 | static FONT_FILE_ENUMERATOR_VTBL: IDWriteFontFileEnumeratorVtbl = IDWriteFontFileEnumeratorVtbl { 111 | parent: implement_iunknown!(static IDWriteFontFileEnumerator, CustomFontFileEnumeratorImpl), 112 | GetCurrentFontFile: CustomFontFileEnumeratorImpl_GetCurrentFontFile, 113 | MoveNext: CustomFontFileEnumeratorImpl_MoveNext, 114 | }; 115 | 116 | impl CustomFontFileEnumeratorImpl { 117 | pub fn new(font_files: Vec>) -> CustomFontFileEnumeratorImpl { 118 | CustomFontFileEnumeratorImpl { 119 | _refcount: AtomicUsize::new(1), 120 | font_files, 121 | index: -1, 122 | } 123 | } 124 | } 125 | 126 | #[allow(non_snake_case)] 127 | unsafe extern "system" fn CustomFontFileEnumeratorImpl_GetCurrentFontFile( 128 | this: *mut IDWriteFontFileEnumerator, 129 | out_font_file: *mut *mut IDWriteFontFile, 130 | ) -> HRESULT { 131 | let this = CustomFontFileEnumeratorImpl::from_interface(this); 132 | if this.index < 0 || this.index >= this.font_files.len() as isize { 133 | return E_INVALIDARG; 134 | } 135 | let new_font_file = this.font_files[this.index as usize].clone(); 136 | *out_font_file = new_font_file.as_raw(); 137 | mem::forget(new_font_file); 138 | S_OK 139 | } 140 | 141 | #[allow(non_snake_case)] 142 | unsafe extern "system" fn CustomFontFileEnumeratorImpl_MoveNext( 143 | this: *mut IDWriteFontFileEnumerator, 144 | has_current_file: *mut BOOL, 145 | ) -> HRESULT { 146 | let this = CustomFontFileEnumeratorImpl::from_interface(this); 147 | let font_file_count = this.font_files.len() as isize; 148 | if this.index < font_file_count { 149 | this.index += 1 150 | } 151 | *has_current_file = if this.index >= 0 && this.index < font_file_count { 152 | TRUE 153 | } else { 154 | FALSE 155 | }; 156 | S_OK 157 | } 158 | -------------------------------------------------------------------------------- /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 | use std::sync::Arc; 7 | 8 | #[test] 9 | fn test_system_family_iter() { 10 | let system_fc = FontCollection::system(); 11 | let count = system_fc.families_iter().count(); 12 | assert!(count > 0); 13 | assert!(system_fc 14 | .families_iter() 15 | .any(|f| f.name() == "Arial")); 16 | } 17 | 18 | #[test] 19 | fn test_descriptor_round_trip() { 20 | let system_fc = FontCollection::system(); 21 | 22 | let arial_family = system_fc.get_font_family_by_name("Arial").unwrap(); 23 | let arial_font = arial_family.get_first_matching_font( 24 | FontWeight::Regular, 25 | FontStretch::Normal, 26 | FontStyle::Normal, 27 | ); 28 | 29 | let descriptor = arial_font.to_descriptor(); 30 | assert!(descriptor.family_name == "Arial"); 31 | 32 | let arial_font_2 = system_fc.get_font_from_descriptor(&descriptor).unwrap(); 33 | let descriptor2 = arial_font_2.to_descriptor(); 34 | assert_eq!(descriptor, descriptor2); 35 | } 36 | 37 | #[test] 38 | fn test_get_font_file_bytes() { 39 | let system_fc = FontCollection::system(); 40 | 41 | let arial_family = system_fc.get_font_family_by_name("Arial").unwrap(); 42 | let arial_font = arial_family.get_first_matching_font( 43 | FontWeight::Regular, 44 | FontStretch::Normal, 45 | FontStyle::Normal, 46 | ); 47 | let face = arial_font.create_font_face(); 48 | let files = face.get_files(); 49 | assert!(!files.is_empty()); 50 | 51 | let bytes = files[0].get_font_file_bytes(); 52 | assert!(!bytes.is_empty()); 53 | } 54 | 55 | #[test] 56 | fn test_font_file_is_monospace() { 57 | let system_fc = FontCollection::system(); 58 | 59 | let arial_family = system_fc.get_font_family_by_name("Arial").unwrap(); 60 | let arial_font = arial_family.get_first_matching_font( 61 | FontWeight::Regular, 62 | FontStretch::Normal, 63 | FontStyle::Normal, 64 | ); 65 | assert!(arial_font.is_monospace() == Some(false)); 66 | 67 | let courier_new_family = system_fc.get_font_family_by_name("Courier New").unwrap(); 68 | let courier_new_font = courier_new_family.get_first_matching_font( 69 | FontWeight::Regular, 70 | FontStretch::Normal, 71 | FontStyle::Normal, 72 | ); 73 | assert!(courier_new_font.is_monospace() == Some(true)); 74 | } 75 | 76 | #[test] 77 | fn test_create_font_file_from_bytes() { 78 | let system_fc = FontCollection::system(); 79 | 80 | let arial_family = system_fc.get_font_family_by_name("Arial").unwrap(); 81 | let arial_font = arial_family.get_first_matching_font( 82 | FontWeight::Regular, 83 | FontStretch::Normal, 84 | FontStyle::Normal, 85 | ); 86 | let face = arial_font.create_font_face(); 87 | let files = face.get_files(); 88 | assert!(!files.is_empty()); 89 | 90 | let bytes = files[0].get_font_file_bytes(); 91 | assert!(!bytes.is_empty()); 92 | 93 | // now go back 94 | #[allow(deprecated)] 95 | let new_font = FontFile::new_from_data(Arc::new(bytes.clone())); 96 | assert!(new_font.is_some()); 97 | 98 | let new_font = FontFile::new_from_buffer(Arc::new(bytes)); 99 | assert!(new_font.is_some()); 100 | 101 | let _new_font = new_font.unwrap(); 102 | } 103 | 104 | #[test] 105 | fn test_glyph_image() { 106 | let system_fc = FontCollection::system(); 107 | let arial_family = system_fc.get_font_family_by_name("Arial").unwrap(); 108 | let arial_font = arial_family.get_first_matching_font( 109 | FontWeight::Regular, 110 | FontStretch::Normal, 111 | FontStyle::Normal, 112 | ); 113 | 114 | let face = arial_font.create_font_face(); 115 | let a_index = face.get_glyph_indices(&['A' as u32])[0]; 116 | 117 | let gm = face.get_design_glyph_metrics(&[a_index], false)[0]; 118 | 119 | let device_pixel_ratio = 1.0f32; 120 | let em_size = 10.0f32; 121 | 122 | let design_units_per_em = match face.metrics() { 123 | FontMetrics::Metrics0(ref metrics) => metrics.designUnitsPerEm, 124 | FontMetrics::Metrics1(ref metrics) => metrics.designUnitsPerEm, 125 | }; 126 | let design_units_per_pixel = design_units_per_em as f32 / 16.; 127 | 128 | let scaled_design_units_to_pixels = (em_size * device_pixel_ratio) / design_units_per_pixel; 129 | 130 | let width = (gm.advanceWidth as i32 - (gm.leftSideBearing + gm.rightSideBearing)) as f32 131 | * scaled_design_units_to_pixels; 132 | let height = (gm.advanceHeight as i32 - (gm.topSideBearing + gm.bottomSideBearing)) as f32 133 | * scaled_design_units_to_pixels; 134 | let x = (-gm.leftSideBearing) as f32 * scaled_design_units_to_pixels; 135 | let y = (gm.verticalOriginY - gm.topSideBearing) as f32 * scaled_design_units_to_pixels; 136 | 137 | // FIXME I'm pretty sure we need to do a proper RoundOut type 138 | // operation on this rect to properly handle any aliasing 139 | let left_i = x.floor() as i32; 140 | let top_i = (height - y).floor() as i32; 141 | let width_u = width.ceil() as u32; 142 | let height_u = height.ceil() as u32; 143 | 144 | println!( 145 | "GlyphDimensions: {} {} {} {}", 146 | left_i, top_i, width_u, height_u 147 | ); 148 | 149 | let gdi_interop = GdiInterop::create(); 150 | let rt = gdi_interop.create_bitmap_render_target(width_u, height_u); 151 | let rp = RenderingParams::create_for_primary_monitor(); 152 | rt.set_pixels_per_dip(device_pixel_ratio); 153 | rt.draw_glyph_run( 154 | x, 155 | y, 156 | DWRITE_MEASURING_MODE_NATURAL, 157 | &face, 158 | em_size, 159 | &[a_index], 160 | &[0f32], 161 | &[GlyphOffset { 162 | advanceOffset: 0., 163 | ascenderOffset: 0., 164 | }], 165 | &rp, 166 | &(255.0f32, 255.0f32, 255.0f32), 167 | ); 168 | let bytes = rt.get_opaque_values_as_mask(); 169 | println!("bytes length: {}", bytes.len()); 170 | } 171 | -------------------------------------------------------------------------------- /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_serialization")] 8 | extern crate serde; 9 | #[cfg_attr(feature = "serde_serialization", macro_use)] 10 | #[cfg(feature = "serde_serialization")] 11 | extern crate serde_derive; 12 | 13 | #[macro_use] 14 | extern crate lazy_static; 15 | extern crate libc; 16 | extern crate winapi; 17 | 18 | include!("types.rs"); 19 | 20 | use std::ffi::CString; 21 | use std::ptr; 22 | use winapi::shared::guiddef::REFIID; 23 | use winapi::shared::winerror::S_OK; 24 | use winapi::um::dwrite::IDWriteFactory; 25 | use winapi::um::dwrite::IDWriteRenderingParams; 26 | use winapi::um::dwrite::DWRITE_FACTORY_TYPE; 27 | use winapi::um::dwrite::DWRITE_FACTORY_TYPE_SHARED; 28 | use winapi::um::unknwnbase::IUnknown; 29 | use winapi::um::winnt::LPCSTR; 30 | use winapi::Interface; 31 | 32 | pub use winapi::um::winnt::HRESULT; 33 | 34 | mod helpers; 35 | use helpers::ToWide; 36 | use std::os::raw::c_void; 37 | 38 | #[cfg(test)] 39 | mod test; 40 | 41 | // We still use the DWrite structs for things like metrics; re-export them 42 | // here 43 | pub use winapi::shared::windef::RECT; 44 | pub use winapi::um::dcommon::DWRITE_MEASURING_MODE; 45 | pub use winapi::um::dcommon::{ 46 | DWRITE_MEASURING_MODE_GDI_CLASSIC, DWRITE_MEASURING_MODE_GDI_NATURAL, 47 | DWRITE_MEASURING_MODE_NATURAL, 48 | }; 49 | pub use winapi::um::dwrite::DWRITE_FONT_METRICS as FontMetrics0; 50 | pub use winapi::um::dwrite::DWRITE_FONT_SIMULATIONS; 51 | pub use winapi::um::dwrite::DWRITE_GLYPH_OFFSET as GlyphOffset; 52 | pub use winapi::um::dwrite::DWRITE_RENDERING_MODE; 53 | pub use winapi::um::dwrite::DWRITE_TEXTURE_TYPE; 54 | pub use winapi::um::dwrite::{DWRITE_TEXTURE_ALIASED_1x1, DWRITE_TEXTURE_CLEARTYPE_3x1}; 55 | pub use winapi::um::dwrite::{ 56 | DWRITE_FONT_SIMULATIONS_BOLD, DWRITE_FONT_SIMULATIONS_NONE, DWRITE_FONT_SIMULATIONS_OBLIQUE, 57 | }; 58 | pub use winapi::um::dwrite::{DWRITE_GLYPH_RUN, DWRITE_MATRIX}; 59 | pub use winapi::um::dwrite::{ 60 | DWRITE_RENDERING_MODE_ALIASED, DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC, 61 | DWRITE_RENDERING_MODE_CLEARTYPE_GDI_NATURAL, DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL, 62 | DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC, DWRITE_RENDERING_MODE_DEFAULT, 63 | DWRITE_RENDERING_MODE_GDI_CLASSIC, DWRITE_RENDERING_MODE_GDI_NATURAL, 64 | DWRITE_RENDERING_MODE_NATURAL, DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC, 65 | DWRITE_RENDERING_MODE_OUTLINE, 66 | }; 67 | pub use winapi::um::dwrite_1::DWRITE_FONT_METRICS1 as FontMetrics1; 68 | pub use winapi::um::dwrite_3::DWRITE_FONT_AXIS_VALUE; 69 | use winapi::um::libloaderapi::{GetProcAddress, LoadLibraryW}; 70 | 71 | #[macro_use] 72 | mod com_helpers; 73 | 74 | mod bitmap_render_target; 75 | pub use bitmap_render_target::BitmapRenderTarget; 76 | mod font; 77 | pub use font::{Font, FontMetrics, InformationalStringId}; 78 | mod font_collection; 79 | pub use font_collection::FontCollection; 80 | mod font_face; 81 | pub use font_face::{FontFace, FontFaceType}; 82 | mod font_fallback; 83 | pub use font_fallback::{FallbackResult, FontFallback}; 84 | mod font_family; 85 | pub use font_family::FontFamily; 86 | mod font_file; 87 | pub use font_file::FontFile; 88 | mod gdi_interop; 89 | pub use gdi_interop::GdiInterop; 90 | mod outline_builder; 91 | pub use outline_builder::OutlineBuilder; 92 | mod rendering_params; 93 | pub use rendering_params::RenderingParams; 94 | mod text_analysis_source; 95 | pub use text_analysis_source::TextAnalysisSource; 96 | mod glyph_run_analysis; 97 | pub use glyph_run_analysis::GlyphRunAnalysis; 98 | 99 | // This is an internal implementation of FontFileLoader, for our utility 100 | // functions. We don't wrap the DWriteFontFileLoader interface and 101 | // related things. 102 | mod font_file_loader_impl; 103 | 104 | // This is an implementation of `FontCollectionLoader` for client code. 105 | mod font_collection_impl; 106 | pub use font_collection_impl::CustomFontCollectionLoaderImpl; 107 | 108 | // This is an implementation of `TextAnalysisSource` for client code. 109 | mod text_analysis_source_impl; 110 | pub use text_analysis_source_impl::{ 111 | CustomTextAnalysisSourceImpl, NumberSubstitution, TextAnalysisSourceMethods, 112 | }; 113 | 114 | // This is an internal implementation of `GeometrySink` so that we can 115 | // expose `IDWriteGeometrySink` in an idiomatic way. 116 | mod geometry_sink_impl; 117 | 118 | lazy_static! { 119 | static ref DWRITE_FACTORY_RAW_PTR: usize = { 120 | unsafe { 121 | type DWriteCreateFactoryType = 122 | extern "system" fn(DWRITE_FACTORY_TYPE, REFIID, *mut *mut IUnknown) -> HRESULT; 123 | 124 | let dwrite_dll = LoadLibraryW("dwrite.dll".to_wide_null().as_ptr()); 125 | assert!(!dwrite_dll.is_null()); 126 | let create_factory_name = CString::new("DWriteCreateFactory").unwrap(); 127 | let dwrite_create_factory_ptr = 128 | GetProcAddress(dwrite_dll, create_factory_name.as_ptr() as LPCSTR); 129 | assert!(!dwrite_create_factory_ptr.is_null()); 130 | 131 | let dwrite_create_factory = mem::transmute::<*const c_void, DWriteCreateFactoryType>( 132 | dwrite_create_factory_ptr as *const _, 133 | ); 134 | 135 | let mut factory: *mut IDWriteFactory = ptr::null_mut(); 136 | let hr = dwrite_create_factory( 137 | DWRITE_FACTORY_TYPE_SHARED, 138 | &IDWriteFactory::uuidof(), 139 | &mut factory as *mut *mut IDWriteFactory as *mut *mut IUnknown, 140 | ); 141 | assert!(hr == S_OK); 142 | factory as usize 143 | } 144 | }; 145 | static ref DEFAULT_DWRITE_RENDERING_PARAMS_RAW_PTR: usize = { 146 | unsafe { 147 | let mut default_rendering_params: *mut IDWriteRenderingParams = ptr::null_mut(); 148 | let hr = (*DWriteFactory()).CreateRenderingParams(&mut default_rendering_params); 149 | assert!(hr == S_OK); 150 | default_rendering_params as usize 151 | } 152 | }; 153 | } // end lazy static 154 | 155 | // FIXME vlad would be nice to return, say, FactoryPtr 156 | // that has a DerefMut impl, so that we can write 157 | // DWriteFactory().SomeOperation() as opposed to 158 | // (*DWriteFactory()).SomeOperation() 159 | #[allow(non_snake_case)] 160 | fn DWriteFactory() -> *mut IDWriteFactory { 161 | (*DWRITE_FACTORY_RAW_PTR) as *mut IDWriteFactory 162 | } 163 | 164 | #[allow(non_snake_case)] 165 | fn DefaultDWriteRenderParams() -> *mut IDWriteRenderingParams { 166 | (*DEFAULT_DWRITE_RENDERING_PARAMS_RAW_PTR) as *mut IDWriteRenderingParams 167 | } 168 | -------------------------------------------------------------------------------- /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 std::cell::UnsafeCell; 6 | use std::mem; 7 | use std::ptr; 8 | use std::sync::atomic::{AtomicUsize, Ordering}; 9 | use winapi::shared::minwindef::{BOOL, FALSE, TRUE}; 10 | use winapi::shared::winerror::S_OK; 11 | use winapi::um::dwrite::IDWriteFontCollectionLoader; 12 | use winapi::um::dwrite::{IDWriteFont, IDWriteFontCollection, IDWriteFontFamily}; 13 | use winapi::um::winnt::HRESULT; 14 | use wio::com::ComPtr; 15 | 16 | use super::{DWriteFactory, Font, FontDescriptor, FontFace, FontFamily}; 17 | use crate::helpers::*; 18 | 19 | static NEXT_ID: AtomicUsize = AtomicUsize::new(0); 20 | 21 | pub struct FontCollectionFamilyIterator { 22 | collection: ComPtr, 23 | curr: u32, 24 | count: u32, 25 | } 26 | 27 | impl Iterator for FontCollectionFamilyIterator { 28 | type Item = FontFamily; 29 | fn next(&mut self) -> Option { 30 | if self.curr == self.count { 31 | return None; 32 | } 33 | 34 | unsafe { 35 | let mut family: *mut IDWriteFontFamily = ptr::null_mut(); 36 | let hr = self.collection.GetFontFamily(self.curr, &mut family); 37 | assert!(hr == 0); 38 | self.curr += 1; 39 | Some(FontFamily::take(ComPtr::from_raw(family))) 40 | } 41 | } 42 | } 43 | 44 | pub struct FontCollection { 45 | native: UnsafeCell>, 46 | } 47 | 48 | impl FontCollection { 49 | pub fn get_system(update: bool) -> FontCollection { 50 | unsafe { 51 | let mut native: *mut IDWriteFontCollection = ptr::null_mut(); 52 | let hr = (*DWriteFactory()) 53 | .GetSystemFontCollection(&mut native, if update { TRUE } else { FALSE }); 54 | assert!(hr == 0); 55 | 56 | FontCollection { 57 | native: UnsafeCell::new(ComPtr::from_raw(native)), 58 | } 59 | } 60 | } 61 | 62 | pub fn system() -> FontCollection { 63 | FontCollection::get_system(false) 64 | } 65 | 66 | pub fn take(native: ComPtr) -> FontCollection { 67 | FontCollection { 68 | native: UnsafeCell::new(native), 69 | } 70 | } 71 | 72 | pub fn from_loader(collection_loader: ComPtr) -> FontCollection { 73 | unsafe { 74 | let factory = DWriteFactory(); 75 | assert_eq!( 76 | (*factory).RegisterFontCollectionLoader(collection_loader.clone().into_raw()), 77 | S_OK 78 | ); 79 | let mut collection: *mut IDWriteFontCollection = ptr::null_mut(); 80 | let id = NEXT_ID.fetch_add(1, Ordering::SeqCst); 81 | assert_eq!( 82 | (*factory).CreateCustomFontCollection( 83 | collection_loader.clone().into_raw(), 84 | &id as *const usize as *const _, 85 | mem::size_of::() as u32, 86 | &mut collection 87 | ), 88 | S_OK 89 | ); 90 | FontCollection::take(ComPtr::from_raw(collection)) 91 | } 92 | } 93 | 94 | pub unsafe fn as_ptr(&self) -> *mut IDWriteFontCollection { 95 | (*self.native.get()).as_raw() 96 | } 97 | 98 | pub fn families_iter(&self) -> FontCollectionFamilyIterator { 99 | unsafe { 100 | FontCollectionFamilyIterator { 101 | collection: (*self.native.get()).clone(), 102 | curr: 0, 103 | count: (*self.native.get()).GetFontFamilyCount(), 104 | } 105 | } 106 | } 107 | 108 | pub fn get_font_family_count(&self) -> u32 { 109 | unsafe { (*self.native.get()).GetFontFamilyCount() } 110 | } 111 | 112 | #[deprecated(note = "Use `font_family` instead.")] 113 | pub fn get_font_family(&self, index: u32) -> FontFamily { 114 | self.font_family(index).unwrap() 115 | } 116 | 117 | /// Returns the [`FontFamily`] at the given index. 118 | pub fn font_family(&self, index: u32) -> Result { 119 | let mut family: *mut IDWriteFontFamily = ptr::null_mut(); 120 | unsafe { 121 | let hr = (*self.native.get()).GetFontFamily(index, &mut family); 122 | if hr != S_OK { 123 | return Err(hr); 124 | } 125 | Ok(FontFamily::take(ComPtr::from_raw(family))) 126 | } 127 | } 128 | 129 | #[deprecated(note = "Use `font_from_descriptor` instead.")] 130 | pub fn get_font_from_descriptor(&self, desc: &FontDescriptor) -> Option { 131 | self.font_from_descriptor(desc).unwrap() 132 | } 133 | 134 | /// Find a font matching the given font descriptor in this [`FontCollection`]. 135 | pub fn font_from_descriptor(&self, desc: &FontDescriptor) -> Result, HRESULT> { 136 | if let Some(family) = self.font_family_by_name(&desc.family_name)? { 137 | let font = family.first_matching_font(desc.weight, desc.stretch, desc.style)?; 138 | // Exact matches only here 139 | if font.weight() == desc.weight 140 | && font.stretch() == desc.stretch 141 | && font.style() == desc.style 142 | { 143 | return Ok(Some(font)); 144 | } 145 | } 146 | 147 | Ok(None) 148 | } 149 | 150 | #[deprecated(note = "Use `font_from_face` instead.")] 151 | pub fn get_font_from_face(&self, face: &FontFace) -> Option { 152 | self.font_from_face(face).ok() 153 | } 154 | 155 | /// Get a [`Font`] from the given [`FontFace`]. 156 | pub fn font_from_face(&self, face: &FontFace) -> Result { 157 | let mut font: *mut IDWriteFont = ptr::null_mut(); 158 | unsafe { 159 | let hr = (*self.native.get()).GetFontFromFontFace(face.as_ptr(), &mut font); 160 | if hr != S_OK { 161 | return Err(hr); 162 | } 163 | Ok(Font::take(ComPtr::from_raw(font))) 164 | } 165 | } 166 | 167 | #[deprecated(note = "Use `font_family_by_name` instead.")] 168 | pub fn get_font_family_by_name(&self, family_name: &str) -> Option { 169 | self.font_family_by_name(family_name).unwrap() 170 | } 171 | 172 | /// Find a [`FontFamily`] with the given name. Returns `None` if no family 173 | /// with that name is found. 174 | pub fn font_family_by_name(&self, family_name: &str) -> Result, HRESULT> { 175 | let mut index: u32 = 0; 176 | let mut exists: BOOL = FALSE; 177 | unsafe { 178 | let hr = (*self.native.get()).FindFamilyName( 179 | family_name.to_wide_null().as_ptr(), 180 | &mut index, 181 | &mut exists, 182 | ); 183 | if hr != S_OK { 184 | return Err(hr); 185 | } 186 | if exists == FALSE { 187 | return Ok(None); 188 | } 189 | 190 | let mut family: *mut IDWriteFontFamily = ptr::null_mut(); 191 | let hr = (*self.native.get()).GetFontFamily(index, &mut family); 192 | if hr != S_OK { 193 | return Err(hr); 194 | } 195 | 196 | Ok(Some(FontFamily::take(ComPtr::from_raw(family)))) 197 | } 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /src/font_file_loader_impl.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case, non_upper_case_globals)] 2 | 3 | use std::collections::HashMap; 4 | use std::marker::Send; 5 | use std::sync::atomic::AtomicUsize; 6 | use std::sync::{atomic, Arc, Mutex}; 7 | use std::{mem, ptr}; 8 | use winapi::ctypes::c_void; 9 | use winapi::shared::basetsd::{UINT32, UINT64}; 10 | use winapi::shared::guiddef::REFIID; 11 | use winapi::shared::minwindef::ULONG; 12 | use winapi::shared::winerror::{E_FAIL, E_INVALIDARG, E_NOTIMPL, S_OK}; 13 | use winapi::um::dwrite::IDWriteFontFile; 14 | use winapi::um::dwrite::{IDWriteFontFileLoader, IDWriteFontFileLoaderVtbl}; 15 | use winapi::um::dwrite::{IDWriteFontFileStream, IDWriteFontFileStreamVtbl}; 16 | use winapi::um::unknwnbase::{IUnknown, IUnknownVtbl}; 17 | use winapi::um::winnt::HRESULT; 18 | use wio::com::ComPtr; 19 | 20 | use super::DWriteFactory; 21 | use crate::com_helpers::*; 22 | 23 | struct FontFileLoader; 24 | 25 | const FontFileLoaderVtbl: &IDWriteFontFileLoaderVtbl = &IDWriteFontFileLoaderVtbl { 26 | parent: implement_iunknown!(static IDWriteFontFileLoader, FontFileLoader), 27 | CreateStreamFromKey: { 28 | unsafe extern "system" fn CreateStreamFromKey( 29 | _This: *mut IDWriteFontFileLoader, 30 | fontFileReferenceKey: *const c_void, 31 | fontFileReferenceKeySize: UINT32, 32 | fontFileStream: *mut *mut IDWriteFontFileStream, 33 | ) -> HRESULT { 34 | if fontFileReferenceKey.is_null() || fontFileStream.is_null() { 35 | return E_INVALIDARG; 36 | } 37 | assert!(fontFileReferenceKeySize == mem::size_of::() as UINT32); 38 | let key = *(fontFileReferenceKey as *const usize); 39 | let stream = match FONT_FILE_STREAM_MAP.lock().unwrap().get(&key) { 40 | None => { 41 | *fontFileStream = ptr::null_mut(); 42 | return E_FAIL; 43 | } 44 | Some(&FontFileStreamPtr(file_stream)) => file_stream, 45 | }; 46 | 47 | // This is an addref getter, so make sure to do that! 48 | (*stream).AddRef(); 49 | 50 | *fontFileStream = stream; 51 | S_OK 52 | } 53 | CreateStreamFromKey 54 | }, 55 | }; 56 | 57 | impl Com for FontFileLoader { 58 | type Vtbl = IDWriteFontFileLoaderVtbl; 59 | fn vtbl() -> &'static IDWriteFontFileLoaderVtbl { 60 | FontFileLoaderVtbl 61 | } 62 | } 63 | 64 | impl Com for FontFileLoader { 65 | type Vtbl = IUnknownVtbl; 66 | fn vtbl() -> &'static IUnknownVtbl { 67 | &FontFileLoaderVtbl.parent 68 | } 69 | } 70 | 71 | impl FontFileLoader { 72 | pub fn new() -> FontFileLoader { 73 | FontFileLoader 74 | } 75 | } 76 | 77 | unsafe impl Send for FontFileLoader {} 78 | unsafe impl Sync for FontFileLoader {} 79 | 80 | struct FontFileStream { 81 | refcount: atomic::AtomicUsize, 82 | key: usize, 83 | data: Arc + Sync + Send>, 84 | } 85 | 86 | const FontFileStreamVtbl: &IDWriteFontFileStreamVtbl = &IDWriteFontFileStreamVtbl { 87 | parent: implement_iunknown!(IDWriteFontFileStream, FontFileStream), 88 | ReadFileFragment: { 89 | unsafe extern "system" fn ReadFileFragment( 90 | This: *mut IDWriteFontFileStream, 91 | fragmentStart: *mut *const c_void, 92 | fileOffset: UINT64, 93 | fragmentSize: UINT64, 94 | fragmentContext: *mut *mut c_void, 95 | ) -> HRESULT { 96 | let this = FontFileStream::from_interface(This); 97 | *fragmentContext = ptr::null_mut(); 98 | let data = (*this.data).as_ref(); 99 | if (fileOffset + fragmentSize) as usize > data.len() { 100 | return E_INVALIDARG; 101 | } 102 | let index = fileOffset as usize; 103 | *fragmentStart = data[index..].as_ptr() as *const c_void; 104 | S_OK 105 | } 106 | ReadFileFragment 107 | }, 108 | ReleaseFileFragment: { 109 | unsafe extern "system" fn ReleaseFileFragment( 110 | _This: *mut IDWriteFontFileStream, 111 | _fragmentContext: *mut c_void, 112 | ) { 113 | } 114 | ReleaseFileFragment 115 | }, 116 | GetFileSize: { 117 | unsafe extern "system" fn GetFileSize( 118 | This: *mut IDWriteFontFileStream, 119 | fileSize: *mut UINT64, 120 | ) -> HRESULT { 121 | let this = FontFileStream::from_interface(This); 122 | *fileSize = (*this.data).as_ref().len() as UINT64; 123 | S_OK 124 | } 125 | GetFileSize 126 | }, 127 | GetLastWriteTime: { 128 | unsafe extern "system" fn GetLastWriteTime( 129 | _This: *mut IDWriteFontFileStream, 130 | _lastWriteTime: *mut UINT64, 131 | ) -> HRESULT { 132 | E_NOTIMPL 133 | } 134 | GetLastWriteTime 135 | }, 136 | }; 137 | 138 | impl FontFileStream { 139 | pub fn new(key: usize, data: Arc + Sync + Send>) -> FontFileStream { 140 | FontFileStream { 141 | refcount: AtomicUsize::new(1), 142 | key, 143 | data, 144 | } 145 | } 146 | } 147 | 148 | impl Drop for FontFileStream { 149 | fn drop(&mut self) { 150 | DataFontHelper::unregister_font_data(self.key); 151 | } 152 | } 153 | 154 | impl Com for FontFileStream { 155 | type Vtbl = IDWriteFontFileStreamVtbl; 156 | fn vtbl() -> &'static IDWriteFontFileStreamVtbl { 157 | FontFileStreamVtbl 158 | } 159 | } 160 | 161 | impl Com for FontFileStream { 162 | type Vtbl = IUnknownVtbl; 163 | fn vtbl() -> &'static IUnknownVtbl { 164 | &FontFileStreamVtbl.parent 165 | } 166 | } 167 | 168 | struct FontFileStreamPtr(*mut IDWriteFontFileStream); 169 | 170 | unsafe impl Send for FontFileStreamPtr {} 171 | 172 | static mut FONT_FILE_KEY: atomic::AtomicUsize = AtomicUsize::new(0); 173 | 174 | #[derive(Clone)] 175 | struct FontFileLoaderWrapper(ComPtr); 176 | 177 | unsafe impl Send for FontFileLoaderWrapper {} 178 | unsafe impl Sync for FontFileLoaderWrapper {} 179 | 180 | lazy_static! { 181 | static ref FONT_FILE_STREAM_MAP: Mutex> = 182 | Mutex::new(HashMap::new()); 183 | static ref FONT_FILE_LOADER: Mutex = { 184 | unsafe { 185 | let ffl_native = FontFileLoader::new(); 186 | let ffl = ComPtr::::from_raw(ffl_native.into_interface()); 187 | let hr = (*DWriteFactory()).RegisterFontFileLoader(ffl.as_raw()); 188 | assert!(hr == 0); 189 | Mutex::new(FontFileLoaderWrapper(ffl)) 190 | } 191 | }; 192 | } 193 | 194 | pub(crate) struct DataFontHelper; 195 | 196 | impl DataFontHelper { 197 | pub(crate) fn register_font_buffer( 198 | font_data: Arc + Sync + Send>, 199 | ) -> ( 200 | ComPtr, 201 | ComPtr, 202 | usize, 203 | ) { 204 | unsafe { 205 | let key = FONT_FILE_KEY.fetch_add(1, atomic::Ordering::Relaxed); 206 | let font_file_stream_native = FontFileStream::new(key, font_data); 207 | let font_file_stream: ComPtr = 208 | ComPtr::from_raw(font_file_stream_native.into_interface()); 209 | 210 | { 211 | let mut map = FONT_FILE_STREAM_MAP.lock().unwrap(); 212 | map.insert(key, FontFileStreamPtr(font_file_stream.as_raw())); 213 | } 214 | 215 | let mut font_file: *mut IDWriteFontFile = ptr::null_mut(); 216 | { 217 | let loader = FONT_FILE_LOADER.lock().unwrap(); 218 | let hr = (*DWriteFactory()).CreateCustomFontFileReference( 219 | mem::transmute(&key), 220 | mem::size_of::() as UINT32, 221 | loader.0.as_raw(), 222 | &mut font_file, 223 | ); 224 | assert!(hr == S_OK); 225 | } 226 | let font_file = ComPtr::from_raw(font_file); 227 | 228 | (font_file, font_file_stream, key) 229 | } 230 | } 231 | 232 | fn unregister_font_data(key: usize) { 233 | let mut map = FONT_FILE_STREAM_MAP.lock().unwrap(); 234 | if map.remove(&key).is_none() { 235 | panic!("unregister_font_data: trying to unregister key that is no longer registered"); 236 | } 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /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 | use std::mem; 7 | use std::ptr; 8 | use winapi::shared::minwindef::{FALSE, TRUE}; 9 | use winapi::shared::winerror::S_OK; 10 | use winapi::um::dwrite::IDWriteFont; 11 | use winapi::um::dwrite::IDWriteFontFace; 12 | use winapi::um::dwrite::IDWriteFontFamily; 13 | use winapi::um::dwrite::IDWriteLocalizedStrings; 14 | use winapi::um::dwrite::DWRITE_FONT_METRICS; 15 | use winapi::um::dwrite::DWRITE_INFORMATIONAL_STRING_COPYRIGHT_NOTICE; 16 | use winapi::um::dwrite::DWRITE_INFORMATIONAL_STRING_DESCRIPTION; 17 | use winapi::um::dwrite::DWRITE_INFORMATIONAL_STRING_DESIGNER; 18 | use winapi::um::dwrite::DWRITE_INFORMATIONAL_STRING_DESIGNER_URL; 19 | use winapi::um::dwrite::DWRITE_INFORMATIONAL_STRING_DESIGN_SCRIPT_LANGUAGE_TAG; 20 | use winapi::um::dwrite::DWRITE_INFORMATIONAL_STRING_FONT_VENDOR_URL; 21 | use winapi::um::dwrite::DWRITE_INFORMATIONAL_STRING_FULL_NAME; 22 | use winapi::um::dwrite::DWRITE_INFORMATIONAL_STRING_ID; 23 | use winapi::um::dwrite::DWRITE_INFORMATIONAL_STRING_LICENSE_DESCRIPTION; 24 | use winapi::um::dwrite::DWRITE_INFORMATIONAL_STRING_LICENSE_INFO_URL; 25 | use winapi::um::dwrite::DWRITE_INFORMATIONAL_STRING_MANUFACTURER; 26 | use winapi::um::dwrite::DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_CID_NAME; 27 | use winapi::um::dwrite::DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_NAME; 28 | use winapi::um::dwrite::DWRITE_INFORMATIONAL_STRING_PREFERRED_FAMILY_NAMES; 29 | use winapi::um::dwrite::DWRITE_INFORMATIONAL_STRING_PREFERRED_SUBFAMILY_NAMES; 30 | use winapi::um::dwrite::DWRITE_INFORMATIONAL_STRING_SAMPLE_TEXT; 31 | use winapi::um::dwrite::DWRITE_INFORMATIONAL_STRING_SUPPORTED_SCRIPT_LANGUAGE_TAG; 32 | use winapi::um::dwrite::DWRITE_INFORMATIONAL_STRING_TRADEMARK; 33 | use winapi::um::dwrite::DWRITE_INFORMATIONAL_STRING_VERSION_STRINGS; 34 | use winapi::um::dwrite::DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMES; 35 | use winapi::um::dwrite::DWRITE_INFORMATIONAL_STRING_WIN32_SUBFAMILY_NAMES; 36 | use winapi::um::dwrite::DWRITE_INFORMATIONAL_STRING_WWS_FAMILY_NAME; 37 | use winapi::um::dwrite_1::{IDWriteFont1, DWRITE_FONT_METRICS1}; 38 | use wio::com::ComPtr; 39 | 40 | use super::*; 41 | use helpers::*; 42 | 43 | pub struct Font { 44 | native: UnsafeCell>, 45 | } 46 | 47 | impl Font { 48 | pub fn take(native: ComPtr) -> Font { 49 | Font { 50 | native: UnsafeCell::new(native), 51 | } 52 | } 53 | 54 | pub unsafe fn as_ptr(&self) -> *mut IDWriteFont { 55 | (*self.native.get()).as_raw() 56 | } 57 | 58 | pub fn to_descriptor(&self) -> FontDescriptor { 59 | FontDescriptor { 60 | family_name: self.family_name(), 61 | stretch: self.stretch(), 62 | style: self.style(), 63 | weight: self.weight(), 64 | } 65 | } 66 | 67 | pub fn stretch(&self) -> FontStretch { 68 | unsafe { mem::transmute::((*self.native.get()).GetStretch()) } 69 | } 70 | 71 | pub fn style(&self) -> FontStyle { 72 | unsafe { mem::transmute::((*self.native.get()).GetStyle()) } 73 | } 74 | 75 | pub fn weight(&self) -> FontWeight { 76 | unsafe { FontWeight::from_u32((*self.native.get()).GetWeight()) } 77 | } 78 | 79 | pub fn is_monospace(&self) -> Option { 80 | unsafe { 81 | let font1: Option> = (*self.native.get()).cast().ok(); 82 | font1.map(|font| font.IsMonospacedFont() == TRUE) 83 | } 84 | } 85 | 86 | pub fn simulations(&self) -> FontSimulations { 87 | unsafe { mem::transmute::((*self.native.get()).GetSimulations()) } 88 | } 89 | 90 | pub fn family_name(&self) -> String { 91 | unsafe { 92 | let mut family: *mut IDWriteFontFamily = ptr::null_mut(); 93 | let hr = (*self.native.get()).GetFontFamily(&mut family); 94 | assert!(hr == 0); 95 | 96 | FontFamily::take(ComPtr::from_raw(family)).name() 97 | } 98 | } 99 | 100 | pub fn face_name(&self) -> String { 101 | unsafe { 102 | let mut names: *mut IDWriteLocalizedStrings = ptr::null_mut(); 103 | let hr = (*self.native.get()).GetFaceNames(&mut names); 104 | assert!(hr == 0); 105 | 106 | get_locale_string(&mut ComPtr::from_raw(names)) 107 | } 108 | } 109 | 110 | pub fn informational_string(&self, id: InformationalStringId) -> Option { 111 | unsafe { 112 | let mut names: *mut IDWriteLocalizedStrings = ptr::null_mut(); 113 | let mut exists = FALSE; 114 | let id = id as DWRITE_INFORMATIONAL_STRING_ID; 115 | let hr = (*self.native.get()).GetInformationalStrings(id, &mut names, &mut exists); 116 | assert!(hr == S_OK); 117 | if exists == TRUE { 118 | Some(get_locale_string(&mut ComPtr::from_raw(names))) 119 | } else { 120 | None 121 | } 122 | } 123 | } 124 | 125 | pub fn create_font_face(&self) -> FontFace { 126 | // FIXME create_font_face should cache the FontFace and return it, 127 | // there's a 1:1 relationship 128 | unsafe { 129 | let mut face: *mut IDWriteFontFace = ptr::null_mut(); 130 | let hr = (*self.native.get()).CreateFontFace(&mut face); 131 | assert!(hr == 0); 132 | FontFace::take(ComPtr::from_raw(face)) 133 | } 134 | } 135 | 136 | pub fn metrics(&self) -> FontMetrics { 137 | unsafe { 138 | let font_1: Option> = (*self.native.get()).cast().ok(); 139 | match font_1 { 140 | None => { 141 | let mut metrics = mem::zeroed(); 142 | (*self.native.get()).GetMetrics(&mut metrics); 143 | FontMetrics::Metrics0(metrics) 144 | } 145 | Some(font_1) => { 146 | let mut metrics_1 = mem::zeroed(); 147 | font_1.GetMetrics(&mut metrics_1); 148 | FontMetrics::Metrics1(metrics_1) 149 | } 150 | } 151 | } 152 | } 153 | } 154 | 155 | impl Clone for Font { 156 | fn clone(&self) -> Font { 157 | unsafe { 158 | Font { 159 | native: UnsafeCell::new((*self.native.get()).clone()), 160 | } 161 | } 162 | } 163 | } 164 | 165 | #[repr(u32)] 166 | #[derive(Clone, Copy, Debug, PartialEq)] 167 | pub enum InformationalStringId { 168 | FullName = DWRITE_INFORMATIONAL_STRING_FULL_NAME, 169 | PostscriptName = DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_NAME, 170 | PostscriptCidName = DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_CID_NAME, 171 | CopyrightNotice = DWRITE_INFORMATIONAL_STRING_COPYRIGHT_NOTICE, 172 | Description = DWRITE_INFORMATIONAL_STRING_DESCRIPTION, 173 | Designer = DWRITE_INFORMATIONAL_STRING_DESIGNER, 174 | DesignerUrl = DWRITE_INFORMATIONAL_STRING_DESIGNER_URL, 175 | DesignScriptLanguageTag = DWRITE_INFORMATIONAL_STRING_DESIGN_SCRIPT_LANGUAGE_TAG, 176 | VendorUrl = DWRITE_INFORMATIONAL_STRING_FONT_VENDOR_URL, 177 | LicenseDescription = DWRITE_INFORMATIONAL_STRING_LICENSE_DESCRIPTION, 178 | LicenseInfoUrl = DWRITE_INFORMATIONAL_STRING_LICENSE_INFO_URL, 179 | Manufacturer = DWRITE_INFORMATIONAL_STRING_MANUFACTURER, 180 | PreferredFamilyNames = DWRITE_INFORMATIONAL_STRING_PREFERRED_FAMILY_NAMES, 181 | PreferredSubfamilyNames = DWRITE_INFORMATIONAL_STRING_PREFERRED_SUBFAMILY_NAMES, 182 | SampleText = DWRITE_INFORMATIONAL_STRING_SAMPLE_TEXT, 183 | SupportedScriptLanguageTag = DWRITE_INFORMATIONAL_STRING_SUPPORTED_SCRIPT_LANGUAGE_TAG, 184 | Trademark = DWRITE_INFORMATIONAL_STRING_TRADEMARK, 185 | Version = DWRITE_INFORMATIONAL_STRING_VERSION_STRINGS, 186 | Win32FamilyNames = DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMES, 187 | Win32SubfamilyNames = DWRITE_INFORMATIONAL_STRING_WIN32_SUBFAMILY_NAMES, 188 | WwsFamilyName = DWRITE_INFORMATIONAL_STRING_WWS_FAMILY_NAME, 189 | } 190 | 191 | /// A wrapper around the `DWRITE_FONT_METRICS` and `DWRITE_FONT_METRICS1` types. 192 | pub enum FontMetrics { 193 | /// Windows 7. 194 | Metrics0(DWRITE_FONT_METRICS), 195 | /// Windows 8 and up. 196 | Metrics1(DWRITE_FONT_METRICS1), 197 | } 198 | 199 | impl FontMetrics { 200 | /// Convert self to the Metrics0 arm (throwing away additional information) 201 | pub fn metrics0(self) -> DWRITE_FONT_METRICS { 202 | match self { 203 | FontMetrics::Metrics0(metrics) => metrics, 204 | FontMetrics::Metrics1(metrics) => DWRITE_FONT_METRICS { 205 | designUnitsPerEm: metrics.designUnitsPerEm, 206 | ascent: metrics.ascent, 207 | descent: metrics.descent, 208 | lineGap: metrics.lineGap, 209 | capHeight: metrics.capHeight, 210 | xHeight: metrics.xHeight, 211 | underlinePosition: metrics.underlinePosition, 212 | underlineThickness: metrics.underlineThickness, 213 | strikethroughPosition: metrics.strikethroughPosition, 214 | strikethroughThickness: metrics.strikethroughThickness, 215 | }, 216 | } 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /src/text_analysis_source_impl.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 | //! A custom implementation of the "text analysis source" interface so that 6 | //! we can convey data to the `FontFallback::map_characters` method. 7 | 8 | #![allow(non_snake_case)] 9 | 10 | use std::borrow::Cow; 11 | use std::ffi::OsStr; 12 | use std::mem; 13 | use std::os::windows::ffi::OsStrExt; 14 | use std::ptr::{self, null}; 15 | use std::sync::atomic::AtomicUsize; 16 | use winapi::ctypes::wchar_t; 17 | use winapi::shared::basetsd::UINT32; 18 | use winapi::shared::guiddef::REFIID; 19 | use winapi::shared::minwindef::{FALSE, TRUE, ULONG}; 20 | use winapi::shared::ntdef::LOCALE_NAME_MAX_LENGTH; 21 | use winapi::shared::winerror::{E_INVALIDARG, S_OK}; 22 | use winapi::um::dwrite::IDWriteNumberSubstitution; 23 | use winapi::um::dwrite::IDWriteTextAnalysisSource; 24 | use winapi::um::dwrite::IDWriteTextAnalysisSourceVtbl; 25 | use winapi::um::dwrite::DWRITE_NUMBER_SUBSTITUTION_METHOD; 26 | use winapi::um::dwrite::DWRITE_READING_DIRECTION; 27 | use winapi::um::unknwnbase::{IUnknown, IUnknownVtbl}; 28 | use winapi::um::winnt::HRESULT; 29 | use wio::com::ComPtr; 30 | 31 | use super::DWriteFactory; 32 | use crate::com_helpers::Com; 33 | use crate::helpers::ToWide; 34 | 35 | /// The Rust side of a custom text analysis source implementation. 36 | pub trait TextAnalysisSourceMethods { 37 | /// Determine the locale for a range of text. 38 | /// 39 | /// Return locale and length of text (in utf-16 code units) for which the 40 | /// locale is valid. 41 | fn get_locale_name(&self, text_position: u32) -> (Cow<'_, str>, u32); 42 | 43 | /// Get the text direction for the paragraph. 44 | fn get_paragraph_reading_direction(&self) -> DWRITE_READING_DIRECTION; 45 | } 46 | 47 | #[repr(C)] 48 | pub struct CustomTextAnalysisSourceImpl<'a> { 49 | // NB: This must be the first field. 50 | _refcount: AtomicUsize, 51 | inner: Box, 52 | text: Cow<'a, [wchar_t]>, 53 | number_subst: Option, 54 | locale_buf: [wchar_t; LOCALE_NAME_MAX_LENGTH], 55 | } 56 | 57 | /// A wrapped version of an `IDWriteNumberSubstitution` object. 58 | pub struct NumberSubstitution { 59 | native: ComPtr, 60 | } 61 | 62 | // TODO: implement Clone, for convenience and efficiency? 63 | 64 | static TEXT_ANALYSIS_SOURCE_VTBL: IDWriteTextAnalysisSourceVtbl = IDWriteTextAnalysisSourceVtbl { 65 | parent: implement_iunknown!(static IDWriteTextAnalysisSource, CustomTextAnalysisSourceImpl), 66 | GetLocaleName: CustomTextAnalysisSourceImpl_GetLocaleName, 67 | GetNumberSubstitution: CustomTextAnalysisSourceImpl_GetNumberSubstitution, 68 | GetParagraphReadingDirection: CustomTextAnalysisSourceImpl_GetParagraphReadingDirection, 69 | GetTextAtPosition: CustomTextAnalysisSourceImpl_GetTextAtPosition, 70 | GetTextBeforePosition: CustomTextAnalysisSourceImpl_GetTextBeforePosition, 71 | }; 72 | 73 | impl<'a> CustomTextAnalysisSourceImpl<'a> { 74 | /// Create a new custom TextAnalysisSource for the given text and a trait 75 | /// implementation. 76 | /// 77 | /// Note: this method has no NumberSubsitution specified. See 78 | /// `from_text_and_number_subst_native` if you need number substitution. 79 | pub fn from_text_native( 80 | inner: Box, 81 | text: Cow<'a, [wchar_t]>, 82 | ) -> CustomTextAnalysisSourceImpl<'a> { 83 | assert!(text.len() <= (u32::MAX as usize)); 84 | CustomTextAnalysisSourceImpl { 85 | _refcount: AtomicUsize::new(1), 86 | inner, 87 | text, 88 | number_subst: None, 89 | locale_buf: [0u16; LOCALE_NAME_MAX_LENGTH], 90 | } 91 | } 92 | 93 | /// Create a new custom TextAnalysisSource for the given text and a trait 94 | /// implementation. 95 | /// 96 | /// Note: this method only supports a single `NumberSubstitution` for the 97 | /// entire string. 98 | pub fn from_text_and_number_subst_native( 99 | inner: Box, 100 | text: Cow<'a, [wchar_t]>, 101 | number_subst: NumberSubstitution, 102 | ) -> CustomTextAnalysisSourceImpl<'a> { 103 | assert!(text.len() <= (u32::MAX as usize)); 104 | CustomTextAnalysisSourceImpl { 105 | _refcount: AtomicUsize::new(1), 106 | inner, 107 | text, 108 | number_subst: Some(number_subst), 109 | locale_buf: [0u16; LOCALE_NAME_MAX_LENGTH], 110 | } 111 | } 112 | } 113 | 114 | impl Com for CustomTextAnalysisSourceImpl<'_> { 115 | type Vtbl = IDWriteTextAnalysisSourceVtbl; 116 | #[inline] 117 | fn vtbl() -> &'static IDWriteTextAnalysisSourceVtbl { 118 | &TEXT_ANALYSIS_SOURCE_VTBL 119 | } 120 | } 121 | 122 | impl Com for CustomTextAnalysisSourceImpl<'_> { 123 | type Vtbl = IUnknownVtbl; 124 | #[inline] 125 | fn vtbl() -> &'static IUnknownVtbl { 126 | &TEXT_ANALYSIS_SOURCE_VTBL.parent 127 | } 128 | } 129 | 130 | unsafe extern "system" fn CustomTextAnalysisSourceImpl_GetLocaleName( 131 | this: *mut IDWriteTextAnalysisSource, 132 | text_position: UINT32, 133 | text_length: *mut UINT32, 134 | locale_name: *mut *const wchar_t, 135 | ) -> HRESULT { 136 | let this = CustomTextAnalysisSourceImpl::from_interface(this); 137 | let (locale, text_len) = this.inner.get_locale_name(text_position); 138 | 139 | // Copy the locale data into the buffer 140 | for (i, c) in OsStr::new(&*locale) 141 | .encode_wide() 142 | .chain(Some(0)) 143 | .enumerate() 144 | { 145 | // -1 here is deliberate: it ensures that we never write to the last character in 146 | // this.locale_buf, so that the buffer is always null-terminated. 147 | if i >= this.locale_buf.len() - 1 { 148 | break; 149 | } 150 | 151 | *this.locale_buf.get_unchecked_mut(i) = c; 152 | } 153 | 154 | *text_length = text_len; 155 | *locale_name = this.locale_buf.as_ptr(); 156 | S_OK 157 | } 158 | 159 | unsafe extern "system" fn CustomTextAnalysisSourceImpl_GetNumberSubstitution( 160 | this: *mut IDWriteTextAnalysisSource, 161 | text_position: UINT32, 162 | text_length: *mut UINT32, 163 | number_substitution: *mut *mut IDWriteNumberSubstitution, 164 | ) -> HRESULT { 165 | let this = CustomTextAnalysisSourceImpl::from_interface(this); 166 | if text_position >= (this.text.len() as u32) { 167 | return E_INVALIDARG; 168 | } 169 | 170 | *text_length = (this.text.len() as UINT32) - text_position; 171 | *number_substitution = match &this.number_subst { 172 | Some(number_subst) => { 173 | let com_ptr = &number_subst.native; 174 | com_ptr.AddRef(); 175 | com_ptr.as_raw() 176 | } 177 | None => std::ptr::null_mut(), 178 | }; 179 | 180 | S_OK 181 | } 182 | 183 | unsafe extern "system" fn CustomTextAnalysisSourceImpl_GetParagraphReadingDirection( 184 | this: *mut IDWriteTextAnalysisSource, 185 | ) -> DWRITE_READING_DIRECTION { 186 | let this = CustomTextAnalysisSourceImpl::from_interface(this); 187 | this.inner.get_paragraph_reading_direction() 188 | } 189 | 190 | unsafe extern "system" fn CustomTextAnalysisSourceImpl_GetTextAtPosition( 191 | this: *mut IDWriteTextAnalysisSource, 192 | text_position: UINT32, 193 | text_string: *mut *const wchar_t, 194 | text_length: *mut UINT32, 195 | ) -> HRESULT { 196 | let this = CustomTextAnalysisSourceImpl::from_interface(this); 197 | if text_position >= (this.text.len() as u32) { 198 | *text_string = null(); 199 | *text_length = 0; 200 | return S_OK; 201 | } 202 | *text_string = this.text.as_ptr().add(text_position as usize); 203 | *text_length = (this.text.len() as UINT32) - text_position; 204 | S_OK 205 | } 206 | 207 | unsafe extern "system" fn CustomTextAnalysisSourceImpl_GetTextBeforePosition( 208 | this: *mut IDWriteTextAnalysisSource, 209 | text_position: UINT32, 210 | text_string: *mut *const wchar_t, 211 | text_length: *mut UINT32, 212 | ) -> HRESULT { 213 | let this = CustomTextAnalysisSourceImpl::from_interface(this); 214 | if text_position == 0 || text_position > (this.text.len() as u32) { 215 | *text_string = null(); 216 | *text_length = 0; 217 | return S_OK; 218 | } 219 | *text_string = this.text.as_ptr(); 220 | *text_length = text_position; 221 | S_OK 222 | } 223 | 224 | impl NumberSubstitution { 225 | pub fn new( 226 | subst_method: DWRITE_NUMBER_SUBSTITUTION_METHOD, 227 | locale: &str, 228 | ignore_user_overrides: bool, 229 | ) -> NumberSubstitution { 230 | unsafe { 231 | let mut native: *mut IDWriteNumberSubstitution = ptr::null_mut(); 232 | let hr = (*DWriteFactory()).CreateNumberSubstitution( 233 | subst_method, 234 | locale.to_wide_null().as_ptr(), 235 | if ignore_user_overrides { TRUE } else { FALSE }, 236 | &mut native, 237 | ); 238 | assert_eq!(hr, 0, "error creating number substitution"); 239 | NumberSubstitution { 240 | native: ComPtr::from_raw(native), 241 | } 242 | } 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /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::cell::UnsafeCell; 6 | use std::ffi::OsString; 7 | use std::os::windows::ffi::{OsStrExt, OsStringExt}; 8 | use std::path::Path; 9 | use std::path::PathBuf; 10 | use std::ptr; 11 | use std::slice; 12 | use std::sync::Arc; 13 | use winapi::ctypes::c_void; 14 | use winapi::shared::winerror::S_OK; 15 | use winapi::um::dwrite::{IDWriteFontFace, IDWriteFontFile, IDWriteFontFileStream}; 16 | use winapi::um::dwrite::{IDWriteFontFileLoader, IDWriteLocalFontFileLoader}; 17 | use winapi::um::dwrite::{DWRITE_FONT_FACE_TYPE, DWRITE_FONT_FILE_TYPE_UNKNOWN}; 18 | use winapi::um::dwrite::{DWRITE_FONT_FACE_TYPE_UNKNOWN, DWRITE_FONT_SIMULATIONS}; 19 | use winapi::um::winnt::HRESULT; 20 | use wio::com::ComPtr; 21 | 22 | use super::DWriteFactory; 23 | use crate::font_face::FontFace; 24 | use crate::font_file_loader_impl::DataFontHelper; 25 | 26 | pub struct FontFile { 27 | native: UnsafeCell>, 28 | stream: UnsafeCell>>, 29 | data_key: usize, 30 | face_type: DWRITE_FONT_FACE_TYPE, 31 | } 32 | 33 | impl FontFile { 34 | pub fn new_from_path

(path: P) -> Option 35 | where 36 | P: AsRef, 37 | { 38 | unsafe { 39 | let mut path: Vec = path.as_ref().as_os_str().encode_wide().collect(); 40 | path.push(0); 41 | 42 | let mut font_file: *mut IDWriteFontFile = ptr::null_mut(); 43 | let hr = (*DWriteFactory()).CreateFontFileReference( 44 | path.as_ptr(), 45 | ptr::null(), 46 | &mut font_file, 47 | ); 48 | if hr != 0 || font_file.is_null() { 49 | return None; 50 | } 51 | 52 | let mut ff = FontFile { 53 | native: UnsafeCell::new(ComPtr::from_raw(font_file)), 54 | stream: UnsafeCell::new(None), 55 | data_key: 0, 56 | face_type: DWRITE_FONT_FACE_TYPE_UNKNOWN, 57 | }; 58 | 59 | if ff.analyze() == 0 { 60 | None 61 | } else { 62 | Some(ff) 63 | } 64 | } 65 | } 66 | 67 | #[deprecated(since = "0.11.2", note = "please use `new_from_buffer` instead")] 68 | pub fn new_from_data(data: Arc>) -> Option { 69 | Self::new_from_buffer(data) 70 | } 71 | 72 | pub fn new_from_buffer(data: Arc + Sync + Send>) -> Option { 73 | let (font_file, font_file_stream, key) = DataFontHelper::register_font_buffer(data); 74 | 75 | let mut ff = FontFile { 76 | native: UnsafeCell::new(font_file), 77 | stream: UnsafeCell::new(Some(font_file_stream)), 78 | data_key: key, 79 | face_type: DWRITE_FONT_FACE_TYPE_UNKNOWN, 80 | }; 81 | 82 | if ff.analyze() == 0 { 83 | None 84 | } else { 85 | Some(ff) 86 | } 87 | } 88 | 89 | #[deprecated(since = "0.11.2", note = "please use `analyze_buffer` instead")] 90 | pub fn analyze_data(data: Arc>) -> u32 { 91 | Self::analyze_buffer(data) 92 | } 93 | 94 | pub fn analyze_buffer(buffer: Arc + Sync + Send>) -> u32 { 95 | let (font_file, font_file_stream, key) = DataFontHelper::register_font_buffer(buffer); 96 | 97 | let mut ff = FontFile { 98 | native: UnsafeCell::new(font_file), 99 | stream: UnsafeCell::new(Some(font_file_stream)), 100 | data_key: key, 101 | face_type: DWRITE_FONT_FACE_TYPE_UNKNOWN, 102 | }; 103 | 104 | ff.analyze() 105 | } 106 | 107 | fn analyze(&mut self) -> u32 { 108 | let mut face_type = DWRITE_FONT_FACE_TYPE_UNKNOWN; 109 | let mut num_faces = 0; 110 | unsafe { 111 | let mut supported = 0; 112 | let mut _file_type = DWRITE_FONT_FILE_TYPE_UNKNOWN; 113 | 114 | let hr = (*self.native.get()).Analyze( 115 | &mut supported, 116 | &mut _file_type, 117 | &mut face_type, 118 | &mut num_faces, 119 | ); 120 | if hr != 0 || supported == 0 { 121 | return 0; 122 | } 123 | } 124 | self.face_type = face_type; 125 | num_faces 126 | } 127 | 128 | pub fn take(native: ComPtr) -> FontFile { 129 | let mut ff = FontFile { 130 | native: UnsafeCell::new(native), 131 | stream: UnsafeCell::new(None), 132 | data_key: 0, 133 | face_type: DWRITE_FONT_FACE_TYPE_UNKNOWN, 134 | }; 135 | ff.analyze(); 136 | ff 137 | } 138 | 139 | pub fn data_key(&self) -> Option { 140 | if self.data_key != 0 { 141 | Some(self.data_key) 142 | } else { 143 | None 144 | } 145 | } 146 | 147 | pub(crate) unsafe fn as_com_ptr(&self) -> ComPtr { 148 | (*self.native.get()).clone() 149 | } 150 | 151 | #[deprecated(note = "Use `font_file_bytes` instead.")] 152 | pub fn get_font_file_bytes(&self) -> Vec { 153 | self.font_file_bytes().unwrap() 154 | } 155 | 156 | // This is a helper to read the contents of this FontFile, 157 | // without requiring callers to deal with loaders, keys, 158 | // or streams. 159 | pub fn font_file_bytes(&self) -> Result, HRESULT> { 160 | unsafe { 161 | let mut ref_key: *const c_void = ptr::null(); 162 | let mut ref_key_size: u32 = 0; 163 | let hr = (*self.native.get()).GetReferenceKey(&mut ref_key, &mut ref_key_size); 164 | if hr != S_OK { 165 | return Err(hr); 166 | } 167 | 168 | let mut loader: *mut IDWriteFontFileLoader = ptr::null_mut(); 169 | let hr = (*self.native.get()).GetLoader(&mut loader); 170 | if hr != S_OK { 171 | return Err(hr); 172 | } 173 | let loader = ComPtr::from_raw(loader); 174 | 175 | let mut stream: *mut IDWriteFontFileStream = ptr::null_mut(); 176 | let hr = loader.CreateStreamFromKey(ref_key, ref_key_size, &mut stream); 177 | if hr != S_OK { 178 | return Err(hr); 179 | } 180 | let stream = ComPtr::from_raw(stream); 181 | 182 | let mut file_size: u64 = 0; 183 | let hr = stream.GetFileSize(&mut file_size); 184 | if hr != S_OK { 185 | return Err(hr); 186 | } 187 | 188 | let mut fragment_start: *const c_void = ptr::null(); 189 | let mut fragment_context: *mut c_void = ptr::null_mut(); 190 | let hr = 191 | stream.ReadFileFragment(&mut fragment_start, 0, file_size, &mut fragment_context); 192 | if hr != S_OK { 193 | return Err(hr); 194 | } 195 | 196 | let in_ptr = slice::from_raw_parts(fragment_start as *const u8, file_size as usize); 197 | let bytes = in_ptr.to_vec(); 198 | 199 | stream.ReleaseFileFragment(fragment_context); 200 | 201 | Ok(bytes) 202 | } 203 | } 204 | 205 | #[deprecated(note = "Use `font_file_path` instead.")] 206 | pub fn get_font_file_path(&self) -> Option { 207 | self.font_file_path().ok() 208 | } 209 | 210 | // This is a helper to get the path of a font file, 211 | // without requiring callers to deal with loaders. 212 | pub fn font_file_path(&self) -> Result { 213 | unsafe { 214 | let mut ref_key: *const c_void = ptr::null(); 215 | let mut ref_key_size: u32 = 0; 216 | let hr = (*self.native.get()).GetReferenceKey(&mut ref_key, &mut ref_key_size); 217 | if hr != S_OK { 218 | return Err(hr); 219 | } 220 | 221 | let mut loader: *mut IDWriteFontFileLoader = ptr::null_mut(); 222 | let hr = (*self.native.get()).GetLoader(&mut loader); 223 | if hr != S_OK { 224 | return Err(hr); 225 | } 226 | let loader = ComPtr::from_raw(loader); 227 | 228 | let local_loader: ComPtr = loader.cast()?; 229 | 230 | let mut file_path_len = 0; 231 | let hr = 232 | local_loader.GetFilePathLengthFromKey(ref_key, ref_key_size, &mut file_path_len); 233 | if hr != S_OK { 234 | return Err(hr); 235 | } 236 | 237 | let mut file_path_buf = vec![0; file_path_len as usize + 1]; 238 | let hr = local_loader.GetFilePathFromKey( 239 | ref_key, 240 | ref_key_size, 241 | file_path_buf.as_mut_ptr(), 242 | file_path_len + 1, 243 | ); 244 | if hr != S_OK { 245 | return Err(hr); 246 | } 247 | 248 | if let Some(&0) = file_path_buf.last() { 249 | file_path_buf.pop(); 250 | } 251 | 252 | Ok(PathBuf::from(OsString::from_wide(&file_path_buf))) 253 | } 254 | } 255 | 256 | pub fn create_face( 257 | &self, 258 | face_index: u32, 259 | simulations: DWRITE_FONT_SIMULATIONS, 260 | ) -> Result { 261 | unsafe { 262 | let mut face: *mut IDWriteFontFace = ptr::null_mut(); 263 | let ptr = self.as_com_ptr(); 264 | let hr = (*DWriteFactory()).CreateFontFace( 265 | self.face_type, 266 | 1, 267 | &ptr.as_raw(), 268 | face_index, 269 | simulations, 270 | &mut face, 271 | ); 272 | if hr != 0 { 273 | Err(hr) 274 | } else { 275 | Ok(FontFace::take(ComPtr::from_raw(face))) 276 | } 277 | } 278 | } 279 | } 280 | 281 | impl Clone for FontFile { 282 | fn clone(&self) -> FontFile { 283 | unsafe { 284 | FontFile { 285 | native: UnsafeCell::new((*self.native.get()).clone()), 286 | stream: UnsafeCell::new((*self.stream.get()).clone()), 287 | data_key: self.data_key, 288 | face_type: self.face_type, 289 | } 290 | } 291 | } 292 | } 293 | -------------------------------------------------------------------------------- /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::cell::UnsafeCell; 6 | use std::mem::{self, zeroed}; 7 | use std::slice; 8 | use std::{error, fmt, ptr}; 9 | use winapi::ctypes::c_void; 10 | use winapi::shared::minwindef::{BOOL, FALSE, TRUE}; 11 | use winapi::shared::winerror::S_OK; 12 | use winapi::um::dcommon::DWRITE_MEASURING_MODE; 13 | use winapi::um::dwrite::IDWriteRenderingParams; 14 | use winapi::um::dwrite::DWRITE_FONT_FACE_TYPE_TRUETYPE; 15 | use winapi::um::dwrite::{IDWriteFontFace, IDWriteFontFile}; 16 | use winapi::um::dwrite::{DWRITE_FONT_FACE_TYPE_BITMAP, DWRITE_FONT_FACE_TYPE_CFF}; 17 | use winapi::um::dwrite::{DWRITE_FONT_FACE_TYPE_RAW_CFF, DWRITE_FONT_FACE_TYPE_TYPE1}; 18 | use winapi::um::dwrite::{DWRITE_FONT_FACE_TYPE_TRUETYPE_COLLECTION, DWRITE_FONT_FACE_TYPE_VECTOR}; 19 | use winapi::um::dwrite::{DWRITE_FONT_SIMULATIONS, DWRITE_GLYPH_METRICS}; 20 | use winapi::um::dwrite::{DWRITE_GLYPH_OFFSET, DWRITE_MATRIX, DWRITE_RENDERING_MODE}; 21 | use winapi::um::dwrite::{DWRITE_RENDERING_MODE_DEFAULT, DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC}; 22 | use winapi::um::dwrite_1::IDWriteFontFace1; 23 | use winapi::um::dwrite_3::{ 24 | IDWriteFontFace5, IDWriteFontResource, DWRITE_FONT_AXIS_ATTRIBUTES_VARIABLE, 25 | DWRITE_FONT_AXIS_VALUE, 26 | }; 27 | use winapi::um::winnt::HRESULT; 28 | use winapi::Interface; 29 | use wio::com::ComPtr; 30 | 31 | use super::{DWriteFactory, DefaultDWriteRenderParams, FontFile, FontMetrics}; 32 | use crate::com_helpers::Com; 33 | use crate::geometry_sink_impl::GeometrySinkImpl; 34 | use crate::outline_builder::OutlineBuilder; 35 | use crate::FontSimulations; 36 | 37 | pub struct FontFace { 38 | native: UnsafeCell>, 39 | face1: UnsafeCell>>, 40 | face5: UnsafeCell>>, 41 | } 42 | 43 | impl FontFace { 44 | pub fn take(native: ComPtr) -> FontFace { 45 | let cell = UnsafeCell::new(native); 46 | FontFace { 47 | native: cell, 48 | face1: UnsafeCell::new(None), 49 | face5: UnsafeCell::new(None), 50 | } 51 | } 52 | 53 | pub unsafe fn as_ptr(&self) -> *mut IDWriteFontFace { 54 | (*self.native.get()).as_raw() 55 | } 56 | 57 | unsafe fn raw_files(&self) -> Result, HRESULT> { 58 | let mut number_of_files: u32 = 0; 59 | let hr = (*self.native.get()).GetFiles(&mut number_of_files, ptr::null_mut()); 60 | if hr != S_OK { 61 | return Err(hr); 62 | } 63 | 64 | let mut file_ptrs: Vec<*mut IDWriteFontFile> = 65 | vec![ptr::null_mut(); number_of_files as usize]; 66 | let hr = (*self.native.get()).GetFiles(&mut number_of_files, file_ptrs.as_mut_ptr()); 67 | if hr != S_OK { 68 | return Err(hr); 69 | } 70 | Ok(file_ptrs) 71 | } 72 | 73 | #[deprecated(note = "Use `files` instead.")] 74 | pub fn get_files(&self) -> Vec { 75 | self.files().unwrap() 76 | } 77 | 78 | pub fn files(&self) -> Result, HRESULT> { 79 | unsafe { 80 | self.raw_files().map(|file_ptrs| { 81 | file_ptrs 82 | .iter() 83 | .map(|p| FontFile::take(ComPtr::from_raw(*p))) 84 | .collect() 85 | }) 86 | } 87 | } 88 | 89 | pub fn create_font_face_with_simulations( 90 | &self, 91 | simulations: DWRITE_FONT_SIMULATIONS, 92 | ) -> FontFace { 93 | unsafe { 94 | let file_ptrs = self.raw_files().unwrap(); 95 | let face_type = (*self.native.get()).GetType(); 96 | let face_index = (*self.native.get()).GetIndex(); 97 | let mut face: *mut IDWriteFontFace = ptr::null_mut(); 98 | let hr = (*DWriteFactory()).CreateFontFace( 99 | face_type, 100 | file_ptrs.len() as u32, 101 | file_ptrs.as_ptr(), 102 | face_index, 103 | simulations, 104 | &mut face, 105 | ); 106 | for p in file_ptrs { 107 | let _ = ComPtr::::from_raw(p); 108 | } 109 | assert!(hr == 0); 110 | FontFace::take(ComPtr::from_raw(face)) 111 | } 112 | } 113 | 114 | pub fn get_glyph_count(&self) -> u16 { 115 | unsafe { (*self.native.get()).GetGlyphCount() } 116 | } 117 | 118 | pub fn metrics(&self) -> FontMetrics { 119 | unsafe { 120 | let font_1 = self.get_face1(); 121 | match font_1 { 122 | None => { 123 | let mut metrics = mem::zeroed(); 124 | (*self.native.get()).GetMetrics(&mut metrics); 125 | FontMetrics::Metrics0(metrics) 126 | } 127 | Some(font_1) => { 128 | let mut metrics_1 = mem::zeroed(); 129 | font_1.GetMetrics(&mut metrics_1); 130 | FontMetrics::Metrics1(metrics_1) 131 | } 132 | } 133 | } 134 | } 135 | 136 | #[deprecated(note = "Use `glyph_indices` instead.")] 137 | pub fn get_glyph_indices(&self, code_points: &[u32]) -> Vec { 138 | self.glyph_indices(code_points).unwrap() 139 | } 140 | 141 | pub fn glyph_indices(&self, code_points: &[u32]) -> Result, HRESULT> { 142 | let mut glyph_indices: Vec = vec![0; code_points.len()]; 143 | unsafe { 144 | let hr = (*self.native.get()).GetGlyphIndices( 145 | code_points.as_ptr(), 146 | code_points.len() as u32, 147 | glyph_indices.as_mut_ptr(), 148 | ); 149 | if hr != S_OK { 150 | return Err(hr); 151 | } 152 | Ok(glyph_indices) 153 | } 154 | } 155 | 156 | #[deprecated(note = "Use `design_glyph_metrics` instead.")] 157 | pub fn get_design_glyph_metrics( 158 | &self, 159 | glyph_indices: &[u16], 160 | is_sideways: bool, 161 | ) -> Vec { 162 | self.design_glyph_metrics(glyph_indices, is_sideways) 163 | .unwrap() 164 | } 165 | 166 | pub fn design_glyph_metrics( 167 | &self, 168 | glyph_indices: &[u16], 169 | is_sideways: bool, 170 | ) -> Result, HRESULT> { 171 | unsafe { 172 | let mut metrics: Vec = vec![zeroed(); glyph_indices.len()]; 173 | let hr = (*self.native.get()).GetDesignGlyphMetrics( 174 | glyph_indices.as_ptr(), 175 | glyph_indices.len() as u32, 176 | metrics.as_mut_ptr(), 177 | is_sideways as BOOL, 178 | ); 179 | if hr != S_OK { 180 | return Err(hr); 181 | } 182 | Ok(metrics) 183 | } 184 | } 185 | 186 | #[deprecated(note = "Use `gdi_compatible_glyph_metrics` instead.")] 187 | pub fn get_gdi_compatible_glyph_metrics( 188 | &self, 189 | em_size: f32, 190 | pixels_per_dip: f32, 191 | transform: *const DWRITE_MATRIX, 192 | use_gdi_natural: bool, 193 | glyph_indices: &[u16], 194 | is_sideways: bool, 195 | ) -> Vec { 196 | self.gdi_compatible_glyph_metrics( 197 | em_size, 198 | pixels_per_dip, 199 | transform, 200 | use_gdi_natural, 201 | glyph_indices, 202 | is_sideways, 203 | ) 204 | .unwrap() 205 | } 206 | 207 | pub fn gdi_compatible_glyph_metrics( 208 | &self, 209 | em_size: f32, 210 | pixels_per_dip: f32, 211 | transform: *const DWRITE_MATRIX, 212 | use_gdi_natural: bool, 213 | glyph_indices: &[u16], 214 | is_sideways: bool, 215 | ) -> Result, HRESULT> { 216 | unsafe { 217 | let mut metrics: Vec = vec![zeroed(); glyph_indices.len()]; 218 | let hr = (*self.native.get()).GetGdiCompatibleGlyphMetrics( 219 | em_size, 220 | pixels_per_dip, 221 | transform, 222 | use_gdi_natural as BOOL, 223 | glyph_indices.as_ptr(), 224 | glyph_indices.len() as u32, 225 | metrics.as_mut_ptr(), 226 | is_sideways as BOOL, 227 | ); 228 | if hr != S_OK { 229 | return Err(hr); 230 | } 231 | Ok(metrics) 232 | } 233 | } 234 | 235 | #[deprecated(note = "Use `font_table` instead.")] 236 | pub fn get_font_table(&self, opentype_table_tag: u32) -> Option> { 237 | self.font_table(opentype_table_tag).unwrap() 238 | } 239 | 240 | /// Returns the contents of the OpenType table with the given tag. 241 | /// 242 | /// NB: The bytes of the tag are reversed! You probably want to use the `u32::swap_bytes()` 243 | /// method on the tag value before calling this method. 244 | pub fn font_table(&self, opentype_table_tag: u32) -> Result>, HRESULT> { 245 | let mut table_data_ptr: *const u8 = ptr::null_mut(); 246 | let mut table_size: u32 = 0; 247 | let mut table_context: *mut c_void = ptr::null_mut(); 248 | let mut exists: BOOL = FALSE; 249 | unsafe { 250 | let hr = (*self.native.get()).TryGetFontTable( 251 | opentype_table_tag, 252 | &mut table_data_ptr as *mut *const _ as *mut *const c_void, 253 | &mut table_size, 254 | &mut table_context, 255 | &mut exists, 256 | ); 257 | if hr != S_OK { 258 | return Err(hr); 259 | } 260 | 261 | if exists == FALSE { 262 | return Ok(None); 263 | } 264 | 265 | let table_bytes = slice::from_raw_parts(table_data_ptr, table_size as usize).to_vec(); 266 | 267 | (*self.native.get()).ReleaseFontTable(table_context); 268 | 269 | Ok(Some(table_bytes)) 270 | } 271 | } 272 | 273 | pub fn get_recommended_rendering_mode( 274 | &self, 275 | em_size: f32, 276 | pixels_per_dip: f32, 277 | measure_mode: DWRITE_MEASURING_MODE, 278 | rendering_params: *mut IDWriteRenderingParams, 279 | ) -> DWRITE_RENDERING_MODE { 280 | unsafe { 281 | let mut render_mode: DWRITE_RENDERING_MODE = DWRITE_RENDERING_MODE_DEFAULT; 282 | let hr = (*self.native.get()).GetRecommendedRenderingMode( 283 | em_size, 284 | pixels_per_dip, 285 | measure_mode, 286 | rendering_params, 287 | &mut render_mode, 288 | ); 289 | 290 | if hr != S_OK { 291 | return DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC; 292 | } 293 | 294 | render_mode 295 | } 296 | } 297 | 298 | pub fn get_recommended_rendering_mode_default_params( 299 | &self, 300 | em_size: f32, 301 | pixels_per_dip: f32, 302 | measure_mode: DWRITE_MEASURING_MODE, 303 | ) -> DWRITE_RENDERING_MODE { 304 | self.get_recommended_rendering_mode( 305 | em_size, 306 | pixels_per_dip, 307 | measure_mode, 308 | DefaultDWriteRenderParams(), 309 | ) 310 | } 311 | 312 | #[deprecated(note = "Use `glyph_run_outline` instead.")] 313 | pub fn get_glyph_run_outline( 314 | &self, 315 | em_size: f32, 316 | glyph_indices: &[u16], 317 | glyph_advances: Option<&[f32]>, 318 | glyph_offsets: Option<&[DWRITE_GLYPH_OFFSET]>, 319 | is_sideways: bool, 320 | is_right_to_left: bool, 321 | outline_builder: Box, 322 | ) { 323 | self.glyph_run_outline( 324 | em_size, 325 | glyph_indices, 326 | glyph_advances, 327 | glyph_offsets, 328 | is_sideways, 329 | is_right_to_left, 330 | outline_builder, 331 | ) 332 | .unwrap() 333 | } 334 | 335 | pub fn glyph_run_outline( 336 | &self, 337 | em_size: f32, 338 | glyph_indices: &[u16], 339 | glyph_advances: Option<&[f32]>, 340 | glyph_offsets: Option<&[DWRITE_GLYPH_OFFSET]>, 341 | is_sideways: bool, 342 | is_right_to_left: bool, 343 | outline_builder: Box, 344 | ) -> Result<(), GlyphRunOutlineError> { 345 | let glyph_advances = match glyph_advances { 346 | None => ptr::null(), 347 | Some(glyph_advances) => { 348 | if glyph_advances.len() != glyph_indices.len() { 349 | return Err(GlyphRunOutlineError::InvalidInput); 350 | } 351 | glyph_advances.as_ptr() 352 | } 353 | }; 354 | let glyph_offsets = match glyph_offsets { 355 | None => ptr::null(), 356 | Some(glyph_offsets) => { 357 | if glyph_offsets.len() != glyph_indices.len() { 358 | return Err(GlyphRunOutlineError::InvalidInput); 359 | } 360 | glyph_offsets.as_ptr() 361 | } 362 | }; 363 | let is_sideways = if is_sideways { TRUE } else { FALSE }; 364 | let is_right_to_left = if is_right_to_left { TRUE } else { FALSE }; 365 | let geometry_sink = GeometrySinkImpl::new(outline_builder); 366 | let geometry_sink = geometry_sink.into_interface(); 367 | unsafe { 368 | let hr = (*self.native.get()).GetGlyphRunOutline( 369 | em_size, 370 | glyph_indices.as_ptr(), 371 | glyph_advances, 372 | glyph_offsets, 373 | glyph_indices.len() as u32, 374 | is_sideways, 375 | is_right_to_left, 376 | geometry_sink, 377 | ); 378 | if hr != S_OK { 379 | return Err(GlyphRunOutlineError::Win32Error(hr)); 380 | } 381 | } 382 | Ok(()) 383 | } 384 | 385 | pub fn has_kerning_pairs(&self) -> bool { 386 | unsafe { 387 | match self.get_face1() { 388 | Some(face1) => face1.HasKerningPairs() == TRUE, 389 | None => false, 390 | } 391 | } 392 | } 393 | 394 | #[deprecated(note = "Use `glyph_pair_kerning_adjustment` instead.")] 395 | pub fn get_glyph_pair_kerning_adjustment(&self, first_glyph: u16, second_glyph: u16) -> i32 { 396 | self.glyph_pair_kerning_adjustment(first_glyph, second_glyph) 397 | .unwrap() 398 | } 399 | 400 | pub fn glyph_pair_kerning_adjustment( 401 | &self, 402 | first_glyph: u16, 403 | second_glyph: u16, 404 | ) -> Result { 405 | unsafe { 406 | match self.get_face1() { 407 | Some(face1) => { 408 | let mut adjustments = [0; 2]; 409 | let hr = face1.GetKerningPairAdjustments( 410 | 2, 411 | [first_glyph, second_glyph].as_ptr(), 412 | adjustments.as_mut_ptr(), 413 | ); 414 | if hr != S_OK { 415 | return Err(hr); 416 | } 417 | 418 | Ok(adjustments[0]) 419 | } 420 | None => Ok(0), 421 | } 422 | } 423 | } 424 | 425 | #[inline] 426 | pub fn get_type(&self) -> FontFaceType { 427 | unsafe { 428 | match (*self.native.get()).GetType() { 429 | DWRITE_FONT_FACE_TYPE_CFF => FontFaceType::Cff, 430 | DWRITE_FONT_FACE_TYPE_RAW_CFF => FontFaceType::RawCff, 431 | DWRITE_FONT_FACE_TYPE_TRUETYPE => FontFaceType::TrueType, 432 | DWRITE_FONT_FACE_TYPE_TRUETYPE_COLLECTION => FontFaceType::TrueTypeCollection, 433 | DWRITE_FONT_FACE_TYPE_TYPE1 => FontFaceType::Type1, 434 | DWRITE_FONT_FACE_TYPE_VECTOR => FontFaceType::Vector, 435 | DWRITE_FONT_FACE_TYPE_BITMAP => FontFaceType::Bitmap, 436 | _ => FontFaceType::Unknown, 437 | } 438 | } 439 | } 440 | 441 | #[inline] 442 | pub fn get_index(&self) -> u32 { 443 | unsafe { (*self.native.get()).GetIndex() } 444 | } 445 | 446 | #[inline] 447 | unsafe fn get_face1(&self) -> Option> { 448 | self.get_interface(&self.face1) 449 | } 450 | 451 | #[inline] 452 | unsafe fn get_face5(&self) -> Option> { 453 | self.get_interface(&self.face5) 454 | } 455 | 456 | #[inline] 457 | unsafe fn get_interface( 458 | &self, 459 | interface: &UnsafeCell>>, 460 | ) -> Option> { 461 | if (*interface.get()).is_none() { 462 | *interface.get() = (*self.native.get()).cast().ok() 463 | } 464 | (*interface.get()).clone() 465 | } 466 | 467 | pub fn has_variations(&self) -> bool { 468 | unsafe { 469 | match self.get_face5() { 470 | Some(face5) => face5.HasVariations() == TRUE, 471 | None => false, 472 | } 473 | } 474 | } 475 | 476 | /// If this font has variations, return a [`Vec Result, HRESULT> { 480 | let face5 = unsafe { self.get_face5() }; 481 | let Some(face5) = face5 else { 482 | return Ok(vec![]); 483 | }; 484 | if unsafe { face5.HasVariations() != TRUE } { 485 | return Ok(vec![]); 486 | } 487 | let axis_count = unsafe { face5.GetFontAxisValueCount() as usize }; 488 | if axis_count == 0 { 489 | return Ok(vec![]); 490 | } 491 | 492 | let mut resource: *mut IDWriteFontResource = ptr::null_mut(); 493 | let hr = unsafe { face5.GetFontResource(&mut resource) }; 494 | if hr != S_OK || resource.is_null() { 495 | return Err(hr); 496 | } 497 | 498 | let mut axis_values = Vec::with_capacity(axis_count); 499 | axis_values.resize( 500 | axis_count, 501 | DWRITE_FONT_AXIS_VALUE { 502 | axisTag: 0, 503 | value: 0., 504 | }, 505 | ); 506 | 507 | let hr = unsafe { face5.GetFontAxisValues(axis_values.as_mut_ptr(), axis_count as u32) }; 508 | if hr != S_OK { 509 | return Err(hr); 510 | } 511 | 512 | let resource = unsafe { &*resource }; 513 | Ok(axis_values 514 | .iter() 515 | .enumerate() 516 | .filter_map(|(index, axis_value)| { 517 | let attributes = unsafe { resource.GetFontAxisAttributes(index as u32) }; 518 | if attributes & DWRITE_FONT_AXIS_ATTRIBUTES_VARIABLE == 0 { 519 | None 520 | } else { 521 | Some(*axis_value) 522 | } 523 | }) 524 | .collect()) 525 | } 526 | 527 | pub fn create_font_face_with_variations( 528 | &self, 529 | simulations: DWRITE_FONT_SIMULATIONS, 530 | axis_values: &[DWRITE_FONT_AXIS_VALUE], 531 | ) -> Option { 532 | unsafe { 533 | if let Some(face5) = self.get_face5() { 534 | let mut resource: *mut IDWriteFontResource = ptr::null_mut(); 535 | let hr = face5.GetFontResource(&mut resource); 536 | if hr == S_OK && !resource.is_null() { 537 | let resource = ComPtr::from_raw(resource); 538 | let mut var_face: *mut IDWriteFontFace5 = ptr::null_mut(); 539 | let hr = resource.CreateFontFace( 540 | simulations, 541 | axis_values.as_ptr(), 542 | axis_values.len() as u32, 543 | &mut var_face, 544 | ); 545 | if hr == S_OK && !var_face.is_null() { 546 | let var_face = ComPtr::from_raw(var_face).cast().unwrap(); 547 | return Some(FontFace::take(var_face)); 548 | } 549 | } 550 | } 551 | None 552 | } 553 | } 554 | 555 | pub fn simulations(&self) -> FontSimulations { 556 | unsafe { 557 | std::mem::transmute::( 558 | (*self.native.get()).GetSimulations(), 559 | ) 560 | } 561 | } 562 | } 563 | 564 | impl Clone for FontFace { 565 | fn clone(&self) -> FontFace { 566 | unsafe { 567 | FontFace { 568 | native: UnsafeCell::new((*self.native.get()).clone()), 569 | face1: UnsafeCell::new(None), 570 | face5: UnsafeCell::new(None), 571 | } 572 | } 573 | } 574 | } 575 | 576 | #[derive(Clone, Copy, Debug, PartialEq)] 577 | pub enum FontFaceType { 578 | Unknown, 579 | Cff, 580 | RawCff, 581 | TrueType, 582 | TrueTypeCollection, 583 | Type1, 584 | Vector, 585 | Bitmap, 586 | } 587 | 588 | #[derive(Debug)] 589 | pub enum GlyphRunOutlineError { 590 | InvalidInput, 591 | Win32Error(HRESULT), 592 | } 593 | 594 | impl fmt::Display for GlyphRunOutlineError { 595 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 596 | match self { 597 | Self::InvalidInput => write!(f, "Invalid input"), 598 | Self::Win32Error(code) => write!(f, "{:#x}", code), 599 | } 600 | } 601 | } 602 | 603 | impl error::Error for GlyphRunOutlineError {} 604 | --------------------------------------------------------------------------------