From 1deaf9136ee25f582e868ebc4fdd15373c010954 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Thu, 26 Jan 2017 10:27:38 -0800 Subject: [PATCH] Don't require users of the library to load OTF tables individually --- examples/dump-outlines.rs | 15 +++--- examples/generate-atlas.rs | 12 ++--- src/glyph_buffer.rs | 20 +++----- src/otf/glyf.rs | 14 ++++-- src/otf/loca.rs | 28 +++++------ src/otf/mod.rs | 99 +++++++++++++++++++------------------- 6 files changed, 90 insertions(+), 98 deletions(-) diff --git a/examples/dump-outlines.rs b/examples/dump-outlines.rs index b77de090..3952872a 100644 --- a/examples/dump-outlines.rs +++ b/examples/dump-outlines.rs @@ -9,20 +9,16 @@ use euclid::Point2D; use memmap::{Mmap, Protection}; use pathfinder::batch::GlyphRange; use pathfinder::charmap::CodepointRange; -use pathfinder::otf::FontData; +use pathfinder::otf::Font; use std::char; use std::env; fn main() { let file = Mmap::open_path(env::args().nth(1).unwrap(), Protection::Read).unwrap(); unsafe { - let font = FontData::new(file.as_slice()); - let cmap = font.cmap_table().unwrap(); - let glyf = font.glyf_table().unwrap(); - let head = font.head_table().unwrap(); - let loca = font.loca_table(&head).unwrap(); + let font = Font::new(file.as_slice()).unwrap(); let codepoint_ranges = [CodepointRange::new('!' as u32, '~' as u32)]; - let glyph_ranges = cmap.glyph_ranges_for_codepoint_ranges(&codepoint_ranges).unwrap(); + let glyph_ranges = font.cmap.glyph_ranges_for_codepoint_ranges(&codepoint_ranges).unwrap(); for (codepoint, glyph_id) in codepoint_ranges.iter() .flat_map(CodepointRange::iter) @@ -34,7 +30,10 @@ fn main() { let mut last_point: Option> = None; let mut last_point_was_off_curve = false; - glyf.for_each_point(&loca, glyph_id as u32, |point| { + font.glyf.as_ref().unwrap().for_each_point(&font.head, + font.loca.as_ref().unwrap(), + glyph_id as u32, + |point| { if point.first_point_in_contour { println!("M {},{}", point.position.x, point.position.y); } else { diff --git a/examples/generate-atlas.rs b/examples/generate-atlas.rs index 97a62d52..f878970c 100644 --- a/examples/generate-atlas.rs +++ b/examples/generate-atlas.rs @@ -20,7 +20,7 @@ use pathfinder::batch::{BatchBuilder, GlyphRange}; use pathfinder::charmap::CodepointRange; use pathfinder::coverage::CoverageBuffer; use pathfinder::glyph_buffer::GlyphBufferBuilder; -use pathfinder::otf::FontData; +use pathfinder::otf::Font; use pathfinder::rasterizer::{Rasterizer, RasterizerOptions}; use std::env; use std::os::raw::c_void; @@ -54,16 +54,12 @@ fn main() { let file = Mmap::open_path(env::args().nth(1).unwrap(), Protection::Read).unwrap(); unsafe { - let font = FontData::new(file.as_slice()); - let cmap = font.cmap_table().unwrap(); - let glyf = font.glyf_table().unwrap(); - let head = font.head_table().unwrap(); - let loca = font.loca_table(&head).unwrap(); + let font = Font::new(file.as_slice()).unwrap(); let codepoint_ranges = [CodepointRange::new('!' as u32, '~' as u32)]; - let glyph_ranges = cmap.glyph_ranges_for_codepoint_ranges(&codepoint_ranges).unwrap(); + let glyph_ranges = font.cmap.glyph_ranges_for_codepoint_ranges(&codepoint_ranges).unwrap(); for (glyph_index, glyph_id) in glyph_ranges.iter().flat_map(GlyphRange::iter).enumerate() { - glyph_buffer_builder.add_glyph(glyph_id as u32, &head, &loca, &glyf).unwrap(); + glyph_buffer_builder.add_glyph(&font, glyph_id as u32).unwrap(); batch_builder.add_glyph(&glyph_buffer_builder, glyph_index as u32, POINT_SIZE).unwrap() } } diff --git a/src/glyph_buffer.rs b/src/glyph_buffer.rs index 4ea81ac7..ca388b27 100644 --- a/src/glyph_buffer.rs +++ b/src/glyph_buffer.rs @@ -11,9 +11,7 @@ use euclid::{Point2D, Rect, Size2D}; use gl::types::{GLsizeiptr, GLuint}; use gl; -use otf::glyf::GlyfTable; -use otf::head::HeadTable; -use otf::loca::LocaTable; +use otf::Font; use std::mem; use std::os::raw::c_void; @@ -39,12 +37,7 @@ impl GlyphBufferBuilder { } } - pub fn add_glyph(&mut self, - glyph_id: u32, - head_table: &HeadTable, - loca_table: &LocaTable, - glyf_table: &GlyfTable) - -> Result<(), ()> { + pub fn add_glyph(&mut self, font: &Font, glyph_id: u32) -> Result<(), ()> { let glyph_index = self.descriptors.len() as u16; let mut point_index = self.vertices.len() as u32; @@ -52,7 +45,10 @@ impl GlyphBufferBuilder { let start_point = point_index; let mut last_point_on_curve = true; - try!(glyf_table.for_each_point(loca_table, glyph_id, |point| { + let glyf_table = try!(font.glyf.ok_or(())); + let loca_table = try!(font.loca.as_ref().ok_or(())); + + try!(glyf_table.for_each_point(&font.head, loca_table, glyph_id, |point| { self.vertices.push(Vertex { x: point.position.x, y: point.position.y, @@ -73,13 +69,13 @@ impl GlyphBufferBuilder { })); // Add a glyph descriptor. - let bounding_rect = try!(glyf_table.bounding_rect(loca_table, glyph_id)); + let bounding_rect = try!(glyf_table.bounding_rect(&font.head, loca_table, glyph_id)); self.descriptors.push(GlyphDescriptor { left: bounding_rect.origin.x as i32, bottom: bounding_rect.origin.y as i32, right: bounding_rect.max_x() as i32, top: bounding_rect.max_y() as i32, - units_per_em: head_table.units_per_em as u32, + units_per_em: font.head.units_per_em as u32, start_point: start_point as u32, start_index: start_index, pad: 0, diff --git a/src/otf/glyf.rs b/src/otf/glyf.rs index 6d0662d0..1917d5ba 100644 --- a/src/otf/glyf.rs +++ b/src/otf/glyf.rs @@ -11,6 +11,7 @@ use byteorder::{BigEndian, ReadBytesExt}; use euclid::{Point2D, Rect, Size2D}; use otf::FontTable; +use otf::head::HeadTable; use otf::loca::LocaTable; use std::mem; use util::Jump; @@ -47,10 +48,14 @@ impl<'a> GlyfTable<'a> { } } - pub fn for_each_point(&self, loca_table: &LocaTable, glyph_id: u32, mut callback: F) + pub fn for_each_point(&self, + head_table: &HeadTable, + loca_table: &LocaTable, + glyph_id: u32, + mut callback: F) -> Result<(), ()> where F: FnMut(&Point) { let mut reader = self.table.bytes; - let offset = try!(loca_table.location_of(glyph_id)); + let offset = try!(loca_table.location_of(head_table, glyph_id)); try!(reader.jump(offset as usize)); let number_of_contours = try!(reader.read_i16::().map_err(drop)); @@ -144,9 +149,10 @@ impl<'a> GlyfTable<'a> { Ok(()) } - pub fn bounding_rect(&self, loca_table: &LocaTable, glyph_id: u32) -> Result, ()> { + pub fn bounding_rect(&self, head_table: &HeadTable, loca_table: &LocaTable, glyph_id: u32) + -> Result, ()> { let mut reader = self.table.bytes; - let offset = try!(loca_table.location_of(glyph_id)); + let offset = try!(loca_table.location_of(head_table, glyph_id)); try!(reader.jump(offset as usize)); let number_of_contours = try!(reader.read_i16::().map_err(drop)); diff --git a/src/otf/loca.rs b/src/otf/loca.rs index 64f5c3e8..1da46500 100644 --- a/src/otf/loca.rs +++ b/src/otf/loca.rs @@ -15,31 +15,27 @@ use util::Jump; pub struct LocaTable<'a> { table: FontTable<'a>, - pub long: bool, } impl<'a> LocaTable<'a> { - pub fn new(loca_table: FontTable<'a>, head_table: &HeadTable) -> Result, ()> { - let long = match head_table.index_to_loc_format { - 0 => false, - 1 => true, - _ => return Err(()), - }; - + pub fn new(loca_table: FontTable<'a>) -> Result, ()> { Ok(LocaTable { table: loca_table, - long: long, }) } - pub fn location_of(&self, glyph_id: u32) -> Result { + pub fn location_of(&self, head_table: &HeadTable, glyph_id: u32) -> Result { let mut reader = self.table.bytes; - if !self.long { - try!(reader.jump(glyph_id as usize * 2)); - Ok(try!(reader.read_u16::().map_err(drop)) as u32 * 2) - } else { - try!(reader.jump(glyph_id as usize * 4)); - reader.read_u32::().map_err(drop) + match head_table.index_to_loc_format { + 0 => { + try!(reader.jump(glyph_id as usize * 2)); + Ok(try!(reader.read_u16::().map_err(drop)) as u32 * 2) + } + 1 => { + try!(reader.jump(glyph_id as usize * 4)); + reader.read_u32::().map_err(drop) + } + _ => Err(()), } } } diff --git a/src/otf/mod.rs b/src/otf/mod.rs index d3d14ed1..229b47ce 100644 --- a/src/otf/mod.rs +++ b/src/otf/mod.rs @@ -34,14 +34,24 @@ const HEAD: u32 = ((b'h' as u32) << 24) | ((b'e' as u32) << 16) | ((b'a' as u32) << 8) | (b'd' 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 LOCA: u32 = ((b'l' as u32) << 24) | ((b'o' as u32) << 16) | ((b'c' as u32) << 8) | (b'a' as u32); -#[derive(Clone, Copy, Debug)] -pub struct FontData<'a> { +pub struct Font<'a> { pub bytes: &'a [u8], + + pub cmap: CmapTable<'a>, + pub head: HeadTable, + pub hmtx: FontTable<'a>, + + pub glyf: Option>, + pub loca: Option>, } #[derive(Clone, Copy, Debug)] @@ -49,16 +59,11 @@ pub struct FontTable<'a> { pub bytes: &'a [u8], } -impl<'a> FontData<'a> { +impl<'a> Font<'a> { #[inline] - pub fn new<'b>(bytes: &'b [u8]) -> FontData<'b> { - FontData { - bytes: bytes, - } - } - - fn table(&self, table_id: u32) -> Result, ()> { - let mut reader = self.bytes; + pub fn new<'b>(bytes: &'b [u8]) -> Result, ()> { + // Read the tables we care about. + let mut reader = bytes; let sfnt_version = try!(reader.read_u32::().map_err(drop)); if sfnt_version != 0x10000 { return Err(()) @@ -67,58 +72,52 @@ impl<'a> FontData<'a> { let num_tables = try!(reader.read_u16::().map_err(drop)); try!(reader.jump(mem::size_of::() * 3)); - let (mut low, mut high) = (0, num_tables); - while low < high { - let mut reader = reader; - let mid = (low + high) / 2; - try!(reader.jump(mid as usize * mem::size_of::() * 4)); + let (mut cmap_table, mut head_table, mut hmtx_table) = (None, None, None); + let (mut glyf_table, mut loca_table) = (None, None); - let current_table_id = try!(reader.read_u32::().map_err(drop)); - if table_id < current_table_id { - high = mid; - continue - } - if table_id > current_table_id { - low = mid + 1; - continue - } + for _ in 0..num_tables { + let table_id = try!(reader.read_u32::().map_err(drop)); - // Skip the checksum, and slurp the offset and length. + // Skip over the checksum. try!(reader.read_u32::().map_err(drop)); + let offset = try!(reader.read_u32::().map_err(drop)) as usize; let length = try!(reader.read_u32::().map_err(drop)) as usize; - let end = offset + length; - if end > self.bytes.len() { + let mut slot = match table_id { + CMAP => &mut cmap_table, + HEAD => &mut head_table, + HMTX => &mut hmtx_table, + GLYF => &mut glyf_table, + LOCA => &mut loca_table, + _ => continue, + }; + + // Make sure there isn't more than one copy of the table. + if slot.is_some() { return Err(()) } - return Ok(Some(FontTable { - bytes: &self.bytes[offset..end], - })) + + *slot = Some(FontTable { + bytes: &bytes[offset..offset + length], + }) } - Ok(None) - } + let loca_table = match loca_table { + None => None, + Some(loca_table) => Some(try!(LocaTable::new(loca_table))), + }; - #[inline] - pub fn cmap_table(&self) -> Result { - self.table(CMAP).and_then(|table| table.ok_or(()).map(CmapTable::new)) - } + Ok(Font { + bytes: bytes, - #[inline] - pub fn glyf_table(&self) -> Result { - self.table(GLYF).and_then(|table| table.ok_or(()).map(GlyfTable::new)) - } + cmap: CmapTable::new(try!(cmap_table.ok_or(()))), + head: try!(HeadTable::new(try!(head_table.ok_or(())))), + hmtx: try!(hmtx_table.ok_or(())), - #[inline] - pub fn head_table(&self) -> Result { - self.table(HEAD).and_then(|table| table.ok_or(()).and_then(HeadTable::new)) - } - - #[inline] - pub fn loca_table(&self, head_table: &HeadTable) -> Result { - let loca_table = try!(self.table(LOCA).and_then(|table| table.ok_or(()))); - LocaTable::new(loca_table, head_table) + glyf: glyf_table.map(GlyfTable::new), + loca: loca_table, + }) } }