Skip to content

Commit 3a193ba

Browse files
authored
fix: support loca tables with 65536 offsets (#226)
A well-formed 65535-glyph font has 65536 loca offsets, which u16::try_from rejected, causing glyf to be dropped entirely and every outline to return None. Widens the index type to u32. Breaking: loca::Table::len() and the array payload types change.
1 parent c7707c1 commit 3a193ba

1 file changed

Lines changed: 9 additions & 16 deletions

File tree

src/tables/loca.rs

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,16 @@ use core::convert::TryFrom;
55
use core::num::NonZeroU16;
66
use core::ops::Range;
77

8-
use crate::parser::{LazyArray16, NumFrom, Stream};
8+
use crate::parser::{LazyArray32, NumFrom, Stream};
99
use crate::{GlyphId, IndexToLocationFormat};
1010

1111
/// An [Index to Location Table](https://docs.microsoft.com/en-us/typography/opentype/spec/loca).
1212
#[derive(Clone, Copy, Debug)]
1313
pub enum Table<'a> {
1414
/// Short offsets.
15-
Short(LazyArray16<'a, u16>),
15+
Short(LazyArray32<'a, u16>),
1616
/// Long offsets.
17-
Long(LazyArray16<'a, u32>),
17+
Long(LazyArray32<'a, u32>),
1818
}
1919

2020
impl<'a> Table<'a> {
@@ -30,11 +30,7 @@ impl<'a> Table<'a> {
3030
// The number of ranges is `maxp.numGlyphs + 1`.
3131
//
3232
// Check for overflow first.
33-
let mut total = if number_of_glyphs.get() == u16::MAX {
34-
number_of_glyphs.get()
35-
} else {
36-
number_of_glyphs.get() + 1
37-
};
33+
let mut total: u32 = number_of_glyphs.get() as u32 + 1;
3834

3935
// By the spec, the number of `loca` offsets is `maxp.numGlyphs + 1`.
4036
// But some malformed fonts can have less glyphs than that.
@@ -47,19 +43,19 @@ impl<'a> Table<'a> {
4743
IndexToLocationFormat::Short => data.len() / 2,
4844
IndexToLocationFormat::Long => data.len() / 4,
4945
};
50-
let actual_total = u16::try_from(actual_total).ok()?;
46+
let actual_total = u32::try_from(actual_total).ok()?;
5147
total = total.min(actual_total);
5248

5349
let mut s = Stream::new(data);
5450
match format {
55-
IndexToLocationFormat::Short => Some(Table::Short(s.read_array16::<u16>(total)?)),
56-
IndexToLocationFormat::Long => Some(Table::Long(s.read_array16::<u32>(total)?)),
51+
IndexToLocationFormat::Short => Some(Table::Short(s.read_array32::<u16>(total)?)),
52+
IndexToLocationFormat::Long => Some(Table::Long(s.read_array32::<u32>(total)?)),
5753
}
5854
}
5955

6056
/// Returns the number of offsets.
6157
#[inline]
62-
pub fn len(&self) -> u16 {
58+
pub fn len(&self) -> u32 {
6359
match self {
6460
Table::Short(array) => array.len(),
6561
Table::Long(array) => array.len(),
@@ -74,10 +70,7 @@ impl<'a> Table<'a> {
7470
/// Returns glyph's range in the `glyf` table.
7571
#[inline]
7672
pub fn glyph_range(&self, glyph_id: GlyphId) -> Option<Range<usize>> {
77-
let glyph_id = glyph_id.0;
78-
if glyph_id == u16::MAX {
79-
return None;
80-
}
73+
let glyph_id = glyph_id.0 as u32;
8174

8275
// Glyph ID must be smaller than total number of values in a `loca` array.
8376
if glyph_id + 1 >= self.len() {

0 commit comments

Comments
 (0)