Split the `otf` module up into `containers` and `tables`.

Putting all container format parsing in `otf/mod.rs` won't scale as we add more, such as WOFF.
This commit is contained in:
Patrick Walton 2017-02-22 10:22:14 -08:00
parent 9fda8f6534
commit 9490fa2d3b
26 changed files with 886 additions and 749 deletions

View File

@ -22,8 +22,8 @@ use memmap::{Mmap, Protection};
use pathfinder::atlas::AtlasBuilder;
use pathfinder::charmap::CodepointRange;
use pathfinder::coverage::CoverageBuffer;
use pathfinder::font::Font;
use pathfinder::outline::OutlineBuilder;
use pathfinder::otf::Font;
use pathfinder::rasterizer::{Rasterizer, RasterizerOptions};
use std::env;
use std::os::raw::c_void;

View File

@ -7,7 +7,7 @@ extern crate pathfinder;
use memmap::{Mmap, Protection};
use pathfinder::charmap::CodepointRange;
use pathfinder::otf::{Font, PointKind};
use pathfinder::font::{Font, PointKind};
use std::char;
use std::env;

View File

@ -21,8 +21,8 @@ use memmap::{Mmap, Protection};
use pathfinder::atlas::AtlasBuilder;
use pathfinder::charmap::CodepointRange;
use pathfinder::coverage::CoverageBuffer;
use pathfinder::font::Font;
use pathfinder::outline::OutlineBuilder;
use pathfinder::otf::Font;
use pathfinder::rasterizer::{Rasterizer, RasterizerOptions};
use std::env;
use std::os::raw::c_void;

View File

@ -23,7 +23,7 @@ use pathfinder::atlas::AtlasBuilder;
use pathfinder::charmap::CodepointRanges;
use pathfinder::coverage::CoverageBuffer;
use pathfinder::error::RasterError;
use pathfinder::otf::Font;
use pathfinder::font::Font;
use pathfinder::rasterizer::{DrawAtlasProfilingEvents, Rasterizer, RasterizerOptions};
use pathfinder::typesetter::{GlyphStore, PositionedGlyph, Typesetter};
use std::char;

98
src/containers/dfont.rs Normal file
View File

@ -0,0 +1,98 @@
// Copyright 2017 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Apple Font Suitcase (`.dfont`) files.
//!
//! See: https://github.com/kreativekorp/ksfl/wiki/Macintosh-Resource-File-Format
use byteorder::{BigEndian, ReadBytesExt};
use error::FontError;
use font::Font;
use std::mem;
use util::Jump;
const SFNT: u32 = ((b's' as u32) << 24) |
((b'f' as u32) << 16) |
((b'n' as u32) << 8) |
(b't' as u32);
impl<'a> Font<'a> {
/// https://github.com/kreativekorp/ksfl/wiki/Macintosh-Resource-File-Format
pub fn from_dfont_index<'b>(bytes: &'b [u8], index: u32) -> Result<Font<'b>, FontError> {
let mut reader = bytes;
// Read the Mac resource file header.
let resource_data_offset = try!(reader.read_u32::<BigEndian>().map_err(FontError::eof));
let resource_map_offset = try!(reader.read_u32::<BigEndian>().map_err(FontError::eof));
let _resource_data_size = try!(reader.read_u32::<BigEndian>().map_err(FontError::eof));
let _resource_map_size = try!(reader.read_u32::<BigEndian>().map_err(FontError::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).map_err(FontError::eof));
// Read the type list and name list offsets.
let type_list_offset = try!(reader.read_u16::<BigEndian>().map_err(FontError::eof));
let _name_list_offset = try!(reader.read_u16::<BigEndian>().map_err(FontError::eof));
// Move to the type list.
reader = bytes;
try!(reader.jump(resource_map_offset as usize + type_list_offset as usize)
.map_err(FontError::eof));
// Find the 'sfnt' type.
let type_count = (try!(reader.read_i16::<BigEndian>().map_err(FontError::eof)) + 1) as usize;
let mut resource_count_and_list_offset = None;
for _ in 0..type_count {
let type_id = try!(reader.read_u32::<BigEndian>().map_err(FontError::eof));
let resource_count = try!(reader.read_u16::<BigEndian>().map_err(FontError::eof));
let resource_list_offset = try!(reader.read_u16::<BigEndian>().map_err(FontError::eof));
if type_id == SFNT {
resource_count_and_list_offset = Some((resource_count, resource_list_offset));
break
}
}
// Unpack the resource count and list offset.
let resource_count;
match resource_count_and_list_offset {
None => return Err(FontError::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).map_err(FontError::eof));
}
}
// Check whether the index is in bounds.
if index >= resource_count as u32 + 1 {
return Err(FontError::FontIndexOutOfBounds)
}
// Find the font we're interested in.
try!(reader.jump(index as usize * (mem::size_of::<u16>() * 2 + mem::size_of::<u32>() * 2))
.map_err(FontError::eof));
let _sfnt_id = try!(reader.read_u16::<BigEndian>().map_err(FontError::eof));
let _sfnt_name_offset = try!(reader.read_u16::<BigEndian>().map_err(FontError::eof));
let sfnt_data_offset = try!(reader.read_u32::<BigEndian>().map_err(FontError::eof)) &
0x00ffffff;
let _sfnt_ptr = try!(reader.read_u32::<BigEndian>().map_err(FontError::eof));
// Load the resource.
reader = bytes;
try!(reader.jump(resource_data_offset as usize + sfnt_data_offset as usize)
.map_err(FontError::eof));
let sfnt_size = try!(reader.read_u32::<BigEndian>().map_err(FontError::eof));
Font::from_otf(&reader[0..sfnt_size as usize], 0)
}
}

16
src/containers/mod.rs Normal file
View File

@ -0,0 +1,16 @@
// Copyright 2017 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Various kinds of files that can contain OpenType fonts.
pub mod dfont;
pub mod otf;
pub mod ttc;

132
src/containers/otf.rs Normal file
View File

@ -0,0 +1,132 @@
// Copyright 2017 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! OpenType `.otf` files.
//!
//! See Microsoft's spec: https://www.microsoft.com/typography/otspec/otff.htm
use byteorder::{BigEndian, ReadBytesExt};
use error::FontError;
use font::{Font, FontTable};
use std::mem;
use tables::cff::{self, CffTable};
use tables::cmap::{self, CmapTable};
use tables::glyf::{self, GlyfTable};
use tables::head::{self, HeadTable};
use tables::hhea::{self, HheaTable};
use tables::hmtx::{self, HmtxTable};
use tables::kern::{self, KernTable};
use tables::loca::{self, LocaTable};
use tables::os_2::{self, Os2Table};
use util::Jump;
const OTTO: u32 = ((b'O' as u32) << 24) |
((b'T' as u32) << 16) |
((b'T' as u32) << 8) |
(b'O' as u32);
pub static SFNT_VERSIONS: [u32; 3] = [
0x10000,
((b't' as u32) << 24) | ((b'r' as u32) << 16) | ((b'u' as u32) << 8) | (b'e' as u32),
OTTO,
];
#[doc(hidden)]
pub struct FontTables<'a> {
pub cmap: CmapTable<'a>,
pub head: HeadTable,
pub hhea: HheaTable,
pub hmtx: HmtxTable<'a>,
pub os_2: Os2Table,
pub cff: Option<CffTable<'a>>,
pub glyf: Option<GlyfTable<'a>>,
pub loca: Option<LocaTable<'a>>,
pub kern: Option<KernTable<'a>>,
}
impl<'a> Font<'a> {
pub fn from_otf<'b>(bytes: &'b [u8], offset: u32) -> Result<Font<'b>, FontError> {
let mut reader = bytes;
try!(reader.jump(offset as usize).map_err(FontError::eof));
// Check the magic number.
if !SFNT_VERSIONS.contains(&try!(reader.read_u32::<BigEndian>().map_err(FontError::eof))) {
return Err(FontError::UnknownFormat)
}
let num_tables = try!(reader.read_u16::<BigEndian>().map_err(FontError::eof));
try!(reader.jump(mem::size_of::<u16>() * 3).map_err(FontError::eof));
let (mut cff_table, mut cmap_table) = (None, None);
let (mut glyf_table, mut head_table) = (None, None);
let (mut hhea_table, mut hmtx_table) = (None, None);
let (mut kern_table, mut loca_table) = (None, None);
let mut os_2_table = None;
for _ in 0..num_tables {
let table_id = try!(reader.read_u32::<BigEndian>().map_err(FontError::eof));
// Skip over the checksum.
try!(reader.read_u32::<BigEndian>().map_err(FontError::eof));
let offset = try!(reader.read_u32::<BigEndian>().map_err(FontError::eof)) as usize;
let length = try!(reader.read_u32::<BigEndian>().map_err(FontError::eof)) as usize;
let mut slot = match table_id {
cff::TAG => &mut cff_table,
cmap::TAG => &mut cmap_table,
glyf::TAG => &mut glyf_table,
head::TAG => &mut head_table,
hhea::TAG => &mut hhea_table,
hmtx::TAG => &mut hmtx_table,
kern::TAG => &mut kern_table,
loca::TAG => &mut loca_table,
os_2::TAG => &mut os_2_table,
_ => continue,
};
// Make sure there isn't more than one copy of the table.
if slot.is_some() {
return Err(FontError::Failed)
}
*slot = Some(FontTable {
bytes: &bytes[offset..offset + length],
})
}
let cff_table = match cff_table {
None => None,
Some(cff_table) => Some(try!(CffTable::new(cff_table))),
};
let loca_table = match loca_table {
None => None,
Some(loca_table) => Some(try!(LocaTable::new(loca_table))),
};
let tables = FontTables {
cmap: CmapTable::new(try!(cmap_table.ok_or(FontError::RequiredTableMissing))),
head: try!(HeadTable::new(try!(head_table.ok_or(FontError::RequiredTableMissing)))),
hhea: try!(HheaTable::new(try!(hhea_table.ok_or(FontError::RequiredTableMissing)))),
hmtx: HmtxTable::new(try!(hmtx_table.ok_or(FontError::RequiredTableMissing))),
os_2: try!(Os2Table::new(try!(os_2_table.ok_or(FontError::RequiredTableMissing)))),
cff: cff_table,
glyf: glyf_table.map(GlyfTable::new),
loca: loca_table,
kern: kern_table.and_then(|table| KernTable::new(table).ok()),
};
Ok(Font::from_tables(bytes, tables))
}
}

52
src/containers/ttc.rs Normal file
View File

