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:
parent
9fda8f6534
commit
9490fa2d3b
|
@ -22,8 +22,8 @@ use memmap::{Mmap, Protection};
|
||||||
use pathfinder::atlas::AtlasBuilder;
|
use pathfinder::atlas::AtlasBuilder;
|
||||||
use pathfinder::charmap::CodepointRange;
|
use pathfinder::charmap::CodepointRange;
|
||||||
use pathfinder::coverage::CoverageBuffer;
|
use pathfinder::coverage::CoverageBuffer;
|
||||||
|
use pathfinder::font::Font;
|
||||||
use pathfinder::outline::OutlineBuilder;
|
use pathfinder::outline::OutlineBuilder;
|
||||||
use pathfinder::otf::Font;
|
|
||||||
use pathfinder::rasterizer::{Rasterizer, RasterizerOptions};
|
use pathfinder::rasterizer::{Rasterizer, RasterizerOptions};
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::os::raw::c_void;
|
use std::os::raw::c_void;
|
||||||
|
|
|
@ -7,7 +7,7 @@ extern crate pathfinder;
|
||||||
|
|
||||||
use memmap::{Mmap, Protection};
|
use memmap::{Mmap, Protection};
|
||||||
use pathfinder::charmap::CodepointRange;
|
use pathfinder::charmap::CodepointRange;
|
||||||
use pathfinder::otf::{Font, PointKind};
|
use pathfinder::font::{Font, PointKind};
|
||||||
use std::char;
|
use std::char;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
|
|
|
@ -21,8 +21,8 @@ use memmap::{Mmap, Protection};
|
||||||
use pathfinder::atlas::AtlasBuilder;
|
use pathfinder::atlas::AtlasBuilder;
|
||||||
use pathfinder::charmap::CodepointRange;
|
use pathfinder::charmap::CodepointRange;
|
||||||
use pathfinder::coverage::CoverageBuffer;
|
use pathfinder::coverage::CoverageBuffer;
|
||||||
|
use pathfinder::font::Font;
|
||||||
use pathfinder::outline::OutlineBuilder;
|
use pathfinder::outline::OutlineBuilder;
|
||||||
use pathfinder::otf::Font;
|
|
||||||
use pathfinder::rasterizer::{Rasterizer, RasterizerOptions};
|
use pathfinder::rasterizer::{Rasterizer, RasterizerOptions};
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::os::raw::c_void;
|
use std::os::raw::c_void;
|
||||||
|
|
|
@ -23,7 +23,7 @@ use pathfinder::atlas::AtlasBuilder;
|
||||||
use pathfinder::charmap::CodepointRanges;
|
use pathfinder::charmap::CodepointRanges;
|
||||||
use pathfinder::coverage::CoverageBuffer;
|
use pathfinder::coverage::CoverageBuffer;
|
||||||
use pathfinder::error::RasterError;
|
use pathfinder::error::RasterError;
|
||||||
use pathfinder::otf::Font;
|
use pathfinder::font::Font;
|
||||||
use pathfinder::rasterizer::{DrawAtlasProfilingEvents, Rasterizer, RasterizerOptions};
|
use pathfinder::rasterizer::{DrawAtlasProfilingEvents, Rasterizer, RasterizerOptions};
|
||||||
use pathfinder::typesetter::{GlyphStore, PositionedGlyph, Typesetter};
|
use pathfinder::typesetter::{GlyphStore, PositionedGlyph, Typesetter};
|
||||||
use std::char;
|
use std::char;
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
55
src/error.rs
55
src/error.rs
|
@ -12,9 +12,60 @@
|
||||||
|
|
||||||
use compute_shader;
|
use compute_shader;
|
||||||
use gl::types::GLenum;
|
use gl::types::GLenum;
|
||||||
use otf;
|
|
||||||
use std::io;
|
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.
|
/// An OpenGL error with the given code.
|
||||||
///
|
///
|
||||||
/// You cannot depend on these being reliably returned. Pathfinder does not call `glGetError()`
|
/// You cannot depend on these being reliably returned. Pathfinder does not call `glGetError()`
|
||||||
|
@ -68,7 +119,7 @@ pub enum RasterError {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum GlyphStoreCreationError {
|
pub enum GlyphStoreCreationError {
|
||||||
/// An error occurred when looking up a glyph ID for a character in the font.
|
/// 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.
|
/// An error occurred when uploading the outlines to the GPU.
|
||||||
GlError(GlError),
|
GlError(GlError),
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
}
|
||||||
|
|
|
@ -91,13 +91,15 @@ pub mod atlas;
|
||||||
pub mod charmap;
|
pub mod charmap;
|
||||||
pub mod coverage;
|
pub mod coverage;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod otf;
|
pub mod font;
|
||||||
pub mod outline;
|
pub mod outline;
|
||||||
pub mod rasterizer;
|
pub mod rasterizer;
|
||||||
pub mod shaper;
|
pub mod shaper;
|
||||||
pub mod typesetter;
|
pub mod typesetter;
|
||||||
|
|
||||||
|
mod containers;
|
||||||
mod rect_packer;
|
mod rect_packer;
|
||||||
|
mod tables;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
487
src/otf/mod.rs
487
src/otf/mod.rs
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -10,11 +10,11 @@
|
||||||
|
|
||||||
//! Glyph vectors, uploaded in a resolution-independent manner to the GPU.
|
//! Glyph vectors, uploaded in a resolution-independent manner to the GPU.
|
||||||
|
|
||||||
use error::GlError;
|
use error::{FontError, GlError};
|
||||||
use euclid::Size2D;
|
use euclid::Size2D;
|
||||||
|
use font::{Font, PointKind};
|
||||||
use gl::types::{GLsizeiptr, GLuint};
|
use gl::types::{GLsizeiptr, GLuint};
|
||||||
use gl;
|
use gl;
|
||||||
use otf::{self, Font, PointKind};
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::os::raw::c_void;
|
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
|
/// Adds a new glyph to the outline builder. Returns the glyph index, which is useful for later
|
||||||
/// calls to `Atlas::pack_glyph()`.
|
/// 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 glyph_index = self.descriptors.len() as u16;
|
||||||
|
|
||||||
let mut point_index = self.vertices.len() as u32;
|
let mut point_index = self.vertices.len() as u32;
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
//! the system shaper instead.
|
//! the system shaper instead.
|
||||||
|
|
||||||
use charmap::GlyphMapping;
|
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.
|
/// Shapes the given Unicode text in the given font, returning the proper position for each glyph.
|
||||||
///
|
///
|
||||||
|
|
|
@ -9,14 +9,19 @@
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use byteorder::{BigEndian, ReadBytesExt};
|
use byteorder::{BigEndian, ReadBytesExt};
|
||||||
|
use error::FontError;
|
||||||
use euclid::Point2D;
|
use euclid::Point2D;
|
||||||
use otf::glyf::{Point, PointKind};
|
use font::{FontTable, Point, PointKind};
|
||||||
use otf::{Error, FontTable};
|
|
||||||
use outline::GlyphBounds;
|
use outline::GlyphBounds;
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::u16;
|
use std::u16;
|
||||||
use util::Jump;
|
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)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct CffTable<'a> {
|
pub struct CffTable<'a> {
|
||||||
// The offset of the char strings INDEX.
|
// The offset of the char strings INDEX.
|
||||||
|
@ -26,19 +31,19 @@ pub struct CffTable<'a> {
|
||||||
|
|
||||||
impl<'a> CffTable<'a> {
|
impl<'a> CffTable<'a> {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(table: FontTable) -> Result<CffTable, Error> {
|
pub fn new(table: FontTable) -> Result<CffTable, FontError> {
|
||||||
let mut reader = table.bytes;
|
let mut reader = table.bytes;
|
||||||
|
|
||||||
// Check version.
|
// Check version.
|
||||||
let major = 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(Error::eof));
|
let minor = try!(reader.read_u8().map_err(FontError::eof));
|
||||||
if major != 1 || minor != 0 {
|
if major != 1 || minor != 0 {
|
||||||
return Err(Error::UnsupportedCffVersion)
|
return Err(FontError::UnsupportedCffVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip the header.
|
// Skip the header.
|
||||||
let hdr_size = try!(reader.read_u8().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(Error::eof));
|
try!(reader.jump(hdr_size as usize - 3).map_err(FontError::eof));
|
||||||
|
|
||||||
// Skip the name INDEX.
|
// Skip the name INDEX.
|
||||||
//
|
//
|
||||||
|
@ -47,7 +52,7 @@ impl<'a> CffTable<'a> {
|
||||||
|
|
||||||
// Get the top DICT for our font.
|
// Get the top DICT for our font.
|
||||||
if try!(find_in_index(&mut reader, 0)).is_none() {
|
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.
|
// 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)
|
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;
|
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)) {
|
let char_string_length = match try!(find_in_index(&mut reader, glyph_id)) {
|
||||||
Some(char_string_length) => char_string_length,
|
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];
|
let mut reader = &reader[0..char_string_length as usize];
|
||||||
|
@ -87,20 +92,20 @@ impl<'a> CffTable<'a> {
|
||||||
match b0 {
|
match b0 {
|
||||||
32...246 => try!(stack.push(b0 as i32 - 139)),
|
32...246 => try!(stack.push(b0 as i32 - 139)),
|
||||||
247...250 => {
|
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))
|
try!(stack.push((b0 as i32 - 247) * 256 + b1 as i32 + 108))
|
||||||
}
|
}
|
||||||
251...254 => {
|
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))
|
try!(stack.push((b0 as i32 - 251) * -256 - b1 as i32 - 108))
|
||||||
}
|
}
|
||||||
255 => {
|
255 => {
|
||||||
// FIXME(pcwalton): Don't truncate the lower 16 bits.
|
// 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))
|
16))
|
||||||
}
|
}
|
||||||
28 => {
|
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))
|
try!(stack.push(number))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -349,13 +354,13 @@ impl<'a> CffTable<'a> {
|
||||||
|
|
||||||
// Now skip ⌈hint_count / 8⌉ bytes.
|
// Now skip ⌈hint_count / 8⌉ bytes.
|
||||||
let hint_byte_count = (hint_count as usize + 7) / 8;
|
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 => {
|
20 => {
|
||||||
// Skip ⌈hint_count / 8⌉ bytes.
|
// Skip ⌈hint_count / 8⌉ bytes.
|
||||||
stack.clear();
|
stack.clear();
|
||||||
let hint_byte_count = (hint_count as usize + 7) / 8;
|
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 => {
|
21 => {
|
||||||
// |- dx1 dy1 rmoveto
|
// |- dx1 dy1 rmoveto
|
||||||
|
@ -387,13 +392,13 @@ impl<'a> CffTable<'a> {
|
||||||
12 => {
|
12 => {
|
||||||
// TODO(pcwalton): Support these extended operators.
|
// TODO(pcwalton): Support these extended operators.
|
||||||
let _operator = (12 << 8) |
|
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();
|
stack.clear();
|
||||||
return Err(Error::CffUnimplementedOperator)
|
return Err(FontError::CffUnimplementedOperator)
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
stack.clear();
|
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): Do some caching, perhaps?
|
||||||
// TODO(pcwalton): Compute this at the same time as `for_each_point`, 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();
|
let mut bounds = GlyphBounds::default();
|
||||||
try!(self.for_each_point(glyph_id, |point| {
|
try!(self.for_each_point(glyph_id, |point| {
|
||||||
bounds.left = cmp::min(bounds.left, point.position.x as i32);
|
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
|
// 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.
|
// element if the element was found or `None` otherwise.
|
||||||
fn find_in_index(reader: &mut &[u8], index: u16) -> Result<Option<u32>, Error> {
|
fn find_in_index(reader: &mut &[u8], index: u16) -> Result<Option<u32>, FontError> {
|
||||||
let count = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
|
let count = try!(reader.read_u16::<BigEndian>().map_err(FontError::eof));
|
||||||
if count == 0 {
|
if count == 0 {
|
||||||
return Ok(None)
|
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;
|
let mut offset_reader = *reader;
|
||||||
try!(offset_reader.jump(off_size as usize * cmp::min(index, count) as usize)
|
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 offset = try!(read_offset(&mut offset_reader, off_size));
|
||||||
|
|
||||||
let next_offset = if index < count {
|
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)
|
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)
|
return Ok(next_offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skips over an INDEX by reading the last element in the offset array and seeking the appropriate
|
// Skips over an INDEX by reading the last element in the offset array and seeking the appropriate
|
||||||
// number of bytes forward.
|
// 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)
|
find_in_index(reader, u16::MAX).map(drop)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the integer with the given operator.
|
// 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;
|
let mut last_integer_operand = None;
|
||||||
loop {
|
loop {
|
||||||
let b0 = try!(reader.read_u8().map_err(Error::eof));
|
let b0 = try!(reader.read_u8().map_err(FontError::eof));
|
||||||
match b0 {
|
match b0 {
|
||||||
32...246 => last_integer_operand = Some(b0 as i32 - 139),
|
32...246 => last_integer_operand = Some(b0 as i32 - 139),
|
||||||
247...250 => {
|
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)
|
last_integer_operand = Some((b0 as i32 - 247) * 256 + b1 as i32 + 108)
|
||||||
}
|
}
|
||||||
251...254 => {
|
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)
|
last_integer_operand = Some(-(b0 as i32 - 251) * 256 - b1 as i32 - 108)
|
||||||
}
|
}
|
||||||
28 => {
|
28 => {
|
||||||
last_integer_operand =
|
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 => {
|
29 => {
|
||||||
last_integer_operand =
|
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 => {
|
30 => {
|
||||||
// TODO(pcwalton): Real numbers.
|
// 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 => {
|
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)) {
|
if operator == (((b1 as u16) << 8) | (b0 as u16)) {
|
||||||
match last_integer_operand {
|
match last_integer_operand {
|
||||||
Some(last_integer_operand) => return Ok(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
|
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 {
|
if operator == b0 as u16 {
|
||||||
match last_integer_operand {
|
match last_integer_operand {
|
||||||
Some(last_integer_operand) => return Ok(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
|
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.
|
// 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 {
|
match size {
|
||||||
1 => Ok(try!(reader.read_u8().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(Error::eof)) as u32),
|
2 => Ok(try!(reader.read_u16::<BigEndian>().map_err(FontError::eof)) as u32),
|
||||||
3 => {
|
3 => {
|
||||||
let hi = try!(reader.read_u8().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(Error::eof)) as u32;
|
let lo = try!(reader.read_u16::<BigEndian>().map_err(FontError::eof)) as u32;
|
||||||
Ok((hi << 16) | lo)
|
Ok((hi << 16) | lo)
|
||||||
}
|
}
|
||||||
4 => Ok(try!(reader.read_u32::<BigEndian>().map_err(Error::eof))),
|
4 => Ok(try!(reader.read_u32::<BigEndian>().map_err(FontError::eof))),
|
||||||
_ => Err(Error::CffBadOffset),
|
_ => 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() {
|
if (self.size as usize) < self.array.len() {
|
||||||
self.array[self.size as usize] = value;
|
self.array[self.size as usize] = value;
|
||||||
self.size += 1;
|
self.size += 1;
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(Error::CffStackOverflow)
|
Err(FontError::CffStackOverflow)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,12 +10,18 @@
|
||||||
|
|
||||||
use byteorder::{BigEndian, ReadBytesExt};
|
use byteorder::{BigEndian, ReadBytesExt};
|
||||||
use charmap::{CodepointRange, GlyphMapping, GlyphRange, MappedGlyphRange};
|
use charmap::{CodepointRange, GlyphMapping, GlyphRange, MappedGlyphRange};
|
||||||
use otf::{Error, FontTable};
|
use error::FontError;
|
||||||
|
use font::FontTable;
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::u16;
|
use std::u16;
|
||||||
use util::Jump;
|
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_UNICODE: u16 = 0;
|
||||||
const PLATFORM_ID_MICROSOFT: u16 = 3;
|
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])
|
pub fn glyph_mapping_for_codepoint_ranges(&self, codepoint_ranges: &[CodepointRange])
|
||||||
-> Result<GlyphMapping, Error> {
|
-> Result<GlyphMapping, FontError> {
|
||||||
let mut cmap_reader = self.table.bytes;
|
let mut cmap_reader = self.table.bytes;
|
||||||
|
|
||||||
// Check version.
|
// Check version.
|
||||||
if try!(cmap_reader.read_u16::<BigEndian>().map_err(Error::eof)) != 0 {
|
if try!(cmap_reader.read_u16::<BigEndian>().map_err(FontError::eof)) != 0 {
|
||||||
return Err(Error::UnsupportedCmapVersion)
|
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.
|
// Check platform ID and encoding.
|
||||||
// TODO(pcwalton): Handle more.
|
// TODO(pcwalton): Handle more.
|
||||||
let mut table_found = false;
|
let mut table_found = false;
|
||||||
for _ in 0..num_tables {
|
for _ in 0..num_tables {
|
||||||
let platform_id = try!(cmap_reader.read_u16::<BigEndian>().map_err(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(Error::eof));
|
let encoding_id = try!(cmap_reader.read_u16::<BigEndian>().map_err(FontError::eof));
|
||||||
let offset = try!(cmap_reader.read_u32::<BigEndian>().map_err(Error::eof));
|
let offset = try!(cmap_reader.read_u32::<BigEndian>().map_err(FontError::eof));
|
||||||
match (platform_id, encoding_id) {
|
match (platform_id, encoding_id) {
|
||||||
(PLATFORM_ID_UNICODE, _) |
|
(PLATFORM_ID_UNICODE, _) |
|
||||||
(PLATFORM_ID_MICROSOFT, MICROSOFT_ENCODING_ID_UNICODE_BMP) |
|
(PLATFORM_ID_MICROSOFT, MICROSOFT_ENCODING_ID_UNICODE_BMP) |
|
||||||
(PLATFORM_ID_MICROSOFT, MICROSOFT_ENCODING_ID_UNICODE_UCS4) => {
|
(PLATFORM_ID_MICROSOFT, MICROSOFT_ENCODING_ID_UNICODE_UCS4) => {
|
||||||
// Move to the mapping table.
|
// Move to the mapping table.
|
||||||
cmap_reader = self.table.bytes;
|
cmap_reader = self.table.bytes;
|
||||||
try!(cmap_reader.jump(offset as usize).map_err(Error::eof));
|
try!(cmap_reader.jump(offset as usize).map_err(FontError::eof));
|
||||||
table_found = true;
|
table_found = true;
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -72,11 +78,11 @@ impl<'a> CmapTable<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !table_found {
|
if !table_found {
|
||||||
return Err(Error::UnsupportedCmapEncoding)
|
return Err(FontError::UnsupportedCmapEncoding)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the mapping table format.
|
// 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 {
|
match format {
|
||||||
FORMAT_SEGMENT_MAPPING_TO_DELTA_VALUES => {
|
FORMAT_SEGMENT_MAPPING_TO_DELTA_VALUES => {
|
||||||
self.glyph_mapping_for_codepoint_ranges_segment_mapping_format(cmap_reader,
|
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,
|
self.glyph_mapping_for_codepoint_ranges_segmented_coverage(cmap_reader,
|
||||||
codepoint_ranges)
|
codepoint_ranges)
|
||||||
}
|
}
|
||||||
_ => Err(Error::UnsupportedCmapFormat),
|
_ => Err(FontError::UnsupportedCmapFormat),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,14 +100,14 @@ impl<'a> CmapTable<'a> {
|
||||||
&self,
|
&self,
|
||||||
mut cmap_reader: &[u8],
|
mut cmap_reader: &[u8],
|
||||||
codepoint_ranges: &[CodepointRange])
|
codepoint_ranges: &[CodepointRange])
|
||||||
-> Result<GlyphMapping, Error> {
|
-> Result<GlyphMapping, FontError> {
|
||||||
// Read the mapping table header.
|
// Read the mapping table header.
|
||||||
let _length = 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(Error::eof));
|
let _language = try!(cmap_reader.read_u16::<BigEndian>().map_err(FontError::eof));
|
||||||
let seg_count = try!(cmap_reader.read_u16::<BigEndian>().map_err(Error::eof)) / 2;
|
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(Error::eof));
|
let _search_range = try!(cmap_reader.read_u16::<BigEndian>().map_err(FontError::eof));
|
||||||
let _entry_selector = try!(cmap_reader.read_u16::<BigEndian>().map_err(Error::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(Error::eof));
|
let _range_shift = try!(cmap_reader.read_u16::<BigEndian>().map_err(FontError::eof));
|
||||||
|
|
||||||
// Set up parallel array pointers.
|
// Set up parallel array pointers.
|
||||||
//
|
//
|
||||||
|
@ -110,14 +116,14 @@ impl<'a> CmapTable<'a> {
|
||||||
// are the correct names.
|
// are the correct names.
|
||||||
let (end_codes, mut start_codes) = (cmap_reader, cmap_reader);
|
let (end_codes, mut start_codes) = (cmap_reader, cmap_reader);
|
||||||
try!(start_codes.jump((seg_count as usize + 1) * mem::size_of::<u16>())
|
try!(start_codes.jump((seg_count as usize + 1) * mem::size_of::<u16>())
|
||||||
.map_err(Error::eof));
|
.map_err(FontError::eof));
|
||||||
let mut id_deltas = start_codes;
|
let mut id_deltas = start_codes;
|
||||||
try!(id_deltas.jump(seg_count as usize * mem::size_of::<u16>()).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;
|
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));
|
.map_err(FontError::eof));
|
||||||
let mut glyph_ids = id_range_offsets;
|
let mut glyph_ids = id_range_offsets;
|
||||||
try!(glyph_ids.jump(seg_count as usize * mem::size_of::<u16>()).map_err(Error::eof));
|
try!(glyph_ids.jump(seg_count as usize * mem::size_of::<u16>()).map_err(FontError::eof));
|
||||||
|
|
||||||
// Now perform the lookups.
|
// Now perform the lookups.
|
||||||
let mut glyph_mapping = GlyphMapping::new();
|
let mut glyph_mapping = GlyphMapping::new();
|
||||||
|
@ -146,16 +152,16 @@ impl<'a> CmapTable<'a> {
|
||||||
let mid = (low + high) / 2;
|
let mid = (low + high) / 2;
|
||||||
|
|
||||||
let mut end_code = end_codes;
|
let mut end_code = end_codes;
|
||||||
try!(end_code.jump(mid as usize * 2).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(Error::eof));
|
let end_code = try!(end_code.read_u16::<BigEndian>().map_err(FontError::eof));
|
||||||
if start_codepoint_range > end_code {
|
if start_codepoint_range > end_code {
|
||||||
low = mid + 1;
|
low = mid + 1;
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut start_code = start_codes;
|
let mut start_code = start_codes;
|
||||||
try!(start_code.jump(mid as usize * 2).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(Error::eof));
|
let start_code = try!(start_code.read_u16::<BigEndian>().map_err(FontError::eof));
|
||||||
if start_codepoint_range < start_code {
|
if start_codepoint_range < start_code {
|
||||||
high = mid;
|
high = mid;
|
||||||
continue
|
continue
|
||||||
|
@ -185,15 +191,15 @@ impl<'a> CmapTable<'a> {
|
||||||
let mut end_code = end_codes;
|
let mut end_code = end_codes;
|
||||||
let mut id_range_offset = id_range_offsets;
|
let mut id_range_offset = id_range_offsets;
|
||||||
let mut id_delta = id_deltas;
|
let mut id_delta = id_deltas;
|
||||||
try!(start_code.jump(segment_index as usize * 2).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(Error::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(Error::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(Error::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(Error::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(Error::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>()
|
let id_range_offset = try!(id_range_offset.read_u16::<BigEndian>()
|
||||||
.map_err(Error::eof));
|
.map_err(FontError::eof));
|
||||||
let id_delta = try!(id_delta.read_i16::<BigEndian>().map_err(Error::eof));
|
let id_delta = try!(id_delta.read_i16::<BigEndian>().map_err(FontError::eof));
|
||||||
|
|
||||||
end_codepoint_range = cmp::min(end_codepoint_range, end_code);
|
end_codepoint_range = cmp::min(end_codepoint_range, end_code);
|
||||||
codepoint_range.start = (end_codepoint_range + 1) as u32;
|
codepoint_range.start = (end_codepoint_range + 1) as u32;
|
||||||
|
@ -221,8 +227,8 @@ impl<'a> CmapTable<'a> {
|
||||||
for code_offset in start_code_offset..(end_code_offset + 1) {
|
for code_offset in start_code_offset..(end_code_offset + 1) {
|
||||||
let mut reader = id_range_offsets;
|
let mut reader = id_range_offsets;
|
||||||
try!(reader.jump(segment_index as usize * 2 + code_offset as usize * 2 +
|
try!(reader.jump(segment_index as usize * 2 + code_offset as usize * 2 +
|
||||||
id_range_offset as usize).map_err(Error::eof));
|
id_range_offset as usize).map_err(FontError::eof));
|
||||||
let mut glyph_id = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
|
let mut glyph_id = try!(reader.read_u16::<BigEndian>().map_err(FontError::eof));
|
||||||
if glyph_id == 0 {
|
if glyph_id == 0 {
|
||||||
glyph_mapping.push(MappedGlyphRange {
|
glyph_mapping.push(MappedGlyphRange {
|
||||||
codepoint_start: start_code as u32 + code_offset as u32,
|
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,
|
fn glyph_mapping_for_codepoint_ranges_segmented_coverage(&self,
|
||||||
mut cmap_reader: &[u8],
|
mut cmap_reader: &[u8],
|
||||||
codepoint_ranges: &[CodepointRange])
|
codepoint_ranges: &[CodepointRange])
|
||||||
-> Result<GlyphMapping, Error> {
|
-> Result<GlyphMapping, FontError> {
|
||||||
let _reserved = try!(cmap_reader.read_u16::<BigEndian>().map_err(Error::eof));
|
let _reserved = try!(cmap_reader.read_u16::<BigEndian>().map_err(FontError::eof));
|
||||||
let _length = try!(cmap_reader.read_u32::<BigEndian>().map_err(Error::eof));
|
let _length = try!(cmap_reader.read_u32::<BigEndian>().map_err(FontError::eof));
|
||||||
let _language = try!(cmap_reader.read_u32::<BigEndian>().map_err(Error::eof));
|
let _language = try!(cmap_reader.read_u32::<BigEndian>().map_err(FontError::eof));
|
||||||
let num_groups = try!(cmap_reader.read_u32::<BigEndian>().map_err(Error::eof));
|
let num_groups = try!(cmap_reader.read_u32::<BigEndian>().map_err(FontError::eof));
|
||||||
|
|
||||||
// Now perform the lookups.
|
// Now perform the lookups.
|
||||||
let mut glyph_mapping = GlyphMapping::new();
|
let mut glyph_mapping = GlyphMapping::new();
|
||||||
|
@ -270,11 +276,14 @@ impl<'a> CmapTable<'a> {
|
||||||
|
|
||||||
let mut reader = cmap_reader;
|
let mut reader = cmap_reader;
|
||||||
try!(reader.jump(mid as usize * mem::size_of::<[u32; 3]>())
|
try!(reader.jump(mid as usize * mem::size_of::<[u32; 3]>())
|
||||||
.map_err(Error::eof));
|
.map_err(FontError::eof));
|
||||||
let segment = Segment {
|
let segment = Segment {
|
||||||
start_char_code: try!(reader.read_u32::<BigEndian>().map_err(Error::eof)),
|
start_char_code: try!(reader.read_u32::<BigEndian>()
|
||||||
end_char_code: try!(reader.read_u32::<BigEndian>().map_err(Error::eof)),
|
.map_err(FontError::eof)),
|
||||||
start_glyph_id: try!(reader.read_u32::<BigEndian>().map_err(Error::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 {
|
if codepoint_range.start < segment.start_char_code {
|
|
@ -9,15 +9,21 @@
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use byteorder::{BigEndian, ReadBytesExt};
|
use byteorder::{BigEndian, ReadBytesExt};
|
||||||
|
use error::FontError;
|
||||||
use euclid::Point2D;
|
use euclid::Point2D;
|
||||||
use otf::head::HeadTable;
|
use font::{FontTable, Point, PointKind};
|
||||||
use otf::loca::LocaTable;
|
|
||||||
use otf::{Error, FontTable};
|
|
||||||
use outline::GlyphBounds;
|
use outline::GlyphBounds;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ops::Mul;
|
use std::ops::Mul;
|
||||||
|
use tables::head::HeadTable;
|
||||||
|
use tables::loca::LocaTable;
|
||||||
use util::Jump;
|
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_ZERO: F2Dot14 = F2Dot14(0);
|
||||||
const F2DOT14_ONE: F2Dot14 = F2Dot14(0b0100_0000_0000_0000);
|
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.
|
/// TODO(pcwalton): Add some caching so we don't keep going to the `loca` table all the time.
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct GlyfTable<'a> {
|
pub struct GlyfTable<'a> {
|
||||||
|
@ -91,7 +69,7 @@ impl<'a> GlyfTable<'a> {
|
||||||
loca_table: &LocaTable,
|
loca_table: &LocaTable,
|
||||||
glyph_id: u16,
|
glyph_id: u16,
|
||||||
callback: F)
|
callback: F)
|
||||||
-> Result<(), Error> where F: FnMut(&Point) {
|
-> Result<(), FontError> where F: FnMut(&Point) {
|
||||||
let mut reader = self.table.bytes;
|
let mut reader = self.table.bytes;
|
||||||
|
|
||||||
match try!(loca_table.location_of(head_table, glyph_id)) {
|
match try!(loca_table.location_of(head_table, glyph_id)) {
|
||||||
|
@ -99,11 +77,11 @@ impl<'a> GlyfTable<'a> {
|
||||||
// No points.
|
// No points.
|
||||||
return Ok(())
|
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 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 {
|
if number_of_contours >= 0 {
|
||||||
self.for_each_point_in_simple_glyph(glyph_start, callback)
|
self.for_each_point_in_simple_glyph(glyph_start, callback)
|
||||||
} else {
|
} else {
|
||||||
|
@ -112,25 +90,25 @@ impl<'a> GlyfTable<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn for_each_point_in_simple_glyph<F>(&self, mut reader: &[u8], mut callback: F)
|
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.
|
// 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 {
|
if number_of_contours == 0 {
|
||||||
return Ok(())
|
return Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip over the rest of the header.
|
// 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.
|
// Find out how many points we have.
|
||||||
let mut endpoints_reader = reader;
|
let mut endpoints_reader = reader;
|
||||||
try!(reader.jump(mem::size_of::<u16>() as usize * (number_of_contours as usize - 1))
|
try!(reader.jump(mem::size_of::<u16>() as usize * (number_of_contours as usize - 1))
|
||||||
.map_err(Error::eof));
|
.map_err(FontError::eof));
|
||||||
let number_of_points = try!(reader.read_u16::<BigEndian>().map_err(Error::eof)) + 1;
|
let number_of_points = try!(reader.read_u16::<BigEndian>().map_err(FontError::eof)) + 1;
|
||||||
|
|
||||||
// Skip over hinting instructions.
|
// Skip over hinting instructions.
|
||||||
let instruction_length = try!(reader.read_u16::<BigEndian>().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(Error::eof));
|
try!(reader.jump(instruction_length as usize).map_err(FontError::eof));
|
||||||
|
|
||||||
// Find the offsets of the X and Y coordinates.
|
// Find the offsets of the X and Y coordinates.
|
||||||
let flags_reader = reader;
|
let flags_reader = reader;
|
||||||
|
@ -140,14 +118,14 @@ impl<'a> GlyfTable<'a> {
|
||||||
// Set up the streams.
|
// Set up the streams.
|
||||||
let mut flag_parser = try!(FlagParser::new(flags_reader));
|
let mut flag_parser = try!(FlagParser::new(flags_reader));
|
||||||
let mut x_coordinate_reader = reader;
|
let mut x_coordinate_reader = reader;
|
||||||
try!(reader.jump(x_coordinate_length as usize).map_err(Error::eof));
|
try!(reader.jump(x_coordinate_length as usize).map_err(FontError::eof));
|
||||||
let mut y_coordinate_reader = reader;
|
let mut y_coordinate_reader = reader;
|
||||||
|
|
||||||
// Now parse the contours.
|
// Now parse the contours.
|
||||||
let (mut position, mut point_index) = (Point2D::new(0, 0), 0);
|
let (mut position, mut point_index) = (Point2D::new(0, 0), 0);
|
||||||
for _ in 0..number_of_contours {
|
for _ in 0..number_of_contours {
|
||||||
let contour_point_count = try!(endpoints_reader.read_u16::<BigEndian>()
|
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 first_on_curve_point = None;
|
||||||
let mut initial_off_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);
|
let mut delta = Point2D::new(0, 0);
|
||||||
if flags.contains(X_SHORT_VECTOR) {
|
if flags.contains(X_SHORT_VECTOR) {
|
||||||
delta.x = try!(x_coordinate_reader.read_u8().map_err(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) {
|
if !flags.contains(THIS_X_IS_SAME) {
|
||||||
delta.x = -delta.x
|
delta.x = -delta.x
|
||||||
}
|
}
|
||||||
} else if !flags.contains(THIS_X_IS_SAME) {
|
} else if !flags.contains(THIS_X_IS_SAME) {
|
||||||
delta.x = try!(x_coordinate_reader.read_i16::<BigEndian>().map_err(Error::eof))
|
delta.x = try!(x_coordinate_reader.read_i16::<BigEndian>().map_err(FontError::eof))
|
||||||
}
|
}
|
||||||
if flags.contains(Y_SHORT_VECTOR) {
|
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) {
|
if !flags.contains(THIS_Y_IS_SAME) {
|
||||||
delta.y = -delta.y
|
delta.y = -delta.y
|
||||||
}
|
}
|
||||||
} else if !flags.contains(THIS_Y_IS_SAME) {
|
} else if !flags.contains(THIS_Y_IS_SAME) {
|
||||||
delta.y = try!(y_coordinate_reader.read_i16::<BigEndian>().map_err(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) {
|
if last_point_was_off_curve && !flags.contains(ON_CURVE) {
|
||||||
|
@ -261,21 +239,21 @@ impl<'a> GlyfTable<'a> {
|
||||||
head_table: &HeadTable,
|
head_table: &HeadTable,
|
||||||
loca_table: &LocaTable,
|
loca_table: &LocaTable,
|
||||||
mut callback: F)
|
mut callback: F)
|
||||||
-> Result<(), Error> where F: FnMut(&Point) {
|
-> Result<(), FontError> where F: FnMut(&Point) {
|
||||||
try!(reader.jump(mem::size_of::<i16>() * 5).map_err(Error::eof));
|
try!(reader.jump(mem::size_of::<i16>() * 5).map_err(FontError::eof));
|
||||||
|
|
||||||
loop {
|
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 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);
|
let (arg0, arg1);
|
||||||
if flags.contains(ARG_1_AND_2_ARE_WORDS) {
|
if flags.contains(ARG_1_AND_2_ARE_WORDS) {
|
||||||
arg0 = 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(Error::eof));
|
arg1 = try!(reader.read_i16::<BigEndian>().map_err(FontError::eof));
|
||||||
} else {
|
} else {
|
||||||
arg0 = 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(Error::eof)) as i16;
|
arg1 = try!(reader.read_i8().map_err(FontError::eof)) as i16;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut transform = Mat3x2::identity();
|
let mut transform = Mat3x2::identity();
|
||||||
|
@ -285,22 +263,22 @@ impl<'a> GlyfTable<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if flags.contains(WE_HAVE_A_SCALE) {
|
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.m00 = scale;
|
||||||
transform.m11 = scale;
|
transform.m11 = scale;
|
||||||
} else if flags.contains(WE_HAVE_AN_X_AND_Y_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.m00 = F2Dot14(try!(reader.read_i16::<BigEndian>().map_err(FontError::eof)));
|
||||||
transform.m11 = F2Dot14(try!(reader.read_i16::<BigEndian>().map_err(Error::eof)));
|
transform.m11 = F2Dot14(try!(reader.read_i16::<BigEndian>().map_err(FontError::eof)));
|
||||||
} else if flags.contains(WE_HAVE_A_TWO_BY_TWO) {
|
} else if flags.contains(WE_HAVE_A_TWO_BY_TWO) {
|
||||||
transform.m00 = 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(Error::eof)));
|
transform.m01 = F2Dot14(try!(reader.read_i16::<BigEndian>().map_err(FontError::eof)));
|
||||||
transform.m10 = F2Dot14(try!(reader.read_i16::<BigEndian>().map_err(Error::eof)));
|
transform.m10 = F2Dot14(try!(reader.read_i16::<BigEndian>().map_err(FontError::eof)));
|
||||||
transform.m11 = F2Dot14(try!(reader.read_i16::<BigEndian>().map_err(Error::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)) {
|
if let Some(offset) = try!(loca_table.location_of(head_table, glyph_index)) {
|
||||||
let mut reader = self.table.bytes;
|
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| {
|
try!(self.for_each_point_in_simple_glyph(reader, |point| {
|
||||||
callback(&transform.transform(&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)
|
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;
|
let mut reader = self.table.bytes;
|
||||||
|
|
||||||
match try!(loca_table.location_of(head_table, glyph_id)) {
|
match try!(loca_table.location_of(head_table, glyph_id)) {
|
||||||
|
@ -328,16 +306,16 @@ impl<'a> GlyfTable<'a> {
|
||||||
top: 0,
|
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.
|
// 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 x_min = try!(reader.read_i16::<BigEndian>().map_err(FontError::eof));
|
||||||
let y_min = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
|
let y_min = try!(reader.read_i16::<BigEndian>().map_err(FontError::eof));
|
||||||
let x_max = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
|
let x_max = try!(reader.read_i16::<BigEndian>().map_err(FontError::eof));
|
||||||
let y_max = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
|
let y_max = try!(reader.read_i16::<BigEndian>().map_err(FontError::eof));
|
||||||
Ok(GlyphBounds {
|
Ok(GlyphBounds {
|
||||||
left: x_min as i32,
|
left: x_min as i32,
|
||||||
bottom: y_min as i32,
|
bottom: y_min as i32,
|
||||||
|
@ -351,14 +329,14 @@ impl<'a> GlyfTable<'a> {
|
||||||
// of X coordinates and positions the reader at the start of that list.
|
// of X coordinates and positions the reader at the start of that list.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn calculate_size_of_x_coordinates<'a, 'b>(reader: &'a mut &'b [u8], number_of_points: u16)
|
fn calculate_size_of_x_coordinates<'a, 'b>(reader: &'a mut &'b [u8], number_of_points: u16)
|
||||||
-> Result<u16, Error> {
|
-> Result<u16, FontError> {
|
||||||
let (mut x_coordinate_length, mut points_left) = (0, number_of_points);
|
let (mut x_coordinate_length, mut points_left) = (0, number_of_points);
|
||||||
while points_left > 0 {
|
while points_left > 0 {
|
||||||
let flags = 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) {
|
let repeat_count = if !flags.contains(REPEAT) {
|
||||||
1
|
1
|
||||||
} else {
|
} 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) {
|
if flags.contains(X_SHORT_VECTOR) {
|
||||||
|
@ -381,7 +359,7 @@ struct FlagParser<'a> {
|
||||||
|
|
||||||
impl<'a> FlagParser<'a> {
|
impl<'a> FlagParser<'a> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn new(buffer: &[u8]) -> Result<FlagParser, Error> {
|
fn new(buffer: &[u8]) -> Result<FlagParser, FontError> {
|
||||||
let mut parser = FlagParser {
|
let mut parser = FlagParser {
|
||||||
next: buffer,
|
next: buffer,
|
||||||
current: &buffer[0],
|
current: &buffer[0],
|
||||||
|
@ -392,7 +370,7 @@ impl<'a> FlagParser<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn next(&mut self) -> Result<(), Error> {
|
fn next(&mut self) -> Result<(), FontError> {
|
||||||
if self.repeats_left > 0 {
|
if self.repeats_left > 0 {
|
||||||
self.repeats_left -= 1;
|
self.repeats_left -= 1;
|
||||||
return Ok(())
|
return Ok(())
|
||||||
|
@ -400,7 +378,7 @@ impl<'a> FlagParser<'a> {
|
||||||
|
|
||||||
self.current = match self.next.get(0) {
|
self.current = match self.next.get(0) {
|
||||||
Some(value) => value,
|
Some(value) => value,
|
||||||
None => return Err(Error::UnexpectedEof),
|
None => return Err(FontError::UnexpectedEof),
|
||||||
};
|
};
|
||||||
|
|
||||||
let flags = SimpleFlags::from_bits_truncate(*self.current);
|
let flags = SimpleFlags::from_bits_truncate(*self.current);
|
||||||
|
@ -409,7 +387,7 @@ impl<'a> FlagParser<'a> {
|
||||||
if flags.contains(REPEAT) {
|
if flags.contains(REPEAT) {
|
||||||
self.repeats_left = match self.next.get(0) {
|
self.repeats_left = match self.next.get(0) {
|
||||||
Some(&value) => value,
|
Some(&value) => value,
|
||||||
None => return Err(Error::UnexpectedEof),
|
None => return Err(FontError::UnexpectedEof),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.next = &self.next[1..];
|
self.next = &self.next[1..];
|
|
@ -9,11 +9,17 @@
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use byteorder::{BigEndian, ReadBytesExt};
|
use byteorder::{BigEndian, ReadBytesExt};
|
||||||
use otf::{Error, FontTable};
|
use error::FontError;
|
||||||
|
use font::FontTable;
|
||||||
use outline::GlyphBounds;
|
use outline::GlyphBounds;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use util::Jump;
|
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;
|
const MAGIC_NUMBER: u32 = 0x5f0f3cf5;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -24,33 +30,33 @@ pub struct HeadTable {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HeadTable {
|
impl HeadTable {
|
||||||
pub fn new(table: FontTable) -> Result<HeadTable, Error> {
|
pub fn new(table: FontTable) -> Result<HeadTable, FontError> {
|
||||||
let mut reader = table.bytes;
|
let mut reader = table.bytes;
|
||||||
|
|
||||||
// Check the version.
|
// Check the version.
|
||||||
let major_version = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
|
let major_version = try!(reader.read_u16::<BigEndian>().map_err(FontError::eof));
|
||||||
let minor_version = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
|
let minor_version = try!(reader.read_u16::<BigEndian>().map_err(FontError::eof));
|
||||||
if (major_version, minor_version) != (1, 0) {
|
if (major_version, minor_version) != (1, 0) {
|
||||||
return Err(Error::UnsupportedHeadVersion)
|
return Err(FontError::UnsupportedHeadVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the magic number.
|
// Check the magic number.
|
||||||
try!(reader.jump(mem::size_of::<u32>() * 2).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(Error::eof));
|
let magic_number = try!(reader.read_u32::<BigEndian>().map_err(FontError::eof));
|
||||||
if magic_number != MAGIC_NUMBER {
|
if magic_number != MAGIC_NUMBER {
|
||||||
return Err(Error::UnknownFormat)
|
return Err(FontError::UnknownFormat)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the units per em.
|
// Read the units per em.
|
||||||
try!(reader.jump(mem::size_of::<u16>()).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(Error::eof));
|
let units_per_em = try!(reader.read_u16::<BigEndian>().map_err(FontError::eof));
|
||||||
|
|
||||||
// Read the maximum bounds.
|
// Read the maximum bounds.
|
||||||
try!(reader.jump(mem::size_of::<i64>() * 2).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(Error::eof));
|
let x_min = try!(reader.read_i16::<BigEndian>().map_err(FontError::eof));
|
||||||
let y_min = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
|
let y_min = try!(reader.read_i16::<BigEndian>().map_err(FontError::eof));
|
||||||
let x_max = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
|
let x_max = try!(reader.read_i16::<BigEndian>().map_err(FontError::eof));
|
||||||
let y_max = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
|
let y_max = try!(reader.read_i16::<BigEndian>().map_err(FontError::eof));
|
||||||
let max_glyph_bounds = GlyphBounds {
|
let max_glyph_bounds = GlyphBounds {
|
||||||
left: x_min as i32,
|
left: x_min as i32,
|
||||||
bottom: y_min as i32,
|
bottom: y_min as i32,
|
||||||
|
@ -59,13 +65,14 @@ impl HeadTable {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Read the index-to-location format.
|
// Read the index-to-location format.
|
||||||
try!(reader.jump(mem::size_of::<u16>() * 2 + mem::size_of::<i16>()).map_err(Error::eof));
|
try!(reader.jump(mem::size_of::<u16>() * 2 + mem::size_of::<i16>())
|
||||||
let index_to_loc_format = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
|
.map_err(FontError::eof));
|
||||||
|
let index_to_loc_format = try!(reader.read_i16::<BigEndian>().map_err(FontError::eof));
|
||||||
|
|
||||||
// Check the glyph data format.
|
// 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 {
|
if glyph_data_format != 0 {
|
||||||
return Err(Error::UnsupportedGlyphFormat)
|
return Err(FontError::UnsupportedGlyphFormat)
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(HeadTable {
|
Ok(HeadTable {
|
|
@ -9,10 +9,16 @@
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use byteorder::{BigEndian, ReadBytesExt};
|
use byteorder::{BigEndian, ReadBytesExt};
|
||||||
use otf::{Error, FontTable};
|
use error::FontError;
|
||||||
|
use font::FontTable;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use util::Jump;
|
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)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct HheaTable {
|
pub struct HheaTable {
|
||||||
pub line_gap: i16,
|
pub line_gap: i16,
|
||||||
|
@ -20,24 +26,24 @@ pub struct HheaTable {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HheaTable {
|
impl HheaTable {
|
||||||
pub fn new(table: FontTable) -> Result<HheaTable, Error> {
|
pub fn new(table: FontTable) -> Result<HheaTable, FontError> {
|
||||||
let mut reader = table.bytes;
|
let mut reader = table.bytes;
|
||||||
|
|
||||||
// Check the version.
|
// Check the version.
|
||||||
let major_version = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
|
let major_version = try!(reader.read_u16::<BigEndian>().map_err(FontError::eof));
|
||||||
let minor_version = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
|
let minor_version = try!(reader.read_u16::<BigEndian>().map_err(FontError::eof));
|
||||||
if (major_version, minor_version) != (1, 0) {
|
if (major_version, minor_version) != (1, 0) {
|
||||||
return Err(Error::UnsupportedHheaVersion)
|
return Err(FontError::UnsupportedHheaVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the height-related metrics.
|
// Read the height-related metrics.
|
||||||
let _ascender = 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(Error::eof));
|
let _descender = try!(reader.read_i16::<BigEndian>().map_err(FontError::eof));
|
||||||
let line_gap = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
|
let line_gap = try!(reader.read_i16::<BigEndian>().map_err(FontError::eof));
|
||||||
|
|
||||||
// Read the number of `hmtx` entries.
|
// Read the number of `hmtx` entries.
|
||||||
try!(reader.jump(mem::size_of::<u16>() * 12).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(Error::eof));
|
let number_of_h_metrics = try!(reader.read_u16::<BigEndian>().map_err(FontError::eof));
|
||||||
|
|
||||||
Ok(HheaTable {
|
Ok(HheaTable {
|
||||||
line_gap: line_gap,
|
line_gap: line_gap,
|
|
@ -9,11 +9,17 @@
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use byteorder::{BigEndian, ReadBytesExt};
|
use byteorder::{BigEndian, ReadBytesExt};
|
||||||
use otf::hhea::HheaTable;
|
use error::FontError;
|
||||||
use otf::{Error, FontTable};
|
use font::FontTable;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
use tables::hhea::HheaTable;
|
||||||
use util::Jump;
|
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)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct HmtxTable<'a> {
|
pub struct HmtxTable<'a> {
|
||||||
table: FontTable<'a>,
|
table: FontTable<'a>,
|
||||||
|
@ -27,23 +33,23 @@ impl<'a> HmtxTable<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn metrics_for_glyph(&self, hhea_table: &HheaTable, glyph_id: u16)
|
pub fn metrics_for_glyph(&self, hhea_table: &HheaTable, glyph_id: u16)
|
||||||
-> Result<HorizontalMetrics, Error> {
|
-> Result<HorizontalMetrics, FontError> {
|
||||||
let mut reader = self.table.bytes;
|
let mut reader = self.table.bytes;
|
||||||
|
|
||||||
// Read the advance width.
|
// Read the advance width.
|
||||||
let advance_width;
|
let advance_width;
|
||||||
if glyph_id < hhea_table.number_of_h_metrics {
|
if glyph_id < hhea_table.number_of_h_metrics {
|
||||||
try!(reader.jump(mem::size_of::<u16>() * 2 * glyph_id as usize).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(Error::eof))
|
advance_width = try!(reader.read_u16::<BigEndian>().map_err(FontError::eof))
|
||||||
} else {
|
} else {
|
||||||
try!(reader.jump(mem::size_of::<u16>() * 2 *
|
try!(reader.jump(mem::size_of::<u16>() * 2 *
|
||||||
(hhea_table.number_of_h_metrics - 1) as usize).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(Error::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(Error::eof));
|
try!(reader.jump(mem::size_of::<i16>() * glyph_id as usize).map_err(FontError::eof));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the left-side bearing.
|
// 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 {
|
Ok(HorizontalMetrics {
|
||||||
advance_width: advance_width,
|
advance_width: advance_width,
|
|
@ -9,10 +9,16 @@
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use byteorder::{BigEndian, ReadBytesExt};
|
use byteorder::{BigEndian, ReadBytesExt};
|
||||||
use otf::{Error, FontTable};
|
use error::FontError;
|
||||||
|
use font::FontTable;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use util::Jump;
|
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! {
|
bitflags! {
|
||||||
flags Coverage: u16 {
|
flags Coverage: u16 {
|
||||||
const HORIZONTAL = 1 << 0,
|
const HORIZONTAL = 1 << 0,
|
||||||
|
@ -28,20 +34,20 @@ pub struct KernTable<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> 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 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 {
|
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;
|
let mut horizontal_table = None;
|
||||||
for _ in 0..n_tables {
|
for _ in 0..n_tables {
|
||||||
let mut table_reader = kern_reader;
|
let mut table_reader = kern_reader;
|
||||||
let _version = 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(Error::eof));
|
let length = try!(table_reader.read_u16::<BigEndian>().map_err(FontError::eof));
|
||||||
let coverage = try!(table_reader.read_u16::<BigEndian>().map_err(Error::eof));
|
let coverage = try!(table_reader.read_u16::<BigEndian>().map_err(FontError::eof));
|
||||||
let coverage_flags = Coverage::from_bits_truncate(coverage);
|
let coverage_flags = Coverage::from_bits_truncate(coverage);
|
||||||
|
|
||||||
if coverage_flags.contains(HORIZONTAL) && !coverage_flags.contains(MINIMUM) &&
|
if coverage_flags.contains(HORIZONTAL) && !coverage_flags.contains(MINIMUM) &&
|
||||||
|
@ -51,7 +57,7 @@ impl<'a> KernTable<'a> {
|
||||||
break
|
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 {
|
match horizontal_table {
|
||||||
|
@ -60,25 +66,25 @@ impl<'a> KernTable<'a> {
|
||||||
horizontal_table: horizontal_table,
|
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)
|
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 mut table_reader = self.horizontal_table;
|
||||||
let n_pairs = try!(table_reader.read_u16::<BigEndian>().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(Error::eof));
|
try!(table_reader.jump(mem::size_of::<[u16; 3]>()).map_err(FontError::eof));
|
||||||
|
|
||||||
let (mut low, mut high) = (0, n_pairs as u32);
|
let (mut low, mut high) = (0, n_pairs as u32);
|
||||||
while low < high {
|
while low < high {
|
||||||
let mut reader = table_reader;
|
let mut reader = table_reader;
|
||||||
let mid = (low + high) / 2;
|
let mid = (low + high) / 2;
|
||||||
|
|
||||||
try!(reader.jump(mid as usize * mem::size_of::<[u16; 3]>()).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(Error::eof));
|
let left = try!(reader.read_u16::<BigEndian>().map_err(FontError::eof));
|
||||||
let right = try!(reader.read_u16::<BigEndian>().map_err(Error::eof));
|
let right = try!(reader.read_u16::<BigEndian>().map_err(FontError::eof));
|
||||||
let value = try!(reader.read_i16::<BigEndian>().map_err(Error::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) {
|
if left_glyph_id < left || (left_glyph_id == left && right_glyph_id < right) {
|
||||||
high = mid
|
high = mid
|
|
@ -9,42 +9,48 @@
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use byteorder::{BigEndian, ReadBytesExt};
|
use byteorder::{BigEndian, ReadBytesExt};
|
||||||
use otf::head::HeadTable;
|
use error::FontError;
|
||||||
use otf::{Error, FontTable};
|
use font::FontTable;
|
||||||
|
use tables::head::HeadTable;
|
||||||
use util::Jump;
|
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> {
|
pub struct LocaTable<'a> {
|
||||||
table: FontTable<'a>,
|
table: FontTable<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> LocaTable<'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 {
|
Ok(LocaTable {
|
||||||
table: loca_table,
|
table: loca_table,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn location_of(&self, head_table: &HeadTable, glyph_id: u16)
|
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 mut reader = self.table.bytes;
|
||||||
let (this_location, next_location) = match head_table.index_to_loc_format {
|
let (this_location, next_location) = match head_table.index_to_loc_format {
|
||||||
0 => {
|
0 => {
|
||||||
try!(reader.jump(glyph_id as usize * 2).map_err(Error::eof));
|
try!(reader.jump(glyph_id as usize * 2).map_err(FontError::eof));
|
||||||
let this_location =
|
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>() {
|
let next_location = match reader.read_u16::<BigEndian>() {
|
||||||
Ok(next_location) => Ok(next_location as u32 * 2),
|
Ok(next_location) => Ok(next_location as u32 * 2),
|
||||||
Err(_) => Err(Error::UnexpectedEof),
|
Err(_) => Err(FontError::UnexpectedEof),
|
||||||
};
|
};
|
||||||
(this_location, next_location)
|
(this_location, next_location)
|
||||||
}
|
}
|
||||||
1 => {
|
1 => {
|
||||||
try!(reader.jump(glyph_id as usize * 4).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(Error::eof));
|
let this_location = try!(reader.read_u32::<BigEndian>().map_err(FontError::eof));
|
||||||
let next_location = reader.read_u32::<BigEndian>().map_err(Error::eof);
|
let next_location = reader.read_u32::<BigEndian>().map_err(FontError::eof);
|
||||||
(this_location, next_location)
|
(this_location, next_location)
|
||||||
}
|
}
|
||||||
_ => return Err(Error::UnknownFormat),
|
_ => return Err(FontError::UnknownFormat),
|
||||||
};
|
};
|
||||||
|
|
||||||
if next_location == Ok(this_location) {
|
if next_location == Ok(this_location) {
|
|
@ -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;
|
||||||
|
|
|
@ -9,10 +9,16 @@
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use byteorder::{BigEndian, ReadBytesExt};
|
use byteorder::{BigEndian, ReadBytesExt};
|
||||||
use otf::{Error, FontTable};
|
use error::FontError;
|
||||||
|
use font::FontTable;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use util::Jump;
|
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)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Os2Table {
|
pub struct Os2Table {
|
||||||
pub typo_ascender: i16,
|
pub typo_ascender: i16,
|
||||||
|
@ -21,27 +27,27 @@ pub struct Os2Table {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Os2Table {
|
impl Os2Table {
|
||||||
pub fn new(table: FontTable) -> Result<Os2Table, Error> {
|
pub fn new(table: FontTable) -> Result<Os2Table, FontError> {
|
||||||
let mut reader = table.bytes;
|
let mut reader = table.bytes;
|
||||||
|
|
||||||
// We should be compatible with all versions. If this is greater than version 5, follow
|
// We should be compatible with all versions. If this is greater than version 5, follow
|
||||||
// Postel's law and hope for the best.
|
// 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.
|
// Skip to the line gap.
|
||||||
try!(reader.jump(mem::size_of::<u16>() * 15).map_err(Error::eof));
|
try!(reader.jump(mem::size_of::<u16>() * 15).map_err(FontError::eof));
|
||||||
try!(reader.jump(10).map_err(Error::eof));
|
try!(reader.jump(10).map_err(FontError::eof));
|
||||||
if version == 0 {
|
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 {
|
} 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.
|
// Read the line spacing information.
|
||||||
let typo_ascender = 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(Error::eof));
|
let typo_descender = try!(reader.read_i16::<BigEndian>().map_err(FontError::eof));
|
||||||
let typo_line_gap = try!(reader.read_i16::<BigEndian>().map_err(Error::eof));
|
let typo_line_gap = try!(reader.read_i16::<BigEndian>().map_err(FontError::eof));
|
||||||
|
|
||||||
Ok(Os2Table {
|
Ok(Os2Table {
|
||||||
typo_ascender: typo_ascender,
|
typo_ascender: typo_ascender,
|
|
@ -2,8 +2,8 @@
|
||||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
use charmap::CodepointRange;
|
use charmap::CodepointRange;
|
||||||
|
use font::Font;
|
||||||
use memmap::{Mmap, Protection};
|
use memmap::{Mmap, Protection};
|
||||||
use otf::Font;
|
|
||||||
use outline::OutlineBuilder;
|
use outline::OutlineBuilder;
|
||||||
use test::Bencher;
|
use test::Bencher;
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
use charmap::CodepointRanges;
|
use charmap::CodepointRanges;
|
||||||
use error::GlyphStoreCreationError;
|
use error::GlyphStoreCreationError;
|
||||||
use euclid::{Point2D, Rect};
|
use euclid::{Point2D, Rect};
|
||||||
use otf::Font;
|
use font::Font;
|
||||||
use outline::{OutlineBuilder, Outlines};
|
use outline::{OutlineBuilder, Outlines};
|
||||||
use shaper;
|
use shaper;
|
||||||
use std::u16;
|
use std::u16;
|
||||||
|
@ -186,7 +186,7 @@ impl GlyphStore {
|
||||||
let mut all_glyph_indices = vec![];
|
let mut all_glyph_indices = vec![];
|
||||||
for glyph_id in glyph_ids {
|
for glyph_id in glyph_ids {
|
||||||
let glyph_index = try!(outline_builder.add_glyph(font, glyph_id)
|
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;
|
glyph_id_to_glyph_index[glyph_id as usize] = glyph_index;
|
||||||
all_glyph_indices.push(glyph_index);
|
all_glyph_indices.push(glyph_index);
|
||||||
}
|
}
|
||||||
|
@ -207,7 +207,7 @@ impl GlyphStore {
|
||||||
pub fn from_codepoints(codepoints: &CodepointRanges, font: &Font)
|
pub fn from_codepoints(codepoints: &CodepointRanges, font: &Font)
|
||||||
-> Result<GlyphStore, GlyphStoreCreationError> {
|
-> Result<GlyphStore, GlyphStoreCreationError> {
|
||||||
let mapping = try!(font.glyph_mapping_for_codepoint_ranges(&codepoints.ranges)
|
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();
|
let glyph_ids = mapping.iter().map(|(_, glyph_id)| glyph_id).collect();
|
||||||
GlyphStore::from_glyph_ids(glyph_ids, font)
|
GlyphStore::from_glyph_ids(glyph_ids, font)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue