Skip to content

Commit b0adcc1

Browse files
authored
Sort table records when parsing so unsorted table directories remain searchable (#29)
1 parent c3f23c6 commit b0adcc1

1 file changed

Lines changed: 34 additions & 0 deletions

File tree

src/lib.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,10 @@ fn parse(data: &[u8], index: u32) -> Result<Face<'_>> {
280280
records.push(r.read::<TableRecord>().ok_or(MalformedFont)?);
281281
}
282282

283+
// `Face::table` binary-searches by tag, but fonts in the wild violate
284+
// the spec's requirement that the directory be sorted.
285+
records.sort_by_key(|record| record.tag);
286+
283287
Ok(Face { data, records })
284288
}
285289

@@ -693,6 +697,36 @@ mod tests {
693697
assert_eq!(result, Err(Error::CFFError));
694698
}
695699

700+
#[test]
701+
fn unsorted_table_directory_is_searchable() {
702+
// Two table records deliberately stored in non-alphabetical order
703+
// (`head` before `cmap`): the directory violates the spec's sort
704+
// requirement, but both tables must still be found.
705+
let mut font = Vec::new();
706+
font.extend(0x00010000u32.to_be_bytes());
707+
font.extend(2u16.to_be_bytes());
708+
font.extend(0u16.to_be_bytes());
709+
font.extend(0u16.to_be_bytes());
710+
font.extend(0u16.to_be_bytes());
711+
// Directory (2 records à 16 bytes; data starts at 12 + 32 = 44).
712+
font.extend(*b"head");
713+
font.extend(0u32.to_be_bytes());
714+
font.extend(44u32.to_be_bytes());
715+
font.extend(4u32.to_be_bytes());
716+
font.extend(*b"cmap");
717+
font.extend(0u32.to_be_bytes());
718+
font.extend(48u32.to_be_bytes());
719+
font.extend(4u32.to_be_bytes());
720+
// Table payloads.
721+
font.extend([1, 2, 3, 4]);
722+
font.extend([5, 6, 7, 8]);
723+
724+
let face = parse(&font, 0).unwrap();
725+
726+
assert_eq!(face.table(Tag::HEAD), Some([1, 2, 3, 4].as_slice()));
727+
assert_eq!(face.table(Tag::CMAP), Some([5, 6, 7, 8].as_slice()));
728+
}
729+
696730
#[test]
697731
fn table_record_with_invalid_bounds_returns_none() {
698732
let face = Face {

0 commit comments

Comments
 (0)