Skip to content

Add fallible versions of FontFace methods #66

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 31, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
216 changes: 167 additions & 49 deletions src/font_face.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

Expand Down Expand Up @@ -49,25 +50,35 @@ impl FontFace {
(*self.native.get()).as_raw()
}

unsafe fn get_raw_files(&self) -> Vec<*mut IDWriteFontFile> {
unsafe fn raw_files(&self) -> Result<Vec<*mut IDWriteFontFile>, HRESULT> {
let mut number_of_files: u32 = 0;
let hr = (*self.native.get()).GetFiles(&mut number_of_files, ptr::null_mut());
assert!(hr == 0);
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
if hr != S_OK {
return Err(hr);
}
Ok(file_ptrs)
}

#[deprecated(note = "Use `files` instead.")]
pub fn get_files(&self) -> Vec<FontFile> {
self.files().unwrap()
}

pub fn files(&self) -> Result<Vec<FontFile>, HRESULT> {
unsafe {
let file_ptrs = self.get_raw_files();
file_ptrs
.iter()
.map(|p| FontFile::take(ComPtr::from_raw(*p)))
.collect()
self.raw_files().map(|file_ptrs| {
file_ptrs
.iter()
.map(|p| FontFile::take(ComPtr::from_raw(*p)))
.collect()
})
}
}

Expand All @@ -76,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();
Copy link
Author

@acarl005 acarl005 Mar 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to #64, there is no obvious name change here for a non-panicking method for create_font_face_with_simulations and it can be changed in the next API-breaking release

let face_type = (*self.native.get()).GetType();
let face_index = (*self.native.get()).GetIndex();
let mut face: *mut IDWriteFontFace = ptr::null_mut();
Expand Down Expand Up @@ -118,24 +129,41 @@ impl FontFace {
}
}

#[deprecated(note = "Use `glyph_indices` instead.")]
pub fn get_glyph_indices(&self, code_points: &[u32]) -> Vec<u16> {
self.glyph_indices(code_points).unwrap()
}

pub fn glyph_indices(&self, code_points: &[u32]) -> Result<Vec<u16>, HRESULT> {
let mut glyph_indices: Vec<u16> = vec![0; code_points.len()];
unsafe {
let mut glyph_indices: Vec<u16> = 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<DWRITE_GLYPH_METRICS> {
self.design_glyph_metrics(glyph_indices, is_sideways)
.unwrap()
}

pub fn design_glyph_metrics(
&self,
glyph_indices: &[u16],
is_sideways: bool,
) -> Result<Vec<DWRITE_GLYPH_METRICS>, HRESULT> {
unsafe {
let mut metrics: Vec<DWRITE_GLYPH_METRICS> = vec![zeroed(); glyph_indices.len()];
let hr = (*self.native.get()).GetDesignGlyphMetrics(
Expand All @@ -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,
Expand All @@ -158,6 +189,26 @@ impl FontFace {
glyph_indices: &[u16],
is_sideways: bool,
) -> Vec<DWRITE_GLYPH_METRICS> {
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<Vec<DWRITE_GLYPH_METRICS>, HRESULT> {
unsafe {
let mut metrics: Vec<DWRITE_GLYPH_METRICS> = vec![zeroed(); glyph_indices.len()];
let hr = (*self.native.get()).GetGdiCompatibleGlyphMetrics(
Expand All @@ -170,40 +221,48 @@ 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<Vec<u8>> {
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<Vec<u8>> {
pub fn font_table(&self, opentype_table_tag: u32) -> Result<Option<Vec<u8>>, 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,
&mut table_size,
&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))
}
}

Expand All @@ -224,7 +283,7 @@ impl FontFace {
&mut render_mode,
);

if hr != 0 {
if hr != S_OK {
return DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC;
}

Expand All @@ -246,6 +305,7 @@ impl FontFace {
)
}

#[deprecated(note = "Use `glyph_run_outline` instead.")]
pub fn get_glyph_run_outline(
&self,
em_size: f32,
Expand All @@ -256,25 +316,51 @@ impl FontFace {
is_right_to_left: bool,
outline_builder: Box<dyn OutlineBuilder>,
) {
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<dyn OutlineBuilder>,
) -> 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(),
Expand All @@ -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 {
Expand All @@ -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<i32, HRESULT> {
unsafe {
match self.get_face1() {
Some(face1) => {
Expand All @@ -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),
}
}
}
Expand Down Expand Up @@ -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 {}