From ef235a305981d8d6668591c94a0179d073669bc8 Mon Sep 17 00:00:00 2001 From: Andy Carlson <2yinyang2@gmail.com> Date: Sun, 30 Mar 2025 09:25:36 -0400 Subject: [PATCH 1/3] Add fallible versions of FontFace methods --- src/font_face.rs | 220 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 169 insertions(+), 51 deletions(-) diff --git a/src/font_face.rs b/src/font_face.rs index 2e3d149..a2295a8 100644 --- a/src/font_face.rs +++ b/src/font_face.rs @@ -4,8 +4,8 @@ use std::cell::UnsafeCell; use std::mem::{self, zeroed}; -use std::ptr; use std::slice; +use std::{error, fmt, ptr}; use winapi::ctypes::c_void; use winapi::shared::minwindef::{BOOL, FALSE, TRUE}; use winapi::shared::winerror::S_OK; @@ -21,6 +21,7 @@ use winapi::um::dwrite::{DWRITE_GLYPH_OFFSET, DWRITE_MATRIX, DWRITE_RENDERING_MO use winapi::um::dwrite::{DWRITE_RENDERING_MODE_DEFAULT, DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC}; use winapi::um::dwrite_1::IDWriteFontFace1; use winapi::um::dwrite_3::{IDWriteFontFace5, IDWriteFontResource, DWRITE_FONT_AXIS_VALUE}; +use winapi::um::winnt::HRESULT; use winapi::Interface; use wio::com::ComPtr; @@ -49,26 +50,36 @@ impl FontFace { (*self.native.get()).as_raw() } - unsafe fn get_raw_files(&self) -> Vec<*mut IDWriteFontFile> { + fn raw_files(&self) -> Result, HRESULT> { let mut number_of_files: u32 = 0; - let hr = (*self.native.get()).GetFiles(&mut number_of_files, ptr::null_mut()); - assert!(hr == 0); + unsafe { + let hr = (*self.native.get()).GetFiles(&mut number_of_files, ptr::null_mut()); + if hr != S_OK { + return Err(hr); + } - let mut file_ptrs: Vec<*mut IDWriteFontFile> = - vec![ptr::null_mut(); number_of_files as usize]; - let hr = (*self.native.get()).GetFiles(&mut number_of_files, file_ptrs.as_mut_ptr()); - assert!(hr == 0); - file_ptrs + let mut file_ptrs: Vec<*mut IDWriteFontFile> = + vec![ptr::null_mut(); number_of_files as usize]; + let hr = (*self.native.get()).GetFiles(&mut number_of_files, file_ptrs.as_mut_ptr()); + if hr != S_OK { + return Err(hr); + } + Ok(file_ptrs) + } } + #[deprecated(note = "Use `files` instead.")] pub fn get_files(&self) -> Vec { - unsafe { - let file_ptrs = self.get_raw_files(); + self.files().unwrap() + } + + pub fn files(&self) -> Result, HRESULT> { + self.raw_files().map(|file_ptrs| { file_ptrs .iter() - .map(|p| FontFile::take(ComPtr::from_raw(*p))) + .map(|p| unsafe { FontFile::take(ComPtr::from_raw(*p)) }) .collect() - } + }) } pub fn create_font_face_with_simulations( @@ -118,24 +129,41 @@ impl FontFace { } } + #[deprecated(note = "Use `glyph_indices` instead.")] pub fn get_glyph_indices(&self, code_points: &[u32]) -> Vec { + self.glyph_indices(code_points).unwrap() + } + + pub fn glyph_indices(&self, code_points: &[u32]) -> Result, HRESULT> { + let mut glyph_indices: Vec = vec![0; code_points.len()]; unsafe { - let mut glyph_indices: Vec = vec![0; code_points.len()]; let hr = (*self.native.get()).GetGlyphIndices( code_points.as_ptr(), code_points.len() as u32, glyph_indices.as_mut_ptr(), ); - assert!(hr == 0); - glyph_indices + if hr != S_OK { + return Err(hr); + } + Ok(glyph_indices) } } + #[deprecated(note = "Use `design_glyph_metrics` instead.")] pub fn get_design_glyph_metrics( &self, glyph_indices: &[u16], is_sideways: bool, ) -> Vec { + self.design_glyph_metrics(glyph_indices, is_sideways) + .unwrap() + } + + pub fn design_glyph_metrics( + &self, + glyph_indices: &[u16], + is_sideways: bool, + ) -> Result, HRESULT> { unsafe { let mut metrics: Vec = vec![zeroed(); glyph_indices.len()]; let hr = (*self.native.get()).GetDesignGlyphMetrics( @@ -144,11 +172,14 @@ impl FontFace { metrics.as_mut_ptr(), is_sideways as BOOL, ); - assert!(hr == 0); - metrics + if hr != S_OK { + return Err(hr); + } + Ok(metrics) } } + #[deprecated(note = "Use `gdi_compatible_glyph_metrics` instead.")] pub fn get_gdi_compatible_glyph_metrics( &self, em_size: f32, @@ -158,6 +189,26 @@ impl FontFace { glyph_indices: &[u16], is_sideways: bool, ) -> Vec { + self.gdi_compatible_glyph_metrics( + em_size, + pixels_per_dip, + transform, + use_gdi_natural, + glyph_indices, + is_sideways, + ) + .unwrap() + } + + pub fn gdi_compatible_glyph_metrics( + &self, + em_size: f32, + pixels_per_dip: f32, + transform: *const DWRITE_MATRIX, + use_gdi_natural: bool, + glyph_indices: &[u16], + is_sideways: bool, + ) -> Result, HRESULT> { unsafe { let mut metrics: Vec = vec![zeroed(); glyph_indices.len()]; let hr = (*self.native.get()).GetGdiCompatibleGlyphMetrics( @@ -170,22 +221,28 @@ impl FontFace { metrics.as_mut_ptr(), is_sideways as BOOL, ); - assert!(hr == 0); - metrics + if hr != S_OK { + return Err(hr); + } + Ok(metrics) } } + #[deprecated(note = "Use `font_table` instead.")] + pub fn get_font_table(&self, opentype_table_tag: u32) -> Option> { + self.font_table(opentype_table_tag).unwrap() + } + /// Returns the contents of the OpenType table with the given tag. /// /// NB: The bytes of the tag are reversed! You probably want to use the `u32::swap_bytes()` /// method on the tag value before calling this method. - pub fn get_font_table(&self, opentype_table_tag: u32) -> Option> { + pub fn font_table(&self, opentype_table_tag: u32) -> Result>, HRESULT> { + let mut table_data_ptr: *const u8 = ptr::null_mut(); + let mut table_size: u32 = 0; + let mut table_context: *mut c_void = ptr::null_mut(); + let mut exists: BOOL = FALSE; unsafe { - let mut table_data_ptr: *const u8 = ptr::null_mut(); - let mut table_size: u32 = 0; - let mut table_context: *mut c_void = ptr::null_mut(); - let mut exists: BOOL = FALSE; - let hr = (*self.native.get()).TryGetFontTable( opentype_table_tag, &mut table_data_ptr as *mut *const _ as *mut *const c_void, @@ -193,17 +250,19 @@ impl FontFace { &mut table_context, &mut exists, ); - assert!(hr == 0); + if hr != S_OK { + return Err(hr); + } if exists == FALSE { - return None; + return Ok(None); } let table_bytes = slice::from_raw_parts(table_data_ptr, table_size as usize).to_vec(); (*self.native.get()).ReleaseFontTable(table_context); - Some(table_bytes) + Ok(Some(table_bytes)) } } @@ -224,7 +283,7 @@ impl FontFace { &mut render_mode, ); - if hr != 0 { + if hr != S_OK { return DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC; } @@ -246,6 +305,7 @@ impl FontFace { ) } + #[deprecated(note = "Use `glyph_run_outline` instead.")] pub fn get_glyph_run_outline( &self, em_size: f32, @@ -256,25 +316,51 @@ impl FontFace { is_right_to_left: bool, outline_builder: Box, ) { - unsafe { - let glyph_advances = match glyph_advances { - None => ptr::null(), - Some(glyph_advances) => { - assert_eq!(glyph_advances.len(), glyph_indices.len()); - glyph_advances.as_ptr() + self.glyph_run_outline( + em_size, + glyph_indices, + glyph_advances, + glyph_offsets, + is_sideways, + is_right_to_left, + outline_builder, + ) + .unwrap() + } + + pub fn glyph_run_outline( + &self, + em_size: f32, + glyph_indices: &[u16], + glyph_advances: Option<&[f32]>, + glyph_offsets: Option<&[DWRITE_GLYPH_OFFSET]>, + is_sideways: bool, + is_right_to_left: bool, + outline_builder: Box, + ) -> Result<(), GlyphRunOutlineError> { + let glyph_advances = match glyph_advances { + None => ptr::null(), + Some(glyph_advances) => { + if glyph_advances.len() != glyph_indices.len() { + return Err(GlyphRunOutlineError::InvalidInput); } - }; - let glyph_offsets = match glyph_offsets { - None => ptr::null(), - Some(glyph_offsets) => { - assert_eq!(glyph_offsets.len(), glyph_indices.len()); - glyph_offsets.as_ptr() + glyph_advances.as_ptr() + } + }; + let glyph_offsets = match glyph_offsets { + None => ptr::null(), + Some(glyph_offsets) => { + if glyph_offsets.len() != glyph_indices.len() { + return Err(GlyphRunOutlineError::InvalidInput); } - }; - let is_sideways = if is_sideways { TRUE } else { FALSE }; - let is_right_to_left = if is_right_to_left { TRUE } else { FALSE }; - let geometry_sink = GeometrySinkImpl::new(outline_builder); - let geometry_sink = geometry_sink.into_interface(); + glyph_offsets.as_ptr() + } + }; + let is_sideways = if is_sideways { TRUE } else { FALSE }; + let is_right_to_left = if is_right_to_left { TRUE } else { FALSE }; + let geometry_sink = GeometrySinkImpl::new(outline_builder); + let geometry_sink = geometry_sink.into_interface(); + unsafe { let hr = (*self.native.get()).GetGlyphRunOutline( em_size, glyph_indices.as_ptr(), @@ -285,8 +371,11 @@ impl FontFace { is_right_to_left, geometry_sink, ); - assert_eq!(hr, S_OK); + if hr != S_OK { + return Err(GlyphRunOutlineError::Win32Error(hr)); + } } + Ok(()) } pub fn has_kerning_pairs(&self) -> bool { @@ -298,7 +387,17 @@ impl FontFace { } } + #[deprecated(note = "Use `glyph_pair_kerning_adjustment` instead.")] pub fn get_glyph_pair_kerning_adjustment(&self, first_glyph: u16, second_glyph: u16) -> i32 { + self.glyph_pair_kerning_adjustment(first_glyph, second_glyph) + .unwrap() + } + + pub fn glyph_pair_kerning_adjustment( + &self, + first_glyph: u16, + second_glyph: u16, + ) -> Result { unsafe { match self.get_face1() { Some(face1) => { @@ -308,11 +407,13 @@ impl FontFace { [first_glyph, second_glyph].as_ptr(), adjustments.as_mut_ptr(), ); - assert_eq!(hr, S_OK); + if hr != S_OK { + return Err(hr); + } - adjustments[0] + Ok(adjustments[0]) } - None => 0, + None => Ok(0), } } } @@ -420,3 +521,20 @@ pub enum FontFaceType { Vector, Bitmap, } + +#[derive(Debug)] +pub enum GlyphRunOutlineError { + InvalidInput, + Win32Error(HRESULT), +} + +impl fmt::Display for GlyphRunOutlineError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::InvalidInput => write!(f, "Invalid input"), + Self::Win32Error(code) => write!(f, "{:#x}", code), + } + } +} + +impl error::Error for GlyphRunOutlineError {} From 9eff7108d557766b3b5e6a865b535ac40e788b16 Mon Sep 17 00:00:00 2001 From: Andy Carlson <2yinyang2@gmail.com> Date: Sun, 30 Mar 2025 09:30:27 -0400 Subject: [PATCH 2/3] fix unsafe fn raw_files --- src/font_face.rs | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/font_face.rs b/src/font_face.rs index a2295a8..f3225e2 100644 --- a/src/font_face.rs +++ b/src/font_face.rs @@ -50,22 +50,20 @@ impl FontFace { (*self.native.get()).as_raw() } - fn raw_files(&self) -> Result, HRESULT> { + unsafe fn raw_files(&self) -> Result, HRESULT> { let mut number_of_files: u32 = 0; - unsafe { - let hr = (*self.native.get()).GetFiles(&mut number_of_files, ptr::null_mut()); - if hr != S_OK { - return Err(hr); - } + let hr = (*self.native.get()).GetFiles(&mut number_of_files, ptr::null_mut()); + if hr != S_OK { + return Err(hr); + } - let mut file_ptrs: Vec<*mut IDWriteFontFile> = - vec![ptr::null_mut(); number_of_files as usize]; - let hr = (*self.native.get()).GetFiles(&mut number_of_files, file_ptrs.as_mut_ptr()); - if hr != S_OK { - return Err(hr); - } - Ok(file_ptrs) + let mut file_ptrs: Vec<*mut IDWriteFontFile> = + vec![ptr::null_mut(); number_of_files as usize]; + let hr = (*self.native.get()).GetFiles(&mut number_of_files, file_ptrs.as_mut_ptr()); + if hr != S_OK { + return Err(hr); } + Ok(file_ptrs) } #[deprecated(note = "Use `files` instead.")] @@ -74,12 +72,14 @@ impl FontFace { } pub fn files(&self) -> Result, HRESULT> { - self.raw_files().map(|file_ptrs| { - file_ptrs - .iter() - .map(|p| unsafe { FontFile::take(ComPtr::from_raw(*p)) }) - .collect() - }) + unsafe { + self.raw_files().map(|file_ptrs| { + file_ptrs + .iter() + .map(|p| FontFile::take(ComPtr::from_raw(*p))) + .collect() + }) + } } pub fn create_font_face_with_simulations( From d586ee1cf4a092298a67648668abd9ee3d82661e Mon Sep 17 00:00:00 2001 From: Andy Carlson <2yinyang2@gmail.com> Date: Sun, 30 Mar 2025 09:37:34 -0400 Subject: [PATCH 3/3] fix create_font_face_with_simulations --- src/font_face.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/font_face.rs b/src/font_face.rs index f3225e2..7d9fe4c 100644 --- a/src/font_face.rs +++ b/src/font_face.rs @@ -87,7 +87,7 @@ impl FontFace { simulations: DWRITE_FONT_SIMULATIONS, ) -> FontFace { unsafe { - let file_ptrs = self.get_raw_files(); + let file_ptrs = self.raw_files().unwrap(); let face_type = (*self.native.get()).GetType(); let face_index = (*self.native.get()).GetIndex(); let mut face: *mut IDWriteFontFace = ptr::null_mut();