Skip to content

Commit

Permalink
[{read,write}-fonts] Allow DeltaSetIndexMap repeated trailing entries…
Browse files Browse the repository at this point in the history
… to be omitted

Fixes #667
  • Loading branch information
anthrotype committed Oct 23, 2023
1 parent 481d09a commit 82b93a5
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 7 deletions.
14 changes: 11 additions & 3 deletions read-fonts/src/tables/variations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -516,12 +516,20 @@ impl EntryFormat {
impl<'a> DeltaSetIndexMap<'a> {
/// Returns the delta set index for the specified value.
pub fn get(&self, index: u32) -> Result<DeltaSetIndex, ReadError> {
let (entry_format, data) = match self {
Self::Format0(fmt) => (fmt.entry_format(), fmt.map_data()),
Self::Format1(fmt) => (fmt.entry_format(), fmt.map_data()),
let (entry_format, map_count, data) = match self {
Self::Format0(fmt) => (fmt.entry_format(), fmt.map_count() as u32, fmt.map_data()),
Self::Format1(fmt) => (fmt.entry_format(), fmt.map_count(), fmt.map_data()),
};
let entry_size = entry_format.entry_size();
let data = FontData::new(data);
// "if an index into the mapping array is used that is greater than or equal to
// mapCount, then the last logical entry of the mapping array is used."
// https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats
// #associating-target-items-to-variation-data
let mut index = index;
if index >= map_count {
index = map_count.saturating_sub(1);
}
let offset = index as usize * entry_size as usize;
let entry = match entry_size {
1 => data.read_at::<u8>(offset)? as u32,
Expand Down
19 changes: 15 additions & 4 deletions write-fonts/src/tables/variations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -411,12 +411,21 @@ impl DeltaSetIndexMap {
let outer_shift = 16 - inner_bits;
let entry_size = fmt.entry_size();
assert!((1..=4).contains(&entry_size));
let mut map_data: Vec<u8> = Vec::with_capacity(mapping.len() * entry_size as usize);
for idx in mapping {

// omit trailing entries that are the same as the previous one;
// the last entry is assumed when index is >= map_count
let mut map_count = mapping.len();
while map_count > 1 && mapping[map_count - 1] == mapping[map_count - 2] {
map_count -= 1;
}

let mut map_data: Vec<u8> = Vec::with_capacity(map_count * entry_size as usize);
for idx in mapping.iter().take(map_count) {
let idx = ((idx & 0xFFFF0000) >> outer_shift) | (idx & inner_mask);
// append entry_size bytes to map_data in BigEndian order
map_data.extend_from_slice(&idx.to_be_bytes()[4 - entry_size as usize..]);
}
assert_eq!(map_data.len(), map_count * entry_size as usize);
(fmt, map_data)
}
}
Expand All @@ -429,7 +438,7 @@ where
fn from_iter<T: IntoIterator<Item = I>>(iter: T) -> Self {
let mapping: Vec<u32> = iter.into_iter().map(|v| v.into()).collect();
let (fmt, map_data) = DeltaSetIndexMap::pack_map_data(&mapping);
let map_count = mapping.len();
let map_count = map_data.len() / fmt.entry_size() as usize;
let delta_set_index_map: DeltaSetIndexMap = if map_count <= u16::MAX as usize {
DeltaSetIndexMap::format_0(fmt, map_count as u16, map_data)
} else {
Expand Down Expand Up @@ -591,8 +600,10 @@ mod tests {
}

#[rstest]
// Note how the packed data below is b"\x00\x01" and not b"\x00\x01\x01", for the
// repeated trailing values can be omitted
#[case::one_byte_one_inner_bit(
vec![0, 1, 1], 0b00_0000, 1, 1, b"\x00\x01\x01",
vec![0, 1, 1], 0b00_0000, 1, 1, b"\x00\x01",
)]
#[case::one_byte_two_inner_bits(
vec![0, 1, 2], 0b00_0001, 1, 2, b"\x00\x01\x02",
Expand Down

0 comments on commit 82b93a5

Please sign in to comment.