diff --git a/examples/benchmark.rs b/examples/benchmark.rs index 5d93c31f..9e420d9f 100644 --- a/examples/benchmark.rs +++ b/examples/benchmark.rs @@ -101,9 +101,9 @@ fn main() { println!("cpu,{}", time_per_glyph); let atlas_size = Size2D::new(ATLAS_SIZE, ATLAS_SIZE); - let coverage_buffer = CoverageBuffer::new(&rasterizer.device, &atlas_size).unwrap(); + let coverage_buffer = CoverageBuffer::new(rasterizer.device(), &atlas_size).unwrap(); - let image = rasterizer.device + let image = rasterizer.device() .create_image(Format::R8, buffer::Protection::WriteOnly, &atlas_size) .unwrap(); diff --git a/src/otf/kern.rs b/src/otf/kern.rs new file mode 100644 index 00000000..4744aa35 --- /dev/null +++ b/src/otf/kern.rs @@ -0,0 +1,97 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use byteorder::{BigEndian, ReadBytesExt}; +use otf::{Error, FontTable}; +use std::mem; +use util::Jump; + +bitflags! { + flags Coverage: u16 { + const HORIZONTAL = 1 << 0, + const MINIMUM = 1 << 1, + const CROSS_STREAM = 1 << 2, + const OVERRIDE = 1 << 3, + } +} + +#[derive(Clone, Copy)] +pub struct KernTable<'a> { + table: FontTable<'a>, + horizontal_table: &'a [u8], +} + +impl<'a> KernTable<'a> { + pub fn new(table: FontTable) -> Result { + let mut kern_reader = table.bytes; + let version = try!(kern_reader.read_u16::().map_err(Error::eof)); + if version != 0 { + return Err(Error::UnknownFormat) + } + + let n_tables = try!(kern_reader.read_u16::().map_err(Error::eof)); + let mut horizontal_table = None; + for table_index in 0..n_tables { + let mut table_reader = kern_reader; + let _version = try!(table_reader.read_u16::().map_err(Error::eof)); + let length = try!(table_reader.read_u16::().map_err(Error::eof)); + let coverage = try!(table_reader.read_u16::().map_err(Error::eof)); + let coverage_flags = Coverage::from_bits_truncate(coverage); + + if coverage_flags.contains(HORIZONTAL) && !coverage_flags.contains(MINIMUM) && + !coverage_flags.contains(CROSS_STREAM) && (coverage >> 8) == 0 { + let length = length as usize - mem::size_of::() * 3; + horizontal_table = Some(&table_reader[0..length]); + break + } + + try!(kern_reader.jump(length as usize).map_err(Error::eof)); + } + + match horizontal_table { + Some(horizontal_table) => { + Ok(KernTable { + table: table, + horizontal_table: horizontal_table, + }) + } + None => Err(Error::UnknownFormat), + } + } + + pub fn kerning_for_glyph_pair(&self, left_glyph_id: u16, right_glyph_id: u16) + -> Result { + let mut table_reader = self.horizontal_table; + let n_pairs = try!(table_reader.read_u16::().map_err(Error::eof)); + try!(table_reader.jump(mem::size_of::<[u16; 3]>()).map_err(Error::eof)); + + let (mut low, mut high) = (0, n_pairs as u32); + while low < high { + let mut reader = table_reader; + let mid = (low + high) / 2; + + try!(reader.jump(mid as usize * mem::size_of::<[u16; 3]>()).map_err(Error::eof)); + let left = try!(reader.read_u16::().map_err(Error::eof)); + let right = try!(reader.read_u16::().map_err(Error::eof)); + let value = try!(reader.read_i16::().map_err(Error::eof)); + + if left_glyph_id < left || (left_glyph_id == left && right_glyph_id < right) { + high = mid + } else if left_glyph_id > left || (left_glyph_id == left && right_glyph_id > right) { + low = mid + 1 + } else { + return Ok(value) + } + } + + Ok(0) + } +} + diff --git a/src/otf/mod.rs b/src/otf/mod.rs index 4894f66f..9fcdafdd 100644 --- a/src/otf/mod.rs +++ b/src/otf/mod.rs @@ -17,6 +17,7 @@ use otf::glyf::{GlyfTable, Point}; use otf::head::HeadTable; use otf::hhea::HheaTable; use otf::hmtx::{HmtxTable, HorizontalMetrics}; +use otf::kern::KernTable; use otf::loca::LocaTable; use outline::GlyphBounds; use std::mem; @@ -28,6 +29,7 @@ mod glyf; mod head; mod hhea; mod hmtx; +mod kern; mod loca; const CMAP: u32 = ((b'c' as u32) << 24) | @@ -50,6 +52,10 @@ 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) | @@ -87,6 +93,7 @@ pub struct Font<'a> { glyf: Option>, loca: Option>, + kern: Option>, } #[doc(hidden)] @@ -167,6 +174,7 @@ impl<'a> Font<'a> { let (mut cmap_table, mut head_table) = (None, None); let (mut hhea_table, mut hmtx_table) = (None, None); let (mut glyf_table, mut loca_table) = (None, None); + let mut kern_table = None; for _ in 0..num_tables { let table_id = try!(reader.read_u32::().map_err(Error::eof)); @@ -184,6 +192,7 @@ impl<'a> Font<'a> { HMTX => &mut hmtx_table, GLYF => &mut glyf_table, LOCA => &mut loca_table, + KERN => &mut kern_table, _ => continue, }; @@ -212,6 +221,7 @@ impl<'a> Font<'a> { glyf: glyf_table.map(GlyfTable::new), loca: loca_table, + kern: kern_table.and_then(|table| KernTable::new(table).ok()), }) } @@ -360,6 +370,19 @@ impl<'a> Font<'a> { pub fn metrics_for_glyph(&self, glyph_id: u16) -> Result { 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), + } + } } /// Errors that can occur when parsing OpenType fonts. diff --git a/src/shaper.rs b/src/shaper.rs index 9684a5a0..6174a709 100644 --- a/src/shaper.rs +++ b/src/shaper.rs @@ -24,20 +24,34 @@ use otf::Font; /// For proper operation, the given `glyph_mapping` must include all the glyphs necessary to render /// the string. pub fn shape_text(font: &Font, glyph_mapping: &GlyphMapping, string: &str) -> Vec { - string.chars().map(|ch| { - let glyph_id = glyph_mapping.glyph_for(ch as u32).unwrap_or(0); - let metrics = font.metrics_for_glyph(glyph_id); + let mut chars = string.chars().peekable(); + let mut next_glyph_id = None; + let mut result = vec![]; - let advance = match metrics { - Err(_) => 0, - Ok(metrics) => metrics.advance_width, + while let Some(ch) = chars.next() { + let glyph_id = match next_glyph_id.take() { + None => glyph_mapping.glyph_for(ch as u32).unwrap_or(0), + Some(next_glyph_id) => next_glyph_id, }; - GlyphPos { + let mut advance = match font.metrics_for_glyph(glyph_id) { + Err(_) => 0, + Ok(metrics) => metrics.advance_width as i16, + }; + + if let Some(&next_char) = chars.peek() { + let next_glyph = glyph_mapping.glyph_for(next_char as u32).unwrap_or(0); + next_glyph_id = Some(next_glyph); + advance += font.kerning_for_glyph_pair(glyph_id, next_glyph) + } + + result.push(GlyphPos { glyph_id: glyph_id, advance: advance, - } - }).collect() + }) + } + + result } /// The position of a glyph after shaping. @@ -46,6 +60,6 @@ pub struct GlyphPos { /// The glyph ID to emit. pub glyph_id: u16, /// The amount to move the cursor forward *after* emitting this glyph. - pub advance: u16, + pub advance: i16, } diff --git a/src/tests/buffers.rs b/src/tests/buffers.rs index 50ec62b9..9ffd58f1 100644 --- a/src/tests/buffers.rs +++ b/src/tests/buffers.rs @@ -15,13 +15,13 @@ fn bench_add_glyphs(bencher: &mut Bencher) { unsafe { let font = Font::new(file.as_slice()).unwrap(); let codepoint_ranges = [CodepointRange::new('!' as u32, '~' as u32)]; - let glyph_ranges = font.glyph_ranges_for_codepoint_ranges(&codepoint_ranges) + let glyph_mapping = font.glyph_mapping_for_codepoint_ranges(&codepoint_ranges) .expect("Couldn't find glyph ranges"); bencher.iter(|| { let mut outline_builder = OutlineBuilder::new(); - for glyph_id in glyph_ranges.iter() { - outline_builder.add_glyph(&font, glyph_id).unwrap() + for (_, glyph_id) in glyph_mapping.iter() { + outline_builder.add_glyph(&font, glyph_id).unwrap(); } }); } diff --git a/src/tests/rect_packer.rs b/src/tests/rect_packer.rs index 040ea675..54284da5 100644 --- a/src/tests/rect_packer.rs +++ b/src/tests/rect_packer.rs @@ -5,7 +5,8 @@ use rect_packer::RectPacker; use euclid::{Rect, Size2D}; use std::cmp; -fn pack_objects(available_width: u32, objects: Vec<(u32, u32)>) -> (RectPacker, Vec>) { +fn pack_objects(available_width: u32, objects: Vec<(u32, u32)>) + -> (RectPacker, Vec>, u32) { let objects: Vec<_> = objects.iter() .map(|&(width, height)| Size2D::new(width, height)) .collect(); @@ -18,12 +19,12 @@ fn pack_objects(available_width: u32, objects: Vec<(u32, u32)>) -> (RectPacker, let rects = objects.iter() .map(|object| Rect::new(rect_packer.pack(object).unwrap(), *object)) .collect(); - (rect_packer, rects) + (rect_packer, rects, available_width) } quickcheck! { fn objects_dont_overlap(available_width: u32, objects: Vec<(u32, u32)>) -> bool { - let (_, rects) = pack_objects(available_width, objects); + let (_, rects, _) = pack_objects(available_width, objects); for (i, a) in rects.iter().enumerate() { for b in &rects[(i + 1)..] { assert!(!a.intersects(b)) @@ -33,12 +34,12 @@ quickcheck! { } fn objects_dont_exceed_available_width(available_width: u32, objects: Vec<(u32, u32)>) -> bool { - let (rect_packer, rects) = pack_objects(available_width, objects); - rects.iter().all(|rect| rect.max_x() <= rect_packer.available_width()) + let (_, rects, available_width) = pack_objects(available_width, objects); + rects.iter().all(|rect| rect.max_x() <= available_width) } fn objects_dont_cross_shelves(available_width: u32, objects: Vec<(u32, u32)>) -> bool { - let (rect_packer, rects) = pack_objects(available_width, objects); + let (rect_packer, rects, _) = pack_objects(available_width, objects); rects.iter().all(|rect| { let shelf_height = rect_packer.shelf_height(); rect.is_empty() || rect.origin.y / shelf_height == (rect.max_y() - 1) / shelf_height