Don't require users of the library to load OTF tables individually
This commit is contained in:
parent
30daf10cdd
commit
1deaf9136e
|
@ -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 {
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue