Skip to content

Commit

Permalink
Merge pull request #1362 from googlefonts/subset_hvar
Browse files Browse the repository at this point in the history
[klippa] subset HVAR/VVAR tables
  • Loading branch information
qxliu76 authored Feb 25, 2025
2 parents dd282f7 + 6680a36 commit 1982f4a
Show file tree
Hide file tree
Showing 68 changed files with 1,059 additions and 7 deletions.
643 changes: 643 additions & 0 deletions klippa/src/hvar.rs

Large diffs are not rendered by default.

28 changes: 28 additions & 0 deletions klippa/src/inc_bimap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,34 @@ impl IncBiMap {
pub(crate) fn keys(&self) -> Iter<'_, u32> {
self.back_map.iter()
}

fn clear(&mut self) {
self.forw_map.clear();
self.back_map.clear();
}

// after finished adding all mappings in a random order,
// reassign rhs to lhs so that they are in the same order.
pub(crate) fn sort(&mut self) {
let mut work = self.back_map.clone();
work.sort();

self.clear();
for lhs in work.iter() {
self.add(*lhs);
}
}
}

impl FromIterator<u32> for IncBiMap {
fn from_iter<I: IntoIterator<Item = u32>>(iter: I) -> Self {
let mut m = IncBiMap::default();

for lhs in iter {
m.add(lhs);
}
m
}
}

#[cfg(test)]
Expand Down
16 changes: 15 additions & 1 deletion klippa/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ mod gvar;
mod hdmx;
mod head;
mod hmtx;
mod hvar;
mod inc_bimap;
mod layout;
mod maxp;
Expand All @@ -26,6 +27,7 @@ pub mod serialize;
mod stat;
mod variations;
mod vorg;
mod vvar;
use inc_bimap::IncBiMap;
pub use parsing_util::{
parse_name_ids, parse_name_languages, parse_tag_list, parse_unicodes, populate_gids,
Expand Down Expand Up @@ -56,12 +58,14 @@ use write_fonts::{
gvar::Gvar,
hdmx::Hdmx,
head::Head,
hvar::Hvar,
loca::Loca,
name::Name,
os2::Os2,
post::Post,
sbix::Sbix,
vorg::Vorg,
vvar::Vvar,
},
types::NameId,
FontRef, TableProvider, TopLevelTable,
Expand All @@ -75,7 +79,7 @@ const MAX_NESTING_LEVEL: u8 = 64;
// Support 24-bit gids. This should probably be extended to u32::MAX but
// this causes tests to fail with 'subtract with overflow error'.
// See <https://github.com/googlefonts/fontations/issues/997>
const MAX_GID: GlyphId = GlyphId::new(0xFFFFFF);
const MAX_GID: GlyphId = GlyphId::new(0xFFFFFFFF);

// ref: <https://github.com/harfbuzz/harfbuzz/blob/021b44388667903d7bc9c92c924ad079f13b90ce/src/hb-subset-input.cc#L82>
pub static DEFAULT_LAYOUT_FEATURES: &[Tag] = &[
Expand Down Expand Up @@ -971,6 +975,16 @@ fn subset_table<'a>(
.map_err(|_| SubsetError::SubsetTableError(Hmtx::TAG))?
.subset(plan, font, s, builder),

Hvar::TAG => font
.hvar()
.map_err(|_| SubsetError::SubsetTableError(Hvar::TAG))?
.subset(plan, font, s, builder),

Vvar::TAG => font
.vvar()
.map_err(|_| SubsetError::SubsetTableError(Vvar::TAG))?
.subset(plan, font, s, builder),

//Skip, handled by glyf
Loca::TAG => Ok(()),

Expand Down
107 changes: 103 additions & 4 deletions klippa/src/variations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,26 @@ use crate::{
serialize::{SerializeErrorFlags, Serializer},
Plan, SubsetTable,
};
use fnv::FnvHashMap;
use write_fonts::{
read::{
collections::IntSet,
tables::variations::{ItemVariationData, ItemVariationStore, VariationRegionList},
tables::variations::{
DeltaSetIndexMap, ItemVariationData, ItemVariationStore, VariationRegionList,
},
},
types::{BigEndian, F2Dot14, FixedSize, Offset32},
types::{BigEndian, F2Dot14, FixedSize, GlyphId, Offset32},
};

impl<'a> SubsetTable<'a> for ItemVariationStore<'a> {
type ArgsForSubset = &'a Vec<IncBiMap>;
type ArgsForSubset = &'a [IncBiMap];
type Output = ();

fn subset(
&self,
plan: &Plan,
s: &mut Serializer,
inner_maps: &Vec<IncBiMap>,
inner_maps: &[IncBiMap],
) -> Result<(), SerializeErrorFlags> {
s.embed(self.format())?;

Expand Down Expand Up @@ -419,6 +422,102 @@ fn collect_region_refs(
}
}

pub(crate) struct DeltaSetIndexMapSerializePlan<'a> {
outer_bit_count: u8,
inner_bit_count: u8,
output_map: &'a FnvHashMap<GlyphId, u32>,
map_count: u32,
}

impl<'a> DeltaSetIndexMapSerializePlan<'a> {
pub(crate) fn new(
outer_bit_count: u8,
inner_bit_count: u8,
output_map: &'a FnvHashMap<GlyphId, u32>,
map_count: u32,
) -> Self {
Self {
outer_bit_count,
inner_bit_count,
output_map,
map_count,
}
}

pub(crate) fn width(&self) -> u8 {
(self.outer_bit_count + self.inner_bit_count + 7) / 8
}

pub(crate) fn inner_bit_count(&self) -> u8 {
self.inner_bit_count
}

pub(crate) fn output_map(&self) -> &'a FnvHashMap<GlyphId, u32> {
self.output_map
}

pub(crate) fn map_count(&self) -> u32 {
self.map_count
}
}