@ -0,0 +1,52 @@
// Copyright 2017 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! TrueType Collection (`.ttc`) files.
//!
//! See Microsoft's spec: https://www.microsoft.com/typography/otspec/otff.htm
use byteorder::{BigEndian, ReadBytesExt};
use error::FontError;
use font::Font;
use std::mem;
use util::Jump;
pub const TTCF: u32 = ((b't' as u32) << 24) |
((b't' as u32) << 16) |
((b'c' as u32) << 8) |
(b'f' as u32);
impl<'a> Font<'a> {
/// Creates a new font from a single font within a byte buffer containing the contents of a
/// font collection.
pub fn from_ttc_index<'b>(bytes: &'b [u8], index: u32) -> Result<Font<'b>, FontError> {
let mut reader = bytes;
let magic_number = try!(reader.read_u32::<BigEndian>().map_err(FontError::eof));
if magic_number != TTCF {
return Err(FontError::UnknownFormat)
}
let major_version = try!(reader.read_u16::<BigEndian>().map_err(FontError::eof));
let minor_version = try!(reader.read_u16::<BigEndian>().map_err(FontError::eof));
if (major_version != 1 && major_version != 2) || minor_version != 0 {
return Err(FontError::UnsupportedVersion)
}
let num_fonts = try!(reader.read_u32::<BigEndian>().map_err(FontError::eof));
if index >= num_fonts {
return Err(FontError::FontIndexOutOfBounds)
}
try!(reader.jump(index as usize * mem::size_of::<u32>()).map_err(FontError::eof));
let table_offset = try!(reader.read_u32::<BigEndian>().map_err(FontError::eof));
Font::from_otf(&bytes, table_offset)
}
}

View File

@ -12,9 +12,60 @@
use compute_shader;
use gl::types::GLenum;
use otf;
use std::io;
/// Errors that can occur when parsing OpenType fonts.
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum FontError {
/// A miscellaneous error occurred.
Failed,
/// The file ended unexpectedly.
UnexpectedEof,
/// There is no font with this index in this font collection.
FontIndexOutOfBounds,
/// 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 CFF outlines.
UnsupportedCffVersion,
/// 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,
/// We don't support the declared version of the font's OS/2 and Windows table.
UnsupportedOs2Version,
/// A required table is missing.
RequiredTableMissing,
/// An integer in a CFF DICT was not found.
CffIntegerNotFound,
/// The CFF Top DICT was not found.
CffTopDictNotFound,
/// A CFF `Offset` value was formatted incorrectly.
CffBadOffset,
/// The CFF evaluation stack overflowed.
CffStackOverflow,
/// An unimplemented CFF CharString operator was encountered.
CffUnimplementedOperator,
}
impl FontError {
#[doc(hidden)]
#[inline]
pub fn eof<T>(_: T) -> FontError {
FontError::UnexpectedEof
}
}
/// An OpenGL error with the given code.
///
/// You cannot depend on these being reliably returned. Pathfinder does not call `glGetError()`
@ -68,7 +119,7 @@ pub enum RasterError {
#[derive(Debug)]
pub enum GlyphStoreCreationError {
/// An error occurred when looking up a glyph ID for a character in the font.
OtfError(otf::Error),
FontError(FontError),
/// An error occurred when uploading the outlines to the GPU.
GlError(GlError),
}

222
src/font.rs Normal file
View File

@ -0,0 +1,222 @@
// Copyright 2017 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! OpenType fonts.
use byteorder::{BigEndian, ReadBytesExt};
use charmap::{CodepointRange, GlyphMapping};
use containers::otf::{FontTables, SFNT_VERSIONS};
use containers::ttc::TTCF;
use error::FontError;
use euclid::Point2D;
use outline::GlyphBounds;
use tables::hmtx::HorizontalMetrics;
/// A handle to a font backed by a byte buffer containing the contents of the file (`.ttf`,
/// `.otf`), etc.
///
/// For optimum performance, consider using the `memmap` crate to provide the byte buffer.
pub struct Font<'a> {
pub bytes: &'a [u8],
tables: FontTables<'a>,
}
#[doc(hidden)]
#[derive(Clone, Copy, Debug)]
pub struct FontTable<'a> {
pub bytes: &'a [u8],
}
impl<'a> Font<'a> {
#[doc(hidden)]
pub fn from_tables<'b>(bytes: &'b [u8], tables: FontTables<'b>) -> Font<'b> {
Font {
bytes: bytes,
tables: tables,
}
}
/// Creates a new font from a byte buffer containing the contents of a file or font collection
/// (`.ttf`, `.ttc`, `.otf`, etc.)
///
/// If this is a `.ttc` or `.dfont` collection, this returns the first font within it. If you
/// want to read another one, use the `Font::from_collection_index` API.
///
/// Returns the font on success or an error on failure.
pub fn new<'b>(bytes: &'b [u8]) -> Result<Font<'b>, FontError> {
Font::from_collection_index(bytes, 0)
}
/// Creates a new font from a single font within a byte buffer containing the contents of a
/// file or a font collection (`.ttf`, `.ttc`, `.otf`, etc.)
///
/// If this is a `.ttc` or `.dfont` collection, this returns the appropriate font within it.
///
/// Returns the font on success or an error on failure.
pub fn from_collection_index<'b>(bytes: &'b [u8], index: u32) -> Result<Font<'b>, FontError> {
// Check the magic number.
let mut reader = bytes;
let magic_number = try!(reader.read_u32::<BigEndian>().map_err(FontError::eof));
match magic_number {
TTCF => Font::from_ttc_index(bytes, index),
magic_number if SFNT_VERSIONS.contains(&magic_number) => Font::from_otf(bytes, 0),
0x0100 => Font::from_dfont_index(bytes, index),
_ => Err(FontError::UnknownFormat),
}
}
/// Returns the glyph IDs that map to the given ranges of Unicode codepoints.
///
/// The returned glyph ranges are in the same order as the codepoints.
#[inline]
pub fn glyph_mapping_for_codepoint_ranges(&self, codepoint_ranges: &[CodepointRange])
-> Result<GlyphMapping, FontError> {
self.tables.cmap.glyph_mapping_for_codepoint_ranges(codepoint_ranges)
}
/// Calls the given callback for each point in the supplied glyph's contour.
///
/// This function is the primary method for accessing a glyph's outline.
#[inline]
pub fn for_each_point<F>(&self, glyph_id: u16, callback: F) -> Result<(), FontError>
where F: FnMut(&Point) {
match (self.tables.glyf, self.tables.cff) {
(Some(glyf), None) => {
let loca = match self.tables.loca {
Some(ref loca) => loca,
None => return Err(FontError::RequiredTableMissing),
};
glyf.for_each_point(&self.tables.head, loca, glyph_id, callback)
}
(None, Some(cff)) => cff.for_each_point(glyph_id, callback),
(Some(_), Some(_)) => Err(FontError::Failed),
(None, None) => Ok(()),
}
}
/// Returns the boundaries of the given glyph in font units.
#[inline]
pub fn glyph_bounds(&self, glyph_id: u16) -> Result<GlyphBounds, FontError> {
match (self.tables.glyf, self.tables.cff) {
(Some(glyf), None) => {
let loca = match self.tables.loca {
Some(ref loca) => loca,
None => return Err(FontError::RequiredTableMissing),
};
glyf.glyph_bounds(&self.tables.head, loca, glyph_id)
}
(None, Some(cff)) => cff.glyph_bounds(glyph_id),
(Some(_), Some(_)) => Err(FontError::Failed),
(None, None) => Err(FontError::RequiredTableMissing),
}
}
/// Returns the minimum shelf height that an atlas containing glyphs from this font will need.
#[inline]
pub fn shelf_height(&self, point_size: f32) -> u32 {
// Add 2 to account for the border.
self.tables.head
.max_glyph_bounds
.subpixel_bounds(self.tables.head.units_per_em, point_size)
.round_out()
.size()
.height as u32 + 2
}
/// Returns the number of font units per em.
///
/// An em is traditionally the width of the lowercase letter "m". A typical point size of a
/// font is expressed in number of pixels per em. Thus, in order to convert font units to
/// pixels, you can use an expression like `units * font_size / font.units_per_em()`.
#[inline]
pub fn units_per_em(&self) -> u16 {
self.tables.head.units_per_em
}
/// Returns the horizontal metrics for the glyph with the given ID.
///
/// Horizontal metrics are important for text shaping, as they specify the number of units to
/// advance the pen after typesetting a glyph.
#[inline]
pub fn metrics_for_glyph(&self, glyph_id: u16) -> Result<HorizontalMetrics, FontError> {
self.tables.hmtx.metrics_for_glyph(&self.tables.hhea, glyph_id)
}
/// Returns the kerning between the given two glyph IDs in font units.
///
/// Positive values move glyphs farther apart; negative values move glyphs closer together.
///
/// Zero is returned if no kerning is available in the font.
#[inline]
pub fn kerning_for_glyph_pair(&self, left_glyph_id: u16, right_glyph_id: u16) -> i16 {
match self.tables.kern {
None => 0,
Some(kern) => kern.kerning_for_glyph_pair(left_glyph_id, right_glyph_id).unwrap_or(0),
}
}
/// Returns the distance from the baseline to the top of the text box in font units.
///
/// The following expression computes the baseline-to-baseline height:
/// `font.ascender() - font.descender() + font.line_gap()`.
#[inline]
pub fn ascender(&self) -> i16 {
self.tables.os_2.typo_ascender
}
/// Returns the distance from the baseline to the bottom of the text box in font units.
///
/// The following expression computes the baseline-to-baseline height:
/// `font.ascender() - font.descender() + font.line_gap()`.
#[inline]
pub fn descender(&self) -> i16 {
self.tables.os_2.typo_descender
}
/// Returns the recommended extra gap between lines in font units.
///
/// The following expression computes the baseline-to-baseline height:
/// `font.ascender() - font.descender() + font.line_gap()`.
#[inline]
pub fn line_gap(&self) -> i16 {
self.tables.os_2.typo_line_gap
}
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct Point {
/// Where the point is located in glyph space.
pub position: Point2D<i16>,
/// The index of the point in this contour.
///
/// When iterating over points via `for_each_point`, a value of 0 here indicates that a new
/// contour begins.
pub index_in_contour: u16,
/// The kind of point this is.
pub kind: PointKind,
}
/// The type of point.
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum PointKind {
/// The point is on the curve.
OnCurve,
/// The point is a quadratic control point.
QuadControl,
/// The point is the first cubic control point.
FirstCubicControl,
/// The point is the second cubic control point.
SecondCubicControl,
}

View File

@ -91,13 +91,15 @@ pub mod atlas;
pub mod charmap;
pub mod coverage;
pub mod error;
pub mod otf;
pub mod font;
pub mod outline;
pub mod rasterizer;
pub mod shaper;
pub mod typesetter;
mod containers;
mod rect_packer;
mod tables;
mod util;
#[cfg(test)]

View File

@ -1,487 +0,0 @@
// Copyright 2017 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! OpenType fonts.
pub use otf::glyf::{Point, PointKind};
use byteorder::{BigEndian, ReadBytesExt};
use charmap::{CodepointRange, GlyphMapping};
use otf::cff::CffTable;
use otf::cmap::CmapTable;
use otf::glyf::GlyfTable;
use otf::head::HeadTable;
use otf::hhea::HheaTable;
use otf::hmtx::{HmtxTable, HorizontalMetrics};
use otf::kern::KernTable;
use otf::loca::LocaTable;
use otf::os_2::Os2Table;
use outline::GlyphBounds;
use std::mem;
use std::u16;
use util::Jump;
mod cff;
mod cmap;
mod glyf;
mod head;
mod hhea;
mod hmtx;
mod kern;
mod loca;
mod os_2;
const CFF: u32 = ((b'C' as u32) << 24) |
((b'F' as u32) << 16) |
((b'F' as u32) << 8) |
(b' ' as u32);
const CMAP: u32 = ((b'c' as u32) << 24) |
((b'm' as u32) << 16) |
((b'a' as u32) << 8) |
(b'p' as u32);
const GLYF: u32 = ((b'g' as u32) << 24) |
((b'l' as u32) << 16) |
((b'y' as u32) << 8) |
(b'f' as u32);
const HEAD: u32 = ((b'h' as u32) << 24) |
((b'e' as u32) << 16) |
((b'a' as u32) << 8) |
(b'd' as u32);
const HHEA: u32 = ((b'h' as u32) << 24) |
((b'h' as u32) << 16) |
((b'e' as u32) << 8) |
(b'a' as u32);
const HMTX: u32 = ((b'h' as u32) << 24) |
((b'm' as u32) << 16) |
((b't' as u32) << 8) |
(b'x' as u32);
const KERN: u32 = ((b'k' as u32) << 24) |
((b'e' as u32) << 16) |
((b'r' as u32) << 8) |
(b'n' as u32);
const LOCA: u32 = ((b'l' as u32) << 24) |
((b'o' as u32) << 16) |
((b'c' as u32) << 8) |
(b'a' as u32);
const OS_2: u32 = ((b'O' as u32) << 24) |
((b'S' as u32) << 16) |
((b'/' as u32) << 8) |
(b'2' as u32);
const TTCF: u32 = ((b't' as u32) << 24) |
((b't' as u32) << 16) |
((b'c' as u32) << 8) |
(b'f' as u32);
const OTTO: u32 = ((b'O' as u32) << 24) |
((b'T' as u32) << 16) |
((b'T' as u32) << 8) |
(b'O' as u32);
const SFNT: u32 = ((b's' as u32) << 24) |
((b'f' as u32) << 16) |
((b'n' as u32) << 8) |
(b't' as u32);
static SFNT_VERSIONS: [u32; 3] = [
0x10000,
((b't' as u32) << 24) | ((b'r' as u32) << 16) | ((b'u' as u32) << 8) | (b'e' as u32),
OTTO,
];
/// A handle to a font backed by a byte buffer containing the contents of the file (`.ttf`,
/// `.otf`), etc.
///
/// For optimum performance, consider using the `memmap` crate to provide the byte buffer.
pub struct Font<'a> {
pub bytes: &'a [u8],
cmap: CmapTable<'a>,
head: HeadTable,
hhea: HheaTable,
hmtx: HmtxTable<'a>,
os_2: Os2Table,
cff: Option<CffTable<'a>>,
glyf: Option<GlyfTable<'a>>,
loca: Option<LocaTable<'a>>,
kern: Option<KernTable<'a>>,
}
#[doc(hidden)]
#[derive(Clone, Copy, Debug)]
pub struct FontTable<'a> {
pub bytes: &'a [u8],
}
impl<'a> Font<'a> {
/// Creates a new font from a byte buffer containing the contents of a file or font collection
/// (`.ttf`, `.ttc`, `.otf`, etc.)
///
/// If this is a `.ttc` or `.dfont` collection, this returns the first font within it. If you
/// want to read another one, use the `Font::from_collection_index` API.
///
/// Returns the font on success or an error on failure.
pub fn new<'b>(bytes: &'b [u8]) -> Result<Font<'b>, Error> {
Font::from_collection_index(bytes, 0)
}
/// Creates a new font from a single font within a byte buffer containing the contents of a
/// file or a font collection (`.ttf`, `.ttc`, `.otf`, etc.)
///
/// If this is a `.ttc` or `.dfont` collection, this returns the appropriate font within it.
///
/// Returns the font on success or an error on failure.
pub fn from_collection_index<'b>(bytes: &'b [u8], index: u32) -> Result<Font<'b>, Error> {
// Check the magic number.
let mut reader = bytes;
let 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(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(Error::UnsupportedVersion)
}
let num_fonts = try!(reader.read_u32::<BigEndian>().map_err(Error::eof));
if index >= num_fonts {
return Err(Error::FontIndexOutOfBounds)
}
try!(reader.jump(index as usize * mem::size_of::<u32>()).map_err(Error::eof));
let table_offset = try!(reader.read_u32::<BigEndian>().map_err(Error::eof));
Font::from_otf(&bytes, table_offset)
}
magic_number if SFNT_VERSIONS.contains(&magic_number) => Font::from_otf(bytes, 0),
0x0100 => Font::from_dfont_index(bytes, index),
_ => Err(Error::UnknownFormat),
}
}
fn from_otf<'b>(bytes: &'b [u8], offset: u32) -> Result<Font<'b>, Error> {
let mut reader = bytes;
try!(reader.jump(offset as usize).map_err(Error::eof));
// Check the magic number.
if !SFNT_VERSIONS.contains(&try!(reader.read_u32::<BigEndian>().map_err(Error::eof))) {
return Err(Error::UnknownFormat)
}
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 cff_table, mut cmap_table) = (None, None);
let (mut glyf_table, mut head_table) = (None, None);
let (mut hhea_table, mut hmtx_table) = (None, None);
let (mut kern_table, mut loca_table) = (None, None);
let mut os_2_table = None;
for _ in 0..num_tables {
let table_id = try!(reader.read_u32::<BigEndian>().map_err(Error::eof));
// Skip over the checksum.
try!(reader.read_u32::<BigEndian>().map_err(Error::eof));
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 {
CFF => &mut cff_table,
CMAP => &mut cmap_table,
HEAD => &mut head_table,
HHEA => &mut hhea_table,
HMTX => &mut hmtx_table,
GLYF => &mut glyf_table,
KERN => &mut kern_table,
LOCA => &mut loca_table,
OS_2 => &mut os_2_table,
_ => continue,
};
// Make sure there isn't more than one copy of the table.
if slot.is_some() {
return Err(Error::Failed)
}
*slot = Some(FontTable {
bytes: &bytes[offset..offset + length],
})
}
let cff_table = match cff_table {
None => None,
Some(cff_table) => Some(try!(CffTable::new(cff_table))),
};
let loca_table = match loca_table {
None => None,
Some(loca_table) => Some(try!(LocaTable::new(loca_table))),
};
Ok(Font {
bytes: bytes,
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))),
os_2: try!(Os2Table::new(try!(os_2_table.ok_or(Error::RequiredTableMissing)))),
cff: cff_table,
glyf: glyf_table.map(GlyfTable::new),
loca: loca_table,
kern: kern_table.and_then(|table| KernTable::new(table).ok()),
})
}
/// https://github.com/kreativekorp/ksfl/wiki/Macintosh-Resource-File-Format
fn from_dfont_index<'b>(bytes: &'b [u8], index: u32) -> 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(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).map_err(Error::eof));
// Read the type list and name list offsets.
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)
.map_err(Error::eof));
// Find the 'sfnt' type.
let type_count = (try!(reader.read_i16::<BigEndian>().map_err(Error::eof)) + 1) as usize;
let mut resource_count_and_list_offset = None;
for _ in 0..type_count {
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
}
}
// Unpack the resource count and list offset.
let resource_count;
match resource_count_and_list_offset {
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).map_err(Error::eof));
}
}
// Check whether the index is in bounds.
if index >= resource_count as u32 + 1 {
return Err(Error::FontIndexOutOfBounds)
}
// Find the font we're interested in.
try!(reader.jump(index as usize * (mem::size_of::<u16>() * 2 + mem::size_of::<u32>() * 2))
.map_err(Error::eof));
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)
.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], 0)
}
/// Returns the glyph IDs that map to the given ranges of Unicode codepoints.
///
/// The returned glyph ranges are in the same order as the codepoints.
#[inline]
pub fn glyph_mapping_for_codepoint_ranges(&self, codepoint_ranges: &[CodepointRange])
-> Result<GlyphMapping, Error> {
self.cmap.glyph_mapping_for_codepoint_ranges(codepoint_ranges)
}
/// Calls the given callback for each point in the supplied glyph's contour.
///
/// This function is the primary method for accessing a glyph's outline.
#[inline]
pub fn for_each_point<F>(&self, glyph_id: u16, callback: F) -> Result<(), Error>
where F: FnMut(&Point) {
match (self.glyf, self.cff) {
(Some(glyf), None) => {
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, Some(cff)) => cff.for_each_point(glyph_id, callback),
(Some(_), Some(_)) => Err(Error::Failed),
(None, None) => Ok(()),
}
}
/// Returns the boundaries of the given glyph in font units.
#[inline]
pub fn glyph_bounds(&self, glyph_id: u16) -> Result<GlyphBounds, Error> {
match (self.glyf, self.cff) {
(Some(glyf), None) => {
let loca = match self.loca {
Some(ref loca) => loca,
None => return Err(Error::RequiredTableMissing),
};
glyf.glyph_bounds(&self.head, loca, glyph_id)
}
(None, Some(cff)) => cff.glyph_bounds(glyph_id),
(Some(_), Some(_)) => Err(Error::Failed),
(None, None) => Err(Error::RequiredTableMissing),
}
}
/// Returns the minimum shelf height that an atlas containing glyphs from this font will need.
#[inline]
pub fn shelf_height(&self, point_size: f32) -> u32 {
// Add 2 to account for the border.
self.head
.max_glyph_bounds
.subpixel_bounds(self.head.units_per_em, point_size)
.round_out()
.size()
.height as u32 + 2
}
/// Returns the number of font units per em.
///
/// An em is traditionally the width of the lowercase letter "m". A typical point size of a
/// font is expressed in number of pixels per em. Thus, in order to convert font units to
/// pixels, you can use an expression like `units * font_size / font.units_per_em()`.
#[inline]
pub fn units_per_em(&self) -> u16 {
self.head.units_per_em
}
/// Returns the horizontal metrics for the glyph with the given ID.
///
/// Horizontal metrics are important for text shaping, as they specify the number of units to
/// advance the pen after typesetting a glyph.
#[inline]
pub fn metrics_for_glyph(&self, glyph_id: u16) -> Result<HorizontalMetrics, Error> {
self.hmtx.metrics_for_glyph(&self.hhea, glyph_id)
}
/// Returns the kerning between the given two glyph IDs in font units.
///
/// Positive values move glyphs farther apart; negative values move glyphs closer together.
///
/// Zero is returned if no kerning is available in the font.
#[inline]
pub fn kerning_for_glyph_pair(&self, left_glyph_id: u16, right_glyph_id: u16) -> i16 {
match self.kern {
None => 0,
Some(kern) => kern.kerning_for_glyph_pair(left_glyph_id, right_glyph_id).unwrap_or(0),
}
}
/// Returns the distance from the baseline to the top of the text box in font units.
///
/// The following expression computes the baseline-to-baseline height:
/// `font.ascender() - font.descender() + font.line_gap()`.
#[inline]
pub fn ascender(&self) -> i16 {
self.os_2.typo_ascender
}
/// Returns the distance from the baseline to the bottom of the text box in font units.
///
/// The following expression computes the baseline-to-baseline height:
/// `font.ascender() - font.descender() + font.line_gap()`.
#[inline]
pub fn descender(&self) -> i16 {
self.os_2.typo_descender
}
/// Returns the recommended extra gap between lines in font units.
///
/// The following expression computes the baseline-to-baseline height:
/// `font.ascender() - font.descender() + font.line_gap()`.
#[inline]
pub fn line_gap(&self) -> i16 {
self.os_2.typo_line_gap
}
}
/// 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,
/// There is no font with this index in this font collection.
FontIndexOutOfBounds,
/// 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 CFF outlines.
UnsupportedCffVersion,
/// 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,
/// We don't support the declared version of the font's OS/2 and Windows table.
UnsupportedOs2Version,
/// A required table is missing.
RequiredTableMissing,
/// An integer in a CFF DICT was not found.
CffIntegerNotFound,
/// The CFF Top DICT was not found.
CffTopDictNotFound,
/// A CFF `Offset` value was formatted incorrectly.
CffBadOffset,
/// The CFF evaluation stack overflowed.
CffStackOverflow,
/// An unimplemented CFF CharString operator was encountered.
CffUnimplementedOperator,
}
impl Error {
#[doc(hidden)]
#[inline]
pub fn eof<T>(_: T) -> Error {
Error::UnexpectedEof
}
}

