Add error handling to the OTF format
This commit is contained in:
parent
23fa035178
commit
316123123a
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
156
src/otf/mod.rs
156
src/otf/mod.rs
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue