Initial commit
This commit is contained in:
commit
34cd1ef9b0
|
@ -0,0 +1,11 @@
|
|||
[package]
|
||||
name = "pathfinder"
|
||||
version = "0.1.0"
|
||||
authors = ["Patrick Walton <pcwalton@mimiga.net>"]
|
||||
|
||||
[dependencies]
|
||||
bitflags = "0.7"
|
||||
byteorder = "1"
|
||||
euclid = "0.10"
|
||||
memmap = "0.5"
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
extern crate memmap;
|
||||
extern crate pathfinder;
|
||||
|
||||
use memmap::{Mmap, Protection};
|
||||
use pathfinder::batch::{CodepointBatch, CodepointRange, GlyphBatch};
|
||||
use pathfinder::otf::FontData;
|
||||
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 mut glyph_batch = GlyphBatch::new();
|
||||
glyph_batch.find_glyph_ranges_for_codepoint_ranges(&CodepointBatch {
|
||||
ranges: vec![CodepointRange::new('A' as u32, 'Z' as u32, 0)],
|
||||
fonts: vec![font],
|
||||
}).unwrap();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
// 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.
|
||||
|
||||
use otf::FontData;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct CodepointRange {
|
||||
pub start: u32,
|
||||
pub end: u32,
|
||||
pub font_index: u32,
|
||||
}
|
||||
|
||||
impl CodepointRange {
|
||||
#[inline]
|
||||
pub fn new(start: u32, end: u32, font_index: u32) -> CodepointRange {
|
||||
CodepointRange {
|
||||
start: start,
|
||||
end: end,
|
||||
font_index: font_index,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct GlyphRange {
|
||||
pub start: u32,
|
||||
pub end: u32,
|
||||
pub font_index: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CodepointBatch<'a> {
|
||||
pub ranges: Vec<CodepointRange>,
|
||||
pub fonts: Vec<FontData<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GlyphBatch<'a> {
|
||||
pub ranges: Vec<GlyphRange>,
|
||||
pub fonts: Vec<FontData<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> GlyphBatch<'a> {
|
||||
pub fn new<'b>() -> GlyphBatch<'b> {
|
||||
GlyphBatch {
|
||||
ranges: vec![],
|
||||
fonts: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
// 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.
|
||||
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
extern crate byteorder;
|
||||
extern crate euclid;
|
||||
|
||||
pub mod batch;
|
||||
pub mod otf;
|
||||
mod util;
|
||||
|
|
@ -0,0 +1,296 @@
|
|||
// 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.
|
||||
|
||||
use batch::{CodepointBatch, GlyphBatch};
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
use euclid::Point2D;
|
||||
use std::mem;
|
||||
use util::Jump;
|
||||
|
||||
const CMAP: u32 = ((b'c' as u32) << 24)|((b'm' as u32) << 16)|((b'a' as u32) << 8)|(b'p' as u32);
|
||||
|
||||
bitflags! {
|
||||
flags Flags: u8 {
|
||||
const ON_CURVE = 1 << 0,
|
||||
const X_SHORT_VECTOR = 1 << 1,
|
||||
const Y_SHORT_VECTOR = 1 << 2,
|
||||
const REPEAT = 1 << 3,
|
||||
const THIS_X_IS_SAME = 1 << 4,
|
||||
const THIS_Y_IS_SAME = 1 << 5,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
pub struct Point {
|
||||
position: Point2D<i16>,
|
||||
on_curve: bool,
|
||||
first_point_in_contour: bool,
|
||||
}
|
||||
|
||||
pub fn apply_glyph<F>(glyf_table: &[u8], mut applier: F) -> Result<(), ()> where F: FnMut(&Point) {
|
||||
let mut reader = glyf_table;
|
||||
|
||||
let number_of_contours = try!(reader.read_i16::<BigEndian>().map_err(drop));
|
||||
if number_of_contours < 0 {
|
||||
// TODO(pcwalton): Composite glyphs.
|
||||
return Err(())
|
||||
}
|
||||
try!(reader.jump(mem::size_of::<u16>() * 4));
|
||||
|
||||
// Find out how many points we have.
|
||||
let mut endpoints_reader = reader;
|
||||
try!(reader.jump(mem::size_of::<u16>() as usize * (number_of_contours as usize - 1)));
|
||||
let number_of_points = try!(reader.read_u16::<BigEndian>().map_err(drop));
|
||||
|
||||
// Skip over hinting instructions.
|
||||
let instruction_length = try!(reader.read_u16::<BigEndian>().map_err(drop));
|
||||
try!(reader.jump(instruction_length as usize));
|
||||
|
||||
// Find the offsets of the X and Y coordinates.
|
||||
let flags_reader = reader.clone();
|
||||
let x_coordinate_length = try!(calculate_size_of_x_coordinates(&mut reader, number_of_points));
|
||||
|
||||
// Set up the streams.
|
||||
let mut flag_parser = try!(FlagParser::new(flags_reader));
|
||||
let mut x_coordinate_reader = reader.clone();
|
||||
try!(reader.jump(x_coordinate_length as usize));
|
||||
let mut y_coordinate_reader = reader;
|
||||
|
||||
// Now parse the contours.
|
||||
let (mut position, mut point_index) = (Point2D::new(0, 0), 0);
|
||||
for _ in 0..number_of_contours {
|
||||
let contour_point_count = try!(endpoints_reader.read_u16::<BigEndian>().map_err(drop)) -
|
||||
point_index + 1;
|
||||
let (mut starting_point, mut last_point_was_off_curve) = (Point2D::new(0, 0), false);
|
||||
for contour_point_index in 0..contour_point_count {
|
||||
let flags = Flags::from_bits_truncate(*flag_parser.current);
|
||||
try!(flag_parser.next());
|
||||
|
||||
let mut delta = Point2D::new(0, 0);
|
||||
if flags.contains(X_SHORT_VECTOR) {
|
||||
delta.x = try!(x_coordinate_reader.read_u8().map_err(drop)) as i16;
|
||||
if !flags.contains(THIS_X_IS_SAME) {
|
||||
delta.x = -delta.x
|
||||
}
|
||||
} else if !flags.contains(THIS_X_IS_SAME) {
|
||||
delta.x = try!(x_coordinate_reader.read_i16::<BigEndian>().map_err(drop))
|
||||
}
|
||||
if flags.contains(Y_SHORT_VECTOR) {
|
||||
delta.y = try!(y_coordinate_reader.read_u8().map_err(drop)) as i16;
|
||||
if !flags.contains(THIS_Y_IS_SAME) {
|
||||
delta.y = -delta.y
|
||||
}
|
||||
} else if !flags.contains(THIS_Y_IS_SAME) {
|
||||
delta.y = try!(y_coordinate_reader.read_i16::<BigEndian>().map_err(drop))
|
||||
}
|
||||
|
||||
if last_point_was_off_curve && flags.contains(ON_CURVE) {
|
||||
applier(&Point {
|
||||
position: position + delta / 2,
|
||||
on_curve: true,
|
||||
first_point_in_contour: false,
|
||||
})
|
||||
}
|
||||
|
||||
position = position + delta;
|
||||
|
||||
let first_point_in_contour = contour_point_index == 0;
|
||||
if first_point_in_contour {
|
||||
starting_point = position
|
||||
}
|
||||
|
||||
applier(&Point {
|
||||
position: position,
|
||||
on_curve: flags.contains(ON_CURVE),
|
||||
first_point_in_contour: first_point_in_contour,
|
||||
});
|
||||
|
||||
last_point_was_off_curve = !flags.contains(ON_CURVE);
|
||||
point_index += 1
|
||||
}
|
||||
|
||||
// Close the path.
|
||||
applier(&Point {
|
||||
position: starting_point,
|
||||
on_curve: true,
|
||||
first_point_in_contour: false,
|
||||
})
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Given a reader pointing to the start of the list of flags, returns the size in bytes of the list
|
||||
// of X coordinates and positions the reader at the start of that list.
|
||||
#[inline]
|
||||
fn calculate_size_of_x_coordinates<'a, 'b>(reader: &'a mut &'b [u8], number_of_points: u16)
|
||||
-> Result<u16, ()> {
|
||||
let (mut x_coordinate_length, mut points_left) = (0, number_of_points);
|
||||
while points_left > 0 {
|
||||
let flags = Flags::from_bits_truncate(try!(reader.read_u8().map_err(drop)));
|
||||
let repeat_count = if !flags.contains(REPEAT) {
|
||||
1
|
||||
} else {
|
||||
try!(reader.read_u8().map_err(drop)) as u16
|
||||
};
|
||||
|
||||
if flags.contains(X_SHORT_VECTOR) {
|
||||
x_coordinate_length += repeat_count
|
||||
} else if !flags.contains(THIS_X_IS_SAME) {
|
||||
x_coordinate_length += repeat_count * 2
|
||||
}
|
||||
|
||||
points_left -= repeat_count
|
||||
}
|
||||
|
||||
Ok(x_coordinate_length)
|
||||
}
|
||||
|
||||
struct FlagParser<'a> {
|
||||
next: &'a [u8],
|
||||
current: &'a u8,
|
||||
repeats_left: u8,
|
||||
}
|
||||
|
||||
impl<'a> FlagParser<'a> {
|
||||
#[inline]
|
||||
fn new(buffer: &[u8]) -> Result<FlagParser, ()> {
|
||||
let mut parser = FlagParser {
|
||||
next: buffer,
|
||||
current: &buffer[0],
|
||||
repeats_left: 0,
|
||||
};
|
||||
try!(parser.next());
|
||||
Ok(parser)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Result<(), ()> {
|
||||
if self.repeats_left > 0 {
|
||||
self.repeats_left -= 1;
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
self.current = try!(self.next.get(0).ok_or(()));
|
||||
let flags = Flags::from_bits_truncate(*self.current);
|
||||
self.next = &self.next[1..];
|
||||
|
||||
if flags.contains(REPEAT) {
|
||||
self.repeats_left = *try!(self.next.get(0).ok_or(()));
|
||||
self.next = &self.next[1..];
|
||||
} else {
|
||||
self.repeats_left = 0
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> GlyphBatch<'a> {
|
||||
pub fn find_glyph_ranges_for_codepoint_ranges(&mut self, codepoint_batch: &CodepointBatch<'a>)
|
||||
-> Result<(), ()> {
|
||||
let mut cached_cmap: Option<CachedCmap> = None;
|
||||
for codepoint_range in &codepoint_batch.ranges {
|
||||
let font_index = codepoint_range.font_index;
|
||||
|
||||
match cached_cmap {
|
||||
Some(ref cached_cmap) if cached_cmap.font_index == font_index => {}
|
||||
_ => {
|
||||
let cmap_table = try!(codepoint_batch.fonts[font_index as usize].get_table(CMAP));
|
||||
let cmap_table = try!(cmap_table.ok_or(()));
|
||||
cached_cmap = Some(CachedCmap {
|
||||
font_index: font_index,
|
||||
cmap_table: cmap_table,
|
||||
encoding_record: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
let cached_cmap = cached_cmap.as_mut().unwrap();
|
||||
let mut cmap_table = cached_cmap.cmap_table;
|
||||
|
||||
// Check version.
|
||||
if try!(cmap_table.read_u16::<BigEndian>().map_err(drop)) != 0 {
|
||||
return Err(())
|
||||
}
|
||||
|
||||
let num_tables = try!(cmap_table.read_u16::<BigEndian>().map_err(drop));
|
||||
|
||||
let platform_id = try!(cmap_table.read_u16::<BigEndian>().map_err(drop));
|
||||
let encoding_id = try!(cmap_table.read_u16::<BigEndian>().map_err(drop));
|
||||
println!("platform_id={} encoding_id={}", platform_id, encoding_id);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct CachedCmap<'a> {
|
||||
font_index: u32,
|
||||
cmap_table: &'a [u8],
|
||||
encoding_record: Option<&'a [u8]>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct FontData<'a> {
|
||||
pub bytes: &'a [u8],
|
||||
}
|
||||
|
||||
impl<'a> FontData<'a> {
|
||||
#[inline]
|
||||
pub fn new<'b>(bytes: &'b [u8]) -> FontData<'b> {
|
||||
FontData {
|
||||
bytes: bytes,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_table(&self, table_id: u32) -> Result<Option<&[u8]>, ()> {
|
||||
let mut reader = self.bytes;
|
||||
let sfnt_version = try!(reader.read_u32::<BigEndian>().map_err(drop));
|
||||
if sfnt_version != 0x10000 {
|
||||
return Err(())
|
||||
}
|
||||
|
||||
let num_tables = try!(reader.read_u16::<BigEndian>().map_err(drop));
|
||||
try!(reader.jump(mem::size_of::<u16>() * 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::<u32>() * 4));
|
||||
|
||||
let current_table_id = try!(reader.read_u32::<BigEndian>().map_err(drop));
|
||||
if table_id < current_table_id {
|
||||
high = mid;
|
||||
continue
|
||||
}
|
||||
if table_id > current_table_id {
|
||||
low = mid + 1;
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip the checksum, and slurp the offset and length.
|
||||
try!(reader.read_u32::<BigEndian>().map_err(drop));
|
||||
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 end = offset + length;
|
||||
if end > self.bytes.len() {
|
||||
return Err(())
|
||||
}
|
||||
return Ok(Some(&self.bytes[offset..end]))
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
// 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.
|
||||
|
||||
/// A faster version of `Seek` that supports only forward motion from the current position.
|
||||
pub trait Jump {
|
||||
/// Moves the pointer forward `n` bytes from the *current* position.
|
||||
fn jump(&mut self, n: usize) -> Result<(), ()>;
|
||||
}
|
||||
|
||||
impl<'a> Jump for &'a [u8] {
|
||||
#[inline]
|
||||
fn jump(&mut self, n: usize) -> Result<(), ()> {
|
||||
if n <= self.len() {
|
||||
*self = &(*self)[n..];
|
||||
Ok(())
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue