Add error handling to the OTF format

This commit is contained in:
Patrick Walton 2017-02-03 17:52:33 -08:00
parent 23fa035178
commit 316123123a
8 changed files with 226 additions and 163 deletions

View File

@ -11,7 +11,7 @@
use byteorder::{BigEndian, ReadBytesExt}; use byteorder::{BigEndian, ReadBytesExt};
use charmap::CodepointRange; use charmap::CodepointRange;
use glyph_range::{GlyphRange, GlyphRanges, MappedGlyphRange}; use glyph_range::{GlyphRange, GlyphRanges, MappedGlyphRange};
use otf::FontTable; use otf::{Error, FontTable};
use std::cmp; use std::cmp;
use std::mem; use std::mem;
use std::u16; use std::u16;
@ -40,30 +40,30 @@ impl<'a> CmapTable<'a> {
} }
pub fn glyph_ranges_for_codepoint_ranges(&self, codepoint_ranges: &[CodepointRange]) pub fn glyph_ranges_for_codepoint_ranges(&self, codepoint_ranges: &[CodepointRange])
-> Result<GlyphRanges, ()> { -> Result<GlyphRanges, Error> {
let mut cmap_reader = self.table.bytes; let mut cmap_reader = self.table.bytes;
// Check version. // Check version.
if try!(cmap_reader.read_u16::<BigEndian>().map_err(drop)) != 0 { if try!(cmap_reader.read_u16::<BigEndian>().map_err(Error::eof)) != 0 {
return Err(()) return Err(Error::UnsupportedCmapVersion)
} }
let num_tables = try!(cmap_reader.read_u16::<BigEndian>().map_err(drop)); let num_tables = try!(cmap_reader.read_u16::<BigEndian>().map_err(Error::eof));
// Check platform ID and encoding. // Check platform ID and encoding.
// TODO(pcwalton): Handle more. // TODO(pcwalton): Handle more.
let mut table_found = false; let mut table_found = false;
for _ in 0..num_tables { for _ in 0..num_tables {
let platform_id = try!(cmap_reader.read_u16::<BigEndian>().map_err(drop)); let platform_id = try!(cmap_reader.read_u16::<BigEndian>().map_err(Error::eof));
let encoding_id = try!(cmap_reader.read_u16::<BigEndian>().map_err(drop)); let encoding_id = try!(cmap_reader.read_u16::<BigEndian>().map_err(Error::eof));
let offset = try!(cmap_reader.read_u32::<BigEndian>().map_err(drop)); let offset = try!(cmap_reader.read_u32::<BigEndian>().map_err(Error::eof));
match (platform_id, encoding_id) { match (platform_id, encoding_id) {
(PLATFORM_ID_UNICODE, _) | (PLATFORM_ID_UNICODE, _) |
(PLATFORM_ID_MICROSOFT, MICROSOFT_ENCODING_ID_UNICODE_BMP) | (PLATFORM_ID_MICROSOFT, MICROSOFT_ENCODING_ID_UNICODE_BMP) |
(PLATFORM_ID_MICROSOFT, MICROSOFT_ENCODING_ID_UNICODE_UCS4) => { (PLATFORM_ID_MICROSOFT, MICROSOFT_ENCODING_ID_UNICODE_UCS4) => {
// Move to the mapping table. // Move to the mapping table.
cmap_reader = self.table.bytes; cmap_reader = self.table.bytes;
try!(cmap_reader.jump(offset as usize)); try!(cmap_reader.jump(offset as usize).map_err(Error::eof));
table_found = true; table_found = true;
break break
} }
@ -72,22 +72,22 @@ impl<'a> CmapTable<'a> {
} }
if !table_found { if !table_found {
return Err(()) return Err(Error::UnsupportedCmapEncoding)
} }
// Check the mapping table format. // Check the mapping table format.
let format = try!(cmap_reader.read_u16::<BigEndian>().map_err(drop)); let format = try!(cmap_reader.read_u16::<BigEndian>().map_err(Error::eof));
if format != FORMAT_SEGMENT_MAPPING_TO_DELTA_VALUES { if format != FORMAT_SEGMENT_MAPPING_TO_DELTA_VALUES {
return Err(()) return Err(Error::UnsupportedCmapFormat)
} }
// Read the mapping table header. // Read the mapping table header.
let _length = try!(cmap_reader.read_u16::<BigEndian>().map_err(drop)); let _length = try!(cmap_reader.read_u16::<BigEndian>().map_err(Error::eof));
let _language = try!(cmap_reader.read_u16::<BigEndian>().map_err(drop)); let _language = try!(cmap_reader.read_u16::<BigEndian>().map_err(Error::eof));
let seg_count = try!(cmap_reader.read_u16::<BigEndian>().map_err(drop)) / 2; let seg_count = try!(cmap_reader.read_u16::<BigEndian>().map_err(Error::eof)) / 2;
let _search_range = try!(cmap_reader.read_u16::<BigEndian>().map_err(drop)); let _search_range = try!(cmap_reader.read_u16::<BigEndian>().map_err(Error::eof));
let _entry_selector = try!(cmap_reader.read_u16::<BigEndian>().map_err(drop)); let _entry_selector = try!(cmap_reader.read_u16::<BigEndian>().map_err(Error::eof));
let _range_shift = try!(cmap_reader.read_u16::<BigEndian>().map_err(drop)); let _range_shift = try!(cmap_reader.read_u16::<BigEndian>().map_err(Error::eof));
// Set up parallel array pointers. // Set up parallel array pointers.
// //
@ -95,13 +95,15 @@ impl<'a> CmapTable<'a> {
// respectively in a few places. I believe this is a mistake, and `startCode` and `endCode` // respectively in a few places. I believe this is a mistake, and `startCode` and `endCode`
// are the correct names. // are the correct names.
let (end_codes, mut start_codes) = (cmap_reader, cmap_reader); let (end_codes, mut start_codes) = (cmap_reader, cmap_reader);
try!(start_codes.jump((seg_count as usize + 1) * mem::size_of::<u16>())); try!(start_codes.jump((seg_count as usize + 1) * mem::size_of::<u16>())
.map_err(Error::eof));
let mut id_deltas = start_codes; let mut id_deltas = start_codes;
try!(id_deltas.jump(seg_count as usize * mem::size_of::<u16>())); try!(id_deltas.jump(seg_count as usize * mem::size_of::<u16>()).map_err(Error::eof));
let mut id_range_offsets = id_deltas; let mut id_range_offsets = id_deltas;
try!(id_range_offsets.jump(seg_count as usize * mem::size_of::<u16>())); try!(id_range_offsets.jump(seg_count as usize * mem::size_of::<u16>())
.map_err(Error::eof));
let mut glyph_ids = id_range_offsets; let mut glyph_ids = id_range_offsets;
try!(glyph_ids.jump(seg_count as usize * mem::size_of::<u16>())); try!(glyph_ids.jump(seg_count as usize * mem::size_of::<u16>()).map_err(Error::eof));
// Now perform the lookups. // Now perform the lookups.
let mut glyph_ranges = GlyphRanges::new(); let mut glyph_ranges = GlyphRanges::new();
@ -130,16 +132,16 @@ impl<'a> CmapTable<'a> {
let mid = (low + high) / 2; let mid = (low + high) / 2;
let mut end_code = end_codes; let mut end_code = end_codes;
try!(end_code.jump(mid as usize * 2)); try!(end_code.jump(mid as usize * 2).map_err(Error::eof));
let end_code = try!(end_code.read_u16::<BigEndian>().map_err(drop)); let end_code = try!(end_code.read_u16::<BigEndian>().map_err(Error::eof));
if start_codepoint_range > end_code { if start_codepoint_range > end_code {
low = mid + 1; low = mid + 1;
continue continue
} }
let mut start_code = start_codes; let mut start_code = start_codes;
try!(start_code.jump(mid as usize * 2)); try!(start_code.jump(mid as usize * 2).map_err(Error::eof));
let start_code = try!(start_code.read_u16::<BigEndian>().map_err(drop)); let start_code = try!(start_code.read_u16::<BigEndian>().map_err(Error::eof));
if start_codepoint_range < start_code { if start_codepoint_range < start_code {
high = mid; high = mid;
continue continue
@ -169,14 +171,15 @@ impl<'a> CmapTable<'a> {
let mut end_code = end_codes; let mut end_code = end_codes;
let mut id_range_offset = id_range_offsets; let mut id_range_offset = id_range_offsets;
let mut id_delta = id_deltas; let mut id_delta = id_deltas;
try!(start_code.jump(segment_index as usize * 2)); try!(start_code.jump(segment_index as usize * 2).map_err(Error::eof));
try!(end_code.jump(segment_index as usize * 2)); try!(end_code.jump(segment_index as usize * 2).map_err(Error::eof));
try!(id_range_offset.jump(segment_index as usize * 2)); try!(id_range_offset.jump(segment_index as usize * 2).map_err(Error::eof));
try!(id_delta.jump(segment_index as usize * 2)); try!(id_delta.jump(segment_index as usize * 2).map_err(Error::eof));
let start_code = try!(start_code.read_u16::<BigEndian>().map_err(drop)); let start_code = try!(start_code.read_u16::<BigEndian>().map_err(Error::eof));
let end_code = try!(end_code.read_u16::<BigEndian>().map_err(drop)); let end_code = try!(end_code.read_u16::<BigEndian>().map_err(Error::eof));
let id_range_offset = try!(id_range_offset.read_u16::<BigEndian>().map_err(drop)); let id_range_offset = try!(id_range_offset.read_u16::<BigEndian>()
let id_delta = try!(id_delta.read_i16::<BigEndian>().map_err(drop)); .map_err(Error::eof));
let id_delta = try!(id_delta.read_i16::<BigEndian>().map_err(Error::eof));
end_codepoint_range = cmp::min(end_codepoint_range, end_code); end_codepoint_range = cmp::min(end_codepoint_range, end_code);
codepoint_range.start = (end_codepoint_range + 1) as u32; codepoint_range.start = (end_codepoint_range + 1) as u32;
@ -203,8 +206,9 @@ impl<'a> CmapTable<'a> {
// Otherwise, look up the glyphs individually. // Otherwise, look up the glyphs individually.
for code_offset in start_code_offset..(end_code_offset + 1) { for code_offset in start_code_offset..(end_code_offset + 1) {
let mut glyph_id = glyph_ids; let mut glyph_id = glyph_ids;
try!(glyph_id.jump((id_range_offset as usize + code_offset as usize) * 2)); try!(glyph_id.jump((id_range_offset as usize + code_offset as usize) * 2)
let mut glyph_id = try!(glyph_id.read_u16::<BigEndian>().map_err(drop)); .map_err(Error::eof));
let mut glyph_id = try!(glyph_id.read_u16::<BigEndian>().map_err(Error::eof));
if glyph_id == 0 { if glyph_id == 0 {
glyph_ranges.ranges.push(MappedGlyphRange { glyph_ranges.ranges.push(MappedGlyphRange {
codepoint_start: start_code as u32 + code_offset as u32, codepoint_start: start_code as u32 + code_offset as u32,

View File

@ -10,9 +10,9 @@
use byteorder::{BigEndian, ReadBytesExt}; use byteorder::{BigEndian, ReadBytesExt};
use euclid::Point2D; use euclid::Point2D;
use otf::FontTable;
use otf::head::HeadTable; use otf::head::HeadTable;
use otf::loca::LocaTable; use otf::loca::LocaTable;
use otf::{Error, FontTable};
use outline::GlyphBounds; use outline::GlyphBounds;
use std::mem; use std::mem;
use util::Jump; use util::Jump;
@ -54,7 +54,7 @@ impl<'a> GlyfTable<'a> {
loca_table: &LocaTable, loca_table: &LocaTable,
glyph_id: u16, glyph_id: u16,
mut callback: F) mut callback: F)
-> Result<(), ()> where F: FnMut(&Point) { -> Result<(), Error> where F: FnMut(&Point) {
let mut reader = self.table.bytes; let mut reader = self.table.bytes;
match try!(loca_table.location_of(head_table, glyph_id)) { match try!(loca_table.location_of(head_table, glyph_id)) {
@ -62,24 +62,25 @@ impl<'a> GlyfTable<'a> {
// No points. // No points.
return Ok(()) return Ok(())
} }
Some(offset) => try!(reader.jump(offset as usize)), Some(offset) => try!(reader.jump(offset as usize).map_err(Error::eof)),
} }
let number_of_contours = try!(reader.read_i16::<BigEndian>().map_err(drop)); let number_of_contours = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
if number_of_contours < 0 { if number_of_contours < 0 {
// TODO(pcwalton): Composite glyphs. // TODO(pcwalton): Composite glyphs.
return Err(()) return Err(Error::CompositeGlyph)
} }
try!(reader.jump(mem::size_of::<i16>() * 4)); try!(reader.jump(mem::size_of::<i16>() * 4).map_err(Error::eof));
// Find out how many points we have. // Find out how many points we have.
let mut endpoints_reader = reader; let mut endpoints_reader = reader;
try!(reader.jump(mem::size_of::<u16>() as usize * (number_of_contours as usize - 1))); try!(reader.jump(mem::size_of::<u16>() as usize * (number_of_contours as usize - 1))
let number_of_points = try!(reader.read_u16::<BigEndian>().map_err(drop)) + 1; .map_err(Error::eof));
let number_of_points = try!(reader.read_u16::<BigEndian>().map_err(Error::eof)) + 1;
// Skip over hinting instructions. // Skip over hinting instructions.
let instruction_length = try!(reader.read_u16::<BigEndian>().map_err(drop)); let instruction_length = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
try!(reader.jump(instruction_length as usize)); try!(reader.jump(instruction_length as usize).map_err(Error::eof));
// Find the offsets of the X and Y coordinates. // Find the offsets of the X and Y coordinates.
let flags_reader = reader; let flags_reader = reader;
@ -89,14 +90,14 @@ impl<'a> GlyfTable<'a> {
// Set up the streams. // Set up the streams.
let mut flag_parser = try!(FlagParser::new(flags_reader)); let mut flag_parser = try!(FlagParser::new(flags_reader));
let mut x_coordinate_reader = reader; let mut x_coordinate_reader = reader;
try!(reader.jump(x_coordinate_length as usize)); try!(reader.jump(x_coordinate_length as usize).map_err(Error::eof));
let mut y_coordinate_reader = reader; let mut y_coordinate_reader = reader;
// Now parse the contours. // Now parse the contours.
let (mut position, mut point_index) = (Point2D::new(0, 0), 0); let (mut position, mut point_index) = (Point2D::new(0, 0), 0);
for _ in 0..number_of_contours { for _ in 0..number_of_contours {
let contour_point_count = let contour_point_count = try!(endpoints_reader.read_u16::<BigEndian>()
try!(endpoints_reader.read_u16::<BigEndian>().map_err(drop)) - point_index + 1; .map_err(Error::eof)) - point_index + 1;
let mut first_on_curve_point = None; let mut first_on_curve_point = None;
let mut initial_off_curve_point = None; let mut initial_off_curve_point = None;
@ -109,20 +110,20 @@ impl<'a> GlyfTable<'a> {
let mut delta = Point2D::new(0, 0); let mut delta = Point2D::new(0, 0);
if flags.contains(X_SHORT_VECTOR) { if flags.contains(X_SHORT_VECTOR) {
delta.x = try!(x_coordinate_reader.read_u8().map_err(drop)) as i16; delta.x = try!(x_coordinate_reader.read_u8().map_err(Error::eof)) as i16;
if !flags.contains(THIS_X_IS_SAME) { if !flags.contains(THIS_X_IS_SAME) {
delta.x = -delta.x delta.x = -delta.x
} }
} else if !flags.contains(THIS_X_IS_SAME) { } else if !flags.contains(THIS_X_IS_SAME) {
delta.x = try!(x_coordinate_reader.read_i16::<BigEndian>().map_err(drop)) delta.x = try!(x_coordinate_reader.read_i16::<BigEndian>().map_err(Error::eof))
} }
if flags.contains(Y_SHORT_VECTOR) { if flags.contains(Y_SHORT_VECTOR) {
delta.y = try!(y_coordinate_reader.read_u8().map_err(drop)) as i16; delta.y = try!(y_coordinate_reader.read_u8().map_err(Error::eof)) as i16;
if !flags.contains(THIS_Y_IS_SAME) { if !flags.contains(THIS_Y_IS_SAME) {
delta.y = -delta.y delta.y = -delta.y
} }
} else if !flags.contains(THIS_Y_IS_SAME) { } else if !flags.contains(THIS_Y_IS_SAME) {
delta.y = try!(y_coordinate_reader.read_i16::<BigEndian>().map_err(drop)) delta.y = try!(y_coordinate_reader.read_i16::<BigEndian>().map_err(Error::eof))
} }
if last_point_was_off_curve && !flags.contains(ON_CURVE) { if last_point_was_off_curve && !flags.contains(ON_CURVE) {
@ -189,7 +190,7 @@ impl<'a> GlyfTable<'a> {
} }
pub fn glyph_bounds(&self, head_table: &HeadTable, loca_table: &LocaTable, glyph_id: u16) pub fn glyph_bounds(&self, head_table: &HeadTable, loca_table: &LocaTable, glyph_id: u16)
-> Result<GlyphBounds, ()> { -> Result<GlyphBounds, Error> {
let mut reader = self.table.bytes; let mut reader = self.table.bytes;
match try!(loca_table.location_of(head_table, glyph_id)) { match try!(loca_table.location_of(head_table, glyph_id)) {
@ -202,16 +203,16 @@ impl<'a> GlyfTable<'a> {
top: 0, top: 0,
}) })
} }
Some(offset) => try!(reader.jump(offset as usize)), Some(offset) => try!(reader.jump(offset as usize).map_err(Error::eof)),
} }
// Skip over the number of contours. // Skip over the number of contours.
try!(reader.read_i16::<BigEndian>().map_err(drop)); try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
let x_min = try!(reader.read_i16::<BigEndian>().map_err(drop)); let x_min = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
let y_min = try!(reader.read_i16::<BigEndian>().map_err(drop)); let y_min = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
let x_max = try!(reader.read_i16::<BigEndian>().map_err(drop)); let x_max = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
let y_max = try!(reader.read_i16::<BigEndian>().map_err(drop)); let y_max = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
Ok(GlyphBounds { Ok(GlyphBounds {
left: x_min as i32, left: x_min as i32,
bottom: y_min as i32, bottom: y_min as i32,
@ -225,14 +226,14 @@ impl<'a> GlyfTable<'a> {
// of X coordinates and positions the reader at the start of that list. // of X coordinates and positions the reader at the start of that list.
#[inline] #[inline]
fn calculate_size_of_x_coordinates<'a, 'b>(reader: &'a mut &'b [u8], number_of_points: u16) fn calculate_size_of_x_coordinates<'a, 'b>(reader: &'a mut &'b [u8], number_of_points: u16)
-> Result<u16, ()> { -> Result<u16, Error> {
let (mut x_coordinate_length, mut points_left) = (0, number_of_points); let (mut x_coordinate_length, mut points_left) = (0, number_of_points);
while points_left > 0 { while points_left > 0 {
let flags = Flags::from_bits_truncate(try!(reader.read_u8().map_err(drop))); let flags = Flags::from_bits_truncate(try!(reader.read_u8().map_err(Error::eof)));
let repeat_count = if !flags.contains(REPEAT) { let repeat_count = if !flags.contains(REPEAT) {
1 1
} else { } else {
try!(reader.read_u8().map_err(drop)) as u16 + 1 try!(reader.read_u8().map_err(Error::eof)) as u16 + 1
}; };
if flags.contains(X_SHORT_VECTOR) { if flags.contains(X_SHORT_VECTOR) {
@ -255,7 +256,7 @@ struct FlagParser<'a> {
impl<'a> FlagParser<'a> { impl<'a> FlagParser<'a> {
#[inline] #[inline]
fn new(buffer: &[u8]) -> Result<FlagParser, ()> { fn new(buffer: &[u8]) -> Result<FlagParser, Error> {
let mut parser = FlagParser { let mut parser = FlagParser {
next: buffer, next: buffer,
current: &buffer[0], current: &buffer[0],
@ -266,18 +267,26 @@ impl<'a> FlagParser<'a> {
} }
#[inline] #[inline]
fn next(&mut self) -> Result<(), ()> { fn next(&mut self) -> Result<(), Error> {
if self.repeats_left > 0 { if self.repeats_left > 0 {
self.repeats_left -= 1; self.repeats_left -= 1;
return Ok(()) return Ok(())
} }
self.current = try!(self.next.get(0).ok_or(())); self.current = match self.next.get(0) {
Some(value) => value,
None => return Err(Error::UnexpectedEof),
};
let flags = Flags::from_bits_truncate(*self.current); let flags = Flags::from_bits_truncate(*self.current);
self.next = &self.next[1..]; self.next = &self.next[1..];
if flags.contains(REPEAT) { if flags.contains(REPEAT) {
self.repeats_left = *try!(self.next.get(0).ok_or(())); self.repeats_left = match self.next.get(0) {
Some(&value) => value,
None => return Err(Error::UnexpectedEof),
};
self.next = &self.next[1..]; self.next = &self.next[1..];
} else { } else {
self.repeats_left = 0 self.repeats_left = 0

View File

@ -9,7 +9,7 @@
// except according to those terms. // except according to those terms.
use byteorder::{BigEndian, ReadBytesExt}; use byteorder::{BigEndian, ReadBytesExt};
use otf::FontTable; use otf::{Error, FontTable};
use std::mem; use std::mem;
use util::Jump; use util::Jump;
@ -22,38 +22,38 @@ pub struct HeadTable {
} }
impl HeadTable { impl HeadTable {
pub fn new(table: FontTable) -> Result<HeadTable, ()> { pub fn new(table: FontTable) -> Result<HeadTable, Error> {
let mut reader = table.bytes; let mut reader = table.bytes;
// Check the version. // Check the version.
let major_version = try!(reader.read_u16::<BigEndian>().map_err(drop)); let major_version = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
let minor_version = try!(reader.read_u16::<BigEndian>().map_err(drop)); let minor_version = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
if (major_version, minor_version) != (1, 0) { if (major_version, minor_version) != (1, 0) {
return Err(()) return Err(Error::UnsupportedHeadVersion)
} }
// Check the magic number. // Check the magic number.
try!(reader.jump(mem::size_of::<u32>() * 2)); try!(reader.jump(mem::size_of::<u32>() * 2).map_err(Error::eof));
let magic_number = try!(reader.read_u32::<BigEndian>().map_err(drop)); let magic_number = try!(reader.read_u32::<BigEndian>().map_err(Error::eof));
if magic_number != MAGIC_NUMBER { if magic_number != MAGIC_NUMBER {
return Err(()) return Err(Error::UnknownFormat)
} }
// Read the units per em. // Read the units per em.
try!(reader.jump(mem::size_of::<u16>())); try!(reader.jump(mem::size_of::<u16>()).map_err(Error::eof));
let units_per_em = try!(reader.read_u16::<BigEndian>().map_err(drop)); let units_per_em = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
// Read the index-to-location format. // Read the index-to-location format.
try!(reader.jump(mem::size_of::<i64>() * 2 + try!(reader.jump(mem::size_of::<i64>() * 2 +
mem::size_of::<i16>() * 4 + mem::size_of::<i16>() * 4 +
mem::size_of::<u16>() * 2 + mem::size_of::<u16>() * 2 +
mem::size_of::<i16>())); mem::size_of::<i16>()).map_err(Error::eof));
let index_to_loc_format = try!(reader.read_i16::<BigEndian>().map_err(drop)); let index_to_loc_format = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
// Check the glyph data format. // Check the glyph data format.
let glyph_data_format = try!(reader.read_i16::<BigEndian>().map_err(drop)); let glyph_data_format = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
if glyph_data_format != 0 { if glyph_data_format != 0 {
return Err(()) return Err(Error::UnsupportedGlyphFormat)
} }
Ok(HeadTable { Ok(HeadTable {

View File

@ -9,7 +9,7 @@
// except according to those terms. // except according to those terms.
use byteorder::{BigEndian, ReadBytesExt}; use byteorder::{BigEndian, ReadBytesExt};
use otf::FontTable; use otf::{Error, FontTable};
use std::mem; use std::mem;
use util::Jump; use util::Jump;
@ -19,19 +19,19 @@ pub struct HheaTable {
} }
impl HheaTable { impl HheaTable {
pub fn new(table: FontTable) -> Result<HheaTable, ()> { pub fn new(table: FontTable) -> Result<HheaTable, Error> {
let mut reader = table.bytes; let mut reader = table.bytes;
// Check the version. // Check the version.
let major_version = try!(reader.read_u16::<BigEndian>().map_err(drop)); let major_version = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
let minor_version = try!(reader.read_u16::<BigEndian>().map_err(drop)); let minor_version = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
if (major_version, minor_version) != (1, 0) { if (major_version, minor_version) != (1, 0) {
return Err(()) return Err(Error::UnsupportedHheaVersion)
} }
// Read the number of `hmtx` entries. // Read the number of `hmtx` entries.
try!(reader.jump(mem::size_of::<u16>() * 15)); try!(reader.jump(mem::size_of::<u16>() * 15).map_err(Error::eof));
let number_of_h_metrics = try!(reader.read_u16::<BigEndian>().map_err(drop)); let number_of_h_metrics = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
Ok(HheaTable { Ok(HheaTable {
number_of_h_metrics: number_of_h_metrics, number_of_h_metrics: number_of_h_metrics,

View File

@ -9,8 +9,8 @@
// except according to those terms. // except according to those terms.
use byteorder::{BigEndian, ReadBytesExt}; use byteorder::{BigEndian, ReadBytesExt};
use otf::FontTable;
use otf::hhea::HheaTable; use otf::hhea::HheaTable;
use otf::{Error, FontTable};
use std::mem; use std::mem;
use util::Jump; use util::Jump;
@ -27,23 +27,23 @@ impl<'a> HmtxTable<'a> {
} }
pub fn metrics_for_glyph(&self, hhea_table: &HheaTable, glyph_id: u16) pub fn metrics_for_glyph(&self, hhea_table: &HheaTable, glyph_id: u16)
-> Result<HorizontalMetrics, ()> { -> Result<HorizontalMetrics, Error> {
let mut reader = self.table.bytes; let mut reader = self.table.bytes;
// Read the advance width. // Read the advance width.
let advance_width; let advance_width;
if glyph_id < hhea_table.number_of_h_metrics { if glyph_id < hhea_table.number_of_h_metrics {
try!(reader.jump(mem::size_of::<u16>() * 2 * glyph_id as usize)); try!(reader.jump(mem::size_of::<u16>() * 2 * glyph_id as usize).map_err(Error::eof));
advance_width = try!(reader.read_u16::<BigEndian>().map_err(drop)) advance_width = try!(reader.read_u16::<BigEndian>().map_err(Error::eof))
} else { } else {
try!(reader.jump(mem::size_of::<u16>() * 2 * try!(reader.jump(mem::size_of::<u16>() * 2 *
(hhea_table.number_of_h_metrics - 1) as usize)); (hhea_table.number_of_h_metrics - 1) as usize).map_err(Error::eof));
advance_width = try!(reader.read_u16::<BigEndian>().map_err(drop)); advance_width = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
try!(reader.jump(mem::size_of::<i16>() * glyph_id as usize)); try!(reader.jump(mem::size_of::<i16>() * glyph_id as usize).map_err(Error::eof));
} }
// Read the left-side bearing. // Read the left-side bearing.
let lsb = try!(reader.read_i16::<BigEndian>().map_err(drop)); let lsb = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
Ok(HorizontalMetrics { Ok(HorizontalMetrics {
advance_width: advance_width, advance_width: advance_width,

View File

@ -9,8 +9,8 @@
// except according to those terms. // except according to those terms.
use byteorder::{BigEndian, ReadBytesExt}; use byteorder::{BigEndian, ReadBytesExt};
use otf::FontTable;
use otf::head::HeadTable; use otf::head::HeadTable;
use otf::{Error, FontTable};
use util::Jump; use util::Jump;
pub struct LocaTable<'a> { pub struct LocaTable<'a> {
@ -18,31 +18,33 @@ pub struct LocaTable<'a> {
} }
impl<'a> LocaTable<'a> { impl<'a> LocaTable<'a> {
pub fn new(loca_table: FontTable<'a>) -> Result<LocaTable<'a>, ()> { pub fn new(loca_table: FontTable<'a>) -> Result<LocaTable<'a>, Error> {
Ok(LocaTable { Ok(LocaTable {
table: loca_table, table: loca_table,
}) })
} }
pub fn location_of(&self, head_table: &HeadTable, glyph_id: u16) -> Result<Option<u32>, ()> { pub fn location_of(&self, head_table: &HeadTable, glyph_id: u16)
-> Result<Option<u32>, Error> {
let mut reader = self.table.bytes; let mut reader = self.table.bytes;
let (this_location, next_location) = match head_table.index_to_loc_format { let (this_location, next_location) = match head_table.index_to_loc_format {
0 => { 0 => {
try!(reader.jump(glyph_id as usize * 2)); try!(reader.jump(glyph_id as usize * 2).map_err(Error::eof));
let this_location = try!(reader.read_u16::<BigEndian>().map_err(drop)) as u32 * 2; let this_location =
let next_location = match reader.read_u16::<BigEndian>().map_err(drop) { try!(reader.read_u16::<BigEndian>().map_err(Error::eof)) as u32 * 2;
let next_location = match reader.read_u16::<BigEndian>() {
Ok(next_location) => Ok(next_location as u32 * 2), Ok(next_location) => Ok(next_location as u32 * 2),
Err(_) => Err(()), Err(_) => Err(Error::UnexpectedEof),
}; };
(this_location, next_location) (this_location, next_location)
} }
1 => { 1 => {
try!(reader.jump(glyph_id as usize * 4)); try!(reader.jump(glyph_id as usize * 4).map_err(Error::eof));
let this_location = try!(reader.read_u32::<BigEndian>().map_err(drop)); let this_location = try!(reader.read_u32::<BigEndian>().map_err(Error::eof));
let next_location = reader.read_u32::<BigEndian>().map_err(drop); let next_location = reader.read_u32::<BigEndian>().map_err(Error::eof);
(this_location, next_location) (this_location, next_location)
} }
_ => return Err(()), _ => return Err(Error::UnknownFormat),
}; };
if next_location == Ok(this_location) { if next_location == Ok(this_location) {

View File

@ -86,59 +86,59 @@ pub struct FontTable<'a> {
} }
impl<'a> Font<'a> { impl<'a> Font<'a> {
pub fn new<'b>(bytes: &'b [u8]) -> Result<Font<'b>, ()> { pub fn new<'b>(bytes: &'b [u8]) -> Result<Font<'b>, Error> {
// Check magic number. // Check magic number.
let mut reader = bytes; let mut reader = bytes;
let mut magic_number = try!(reader.read_u32::<BigEndian>().map_err(drop)); let mut magic_number = try!(reader.read_u32::<BigEndian>().map_err(Error::eof));
match magic_number { match magic_number {
TTCF => { TTCF => {
// This is a font collection. Read the first font. // This is a font collection. Read the first font.
// //
// TODO(pcwalton): Provide a mechanism to read others. // TODO(pcwalton): Provide a mechanism to read others.
let major_version = try!(reader.read_u16::<BigEndian>().map_err(drop)); let major_version = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
let minor_version = try!(reader.read_u16::<BigEndian>().map_err(drop)); let minor_version = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
if (major_version != 1 && major_version != 2) || minor_version != 0 { if (major_version != 1 && major_version != 2) || minor_version != 0 {
return Err(()) return Err(Error::UnsupportedVersion)
} }
let num_fonts = try!(reader.read_u32::<BigEndian>().map_err(drop)); let num_fonts = try!(reader.read_u32::<BigEndian>().map_err(Error::eof));
if num_fonts == 0 { if num_fonts == 0 {
return Err(()) return Err(Error::Failed)
} }
let table_offset = try!(reader.read_u32::<BigEndian>().map_err(drop)); let table_offset = try!(reader.read_u32::<BigEndian>().map_err(Error::eof));
Font::from_otf(&bytes[table_offset as usize..]) Font::from_otf(&bytes[table_offset as usize..])
} }
magic_number if SFNT_VERSIONS.contains(&magic_number) => Font::from_otf(bytes), magic_number if SFNT_VERSIONS.contains(&magic_number) => Font::from_otf(bytes),
0x0100 => Font::from_dfont(bytes), 0x0100 => Font::from_dfont(bytes),
_ => Err(()), _ => Err(Error::UnknownFormat),
} }
} }
pub fn from_otf<'b>(bytes: &'b [u8]) -> Result<Font<'b>, ()> { pub fn from_otf<'b>(bytes: &'b [u8]) -> Result<Font<'b>, Error> {
let mut reader = bytes; let mut reader = bytes;
let mut magic_number = try!(reader.read_u32::<BigEndian>().map_err(drop)); let mut magic_number = try!(reader.read_u32::<BigEndian>().map_err(Error::eof));
// Check version. // Check version.
if !SFNT_VERSIONS.contains(&magic_number) { if !SFNT_VERSIONS.contains(&magic_number) {
return Err(()) return Err(Error::UnknownFormat)
} }
let num_tables = try!(reader.read_u16::<BigEndian>().map_err(drop)); let num_tables = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
try!(reader.jump(mem::size_of::<u16>() * 3)); try!(reader.jump(mem::size_of::<u16>() * 3).map_err(Error::eof));
let (mut cmap_table, mut head_table) = (None, None); let (mut cmap_table, mut head_table) = (None, None);
let (mut hhea_table, mut hmtx_table) = (None, None); let (mut hhea_table, mut hmtx_table) = (None, None);
let (mut glyf_table, mut loca_table) = (None, None); let (mut glyf_table, mut loca_table) = (None, None);
for _ in 0..num_tables { for _ in 0..num_tables {
let table_id = try!(reader.read_u32::<BigEndian>().map_err(drop)); let table_id = try!(reader.read_u32::<BigEndian>().map_err(Error::eof));
// Skip over the checksum. // Skip over the checksum.
try!(reader.read_u32::<BigEndian>().map_err(drop)); try!(reader.read_u32::<BigEndian>().map_err(Error::eof));
let offset = try!(reader.read_u32::<BigEndian>().map_err(drop)) as usize; let offset = try!(reader.read_u32::<BigEndian>().map_err(Error::eof)) as usize;
let length = try!(reader.read_u32::<BigEndian>().map_err(drop)) as usize; let length = try!(reader.read_u32::<BigEndian>().map_err(Error::eof)) as usize;
let mut slot = match table_id { let mut slot = match table_id {
CMAP => &mut cmap_table, CMAP => &mut cmap_table,
@ -152,7 +152,7 @@ impl<'a> Font<'a> {
// Make sure there isn't more than one copy of the table. // Make sure there isn't more than one copy of the table.
if slot.is_some() { if slot.is_some() {
return Err(()) return Err(Error::Failed)
} }
*slot = Some(FontTable { *slot = Some(FontTable {
@ -168,10 +168,10 @@ impl<'a> Font<'a> {
Ok(Font { Ok(Font {
bytes: bytes, bytes: bytes,
cmap: CmapTable::new(try!(cmap_table.ok_or(()))), cmap: CmapTable::new(try!(cmap_table.ok_or(Error::RequiredTableMissing))),
head: try!(HeadTable::new(try!(head_table.ok_or(())))), head: try!(HeadTable::new(try!(head_table.ok_or(Error::RequiredTableMissing)))),
hhea: try!(HheaTable::new(try!(hhea_table.ok_or(())))), hhea: try!(HheaTable::new(try!(hhea_table.ok_or(Error::RequiredTableMissing)))),
hmtx: HmtxTable::new(try!(hmtx_table.ok_or(()))), hmtx: HmtxTable::new(try!(hmtx_table.ok_or(Error::RequiredTableMissing))),
glyf: glyf_table.map(GlyfTable::new), glyf: glyf_table.map(GlyfTable::new),
loca: loca_table, loca: loca_table,
@ -179,35 +179,36 @@ impl<'a> Font<'a> {
} }
/// https://github.com/kreativekorp/ksfl/wiki/Macintosh-Resource-File-Format /// https://github.com/kreativekorp/ksfl/wiki/Macintosh-Resource-File-Format
pub fn from_dfont<'b>(bytes: &'b [u8]) -> Result<Font<'b>, ()> { pub fn from_dfont<'b>(bytes: &'b [u8]) -> Result<Font<'b>, Error> {
let mut reader = bytes; let mut reader = bytes;
// Read the Mac resource file header. // Read the Mac resource file header.
let resource_data_offset = try!(reader.read_u32::<BigEndian>().map_err(drop)); let resource_data_offset = try!(reader.read_u32::<BigEndian>().map_err(Error::eof));
let resource_map_offset = try!(reader.read_u32::<BigEndian>().map_err(drop)); let resource_map_offset = try!(reader.read_u32::<BigEndian>().map_err(Error::eof));
let resource_data_size = try!(reader.read_u32::<BigEndian>().map_err(drop)); let resource_data_size = try!(reader.read_u32::<BigEndian>().map_err(Error::eof));
let resource_map_size = try!(reader.read_u32::<BigEndian>().map_err(drop)); let resource_map_size = try!(reader.read_u32::<BigEndian>().map_err(Error::eof));
// Move to the fields we care about in the resource map. // Move to the fields we care about in the resource map.
reader = bytes; reader = bytes;
try!(reader.jump(resource_map_offset as usize + mem::size_of::<u32>() * 5 + try!(reader.jump(resource_map_offset as usize + mem::size_of::<u32>() * 5 +
mem::size_of::<u16>() * 2)); mem::size_of::<u16>() * 2).map_err(Error::eof));
// Read the type list and name list offsets. // Read the type list and name list offsets.
let type_list_offset = try!(reader.read_u16::<BigEndian>().map_err(drop)); let type_list_offset = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
let name_list_offset = try!(reader.read_u16::<BigEndian>().map_err(drop)); let name_list_offset = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
// Move to the type list. // Move to the type list.
reader = bytes; reader = bytes;
try!(reader.jump(resource_map_offset as usize + type_list_offset as usize)); try!(reader.jump(resource_map_offset as usize + type_list_offset as usize)
.map_err(Error::eof));
// Find the 'sfnt' type. // Find the 'sfnt' type.
let type_count = (try!(reader.read_i16::<BigEndian>().map_err(drop)) + 1) as usize; let type_count = (try!(reader.read_i16::<BigEndian>().map_err(Error::eof)) + 1) as usize;
let mut resource_count_and_list_offset = None; let mut resource_count_and_list_offset = None;
for type_index in 0..type_count { for type_index in 0..type_count {
let type_id = try!(reader.read_u32::<BigEndian>().map_err(drop)); let type_id = try!(reader.read_u32::<BigEndian>().map_err(Error::eof));
let resource_count = try!(reader.read_u16::<BigEndian>().map_err(drop)); let resource_count = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
let resource_list_offset = try!(reader.read_u16::<BigEndian>().map_err(drop)); let resource_list_offset = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
if type_id == SFNT { if type_id == SFNT {
resource_count_and_list_offset = Some((resource_count, resource_list_offset)); resource_count_and_list_offset = Some((resource_count, resource_list_offset));
break break
@ -217,12 +218,12 @@ impl<'a> Font<'a> {
// Unpack the resource count and list offset. // Unpack the resource count and list offset.
let resource_count; let resource_count;
match resource_count_and_list_offset { match resource_count_and_list_offset {
None => return Err(()), None => return Err(Error::Failed),
Some((count, resource_list_offset)) => { Some((count, resource_list_offset)) => {
resource_count = count; resource_count = count;
reader = bytes; reader = bytes;
try!(reader.jump(resource_map_offset as usize + type_list_offset as usize + try!(reader.jump(resource_map_offset as usize + type_list_offset as usize +
resource_list_offset as usize)); resource_list_offset as usize).map_err(Error::eof));
} }
} }
@ -230,45 +231,54 @@ impl<'a> Font<'a> {
// //
// TODO(pcwalton): This only gets the first one. Allow the user of this library to select // TODO(pcwalton): This only gets the first one. Allow the user of this library to select
// others. // others.
let sfnt_id = try!(reader.read_u16::<BigEndian>().map_err(drop)); let sfnt_id = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
let sfnt_name_offset = try!(reader.read_u16::<BigEndian>().map_err(drop)); let sfnt_name_offset = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
let sfnt_data_offset = try!(reader.read_u32::<BigEndian>().map_err(drop)) & 0x00ffffff; let sfnt_data_offset = try!(reader.read_u32::<BigEndian>().map_err(Error::eof)) &
let sfnt_ptr = try!(reader.read_u32::<BigEndian>().map_err(drop)); 0x00ffffff;
let sfnt_ptr = try!(reader.read_u32::<BigEndian>().map_err(Error::eof));
// Load the resource. // Load the resource.
reader = bytes; reader = bytes;
try!(reader.jump(resource_data_offset as usize + sfnt_data_offset as usize)); try!(reader.jump(resource_data_offset as usize + sfnt_data_offset as usize)
let sfnt_size = try!(reader.read_u32::<BigEndian>().map_err(drop)); .map_err(Error::eof));
let sfnt_size = try!(reader.read_u32::<BigEndian>().map_err(Error::eof));
Font::from_otf(&reader[0..sfnt_size as usize]) Font::from_otf(&reader[0..sfnt_size as usize])
} }
#[inline] #[inline]
pub fn glyph_ranges_for_codepoint_ranges(&self, codepoint_ranges: &[CodepointRange]) pub fn glyph_ranges_for_codepoint_ranges(&self, codepoint_ranges: &[CodepointRange])
-> Result<GlyphRanges, ()> { -> Result<GlyphRanges, Error> {
self.cmap.glyph_ranges_for_codepoint_ranges(codepoint_ranges) self.cmap.glyph_ranges_for_codepoint_ranges(codepoint_ranges)
} }
#[inline] #[inline]
pub fn for_each_point<F>(&self, glyph_id: u16, callback: F) -> Result<(), ()> pub fn for_each_point<F>(&self, glyph_id: u16, callback: F) -> Result<(), Error>
where F: FnMut(&Point) { where F: FnMut(&Point) {
match self.glyf { match self.glyf {
Some(glyf) => { Some(glyf) => {
glyf.for_each_point(&self.head, let loca = match self.loca {
try!(self.loca.as_ref().ok_or(())), Some(ref loca) => loca,
glyph_id, None => return Err(Error::RequiredTableMissing),
callback) };
glyf.for_each_point(&self.head, loca, glyph_id, callback)
} }
None => Ok(()), None => Ok(()),
} }
} }
#[inline] #[inline]
pub fn glyph_bounds(&self, glyph_id: u16) -> Result<GlyphBounds, ()> { pub fn glyph_bounds(&self, glyph_id: u16) -> Result<GlyphBounds, Error> {
match self.glyf { match self.glyf {
Some(glyf) => { Some(glyf) => {
glyf.glyph_bounds(&self.head, try!(self.loca.as_ref().ok_or(())), glyph_id) let loca = match self.loca {
Some(ref loca) => loca,
None => return Err(Error::RequiredTableMissing),
};
glyf.glyph_bounds(&self.head, loca, glyph_id)
} }
None => Err(()), None => Err(Error::RequiredTableMissing),
} }
} }
@ -278,8 +288,46 @@ impl<'a> Font<'a> {
} }
#[inline] #[inline]
pub fn metrics_for_glyph(&self, glyph_id: u16) -> Result<HorizontalMetrics, ()> { pub fn metrics_for_glyph(&self, glyph_id: u16) -> Result<HorizontalMetrics, Error> {
self.hmtx.metrics_for_glyph(&self.hhea, glyph_id) self.hmtx.metrics_for_glyph(&self.hhea, glyph_id)
} }
} }
/// Errors that can occur when parsing OpenType fonts.
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum Error {
/// A miscellaneous error occurred.
Failed,
/// The file ended unexpectedly.
UnexpectedEof,
/// The file declared that it was in a version of the format we don't support.
UnsupportedVersion,
/// The file was of a format we don't support.
UnknownFormat,
/// The font had a glyph format we don't support.
UnsupportedGlyphFormat,
/// We don't support the declared version of the font's character map.
UnsupportedCmapVersion,
/// The font character map has an unsupported platform/encoding ID.
UnsupportedCmapEncoding,
/// The font character map has an unsupported format.
UnsupportedCmapFormat,
/// We don't support the declared version of the font header.
UnsupportedHeadVersion,
/// We don't support the declared version of the font's horizontal metrics.
UnsupportedHheaVersion,
/// A required table is missing.
RequiredTableMissing,
/// The glyph is a composite glyph.
///
/// TODO(pcwalton): Support these.
CompositeGlyph,
}
impl Error {
#[inline]
pub fn eof<T>(_: T) -> Error {
Error::UnexpectedEof
}
}

View File

@ -11,7 +11,7 @@
use euclid::{Point2D, Rect, Size2D}; use euclid::{Point2D, Rect, Size2D};
use gl::types::{GLsizeiptr, GLuint}; use gl::types::{GLsizeiptr, GLuint};
use gl; use gl;
use otf::Font; use otf::{self, Font};
use std::mem; use std::mem;
use std::os::raw::c_void; use std::os::raw::c_void;
@ -37,7 +37,7 @@ impl OutlineBuilder {
} }
} }
pub fn add_glyph(&mut self, font: &Font, glyph_id: u16) -> Result<(), ()> { pub fn add_glyph(&mut self, font: &Font, glyph_id: u16) -> Result<(), otf::Error> {
let glyph_index = self.descriptors.len() as u16; let glyph_index = self.descriptors.len() as u16;
let mut point_index = self.vertices.len() as u32; let mut point_index = self.vertices.len() as u32;