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

View File

@ -10,9 +10,9 @@
use byteorder::{BigEndian, ReadBytesExt};
use euclid::Point2D;
use otf::FontTable;
use otf::head::HeadTable;
use otf::loca::LocaTable;
use otf::{Error, FontTable};
use outline::GlyphBounds;
use std::mem;
use util::Jump;
@ -54,7 +54,7 @@ impl<'a> GlyfTable<'a> {
loca_table: &LocaTable,
glyph_id: u16,
mut callback: F)
-> Result<(), ()> where F: FnMut(&Point) {
-> Result<(), Error> where F: FnMut(&Point) {
let mut reader = self.table.bytes;
match try!(loca_table.location_of(head_table, glyph_id)) {
@ -62,24 +62,25 @@ impl<'a> GlyfTable<'a> {
// No points.
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 {
// 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.
let mut endpoints_reader = reader;
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;
try!(reader.jump(mem::size_of::<u16>() as usize * (number_of_contours as usize - 1))
.map_err(Error::eof));
let number_of_points = try!(reader.read_u16::<BigEndian>().map_err(Error::eof)) + 1;
// Skip over hinting instructions.
let instruction_length = try!(reader.read_u16::<BigEndian>().map_err(drop));
try!(reader.jump(instruction_length as usize));
let instruction_length = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
try!(reader.jump(instruction_length as usize).map_err(Error::eof));
// Find the offsets of the X and Y coordinates.
let flags_reader = reader;
@ -89,14 +90,14 @@ impl<'a> GlyfTable<'a> {
// Set up the streams.
let mut flag_parser = try!(FlagParser::new(flags_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;
// Now parse the contours.
let (mut position, mut point_index) = (Point2D::new(0, 0), 0);
for _ in 0..number_of_contours {
let contour_point_count =
try!(endpoints_reader.read_u16::<BigEndian>().map_err(drop)) - point_index + 1;
let contour_point_count = try!(endpoints_reader.read_u16::<BigEndian>()
.map_err(Error::eof)) - point_index + 1;
let mut first_on_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);
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) {
delta.x = -delta.x
}
} 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) {
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) {
delta.y = -delta.y
}
} 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) {
@ -189,7 +190,7 @@ impl<'a> GlyfTable<'a> {
}
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;
match try!(loca_table.location_of(head_table, glyph_id)) {
@ -202,16 +203,16 @@ impl<'a> GlyfTable<'a> {
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.
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 y_min = try!(reader.read_i16::<BigEndian>().map_err(drop));
let x_max = try!(reader.read_i16::<BigEndian>().map_err(drop));
let y_max = 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(Error::eof));
let x_max = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
let y_max = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
Ok(GlyphBounds {
left: x_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.
#[inline]
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);
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) {
1
} 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) {
@ -255,7 +256,7 @@ struct FlagParser<'a> {
impl<'a> FlagParser<'a> {
#[inline]
fn new(buffer: &[u8]) -> Result<FlagParser, ()> {
fn new(buffer: &[u8]) -> Result<FlagParser, Error> {
let mut parser = FlagParser {
next: buffer,
current: &buffer[0],
@ -266,18 +267,26 @@ impl<'a> FlagParser<'a> {
}
#[inline]
fn next(&mut self) -> Result<(), ()> {
fn next(&mut self) -> Result<(), Error> {
if self.repeats_left > 0 {
self.repeats_left -= 1;
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);
self.next = &self.next[1..];
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..];
} else {
self.repeats_left = 0

View File

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

View File

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

View File

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

View File

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

View File

@ -86,59 +86,59 @@ pub struct FontTable<'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.
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 {
TTCF => {
// This is a font collection. Read the first font.
//
// TODO(pcwalton): Provide a mechanism to read others.
let major_version = try!(reader.read_u16::<BigEndian>().map_err(drop));
let minor_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(Error::eof));
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 {
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..])
}
magic_number if SFNT_VERSIONS.contains(&magic_number) => Font::from_otf(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 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.
if !SFNT_VERSIONS.contains(&magic_number) {
return Err(())
return Err(Error::UnknownFormat)
}
let num_tables = try!(reader.read_u16::<BigEndian>().map_err(drop));
try!(reader.jump(mem::size_of::<u16>() * 3));
let num_tables = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
try!(reader.jump(mem::size_of::<u16>() * 3).map_err(Error::eof));
let (mut cmap_table, mut head_table) = (None, None);
let (mut hhea_table, mut hmtx_table) = (None, None);
let (mut glyf_table, mut loca_table) = (None, None);
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.
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 length = 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(Error::eof)) as usize;
let mut slot = match table_id {
CMAP => &mut cmap_table,
@ -152,7 +152,7 @@ impl<'a> Font<'a> {
// Make sure there isn't more than one copy of the table.
if slot.is_some() {
return Err(())
return Err(Error::Failed)
}
*slot = Some(FontTable {
@ -168,10 +168,10 @@ impl<'a> Font<'a> {
Ok(Font {
bytes: bytes,
cmap: CmapTable::new(try!(cmap_table.ok_or(()))),
head: try!(HeadTable::new(try!(head_table.ok_or(())))),
hhea: try!(HheaTable::new(try!(hhea_table.ok_or(())))),
hmtx: HmtxTable::new(try!(hmtx_table.ok_or(()))),
cmap: CmapTable::new(try!(cmap_table.ok_or(Error::RequiredTableMissing))),
head: try!(HeadTable::new(try!(head_table.ok_or(Error::RequiredTableMissing)))),
hhea: try!(HheaTable::new(try!(hhea_table.ok_or(Error::RequiredTableMissing)))),
hmtx: HmtxTable::new(try!(hmtx_table.ok_or(Error::RequiredTableMissing))),
glyf: glyf_table.map(GlyfTable::new),
loca: loca_table,
@ -179,35 +179,36 @@ impl<'a> Font<'a> {
}
/// 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;
// Read the Mac resource file header.
let resource_data_offset = try!(reader.read_u32::<BigEndian>().map_err(drop));
let resource_map_offset = try!(reader.read_u32::<BigEndian>().map_err(drop));
let resource_data_size = try!(reader.read_u32::<BigEndian>().map_err(drop));
let resource_map_size = 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(Error::eof));
let resource_data_size = try!(reader.read_u32::<BigEndian>().map_err(Error::eof));
let resource_map_size = try!(reader.read_u32::<BigEndian>().map_err(Error::eof));
// Move to the fields we care about in the resource map.
reader = bytes;
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.
let type_list_offset = try!(reader.read_u16::<BigEndian>().map_err(drop));
let name_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(Error::eof));
// Move to the type list.
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.
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;
for type_index in 0..type_count {
let type_id = try!(reader.read_u32::<BigEndian>().map_err(drop));
let resource_count = try!(reader.read_u16::<BigEndian>().map_err(drop));
let resource_list_offset = try!(reader.read_u16::<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(Error::eof));
let resource_list_offset = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
if type_id == SFNT {
resource_count_and_list_offset = Some((resource_count, resource_list_offset));
break
@ -217,12 +218,12 @@ impl<'a> Font<'a> {
// Unpack the resource count and list offset.
let resource_count;
match resource_count_and_list_offset {
None => return Err(()),
None => return Err(Error::Failed),
Some((count, resource_list_offset)) => {
resource_count = count;
reader = bytes;
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
// others.
let sfnt_id = try!(reader.read_u16::<BigEndian>().map_err(drop));
let sfnt_name_offset = try!(reader.read_u16::<BigEndian>().map_err(drop));
let sfnt_data_offset = try!(reader.read_u32::<BigEndian>().map_err(drop)) & 0x00ffffff;
let sfnt_ptr = try!(reader.read_u32::<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(Error::eof));
let sfnt_data_offset = try!(reader.read_u32::<BigEndian>().map_err(Error::eof)) &
0x00ffffff;
let sfnt_ptr = try!(reader.read_u32::<BigEndian>().map_err(Error::eof));
// Load the resource.
reader = bytes;
try!(reader.jump(resource_data_offset as usize + sfnt_data_offset as usize));
let sfnt_size = try!(reader.read_u32::<BigEndian>().map_err(drop));
try!(reader.jump(resource_data_offset as usize + sfnt_data_offset as usize)
.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])
}
#[inline]
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)
}
#[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) {
match self.glyf {
Some(glyf) => {
glyf.for_each_point(&self.head,
try!(self.loca.as_ref().ok_or(())),
glyph_id,
callback)
let loca = match self.loca {
Some(ref loca) => loca,
None => return Err(Error::RequiredTableMissing),
};
glyf.for_each_point(&self.head, loca, glyph_id, callback)
}
None => Ok(()),
}
}
#[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 {
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]
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)
}
}
/// 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 gl::types::{GLsizeiptr, GLuint};
use gl;
use otf::Font;
use otf::{self, Font};
use std::mem;
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 mut point_index = self.vertices.len() as u32;