Skip to content

Commit

Permalink
feat: Allow getting nominal glyph mapping from font face
Browse files Browse the repository at this point in the history
  • Loading branch information
henkkuli committed Nov 12, 2023
1 parent b6338e9 commit 493224e
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 12 deletions.
30 changes: 27 additions & 3 deletions src/font_face.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::{ffi::c_char, marker::PhantomData, ops::Deref, ptr::null_mut};

use crate::{sys, AllocationError, Blob, CharSet, FontFaceExtractionError, Language};
use crate::{sys, AllocationError, Blob, CharSet, FontFaceExtractionError, Language, Map};

/// A font face is an object that represents a single face from within a font family.
///
Expand Down Expand Up @@ -52,12 +52,22 @@ impl<'a> FontFace<'a> {

/// Collects all of the Unicode characters covered by the font face.
#[doc(alias = "hb_face_collect_unicodes")]
pub fn collect_unicodes(&self) -> Result<CharSet, AllocationError> {
pub fn covered_codepoints(&self) -> Result<CharSet, AllocationError> {
let set = CharSet::new()?;
unsafe { sys::hb_face_collect_unicodes(self.as_raw(), set.as_raw()) };
Ok(set)
}

/// Collects the mapping from Unicode characters to nominal glyphs of the face.
#[doc(alias = "hb_face_collect_nominal_glyph_mapping")]
pub fn nominal_glyph_mapping(&self) -> Result<Map<'static, char, u32>, AllocationError> {
let map = Map::new()?;
unsafe {
sys::hb_face_collect_nominal_glyph_mapping(self.as_raw(), map.as_raw(), null_mut())
};
Ok(map)
}

/// Preprocesses the face and attaches data that will be needed by the subsetter.
///
/// Future subsetting operations can use the precomputed data to speed up the subsetting operation. The
Expand Down Expand Up @@ -557,7 +567,7 @@ mod tests {
#[test]
fn loaded_font_contains_correct_number_of_codepoints_and_glyphs() {
let font_face = FontFace::new(Blob::from_file(NOTO_SANS).unwrap()).unwrap();
assert_eq!(font_face.collect_unicodes().unwrap().len(), 3094);
assert_eq!(font_face.covered_codepoints().unwrap().len(), 3094);
assert_eq!(font_face.glyph_count(), 4671);
}

Expand All @@ -568,6 +578,20 @@ mod tests {
assert_eq!(&*font_face.underlying_blob(), &*blob);
}

#[test]
fn nominal_glyph_mapping_works() {
let font_face = FontFace::new(Blob::from_file(NOTO_SANS).unwrap()).unwrap();
let map = font_face.nominal_glyph_mapping().unwrap();
assert_eq!(map.get('a').unwrap(), 68);
assert_eq!(map.get('b').unwrap(), 69);
assert_eq!(map.get('c').unwrap(), 70);
assert_eq!(map.get('d').unwrap(), 71);
assert_eq!(map.get('e').unwrap(), 72);
assert_eq!(map.get('f').unwrap(), 73);
assert_eq!(map.get('i').unwrap(), 76);
assert_eq!(map.get('ffi').unwrap(), 1656);
}

#[test]
fn convert_into_raw_and_back() {
let font_face = FontFace::new(Blob::from_file(NOTO_SANS).unwrap()).unwrap();
Expand Down
47 changes: 38 additions & 9 deletions src/subset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -371,36 +371,65 @@ mod tests {
let orig = FontFace::new(Blob::from_file(NOTO_SANS).unwrap()).unwrap();
let new = subset.subset_font(&orig).unwrap();
assert_eq!(
orig.collect_unicodes().unwrap().len(),
new.collect_unicodes().unwrap().len()
orig.covered_codepoints().unwrap().len(),
new.covered_codepoints().unwrap().len()
);
assert_eq!(orig.glyph_count(), new.glyph_count());
}

#[test]
fn keeping_codepoints_should_keep_ligatures() {
let font = FontFace::new(Blob::from_file(NOTO_SANS).unwrap()).unwrap();
let mut subset = SubsetInput::new().unwrap();
subset.unicode_set().insert('f');
subset.unicode_set().insert('i');
let font = subset
.subset_font(&FontFace::new(Blob::from_file(NOTO_SANS).unwrap()).unwrap())
.unwrap();
assert_eq!(font.collect_unicodes().unwrap().len(), 2);
let font = subset.subset_font(&font).unwrap();
assert_eq!(font.covered_codepoints().unwrap().len(), 2);
assert_eq!(font.glyph_count(), 6); // TODO: Actually check *which* glyphs are included
// Currently just assuming [empty], f, i, fi, ffi, and ff
}

#[test]
#[ignore]
fn old_to_new_glyph_mapping() {
todo!()
let font = FontFace::new(Blob::from_file(NOTO_SANS).unwrap()).unwrap();
let char_to_glyph = font.nominal_glyph_mapping().unwrap();

// Map 'a' and 'b' to arbitrary glyphs
let mut subset = SubsetInput::new().unwrap();
subset
.old_to_new_glyph_mapping()
.insert(char_to_glyph.get('a').unwrap(), 5);
subset
.old_to_new_glyph_mapping()
.insert(char_to_glyph.get('b').unwrap(), 709);
subset.unicode_set().insert('a');
subset.unicode_set().insert('b');

let font = subset.subset_font(&font).unwrap();
// Most of the glyphs should be empty
assert_eq!(font.glyph_count(), 710);

let char_to_glyph = font.nominal_glyph_mapping().unwrap();
// But the specified ones should be what we set
assert_eq!(char_to_glyph.get('a').unwrap(), 5);
assert_eq!(char_to_glyph.get('b').unwrap(), 709);
}

#[test]
fn convert_into_raw_and_back() {
fn convert_subset_into_raw_and_back() {
let subset = SubsetInput::new().unwrap();
let subset_ptr = subset.into_raw();
let subset = unsafe { SubsetInput::from_raw(subset_ptr) };
drop(subset);
}

#[test]
fn convert_plan_into_raw_and_back() {
let font = FontFace::new(Blob::from_file(NOTO_SANS).unwrap()).unwrap();
let subset = SubsetInput::new().unwrap();
let plan = subset.plan(&font).unwrap();
let plan_ptr = plan.into_raw();
let plan = unsafe { SubsetPlan::from_raw(plan_ptr) };
drop(plan);
}
}

0 comments on commit 493224e

Please sign in to comment.