Don't require users of the library to load OTF tables individually

This commit is contained in:
Patrick Walton 2017-01-26 10:27:38 -08:00
parent 30daf10cdd
commit 1deaf9136e
6 changed files with 90 additions and 98 deletions

View File

@ -9,20 +9,16 @@ use euclid::Point2D;
use memmap::{Mmap, Protection}; use memmap::{Mmap, Protection};
use pathfinder::batch::GlyphRange; use pathfinder::batch::GlyphRange;
use pathfinder::charmap::CodepointRange; use pathfinder::charmap::CodepointRange;
use pathfinder::otf::FontData; use pathfinder::otf::Font;
use std::char; use std::char;
use std::env; use std::env;
fn main() { fn main() {
let file = Mmap::open_path(env::args().nth(1).unwrap(), Protection::Read).unwrap(); let file = Mmap::open_path(env::args().nth(1).unwrap(), Protection::Read).unwrap();
unsafe { unsafe {
let font = FontData::new(file.as_slice()); let font = Font::new(file.as_slice()).unwrap();
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 codepoint_ranges = [CodepointRange::new('!' as u32, '~' as u32)]; 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 for (codepoint, glyph_id) in
codepoint_ranges.iter() codepoint_ranges.iter()
.flat_map(CodepointRange::iter) .flat_map(CodepointRange::iter)
@ -34,7 +30,10 @@ fn main() {
let mut last_point: Option<Point2D<i16>> = None; let mut last_point: Option<Point2D<i16>> = None;
let mut last_point_was_off_curve = false; 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 { if point.first_point_in_contour {
println!("M {},{}", point.position.x, point.position.y); println!("M {},{}", point.position.x, point.position.y);
} else { } else {

View File

@ -20,7 +20,7 @@ use pathfinder::batch::{BatchBuilder, GlyphRange};
use pathfinder::charmap::CodepointRange; use pathfinder::charmap::CodepointRange;
use pathfinder::coverage::CoverageBuffer; use pathfinder::coverage::CoverageBuffer;
use pathfinder::glyph_buffer::GlyphBufferBuilder; use pathfinder::glyph_buffer::GlyphBufferBuilder;
use pathfinder::otf::FontData; 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;
@ -54,16 +54,12 @@ fn main() {
let file = Mmap::open_path(env::args().nth(1).unwrap(), Protection::Read).unwrap(); let file = Mmap::open_path(env::args().nth(1).unwrap(), Protection::Read).unwrap();
unsafe { unsafe {
let font = FontData::new(file.as_slice()); let font = Font::new(file.as_slice()).unwrap();
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 codepoint_ranges = [CodepointRange::new('!' as u32, '~' as u32)]; 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() { 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() batch_builder.add_glyph(&glyph_buffer_builder, glyph_index as u32, POINT_SIZE).unwrap()
} }
} }

View File

@ -11,9 +11,7 @@
use euclid::{Point2D, Rect, Size2D}; use euclid::{Point2D, Rect, Size2D};
use gl::types::{GLsizeiptr, GLuint}; use gl::types::{GLsizeiptr, GLuint};
use gl; use gl;
use otf::glyf::GlyfTable; use otf::Font;
use otf::head::HeadTable;
use otf::loca::LocaTable;
use std::mem; use std::mem;
use std::os::raw::c_void; use std::os::raw::c_void;
@ -39,12 +37,7 @@ impl GlyphBufferBuilder {
} }
} }
pub fn add_glyph(&mut self, pub fn add_glyph(&mut self, font: &Font, glyph_id: u32) -> Result<(), ()> {
glyph_id: u32,
head_table: &HeadTable,
loca_table: &LocaTable,
glyf_table: &GlyfTable)
-> Result<(), ()> {
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;
@ -52,7 +45,10 @@ impl GlyphBufferBuilder {
let start_point = point_index; let start_point = point_index;
let mut last_point_on_curve = true; 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 { self.vertices.push(Vertex {
x: point.position.x, x: point.position.x,
y: point.position.y, y: point.position.y,
@ -73,13 +69,13 @@ impl GlyphBufferBuilder {
})); }));
// Add a glyph descriptor. // 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 { self.descriptors.push(GlyphDescriptor {
left: bounding_rect.origin.x as i32, left: bounding_rect.origin.x as i32,
bottom: bounding_rect.origin.y as i32, bottom: bounding_rect.origin.y as i32,
right: bounding_rect.max_x() as i32, right: bounding_rect.max_x() as i32,
top: bounding_rect.max_y() 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_point: start_point as u32,
start_index: start_index, start_index: start_index,
pad: 0, pad: 0,

View File

@ -11,6 +11,7 @@
use byteorder::{BigEndian, ReadBytesExt}; use byteorder::{BigEndian, ReadBytesExt};
use euclid::{Point2D, Rect, Size2D}; use euclid::{Point2D, Rect, Size2D};
use otf::FontTable; use otf::FontTable;
use otf::head::HeadTable;
use otf::loca::LocaTable; use otf::loca::LocaTable;
use std::mem; use std::mem;
use util::Jump; use util::Jump;
@ -47,10 +48,14 @@ impl<'a> GlyfTable<'a> {
} }
} }
pub fn for_each_point<F>(&self, loca_table: &LocaTable, glyph_id: u32, mut callback: F) pub fn for_each_point<F>(&self,
head_table: &HeadTable,
loca_table: &LocaTable,
glyph_id: u32,
mut callback: F)
-> Result<(), ()> where F: FnMut(&Point) { -> Result<(), ()> where F: FnMut(&Point) {
let mut reader = self.table.bytes; 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)); try!(reader.jump(offset as usize));
let number_of_contours = try!(reader.read_i16::<BigEndian>().map_err(drop)); let number_of_contours = try!(reader.read_i16::<BigEndian>().map_err(drop));
@ -144,9 +149,10 @@ impl<'a> GlyfTable<'a> {
Ok(()) Ok(())
} }
pub fn bounding_rect(&self, loca_table: &LocaTable, glyph_id: u32) -> Result<Rect<i16>, ()> { pub fn bounding_rect(&self, head_table: &HeadTable, loca_table: &LocaTable, glyph_id: u32)
-> Result<Rect<i16>, ()> {
let mut reader = self.table.bytes; 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)); try!(reader.jump(offset as usize));
let number_of_contours = try!(reader.read_i16::<BigEndian>().map_err(drop)); let number_of_contours = try!(reader.read_i16::<BigEndian>().map_err(drop));

View File

@ -15,31 +15,27 @@ use util::Jump;
pub struct LocaTable<'a> { pub struct LocaTable<'a> {
table: FontTable<'a>, table: FontTable<'a>,
pub long: bool,
} }
impl<'a> LocaTable<'a> { impl<'a> LocaTable<'a> {
pub fn new(loca_table: FontTable<'a>, head_table: &HeadTable) -> Result<LocaTable<'a>, ()> { pub fn new(loca_table: FontTable<'a>) -> Result<LocaTable<'a>, ()> {
let long = match head_table.index_to_loc_format {
0 => false,
1 => true,
_ => return Err(()),
};
Ok(LocaTable { Ok(LocaTable {
table: loca_table, table: loca_table,
long: long,
}) })
} }
pub fn location_of(&self, glyph_id: u32) -> Result<u32, ()> { pub fn location_of(&self, head_table: &HeadTable, glyph_id: u32) -> Result<u32, ()> {
let mut reader = self.table.bytes; let mut reader = self.table.bytes;
if !self.long { match head_table.index_to_loc_format {
try!(reader.jump(glyph_id as usize * 2)); 0 => {
Ok(try!(reader.read_u16::<BigEndian>().map_err(drop)) as u32 * 2) try!(reader.jump(glyph_id as usize * 2));
} else { Ok(try!(reader.read_u16::<BigEndian>().map_err(drop)) as u32 * 2)
try!(reader.jump(glyph_id as usize * 4)); }
reader.read_u32::<BigEndian>().map_err(drop) 1 => {
try!(reader.jump(glyph_id as usize * 4));
reader.read_u32::<BigEndian>().map_err(drop)
}
_ => Err(()),
} }
} }
} }

View File

@ -34,14 +34,24 @@ const HEAD: u32 = ((b'h' as u32) << 24) |
((b'e' as u32) << 16) | ((b'e' as u32) << 16) |
((b'a' as u32) << 8) | ((b'a' as u32) << 8) |
(b'd' as u32); (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) | const LOCA: u32 = ((b'l' as u32) << 24) |
((b'o' as u32) << 16) | ((b'o' as u32) << 16) |
((b'c' as u32) << 8) | ((b'c' as u32) << 8) |
(b'a' as u32); (b'a' as u32);
#[derive(Clone, Copy, Debug)] pub struct Font<'a> {
pub struct FontData<'a> {
pub bytes: &'a [u8], pub bytes: &'a [u8],
pub cmap: CmapTable<'a>,
pub head: HeadTable,
pub hmtx: FontTable<'a>,
pub glyf: Option<GlyfTable<'a>>,
pub loca: Option<LocaTable<'a>>,
} }
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
@ -49,16 +59,11 @@ pub struct FontTable<'a> {
pub bytes: &'a [u8], pub bytes: &'a [u8],
} }
impl<'a> FontData<'a> { impl<'a> Font<'a> {
#[inline] #[inline]
pub fn new<'b>(bytes: &'b [u8]) -> FontData<'b> { pub fn new<'b>(bytes: &'b [u8]) -> Result<Font<'b>, ()> {
FontData { // Read the tables we care about.
bytes: bytes, let mut reader = bytes;
}
}
fn table(&self, table_id: u32) -> Result<Option<FontTable>, ()> {
let mut reader = self.bytes;
let sfnt_version = try!(reader.read_u32::<BigEndian>().map_err(drop)); let sfnt_version = try!(reader.read_u32::<BigEndian>().map_err(drop));
if sfnt_version != 0x10000 { if sfnt_version != 0x10000 {
return Err(()) return Err(())
@ -67,58 +72,52 @@ impl<'a> FontData<'a> {
let num_tables = try!(reader.read_u16::<BigEndian>().map_err(drop)); let num_tables = try!(reader.read_u16::<BigEndian>().map_err(drop));
try!(reader.jump(mem::size_of::<u16>() * 3)); try!(reader.jump(mem::size_of::<u16>() * 3));
let (mut low, mut high) = (0, num_tables); let (mut cmap_table, mut head_table, mut hmtx_table) = (None, None, None);
while low < high { let (mut glyf_table, mut loca_table) = (None, None);
let mut reader = reader;
let mid = (low + high) / 2;
try!(reader.jump(mid as usize * mem::size_of::<u32>() * 4));
let current_table_id = try!(reader.read_u32::<BigEndian>().map_err(drop)); for _ in 0..num_tables {
if table_id < current_table_id { let table_id = try!(reader.read_u32::<BigEndian>().map_err(drop));
high = mid;
continue
}
if table_id > current_table_id {
low = mid + 1;
continue
}
// Skip the checksum, and slurp the offset and length. // Skip over the checksum.
try!(reader.read_u32::<BigEndian>().map_err(drop)); try!(reader.read_u32::<BigEndian>().map_err(drop));
let offset = try!(reader.read_u32::<BigEndian>().map_err(drop)) as usize; let offset = try!(reader.read_u32::<BigEndian>().map_err(drop)) as usize;
let length = try!(reader.read_u32::<BigEndian>().map_err(drop)) as usize; let length = try!(reader.read_u32::<BigEndian>().map_err(drop)) as usize;
let end = offset + length; let mut slot = match table_id {
if end > self.bytes.len() { 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 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] Ok(Font {
pub fn cmap_table(&self) -> Result<CmapTable, ()> { bytes: bytes,
self.table(CMAP).and_then(|table| table.ok_or(()).map(CmapTable::new))
}
#[inline] cmap: CmapTable::new(try!(cmap_table.ok_or(()))),
pub fn glyf_table(&self) -> Result<GlyfTable, ()> { head: try!(HeadTable::new(try!(head_table.ok_or(())))),
self.table(GLYF).and_then(|table| table.ok_or(()).map(GlyfTable::new)) hmtx: try!(hmtx_table.ok_or(())),
}
#[inline] glyf: glyf_table.map(GlyfTable::new),
pub fn head_table(&self) -> Result<HeadTable, ()> { loca: loca_table,
self.table(HEAD).and_then(|table| table.ok_or(()).and_then(HeadTable::new)) })
}
#[inline]
pub fn loca_table(&self, head_table: &HeadTable) -> Result<LocaTable, ()> {
let loca_table = try!(self.table(LOCA).and_then(|table| table.ok_or(())));
LocaTable::new(loca_table, head_table)
} }
} }