impl<'a> SubsetTable<'a> for DeltaSetIndexMap<'a> {
type ArgsForSubset = &'a DeltaSetIndexMapSerializePlan<'a>;
type Output = ();

fn subset(
&self,
plan: &Plan,
s: &mut Serializer,
index_map_subset_plan: &'a DeltaSetIndexMapSerializePlan<'a>,
) -> Result<(), SerializeErrorFlags> {
let output_map = index_map_subset_plan.output_map();
let width = index_map_subset_plan.width();
let inner_bit_count = index_map_subset_plan.inner_bit_count();

let map_count = index_map_subset_plan.map_count();
// sanity check
if map_count > 0 && (((inner_bit_count - 1) & (!0xF)) != 0 || (((width - 1) & (!0x3)) != 0))
{
return Err(SerializeErrorFlags::SERIALIZE_ERROR_OTHER);
}

let format: u8 = if map_count <= 0xFFFF { 0 } else { 1 };
s.embed(format)?;

let entry_format = ((width - 1) << 4) | (inner_bit_count - 1);
s.embed(entry_format)?;

if format == 0 {
s.embed(map_count as u16)?;
} else {
s.embed(map_count)?;
}

let num_data_bytes = width as usize * map_count as usize;
let mapdata_pos = s.allocate_size(num_data_bytes, true)?;

let be_byte_index_start = 4 - width as usize;
for (new_gid, _) in plan.new_to_old_gid_list.iter() {
let Some(v) = output_map.get(new_gid) else {
continue;
};
if *v == 0 {
continue;
}

let outer = v >> 16;
let inner = v & 0xFFFF;
let u = (outer << inner_bit_count) | inner;
let data_bytes = u.to_be_bytes();
let data_pos = mapdata_pos + (new_gid.to_u32() as usize) * width as usize;
s.copy_assign_from_bytes(data_pos, data_bytes.get(be_byte_index_start..4).unwrap());
}

Ok(())
}
}

#[cfg(test)]
mod test {
use super::*;
Expand Down
Loading

0 comments on commit 1982f4a

Please sign in to comment.