View File

@ -10,11 +10,11 @@
//! Glyph vectors, uploaded in a resolution-independent manner to the GPU.
use error::GlError;
use error::{FontError, GlError};
use euclid::Size2D;
use font::{Font, PointKind};
use gl::types::{GLsizeiptr, GLuint};
use gl;
use otf::{self, Font, PointKind};
use std::mem;
use std::os::raw::c_void;
@ -44,7 +44,7 @@ impl OutlineBuilder {
/// Adds a new glyph to the outline builder. Returns the glyph index, which is useful for later
/// calls to `Atlas::pack_glyph()`.
pub fn add_glyph(&mut self, font: &Font, glyph_id: u16) -> Result<u16, otf::Error> {
pub fn add_glyph(&mut self, font: &Font, glyph_id: u16) -> Result<u16, FontError> {
let glyph_index = self.descriptors.len() as u16;
let mut point_index = self.vertices.len() as u32;

View File

@ -15,7 +15,7 @@
//! the system shaper instead.
use charmap::GlyphMapping;
use otf::Font;
use font::Font;
/// Shapes the given Unicode text in the given font, returning the proper position for each glyph.
///

View File

@ -9,14 +9,19 @@
// except according to those terms.
use byteorder::{BigEndian, ReadBytesExt};
use error::FontError;
use euclid::Point2D;
use otf::glyf::{Point, PointKind};
use otf::{Error, FontTable};
use font::{FontTable, Point, PointKind};
use outline::GlyphBounds;
use std::cmp;
use std::u16;
use util::Jump;
pub const TAG: u32 = ((b'C' as u32) << 24) |
((b'F' as u32) << 16) |
((b'F' as u32) << 8) |
(b' ' as u32);
#[derive(Clone, Copy, Debug)]
pub struct CffTable<'a> {
// The offset of the char strings INDEX.
@ -26,19 +31,19 @@ pub struct CffTable<'a> {
impl<'a> CffTable<'a> {
#[inline]
pub fn new(table: FontTable) -> Result<CffTable, Error> {
pub fn new(table: FontTable) -> Result<CffTable, FontError> {
let mut reader = table.bytes;
// Check version.
let major = try!(reader.read_u8().map_err(Error::eof));
let minor = try!(reader.read_u8().map_err(Error::eof));
let major = try!(reader.read_u8().map_err(FontError::eof));
let minor = try!(reader.read_u8().map_err(FontError::eof));
if major != 1 || minor != 0 {
return Err(Error::UnsupportedCffVersion)
return Err(FontError::UnsupportedCffVersion)
}
// Skip the header.
let hdr_size = try!(reader.read_u8().map_err(Error::eof));
try!(reader.jump(hdr_size as usize - 3).map_err(Error::eof));
let hdr_size = try!(reader.read_u8().map_err(FontError::eof));
try!(reader.jump(hdr_size as usize - 3).map_err(FontError::eof));
// Skip the name INDEX.
//
@ -47,7 +52,7 @@ impl<'a> CffTable<'a> {
// Get the top DICT for our font.
if try!(find_in_index(&mut reader, 0)).is_none() {
return Err(Error::CffTopDictNotFound)
return Err(FontError::CffTopDictNotFound)
}
// Find the CharStrings offset within the top DICT.
@ -67,13 +72,13 @@ impl<'a> CffTable<'a> {
}
pub fn for_each_point<F>(&self, glyph_id: u16, mut callback: F)
-> Result<(), Error> where F: FnMut(&Point) {
-> Result<(), FontError> where F: FnMut(&Point) {
let mut reader = self.table.bytes;
try!(reader.jump(self.char_strings as usize).map_err(Error::eof));
try!(reader.jump(self.char_strings as usize).map_err(FontError::eof));
let char_string_length = match try!(find_in_index(&mut reader, glyph_id)) {
Some(char_string_length) => char_string_length,
None => return Err(Error::UnexpectedEof),
None => return Err(FontError::UnexpectedEof),
};
let mut reader = &reader[0..char_string_length as usize];
@ -87,20 +92,20 @@ impl<'a> CffTable<'a> {
match b0 {
32...246 => try!(stack.push(b0 as i32 - 139)),
247...250 => {
let b1 = try!(reader.read_u8().map_err(Error::eof));
let b1 = try!(reader.read_u8().map_err(FontError::eof));
try!(stack.push((b0 as i32 - 247) * 256 + b1 as i32 + 108))
}
251...254 => {
let b1 = try!(reader.read_u8().map_err(Error::eof));
let b1 = try!(reader.read_u8().map_err(FontError::eof));
try!(stack.push((b0 as i32 - 251) * -256 - b1 as i32 - 108))
}
255 => {
// FIXME(pcwalton): Don't truncate the lower 16 bits.
try!(stack.push(try!(reader.read_i32::<BigEndian>().map_err(Error::eof)) >>
try!(stack.push(try!(reader.read_i32::<BigEndian>().map_err(FontError::eof)) >>
16))
}
28 => {
let number = try!(reader.read_i16::<BigEndian>().map_err(Error::eof)) as i32;
let number = try!(reader.read_i16::<BigEndian>().map_err(FontError::eof)) as i32;
try!(stack.push(number))
}
@ -349,13 +354,13 @@ impl<'a> CffTable<'a> {
// Now skip ⌈hint_count / 8⌉ bytes.
let hint_byte_count = (hint_count as usize + 7) / 8;
try!(reader.jump(hint_byte_count).map_err(Error::eof));
try!(reader.jump(hint_byte_count).map_err(FontError::eof));
}
20 => {
// Skip ⌈hint_count / 8⌉ bytes.
stack.clear();
let hint_byte_count = (hint_count as usize + 7) / 8;
try!(reader.jump(hint_byte_count).map_err(Error::eof));
try!(reader.jump(hint_byte_count).map_err(FontError::eof));
}
21 => {
// |- dx1 dy1 rmoveto
@ -387,13 +392,13 @@ impl<'a> CffTable<'a> {
12 => {
// TODO(pcwalton): Support these extended operators.
let _operator = (12 << 8) |
(try!(reader.read_u8().map_err(Error::eof)) as u32);
(try!(reader.read_u8().map_err(FontError::eof)) as u32);
stack.clear();
return Err(Error::CffUnimplementedOperator)
return Err(FontError::CffUnimplementedOperator)
}
_ => {
stack.clear();
return Err(Error::CffUnimplementedOperator)
return Err(FontError::CffUnimplementedOperator)
}
}
}
@ -404,7 +409,7 @@ impl<'a> CffTable<'a> {
// TODO(pcwalton): Do some caching, perhaps?
// TODO(pcwalton): Compute this at the same time as `for_each_point`, perhaps?
pub fn glyph_bounds(&self, glyph_id: u16) -> Result<GlyphBounds, Error> {
pub fn glyph_bounds(&self, glyph_id: u16) -> Result<GlyphBounds, FontError> {
let mut bounds = GlyphBounds::default();
try!(self.for_each_point(glyph_id, |point| {
bounds.left = cmp::min(bounds.left, point.position.x as i32);
@ -418,17 +423,17 @@ impl<'a> CffTable<'a> {
// Moves the reader to the location of the given element in the index. Returns the length of the
// element if the element was found or `None` otherwise.
fn find_in_index(reader: &mut &[u8], index: u16) -> Result<Option<u32>, Error> {
let count = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
fn find_in_index(reader: &mut &[u8], index: u16) -> Result<Option<u32>, FontError> {
let count = try!(reader.read_u16::<BigEndian>().map_err(FontError::eof));
if count == 0 {
return Ok(None)
}
let off_size = try!(reader.read_u8().map_err(Error::eof));
let off_size = try!(reader.read_u8().map_err(FontError::eof));
let mut offset_reader = *reader;
try!(offset_reader.jump(off_size as usize * cmp::min(index, count) as usize)
.map_err(Error::eof));
.map_err(FontError::eof));
let offset = try!(read_offset(&mut offset_reader, off_size));
let next_offset = if index < count {
@ -438,49 +443,49 @@ fn find_in_index(reader: &mut &[u8], index: u16) -> Result<Option<u32>, Error> {
};
try!(reader.jump(off_size as usize * (count as usize + 1) + offset as usize - 1)
.map_err(Error::eof));
.map_err(FontError::eof));
return Ok(next_offset)
}
// Skips over an INDEX by reading the last element in the offset array and seeking the appropriate
// number of bytes forward.
fn skip_index(reader: &mut &[u8]) -> Result<(), Error> {
fn skip_index(reader: &mut &[u8]) -> Result<(), FontError> {
find_in_index(reader, u16::MAX).map(drop)
}
// Returns the integer with the given operator.
fn get_integer_in_dict(reader: &mut &[u8], operator: u16) -> Result<i32, Error> {
fn get_integer_in_dict(reader: &mut &[u8], operator: u16) -> Result<i32, FontError> {
let mut last_integer_operand = None;
loop {
let b0 = try!(reader.read_u8().map_err(Error::eof));
let b0 = try!(reader.read_u8().map_err(FontError::eof));
match b0 {
32...246 => last_integer_operand = Some(b0 as i32 - 139),
247...250 => {
let b1 = try!(reader.read_u8().map_err(Error::eof));
let b1 = try!(reader.read_u8().map_err(FontError::eof));
last_integer_operand = Some((b0 as i32 - 247) * 256 + b1 as i32 + 108)
}
251...254 => {
let b1 = try!(reader.read_u8().map_err(Error::eof));
let b1 = try!(reader.read_u8().map_err(FontError::eof));
last_integer_operand = Some(-(b0 as i32 - 251) * 256 - b1 as i32 - 108)
}
28 => {
last_integer_operand =
Some(try!(reader.read_i16::<BigEndian>().map_err(Error::eof)) as i32)
Some(try!(reader.read_i16::<BigEndian>().map_err(FontError::eof)) as i32)
}
29 => {
last_integer_operand =
Some(try!(reader.read_i32::<BigEndian>().map_err(Error::eof)) as i32)
Some(try!(reader.read_i32::<BigEndian>().map_err(FontError::eof)) as i32)
}
30 => {
// TODO(pcwalton): Real numbers.
while (try!(reader.read_u8().map_err(Error::eof)) & 0xf) != 0xf {}
while (try!(reader.read_u8().map_err(FontError::eof)) & 0xf) != 0xf {}
}
12 => {
let b1 = try!(reader.read_u8().map_err(Error::eof));
let b1 = try!(reader.read_u8().map_err(FontError::eof));
if operator == (((b1 as u16) << 8) | (b0 as u16)) {
match last_integer_operand {
Some(last_integer_operand) => return Ok(last_integer_operand),
None => return Err(Error::CffIntegerNotFound),
None => return Err(FontError::CffIntegerNotFound),
}
}
last_integer_operand = None
@ -489,7 +494,7 @@ fn get_integer_in_dict(reader: &mut &[u8], operator: u16) -> Result<i32, Error>
if operator == b0 as u16 {
match last_integer_operand {
Some(last_integer_operand) => return Ok(last_integer_operand),
None => return Err(Error::CffIntegerNotFound),
None => return Err(FontError::CffIntegerNotFound),
}
}
last_integer_operand = None
@ -499,17 +504,17 @@ fn get_integer_in_dict(reader: &mut &[u8], operator: u16) -> Result<i32, Error>
}
// Reads an Offset with the given size.
fn read_offset(reader: &mut &[u8], size: u8) -> Result<u32, Error> {
fn read_offset(reader: &mut &[u8], size: u8) -> Result<u32, FontError> {
match size {
1 => Ok(try!(reader.read_u8().map_err(Error::eof)) as u32),
2 => Ok(try!(reader.read_u16::<BigEndian>().map_err(Error::eof)) as u32),
1 => Ok(try!(reader.read_u8().map_err(FontError::eof)) as u32),
2 => Ok(try!(reader.read_u16::<BigEndian>().map_err(FontError::eof)) as u32),
3 => {
let hi = try!(reader.read_u8().map_err(Error::eof)) as u32;
let lo = try!(reader.read_u16::<BigEndian>().map_err(Error::eof)) as u32;
let hi = try!(reader.read_u8().map_err(FontError::eof)) as u32;
let lo = try!(reader.read_u16::<BigEndian>().map_err(FontError::eof)) as u32;
Ok((hi << 16) | lo)
}
4 => Ok(try!(reader.read_u32::<BigEndian>().map_err(Error::eof))),
_ => Err(Error::CffBadOffset),
4 => Ok(try!(reader.read_u32::<BigEndian>().map_err(FontError::eof))),
_ => Err(FontError::CffBadOffset),
}
}
@ -527,13 +532,13 @@ impl EvaluationStack {
}
}
fn push(&mut self, value: i32) -> Result<(), Error> {
fn push(&mut self, value: i32) -> Result<(), FontError> {
if (self.size as usize) < self.array.len() {
self.array[self.size as usize] = value;
self.size += 1;
Ok(())
} else {
Err(Error::CffStackOverflow)
Err(FontError::CffStackOverflow)
}
}

View File

@ -10,12 +10,18 @@
use byteorder::{BigEndian, ReadBytesExt};
use charmap::{CodepointRange, GlyphMapping, GlyphRange, MappedGlyphRange};
use otf::{Error, FontTable};
use error::FontError;
use font::FontTable;
use std::cmp;
use std::mem;
use std::u16;
use util::Jump;
pub const TAG: u32 = ((b'c' as u32) << 24) |
((b'm' as u32) << 16) |
((b'a' as u32) << 8) |
(b'p' as u32);
const PLATFORM_ID_UNICODE: u16 = 0;
const PLATFORM_ID_MICROSOFT: u16 = 3;
@ -40,30 +46,30 @@ impl<'a> CmapTable<'a> {
}
pub fn glyph_mapping_for_codepoint_ranges(&self, codepoint_ranges: &[CodepointRange])
-> Result<GlyphMapping, Error> {
-> Result<GlyphMapping, FontError> {
let mut cmap_reader = self.table.bytes;
// Check version.
if try!(cmap_reader.read_u16::<BigEndian>().map_err(Error::eof)) != 0 {
return Err(Error::UnsupportedCmapVersion)
if try!(cmap_reader.read_u16::<BigEndian>().map_err(FontError::eof)) != 0 {
return Err(FontError::UnsupportedCmapVersion)
}
let num_tables = try!(cmap_reader.read_u16::<BigEndian>().map_err(Error::eof));
let num_tables = try!(cmap_reader.read_u16::<BigEndian>().map_err(FontError::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(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));
let platform_id = try!(cmap_reader.read_u16::<BigEndian>().map_err(FontError::eof));
let encoding_id = try!(cmap_reader.read_u16::<BigEndian>().map_err(FontError::eof));
let offset = try!(cmap_reader.read_u32::<BigEndian>().map_err(FontError::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).map_err(Error::eof));
try!(cmap_reader.jump(offset as usize).map_err(FontError::eof));
table_found = true;
break
}
@ -72,11 +78,11 @@ impl<'a> CmapTable<'a> {
}
if !table_found {
return Err(Error::UnsupportedCmapEncoding)
return Err(FontError::UnsupportedCmapEncoding)
}
// Check the mapping table format.
let format = try!(cmap_reader.read_u16::<BigEndian>().map_err(Error::eof));
let format = try!(cmap_reader.read_u16::<BigEndian>().map_err(FontError::eof));
match format {
FORMAT_SEGMENT_MAPPING_TO_DELTA_VALUES => {
self.glyph_mapping_for_codepoint_ranges_segment_mapping_format(cmap_reader,
@ -86,7 +92,7 @@ impl<'a> CmapTable<'a> {
self.glyph_mapping_for_codepoint_ranges_segmented_coverage(cmap_reader,
codepoint_ranges)
}
_ => Err(Error::UnsupportedCmapFormat),
_ => Err(FontError::UnsupportedCmapFormat),
}
}
@ -94,14 +100,14 @@ impl<'a> CmapTable<'a> {
&self,
mut cmap_reader: &[u8],
codepoint_ranges: &[CodepointRange])
-> Result<GlyphMapping, Error> {
-> Result<GlyphMapping, FontError> {
// Read the mapping table header.
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));
let _length = try!(cmap_reader.read_u16::<BigEndian>().map_err(FontError::eof));
let _language = try!(cmap_reader.read_u16::<BigEndian>().map_err(FontError::eof));
let seg_count = try!(cmap_reader.read_u16::<BigEndian>().map_err(FontError::eof)) / 2;
let _search_range = try!(cmap_reader.read_u16::<BigEndian>().map_err(FontError::eof));
let _entry_selector = try!(cmap_reader.read_u16::<BigEndian>().map_err(FontError::eof));
let _range_shift = try!(cmap_reader.read_u16::<BigEndian>().map_err(FontError::eof));
// Set up parallel array pointers.
//
@ -110,14 +116,14 @@ impl<'a> CmapTable<'a> {
// 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>())
.map_err(Error::eof));
.map_err(FontError::eof));
let mut id_deltas = start_codes;
try!(id_deltas.jump(seg_count as usize * mem::size_of::<u16>()).map_err(Error::eof));
try!(id_deltas.jump(seg_count as usize * mem::size_of::<u16>()).map_err(FontError::eof));
let mut id_range_offsets = id_deltas;
try!(id_range_offsets.jump(seg_count as usize * mem::size_of::<u16>())
.map_err(Error::eof));
.map_err(FontError::eof));
let mut glyph_ids = id_range_offsets;
try!(glyph_ids.jump(seg_count as usize * mem::size_of::<u16>()).map_err(Error::eof));
try!(glyph_ids.jump(seg_count as usize * mem::size_of::<u16>()).map_err(FontError::eof));
// Now perform the lookups.
let mut glyph_mapping = GlyphMapping::new();
@ -146,16 +152,16 @@ impl<'a> CmapTable<'a> {
let mid = (low + high) / 2;
let mut end_code = end_codes;
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));
try!(end_code.jump(mid as usize * 2).map_err(FontError::eof));
let end_code = try!(end_code.read_u16::<BigEndian>().map_err(FontError::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).map_err(Error::eof));
let start_code = try!(start_code.read_u16::<BigEndian>().map_err(Error::eof));
try!(start_code.jump(mid as usize * 2).map_err(FontError::eof));
let start_code = try!(start_code.read_u16::<BigEndian>().map_err(FontError::eof));
if start_codepoint_range < start_code {
high = mid;
continue
@ -185,15 +191,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).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));
try!(start_code.jump(segment_index as usize * 2).map_err(FontError::eof));
try!(end_code.jump(segment_index as usize * 2).map_err(FontError::eof));
try!(id_range_offset.jump(segment_index as usize * 2).map_err(FontError::eof));
try!(id_delta.jump(segment_index as usize * 2).map_err(FontError::eof));
let start_code = try!(start_code.read_u16::<BigEndian>().map_err(FontError::eof));
let end_code = try!(end_code.read_u16::<BigEndian>().map_err(FontError::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));
.map_err(FontError::eof));
let id_delta = try!(id_delta.read_i16::<BigEndian>().map_err(FontError::eof));
end_codepoint_range = cmp::min(end_codepoint_range, end_code);
codepoint_range.start = (end_codepoint_range + 1) as u32;
@ -221,8 +227,8 @@ impl<'a> CmapTable<'a> {
for code_offset in start_code_offset..(end_code_offset + 1) {
let mut reader = id_range_offsets;
try!(reader.jump(segment_index as usize * 2 + code_offset as usize * 2 +
id_range_offset as usize).map_err(Error::eof));
let mut glyph_id = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
id_range_offset as usize).map_err(FontError::eof));
let mut glyph_id = try!(reader.read_u16::<BigEndian>().map_err(FontError::eof));
if glyph_id == 0 {
glyph_mapping.push(MappedGlyphRange {
codepoint_start: start_code as u32 + code_offset as u32,
@ -251,11 +257,11 @@ impl<'a> CmapTable<'a> {
fn glyph_mapping_for_codepoint_ranges_segmented_coverage(&self,
mut cmap_reader: &[u8],
codepoint_ranges: &[CodepointRange])
-> Result<GlyphMapping, Error> {
let _reserved = try!(cmap_reader.read_u16::<BigEndian>().map_err(Error::eof));
let _length = try!(cmap_reader.read_u32::<BigEndian>().map_err(Error::eof));
let _language = try!(cmap_reader.read_u32::<BigEndian>().map_err(Error::eof));
let num_groups = try!(cmap_reader.read_u32::<BigEndian>().map_err(Error::eof));
-> Result<GlyphMapping, FontError> {
let _reserved = try!(cmap_reader.read_u16::<BigEndian>().map_err(FontError::eof));
let _length = try!(cmap_reader.read_u32::<BigEndian>().map_err(FontError::eof));
let _language = try!(cmap_reader.read_u32::<BigEndian>().map_err(FontError::eof));
let num_groups = try!(cmap_reader.read_u32::<BigEndian>().map_err(FontError::eof));
// Now perform the lookups.
let mut glyph_mapping = GlyphMapping::new();
@ -270,11 +276,14 @@ impl<'a> CmapTable<'a> {
let mut reader = cmap_reader;
try!(reader.jump(mid as usize * mem::size_of::<[u32; 3]>())
.map_err(Error::eof));
.map_err(FontError::eof));
let segment = Segment {
start_char_code: try!(reader.read_u32::<BigEndian>().map_err(Error::eof)),
end_char_code: try!(reader.read_u32::<BigEndian>().map_err(Error::eof)),
start_glyph_id: try!(reader.read_u32::<BigEndian>().map_err(Error::eof)),
start_char_code: try!(reader.read_u32::<BigEndian>()
.map_err(FontError::eof)),
end_char_code: try!(reader.read_u32::<BigEndian>()
.map_err(FontError::eof)),
start_glyph_id: try!(reader.read_u32::<BigEndian>()
.map_err(FontError::eof)),
};
if codepoint_range.start < segment.start_char_code {

View File

@ -9,15 +9,21 @@
// except according to those terms.
use byteorder::{BigEndian, ReadBytesExt};
use error::FontError;
use euclid::Point2D;
use otf::head::HeadTable;
use otf::loca::LocaTable;
use otf::{Error, FontTable};
use font::{FontTable, Point, PointKind};
use outline::GlyphBounds;
use std::mem;
use std::ops::Mul;
use tables::head::HeadTable;
use tables::loca::LocaTable;
use util::Jump;
pub const TAG: u32 = ((b'g' as u32) << 24) |
((b'l' as u32) << 16) |
((b'y' as u32) << 8) |
(b'f' as u32);
const F2DOT14_ZERO: F2Dot14 = F2Dot14(0);
const F2DOT14_ONE: F2Dot14 = F2Dot14(0b0100_0000_0000_0000);
@ -44,34 +50,6 @@ bitflags! {
}
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct Point {
/// Where the point is located in glyph space.
pub position: Point2D<i16>,
/// The index of the point in this contour.
///
/// When iterating over points via `for_each_point`, a value of 0 here indicates that a new
/// contour begins.
pub index_in_contour: u16,
/// The kind of point this is.
pub kind: PointKind,
}
/// The type of point.
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum PointKind {
/// The point is on the curve.
OnCurve,
/// The point is a quadratic control point.
QuadControl,
/// The point is the first cubic control point.
FirstCubicControl,
/// The point is the second cubic control point.
SecondCubicControl,
}
/// TODO(pcwalton): Add some caching so we don't keep going to the `loca` table all the time.
#[derive(Clone, Copy, Debug)]
pub struct GlyfTable<'a> {
@ -91,7 +69,7 @@ impl<'a> GlyfTable<'a> {
loca_table: &LocaTable,
glyph_id: u16,
callback: F)
-> Result<(), Error> where F: FnMut(&Point) {
-> Result<(), FontError> where F: FnMut(&Point) {
let mut reader = self.table.bytes;
match try!(loca_table.location_of(head_table, glyph_id)) {
@ -99,11 +77,11 @@ impl<'a> GlyfTable<'a> {
// No points.
return Ok(())
}
Some(offset) => try!(reader.jump(offset as usize).map_err(Error::eof)),
Some(offset) => try!(reader.jump(offset as usize).map_err(FontError::eof)),
}
let glyph_start = reader;
let number_of_contours = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
let number_of_contours = try!(reader.read_i16::<BigEndian>().map_err(FontError::eof));
if number_of_contours >= 0 {
self.for_each_point_in_simple_glyph(glyph_start, callback)
} else {
@ -112,25 +90,25 @@ impl<'a> GlyfTable<'a> {
}
fn for_each_point_in_simple_glyph<F>(&self, mut reader: &[u8], mut callback: F)
-> Result<(), Error> where F: FnMut(&Point) {
-> Result<(), FontError> where F: FnMut(&Point) {
// Determine how many contours we have.
let number_of_contours = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
let number_of_contours = try!(reader.read_i16::<BigEndian>().map_err(FontError::eof));
if number_of_contours == 0 {
return Ok(())
}
// Skip over the rest of the header.
try!(reader.jump(mem::size_of::<i16>() * 4).map_err(Error::eof));
try!(reader.jump(mem::size_of::<i16>() * 4).map_err(FontError::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))
.map_err(Error::eof));
let number_of_points = try!(reader.read_u16::<BigEndian>().map_err(Error::eof)) + 1;
.map_err(FontError::eof));
let number_of_points = try!(reader.read_u16::<BigEndian>().map_err(FontError::eof)) + 1;
// Skip over hinting instructions.
let instruction_length = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
try!(reader.jump(instruction_length as usize).map_err(Error::eof));
let instruction_length = try!(reader.read_u16::<BigEndian>().map_err(FontError::eof));
try!(reader.jump(instruction_length as usize).map_err(FontError::eof));
// Find the offsets of the X and Y coordinates.
let flags_reader = reader;
@ -140,14 +118,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).map_err(Error::eof));
try!(reader.jump(x_coordinate_length as usize).map_err(FontError::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(Error::eof)) - point_index + 1;
.map_err(FontError::eof)) - point_index + 1;
let mut first_on_curve_point = None;
let mut initial_off_curve_point = None;
@ -160,20 +138,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(Error::eof)) as i16;
delta.x = try!(x_coordinate_reader.read_u8().map_err(FontError::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(Error::eof))
delta.x = try!(x_coordinate_reader.read_i16::<BigEndian>().map_err(FontError::eof))
}
if flags.contains(Y_SHORT_VECTOR) {
delta.y = try!(y_coordinate_reader.read_u8().map_err(Error::eof)) as i16;
delta.y = try!(y_coordinate_reader.read_u8().map_err(FontError::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(Error::eof))
delta.y = try!(y_coordinate_reader.read_i16::<BigEndian>().map_err(FontError::eof))
}
if last_point_was_off_curve && !flags.contains(ON_CURVE) {
@ -261,21 +239,21 @@ impl<'a> GlyfTable<'a> {
head_table: &HeadTable,
loca_table: &LocaTable,
mut callback: F)
-> Result<(), Error> where F: FnMut(&Point) {
try!(reader.jump(mem::size_of::<i16>() * 5).map_err(Error::eof));
-> Result<(), FontError> where F: FnMut(&Point) {
try!(reader.jump(mem::size_of::<i16>() * 5).map_err(FontError::eof));
loop {
let flags = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
let flags = try!(reader.read_u16::<BigEndian>().map_err(FontError::eof));
let flags = CompositeFlags::from_bits_truncate(flags);
let glyph_index = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
let glyph_index = try!(reader.read_u16::<BigEndian>().map_err(FontError::eof));
let (arg0, arg1);
if flags.contains(ARG_1_AND_2_ARE_WORDS) {
arg0 = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
arg1 = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
arg0 = try!(reader.read_i16::<BigEndian>().map_err(FontError::eof));
arg1 = try!(reader.read_i16::<BigEndian>().map_err(FontError::eof));
} else {
arg0 = try!(reader.read_i8().map_err(Error::eof)) as i16;
arg1 = try!(reader.read_i8().map_err(Error::eof)) as i16;
arg0 = try!(reader.read_i8().map_err(FontError::eof)) as i16;
arg1 = try!(reader.read_i8().map_err(FontError::eof)) as i16;
}
let mut transform = Mat3x2::identity();
@ -285,22 +263,22 @@ impl<'a> GlyfTable<'a> {
}
if flags.contains(WE_HAVE_A_SCALE) {
let scale = F2Dot14(try!(reader.read_i16::<BigEndian>().map_err(Error::eof)));
let scale = F2Dot14(try!(reader.read_i16::<BigEndian>().map_err(FontError::eof)));
transform.m00 = scale;
transform.m11 = scale;
} else if flags.contains(WE_HAVE_AN_X_AND_Y_SCALE) {
transform.m00 = F2Dot14(try!(reader.read_i16::<BigEndian>().map_err(Error::eof)));
transform.m11 = F2Dot14(try!(reader.read_i16::<BigEndian>().map_err(Error::eof)));
transform.m00 = F2Dot14(try!(reader.read_i16::<BigEndian>().map_err(FontError::eof)));
transform.m11 = F2Dot14(try!(reader.read_i16::<BigEndian>().map_err(FontError::eof)));
} else if flags.contains(WE_HAVE_A_TWO_BY_TWO) {
transform.m00 = F2Dot14(try!(reader.read_i16::<BigEndian>().map_err(Error::eof)));
transform.m01 = F2Dot14(try!(reader.read_i16::<BigEndian>().map_err(Error::eof)));
transform.m10 = F2Dot14(try!(reader.read_i16::<BigEndian>().map_err(Error::eof)));
transform.m11 = F2Dot14(try!(reader.read_i16::<BigEndian>().map_err(Error::eof)));
transform.m00 = F2Dot14(try!(reader.read_i16::<BigEndian>().map_err(FontError::eof)));
transform.m01 = F2Dot14(try!(reader.read_i16::<BigEndian>().map_err(FontError::eof)));
transform.m10 = F2Dot14(try!(reader.read_i16::<BigEndian>().map_err(FontError::eof)));
transform.m11 = F2Dot14(try!(reader.read_i16::<BigEndian>().map_err(FontError::eof)));
}
if let Some(offset) = try!(loca_table.location_of(head_table, glyph_index)) {
let mut reader = self.table.bytes;
try!(reader.jump(offset as usize).map_err(Error::eof));
try!(reader.jump(offset as usize).map_err(FontError::eof));
try!(self.for_each_point_in_simple_glyph(reader, |point| {
callback(&transform.transform(&point))
}));
@ -315,7 +293,7 @@ impl<'a> GlyfTable<'a> {
}
pub fn glyph_bounds(&self, head_table: &HeadTable, loca_table: &LocaTable, glyph_id: u16)
-> Result<GlyphBounds, Error> {
-> Result<GlyphBounds, FontError> {
let mut reader = self.table.bytes;
match try!(loca_table.location_of(head_table, glyph_id)) {
@ -328,16 +306,16 @@ impl<'a> GlyfTable<'a> {
top: 0,
})
}
Some(offset) => try!(reader.jump(offset as usize).map_err(Error::eof)),
Some(offset) => try!(reader.jump(offset as usize).map_err(FontError::eof)),
}
// Skip over the number of contours.
try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
try!(reader.read_i16::<BigEndian>().map_err(FontError::eof));
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));
let x_min = try!(reader.read_i16::<BigEndian>().map_err(FontError::eof));
let y_min = try!(reader.read_i16::<BigEndian>().map_err(FontError::eof));
let x_max = try!(reader.read_i16::<BigEndian>().map_err(FontError::eof));
let y_max = try!(reader.read_i16::<BigEndian>().map_err(FontError::eof));
Ok(GlyphBounds {
left: x_min as i32,
bottom: y_min as i32,
@ -351,14 +329,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, Error> {
-> Result<u16, FontError> {
let (mut x_coordinate_length, mut points_left) = (0, number_of_points);
while points_left > 0 {
let flags = SimpleFlags::from_bits_truncate(try!(reader.read_u8().map_err(Error::eof)));
let flags = SimpleFlags::from_bits_truncate(try!(reader.read_u8().map_err(FontError::eof)));
let repeat_count = if !flags.contains(REPEAT) {
1
} else {
try!(reader.read_u8().map_err(Error::eof)) as u16 + 1
try!(reader.read_u8().map_err(FontError::eof)) as u16 + 1
};
if flags.contains(X_SHORT_VECTOR) {
@ -381,7 +359,7 @@ struct FlagParser<'a> {
impl<'a> FlagParser<'a> {
#[inline]
fn new(buffer: &[u8]) -> Result<FlagParser, Error> {
fn new(buffer: &[u8]) -> Result<FlagParser, FontError> {
let mut parser = FlagParser {
next: buffer,
current: &buffer[0],
@ -392,7 +370,7 @@ impl<'a> FlagParser<'a> {
}
#[inline]
fn next(&mut self) -> Result<(), Error> {
fn next(&mut self) -> Result<(), FontError> {
if self.repeats_left > 0 {
self.repeats_left -= 1;
return Ok(())
@ -400,7 +378,7 @@ impl<'a> FlagParser<'a> {
self.current = match self.next.get(0) {
Some(value) => value,
None => return Err(Error::UnexpectedEof),
None => return Err(FontError::UnexpectedEof),
};
let flags = SimpleFlags::from_bits_truncate(*self.current);
@ -409,7 +387,7 @@ impl<'a> FlagParser<'a> {
if flags.contains(REPEAT) {
self.repeats_left = match self.next.get(0) {
Some(&value) => value,
None => return Err(Error::UnexpectedEof),
None => return Err(FontError::UnexpectedEof),
};
self.next = &self.next[1..];

View File

@ -9,11 +9,17 @@
// except according to those terms.
use byteorder::{BigEndian, ReadBytesExt};
use otf::{Error, FontTable};
use error::FontError;
use font::FontTable;
use outline::GlyphBounds;
use std::mem;
use util::Jump;
pub const TAG: u32 = ((b'h' as u32) << 24) |
((b'e' as u32) << 16) |
((b'a' as u32) << 8) |
(b'd' as u32);
const MAGIC_NUMBER: u32 = 0x5f0f3cf5;
#[derive(Clone, Debug)]
@ -24,33 +30,33 @@ pub struct HeadTable {
}
impl HeadTable {
pub fn new(table: FontTable) -> Result<HeadTable, Error> {
pub fn new(table: FontTable) -> Result<HeadTable, FontError> {
let mut reader = table.bytes;
// Check the version.
let major_version = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
let minor_version = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
let major_version = try!(reader.read_u16::<BigEndian>().map_err(FontError::eof));
let minor_version = try!(reader.read_u16::<BigEndian>().map_err(FontError::eof));
if (major_version, minor_version) != (1, 0) {
return Err(Error::UnsupportedHeadVersion)
return Err(FontError::UnsupportedHeadVersion)
}
// Check the magic number.
try!(reader.jump(mem::size_of::<u32>() * 2).map_err(Error::eof));
let magic_number = try!(reader.read_u32::<BigEndian>().map_err(Error::eof));
try!(reader.jump(mem::size_of::<u32>() * 2).map_err(FontError::eof));
let magic_number = try!(reader.read_u32::<BigEndian>().map_err(FontError::eof));
if magic_number != MAGIC_NUMBER {
return Err(Error::UnknownFormat)
return Err(FontError::UnknownFormat)
}
// Read the units per em.
try!(reader.jump(mem::size_of::<u16>()).map_err(Error::eof));
let units_per_em = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
try!(reader.jump(mem::size_of::<u16>()).map_err(FontError::eof));
let units_per_em = try!(reader.read_u16::<BigEndian>().map_err(FontError::eof));
// Read the maximum bounds.
try!(reader.jump(mem::size_of::<i64>() * 2).map_err(Error::eof));
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));
try!(reader.jump(mem::size_of::<i64>() * 2).map_err(FontError::eof));
let x_min = try!(reader.read_i16::<BigEndian>().map_err(FontError::eof));
let y_min = try!(reader.read_i16::<BigEndian>().map_err(FontError::eof));
let x_max = try!(reader.read_i16::<BigEndian>().map_err(FontError::eof));
let y_max = try!(reader.read_i16::<BigEndian>().map_err(FontError::eof));
let max_glyph_bounds = GlyphBounds {
left: x_min as i32,
bottom: y_min as i32,
@ -59,13 +65,14 @@ impl HeadTable {
};
// Read the index-to-location format.
try!(reader.jump(mem::size_of::<u16>() * 2 + mem::size_of::<i16>()).map_err(Error::eof));
let index_to_loc_format = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
try!(reader.jump(mem::size_of::<u16>() * 2 + mem::size_of::<i16>())
.map_err(FontError::eof));
let index_to_loc_format = try!(reader.read_i16::<BigEndian>().map_err(FontError::eof));
// Check the glyph data format.
let glyph_data_format = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
let glyph_data_format = try!(reader.read_i16::<BigEndian>().map_err(FontError::eof));
if glyph_data_format != 0 {
return Err(Error::UnsupportedGlyphFormat)
return Err(FontError::UnsupportedGlyphFormat)
}
Ok(HeadTable {

View File

@ -9,10 +9,16 @@
// except according to those terms.
use byteorder::{BigEndian, ReadBytesExt};
use otf::{Error, FontTable};
use error::FontError;
use font::FontTable;
use std::mem;
use util::Jump;
pub const TAG: u32 = ((b'h' as u32) << 24) |
((b'h' as u32) << 16) |
((b'e' as u32) << 8) |
(b'a' as u32);
#[derive(Clone, Debug)]
pub struct HheaTable {
pub line_gap: i16,
@ -20,24 +26,24 @@ pub struct HheaTable {
}
impl HheaTable {
pub fn new(table: FontTable) -> Result<HheaTable, Error> {
pub fn new(table: FontTable) -> Result<HheaTable, FontError> {
let mut reader = table.bytes;
// Check the version.
let major_version = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
let minor_version = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
let major_version = try!(reader.read_u16::<BigEndian>().map_err(FontError::eof));
let minor_version = try!(reader.read_u16::<BigEndian>().map_err(FontError::eof));
if (major_version, minor_version) != (1, 0) {
return Err(Error::UnsupportedHheaVersion)
return Err(FontError::UnsupportedHheaVersion)
}
// Read the height-related metrics.
let _ascender = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
let _descender = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
let line_gap = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
let _ascender = try!(reader.read_i16::<BigEndian>().map_err(FontError::eof));
let _descender = try!(reader.read_i16::<BigEndian>().map_err(FontError::eof));
let line_gap = try!(reader.read_i16::<BigEndian>().map_err(FontError::eof));
// Read the number of `hmtx` entries.
try!(reader.jump(mem::size_of::<u16>() * 12).map_err(Error::eof));
let number_of_h_metrics = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
try!(reader.jump(mem::size_of::<u16>() * 12).map_err(FontError::eof));
let number_of_h_metrics = try!(reader.read_u16::<BigEndian>().map_err(FontError::eof));
Ok(HheaTable {
line_gap: line_gap,

View File

@ -9,11 +9,17 @@
// except according to those terms.
use byteorder::{BigEndian, ReadBytesExt};
use otf::hhea::HheaTable;
use otf::{Error, FontTable};
use error::FontError;
use font::FontTable;
use std::mem;
use tables::hhea::HheaTable;
use util::Jump;
pub const TAG: u32 = ((b'h' as u32) << 24) |
((b'm' as u32) << 16) |
((b't' as u32) << 8) |
(b'x' as u32);
#[derive(Clone, Copy)]
pub struct HmtxTable<'a> {
table: FontTable<'a>,
@ -27,23 +33,23 @@ impl<'a> HmtxTable<'a> {
}
pub fn metrics_for_glyph(&self, hhea_table: &HheaTable, glyph_id: u16)
-> Result<HorizontalMetrics, Error> {
-> Result<HorizontalMetrics, FontError> {
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).map_err(Error::eof));
advance_width = try!(reader.read_u16::<BigEndian>().map_err(Error::eof))
try!(reader.jump(mem::size_of::<u16>() * 2 * glyph_id as usize).map_err(FontError::eof));
advance_width = try!(reader.read_u16::<BigEndian>().map_err(FontError::eof))
} else {
try!(reader.jump(mem::size_of::<u16>() * 2 *
(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));
(hhea_table.number_of_h_metrics - 1) as usize).map_err(FontError::eof));
advance_width = try!(reader.read_u16::<BigEndian>().map_err(FontError::eof));
try!(reader.jump(mem::size_of::<i16>() * glyph_id as usize).map_err(FontError::eof));
}
// Read the left-side bearing.
let lsb = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
let lsb = try!(reader.read_i16::<BigEndian>().map_err(FontError::eof));
Ok(HorizontalMetrics {
advance_width: advance_width,

View File

@ -9,10 +9,16 @@
// except according to those terms.
use byteorder::{BigEndian, ReadBytesExt};
use otf::{Error, FontTable};
use error::FontError;
use font::FontTable;
use std::mem;
use util::Jump;
pub const TAG: u32 = ((b'k' as u32) << 24) |
((b'e' as u32) << 16) |
((b'r' as u32) << 8) |
(b'n' as u32);
bitflags! {
flags Coverage: u16 {
const HORIZONTAL = 1 << 0,
@ -28,20 +34,20 @@ pub struct KernTable<'a> {
}
impl<'a> KernTable<'a> {
pub fn new(table: FontTable) -> Result<KernTable, Error> {
pub fn new(table: FontTable) -> Result<KernTable, FontError> {
let mut kern_reader = table.bytes;
let version = try!(kern_reader.read_u16::<BigEndian>().map_err(Error::eof));
let version = try!(kern_reader.read_u16::<BigEndian>().map_err(FontError::eof));
if version != 0 {
return Err(Error::UnknownFormat)
return Err(FontError::UnknownFormat)
}
let n_tables = try!(kern_reader.read_u16::<BigEndian>().map_err(Error::eof));
let n_tables = try!(kern_reader.read_u16::<BigEndian>().map_err(FontError::eof));
let mut horizontal_table = None;
for _ in 0..n_tables {
let mut table_reader = kern_reader;
let _version = try!(table_reader.read_u16::<BigEndian>().map_err(Error::eof));
let length = try!(table_reader.read_u16::<BigEndian>().map_err(Error::eof));
let coverage = try!(table_reader.read_u16::<BigEndian>().map_err(Error::eof));
let _version = try!(table_reader.read_u16::<BigEndian>().map_err(FontError::eof));
let length = try!(table_reader.read_u16::<BigEndian>().map_err(FontError::eof));
let coverage = try!(table_reader.read_u16::<BigEndian>().map_err(FontError::eof));
let coverage_flags = Coverage::from_bits_truncate(coverage);
if coverage_flags.contains(HORIZONTAL) && !coverage_flags.contains(MINIMUM) &&
@ -51,7 +57,7 @@ impl<'a> KernTable<'a> {
break
}
try!(kern_reader.jump(length as usize).map_err(Error::eof));
try!(kern_reader.jump(length as usize).map_err(FontError::eof));
}
match horizontal_table {
@ -60,25 +66,25 @@ impl<'a> KernTable<'a> {
horizontal_table: horizontal_table,
})
}
None => Err(Error::UnknownFormat),
None => Err(FontError::UnknownFormat),
}
}
pub fn kerning_for_glyph_pair(&self, left_glyph_id: u16, right_glyph_id: u16)
-> Result<i16, Error> {
-> Result<i16, FontError> {
let mut table_reader = self.horizontal_table;
let n_pairs = try!(table_reader.read_u16::<BigEndian>().map_err(Error::eof));
try!(table_reader.jump(mem::size_of::<[u16; 3]>()).map_err(Error::eof));
let n_pairs = try!(table_reader.read_u16::<BigEndian>().map_err(FontError::eof));
try!(table_reader.jump(mem::size_of::<[u16; 3]>()).map_err(FontError::eof));
let (mut low, mut high) = (0, n_pairs as u32);
while low < high {
let mut reader = table_reader;
let mid = (low + high) / 2;
try!(reader.jump(mid as usize * mem::size_of::<[u16; 3]>()).map_err(Error::eof));
let left = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
let right = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
let value = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
try!(reader.jump(mid as usize * mem::size_of::<[u16; 3]>()).map_err(FontError::eof));
let left = try!(reader.read_u16::<BigEndian>().map_err(FontError::eof));
let right = try!(reader.read_u16::<BigEndian>().map_err(FontError::eof));
let value = try!(reader.read_i16::<BigEndian>().map_err(FontError::eof));
if left_glyph_id < left || (left_glyph_id == left && right_glyph_id < right) {
high = mid

View File

@ -9,42 +9,48 @@
// except according to those terms.
use byteorder::{BigEndian, ReadBytesExt};
use otf::head::HeadTable;
use otf::{Error, FontTable};
use error::FontError;
use font::FontTable;
use tables::head::HeadTable;
use util::Jump;
pub const TAG: u32 = ((b'l' as u32) << 24) |
((b'o' as u32) << 16) |
((b'c' as u32) << 8) |
(b'a' as u32);
pub struct LocaTable<'a> {
table: FontTable<'a>,
}
impl<'a> LocaTable<'a> {
pub fn new(loca_table: FontTable<'a>) -> Result<LocaTable<'a>, Error> {
pub fn new(loca_table: FontTable<'a>) -> Result<LocaTable<'a>, FontError> {
Ok(LocaTable {
table: loca_table,
})
}
pub fn location_of(&self, head_table: &HeadTable, glyph_id: u16)
-> Result<Option<u32>, Error> {
-> Result<Option<u32>, FontError> {
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).map_err(Error::eof));
try!(reader.jump(glyph_id as usize * 2).map_err(FontError::eof));
let this_location =
try!(reader.read_u16::<BigEndian>().map_err(Error::eof)) as u32 * 2;
try!(reader.read_u16::<BigEndian>().map_err(FontError::eof)) as u32 * 2;
let next_location = match reader.read_u16::<BigEndian>() {
Ok(next_location) => Ok(next_location as u32 * 2),
Err(_) => Err(Error::UnexpectedEof),
Err(_) => Err(FontError::UnexpectedEof),
};
(this_location, next_location)
}
1 => {
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);
try!(reader.jump(glyph_id as usize * 4).map_err(FontError::eof));
let this_location = try!(reader.read_u32::<BigEndian>().map_err(FontError::eof));
let next_location = reader.read_u32::<BigEndian>().map_err(FontError::eof);
(this_location, next_location)
}
_ => return Err(Error::UnknownFormat),
_ => return Err(FontError::UnknownFormat),
};
if next_location == Ok(this_location) {

22
src/tables/mod.rs Normal file
View File

@ -0,0 +1,22 @@
// Copyright 2017 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! OpenType fonts.
pub mod cff;
pub mod cmap;
pub mod glyf;
pub mod head;
pub mod hhea;
pub mod hmtx;
pub mod kern;
pub mod loca;
pub mod os_2;

View File

@ -9,10 +9,16 @@
// except according to those terms.
use byteorder::{BigEndian, ReadBytesExt};
use otf::{Error, FontTable};
use error::FontError;
use font::FontTable;
use std::mem;
use util::Jump;
pub const TAG: u32 = ((b'O' as u32) << 24) |
((b'S' as u32) << 16) |
((b'/' as u32) << 8) |
(b'2' as u32);
#[derive(Clone, Debug)]
pub struct Os2Table {
pub typo_ascender: i16,
@ -21,27 +27,27 @@ pub struct Os2Table {
}
impl Os2Table {
pub fn new(table: FontTable) -> Result<Os2Table, Error> {
pub fn new(table: FontTable) -> Result<Os2Table, FontError> {
let mut reader = table.bytes;
// We should be compatible with all versions. If this is greater than version 5, follow
// Postel's law and hope for the best.
let version = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
let version = try!(reader.read_u16::<BigEndian>().map_err(FontError::eof));
// Skip to the line gap.
try!(reader.jump(mem::size_of::<u16>() * 15).map_err(Error::eof));
try!(reader.jump(10).map_err(Error::eof));
try!(reader.jump(mem::size_of::<u16>() * 15).map_err(FontError::eof));
try!(reader.jump(10).map_err(FontError::eof));
if version == 0 {
try!(reader.jump(mem::size_of::<u32>() * 2).map_err(Error::eof));
try!(reader.jump(mem::size_of::<u32>() * 2).map_err(FontError::eof));
} else {
try!(reader.jump(mem::size_of::<u32>() * 5).map_err(Error::eof));
try!(reader.jump(mem::size_of::<u32>() * 5).map_err(FontError::eof));
}
try!(reader.jump(mem::size_of::<u16>() * 3).map_err(Error::eof));
try!(reader.jump(mem::size_of::<u16>() * 3).map_err(FontError::eof));
// Read the line spacing information.
let typo_ascender = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
let typo_descender = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
let typo_line_gap = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
let typo_ascender = try!(reader.read_i16::<BigEndian>().map_err(FontError::eof));
let typo_descender = try!(reader.read_i16::<BigEndian>().map_err(FontError::eof));
let typo_line_gap = try!(reader.read_i16::<BigEndian>().map_err(FontError::eof));
Ok(Os2Table {
typo_ascender: typo_ascender,

View File

@ -2,8 +2,8 @@
* http://creativecommons.org/publicdomain/zero/1.0/ */
use charmap::CodepointRange;
use font::Font;
use memmap::{Mmap, Protection};
use otf::Font;
use outline::OutlineBuilder;
use test::Bencher;

View File

@ -17,7 +17,7 @@
use charmap::CodepointRanges;
use error::GlyphStoreCreationError;
use euclid::{Point2D, Rect};
use otf::Font;
use font::Font;
use outline::{OutlineBuilder, Outlines};
use shaper;
use std::u16;
@ -186,7 +186,7 @@ impl GlyphStore {
let mut all_glyph_indices = vec![];
for glyph_id in glyph_ids {
let glyph_index = try!(outline_builder.add_glyph(font, glyph_id)
.map_err(GlyphStoreCreationError::OtfError));
.map_err(GlyphStoreCreationError::FontError));
glyph_id_to_glyph_index[glyph_id as usize] = glyph_index;
all_glyph_indices.push(glyph_index);
}
@ -207,7 +207,7 @@ impl GlyphStore {
pub fn from_codepoints(codepoints: &CodepointRanges, font: &Font)
-> Result<GlyphStore, GlyphStoreCreationError> {
let mapping = try!(font.glyph_mapping_for_codepoint_ranges(&codepoints.ranges)
.map_err(GlyphStoreCreationError::OtfError));
.map_err(GlyphStoreCreationError::FontError));
let glyph_ids = mapping.iter().map(|(_, glyph_id)| glyph_id).collect();
GlyphStore::from_glyph_ids(glyph_ids, font)
}