diff --git a/src/otf/cmap.rs b/src/otf/cmap.rs index 3a7a7767..73fadbad 100644 --- a/src/otf/cmap.rs +++ b/src/otf/cmap.rs @@ -25,6 +25,7 @@ const MICROSOFT_ENCODING_ID_UNICODE_BMP: u16 = 1; const MICROSOFT_ENCODING_ID_UNICODE_UCS4: u16 = 10; const FORMAT_SEGMENT_MAPPING_TO_DELTA_VALUES: u16 = 4; +const FORMAT_SEGMENTED_COVERAGE: u16 = 12; const MISSING_GLYPH: u16 = 0; @@ -78,10 +79,24 @@ impl<'a> CmapTable<'a> { // Check the mapping table format. let format = try!(cmap_reader.read_u16::().map_err(Error::eof)); - if format != FORMAT_SEGMENT_MAPPING_TO_DELTA_VALUES { - return Err(Error::UnsupportedCmapFormat) + match format { + FORMAT_SEGMENT_MAPPING_TO_DELTA_VALUES => { + self.glyph_ranges_for_codepoint_ranges_segment_mapping_format(cmap_reader, + codepoint_ranges) + } + FORMAT_SEGMENTED_COVERAGE => { + self.glyph_ranges_for_codepoint_ranges_segmented_coverage(cmap_reader, + codepoint_ranges) + } + _ => Err(Error::UnsupportedCmapFormat), } + } + fn glyph_ranges_for_codepoint_ranges_segment_mapping_format( + &self, + mut cmap_reader: &[u8], + codepoint_ranges: &[CodepointRange]) + -> Result { // Read the mapping table header. let _length = try!(cmap_reader.read_u16::().map_err(Error::eof)); let _language = try!(cmap_reader.read_u16::().map_err(Error::eof)); @@ -234,5 +249,82 @@ impl<'a> CmapTable<'a> { Ok(glyph_ranges) } + + fn glyph_ranges_for_codepoint_ranges_segmented_coverage(&self, + mut cmap_reader: &[u8], + codepoint_ranges: &[CodepointRange]) + -> Result { + let _reserved = try!(cmap_reader.read_u16::().map_err(Error::eof)); + let _length = try!(cmap_reader.read_u32::().map_err(Error::eof)); + let _language = try!(cmap_reader.read_u32::().map_err(Error::eof)); + let num_groups = try!(cmap_reader.read_u32::().map_err(Error::eof)); + + // Now perform the lookups. + let mut glyph_ranges = GlyphRanges::new(); + for codepoint_range in codepoint_ranges { + let mut codepoint_range = *codepoint_range; + while codepoint_range.end >= codepoint_range.start { + // Binary search to find the segment. + let (mut low, mut high) = (0, num_groups); + let mut found_segment = None; + while low < high { + let mid = (low + high) / 2; + + let mut reader = cmap_reader; + try!(reader.jump(mid as usize * mem::size_of::<[u32; 3]>()) + .map_err(Error::eof)); + let segment = Segment { + start_char_code: try!(reader.read_u32::().map_err(Error::eof)), + end_char_code: try!(reader.read_u32::().map_err(Error::eof)), + start_glyph_id: try!(reader.read_u32::().map_err(Error::eof)), + }; + + if codepoint_range.start < segment.start_char_code { + high = mid + } else if codepoint_range.start > segment.end_char_code { + low = mid + 1 + } else { + found_segment = Some(segment); + break + } + } + + match found_segment { + None => { + glyph_ranges.ranges.push(MappedGlyphRange { + codepoint_start: codepoint_range.start, + glyphs: GlyphRange { + start: MISSING_GLYPH, + end: MISSING_GLYPH, + }, + }); + codepoint_range.start += 1 + } + Some(segment) => { + let end = cmp::min(codepoint_range.end, segment.end_char_code); + glyph_ranges.ranges.push(MappedGlyphRange { + codepoint_start: codepoint_range.start, + glyphs: GlyphRange { + start: (segment.start_glyph_id + codepoint_range.start - + segment.start_char_code) as u16, + end: (segment.start_glyph_id + end - segment.start_char_code) as + u16, + }, + }); + codepoint_range.start = end + 1 + } + } + } + } + + Ok(glyph_ranges) + } +} + +#[derive(Clone, Copy)] +struct Segment { + start_char_code: u32, + end_char_code: u32, + start_glyph_id: u32, } diff --git a/src/otf/mod.rs b/src/otf/mod.rs index 2c6ed697..05d3b09f 100644 --- a/src/otf/mod.rs +++ b/src/otf/mod.rs @@ -58,6 +58,10 @@ const TTCF: u32 = ((b't' as u32) << 24) | ((b'c' as u32) << 8) | (b'f' as u32); +const OTTO: u32 = ((b'O' as u32) << 24) | + ((b'T' as u32) << 16) | + ((b'T' as u32) << 8) | + (b'O' as u32); const SFNT: u32 = ((b's' as u32) << 24) | ((b'f' as u32) << 16) | ((b'n' as u32) << 8) | @@ -111,6 +115,10 @@ impl<'a> Font<'a> { } magic_number if SFNT_VERSIONS.contains(&magic_number) => Font::from_otf(bytes, 0), 0x0100 => Font::from_dfont(bytes), + OTTO => { + // TODO(pcwalton): Support CFF outlines. + Err(Error::UnsupportedCffOutlines) + } _ => Err(Error::UnknownFormat), } } @@ -122,7 +130,10 @@ impl<'a> Font<'a> { let mut magic_number = try!(reader.read_u32::().map_err(Error::eof)); // Check version. - if !SFNT_VERSIONS.contains(&magic_number) { + if magic_number == OTTO { + // TODO(pcwalton): Support CFF outlines. + return Err(Error::UnsupportedCffOutlines) + } else if !SFNT_VERSIONS.contains(&magic_number) { return Err(Error::UnknownFormat) } @@ -316,6 +327,8 @@ pub enum Error { UnsupportedVersion, /// The file was of a format we don't support. UnknownFormat, + /// The font has CFF outlines, which we don't yet support. + UnsupportedCffOutlines, /// The font had a glyph format we don't support. UnsupportedGlyphFormat, /// We don't support the declared version of the font's character map.