aboutsummaryrefslogtreecommitdiffstats
path: root/components/gfx/text/glyph.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/gfx/text/glyph.rs')
-rw-r--r--components/gfx/text/glyph.rs752
1 files changed, 752 insertions, 0 deletions
diff --git a/components/gfx/text/glyph.rs b/components/gfx/text/glyph.rs
new file mode 100644
index 00000000000..2ea2d7c5d2e
--- /dev/null
+++ b/components/gfx/text/glyph.rs
@@ -0,0 +1,752 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use servo_util::vec::*;
+use servo_util::range;
+use servo_util::range::{Range, RangeIndex, IntRangeIndex, EachIndex};
+use servo_util::geometry::Au;
+
+use std::cmp::{PartialOrd, PartialEq};
+use std::num::{NumCast, Zero};
+use std::mem;
+use std::u16;
+use std::vec::Vec;
+use geom::point::Point2D;
+
+/// GlyphEntry is a port of Gecko's CompressedGlyph scheme for storing glyph data compactly.
+///
+/// In the common case (reasonable glyph advances, no offsets from the font em-box, and one glyph
+/// per character), we pack glyph advance, glyph id, and some flags into a single u32.
+///
+/// In the uncommon case (multiple glyphs per unicode character, large glyph index/advance, or
+/// glyph offsets), we pack the glyph count into GlyphEntry, and store the other glyph information
+/// in DetailedGlyphStore.
+#[deriving(Clone)]
+struct GlyphEntry {
+ value: u32,
+}
+
+impl GlyphEntry {
+ fn new(value: u32) -> GlyphEntry {
+ GlyphEntry {
+ value: value,
+ }
+ }
+
+ fn initial() -> GlyphEntry {
+ GlyphEntry::new(0)
+ }
+
+ // Creates a GlyphEntry for the common case
+ fn simple(id: GlyphId, advance: Au) -> GlyphEntry {
+ assert!(is_simple_glyph_id(id));
+ assert!(is_simple_advance(advance));
+
+ let id_mask = id as u32;
+ let Au(advance) = advance;
+ let advance_mask = (advance as u32) << GLYPH_ADVANCE_SHIFT as uint;
+
+ GlyphEntry::new(id_mask | advance_mask | FLAG_IS_SIMPLE_GLYPH)
+ }
+
+ // Create a GlyphEntry for uncommon case; should be accompanied by
+ // initialization of the actual DetailedGlyph data in DetailedGlyphStore
+ fn complex(starts_cluster: bool, starts_ligature: bool, glyph_count: int) -> GlyphEntry {
+ assert!(glyph_count <= u16::MAX as int);
+
+ debug!("creating complex glyph entry: starts_cluster={}, starts_ligature={}, \
+ glyph_count={}",
+ starts_cluster,
+ starts_ligature,
+ glyph_count);
+
+ let mut val = FLAG_NOT_MISSING;
+
+ if !starts_cluster {
+ val |= FLAG_NOT_CLUSTER_START;
+ }
+ if !starts_ligature {
+ val |= FLAG_NOT_LIGATURE_GROUP_START;
+ }
+ val |= (glyph_count as u32) << GLYPH_COUNT_SHIFT as uint;
+
+ GlyphEntry::new(val)
+ }
+
+ /// Create a GlyphEntry for the case where glyphs couldn't be found for the specified
+ /// character.
+ fn missing(glyph_count: int) -> GlyphEntry {
+ assert!(glyph_count <= u16::MAX as int);
+
+ GlyphEntry::new((glyph_count as u32) << GLYPH_COUNT_SHIFT as uint)
+ }
+}
+
+/// The id of a particular glyph within a font
+pub type GlyphId = u32;
+
+// TODO: unify with bit flags?
+#[deriving(PartialEq)]
+pub enum BreakType {
+ BreakTypeNone,
+ BreakTypeNormal,
+ BreakTypeHyphen,
+}
+
+static BREAK_TYPE_NONE: u8 = 0x0;
+static BREAK_TYPE_NORMAL: u8 = 0x1;
+static BREAK_TYPE_HYPHEN: u8 = 0x2;
+
+fn break_flag_to_enum(flag: u8) -> BreakType {
+ if (flag & BREAK_TYPE_NORMAL) != 0 {
+ BreakTypeNormal
+ } else if (flag & BREAK_TYPE_HYPHEN) != 0 {
+ BreakTypeHyphen
+ } else {
+ BreakTypeNone
+ }
+}
+
+fn break_enum_to_flag(e: BreakType) -> u8 {
+ match e {
+ BreakTypeNone => BREAK_TYPE_NONE,
+ BreakTypeNormal => BREAK_TYPE_NORMAL,
+ BreakTypeHyphen => BREAK_TYPE_HYPHEN,
+ }
+}
+
+// TODO: make this more type-safe.
+
+static FLAG_CHAR_IS_SPACE: u32 = 0x10000000;
+// These two bits store some BREAK_TYPE_* flags
+static FLAG_CAN_BREAK_MASK: u32 = 0x60000000;
+static FLAG_CAN_BREAK_SHIFT: u32 = 29;
+static FLAG_IS_SIMPLE_GLYPH: u32 = 0x80000000;
+
+// glyph advance; in Au's.
+static GLYPH_ADVANCE_MASK: u32 = 0x0FFF0000;
+static GLYPH_ADVANCE_SHIFT: u32 = 16;
+static GLYPH_ID_MASK: u32 = 0x0000FFFF;
+
+// Non-simple glyphs (more than one glyph per char; missing glyph,
+// newline, tab, large advance, or nonzero x/y offsets) may have one
+// or more detailed glyphs associated with them. They are stored in a
+// side array so that there is a 1:1 mapping of GlyphEntry to
+// unicode char.
+
+// The number of detailed glyphs for this char. If the char couldn't
+// be mapped to a glyph (!FLAG_NOT_MISSING), then this actually holds
+// the UTF8 code point instead.
+static GLYPH_COUNT_MASK: u32 = 0x00FFFF00;
+static GLYPH_COUNT_SHIFT: u32 = 8;
+// N.B. following Gecko, these are all inverted so that a lot of
+// missing chars can be memset with zeros in one fell swoop.
+static FLAG_NOT_MISSING: u32 = 0x00000001;
+static FLAG_NOT_CLUSTER_START: u32 = 0x00000002;
+static FLAG_NOT_LIGATURE_GROUP_START: u32 = 0x00000004;
+
+static FLAG_CHAR_IS_TAB: u32 = 0x00000008;
+static FLAG_CHAR_IS_NEWLINE: u32 = 0x00000010;
+//static FLAG_CHAR_IS_LOW_SURROGATE: u32 = 0x00000020;
+//static CHAR_IDENTITY_FLAGS_MASK: u32 = 0x00000038;
+
+fn is_simple_glyph_id(id: GlyphId) -> bool {
+ ((id as u32) & GLYPH_ID_MASK) == id
+}
+
+fn is_simple_advance(advance: Au) -> bool {
+ let unsignedAu = advance.to_u32().unwrap();
+ (unsignedAu & (GLYPH_ADVANCE_MASK >> GLYPH_ADVANCE_SHIFT as uint)) == unsignedAu
+}
+
+type DetailedGlyphCount = u16;
+
+// Getters and setters for GlyphEntry. Setter methods are functional,
+// because GlyphEntry is immutable and only a u32 in size.
+impl GlyphEntry {
+ // getter methods
+ #[inline(always)]
+ fn advance(&self) -> Au {
+ NumCast::from((self.value & GLYPH_ADVANCE_MASK) >> GLYPH_ADVANCE_SHIFT as uint).unwrap()
+ }
+
+ fn id(&self) -> GlyphId {
+ self.value & GLYPH_ID_MASK
+ }
+
+ fn is_ligature_start(&self) -> bool {
+ self.has_flag(!FLAG_NOT_LIGATURE_GROUP_START)
+ }
+
+ fn is_cluster_start(&self) -> bool {
+ self.has_flag(!FLAG_NOT_CLUSTER_START)
+ }
+
+ // True if original char was normal (U+0020) space. Other chars may
+ // map to space glyph, but this does not account for them.
+ fn char_is_space(&self) -> bool {
+ self.has_flag(FLAG_CHAR_IS_SPACE)
+ }
+
+ fn char_is_tab(&self) -> bool {
+ !self.is_simple() && self.has_flag(FLAG_CHAR_IS_TAB)
+ }
+
+ fn char_is_newline(&self) -> bool {
+ !self.is_simple() && self.has_flag(FLAG_CHAR_IS_NEWLINE)
+ }
+
+ fn can_break_before(&self) -> BreakType {
+ let flag = ((self.value & FLAG_CAN_BREAK_MASK) >> FLAG_CAN_BREAK_SHIFT as uint) as u8;
+ break_flag_to_enum(flag)
+ }
+
+ // setter methods
+ #[inline(always)]
+ fn set_char_is_space(&self) -> GlyphEntry {
+ GlyphEntry::new(self.value | FLAG_CHAR_IS_SPACE)
+ }
+
+ #[inline(always)]
+ fn set_char_is_tab(&self) -> GlyphEntry {
+ assert!(!self.is_simple());
+ GlyphEntry::new(self.value | FLAG_CHAR_IS_TAB)
+ }
+
+ #[inline(always)]
+ fn set_char_is_newline(&self) -> GlyphEntry {
+ assert!(!self.is_simple());
+ GlyphEntry::new(self.value | FLAG_CHAR_IS_NEWLINE)
+ }
+
+ #[inline(always)]
+ fn set_can_break_before(&self, e: BreakType) -> GlyphEntry {
+ let flag = (break_enum_to_flag(e) as u32) << FLAG_CAN_BREAK_SHIFT as uint;
+ GlyphEntry::new(self.value | flag)
+ }
+
+ // helper methods
+
+ fn glyph_count(&self) -> u16 {
+ assert!(!self.is_simple());
+ ((self.value & GLYPH_COUNT_MASK) >> GLYPH_COUNT_SHIFT as uint) as u16
+ }
+
+ #[inline(always)]
+ fn is_simple(&self) -> bool {
+ self.has_flag(FLAG_IS_SIMPLE_GLYPH)
+ }
+
+ #[inline(always)]
+ fn has_flag(&self, flag: u32) -> bool {
+ (self.value & flag) != 0
+ }
+
+ #[inline(always)]
+ fn adapt_character_flags_of_entry(&self, other: GlyphEntry) -> GlyphEntry {
+ GlyphEntry { value: self.value | other.value }
+ }
+}
+
+// Stores data for a detailed glyph, in the case that several glyphs
+// correspond to one character, or the glyph's data couldn't be packed.
+#[deriving(Clone)]
+struct DetailedGlyph {
+ id: GlyphId,
+ // glyph's advance, in the text's direction (RTL or RTL)
+ advance: Au,
+ // glyph's offset from the font's em-box (from top-left)
+ offset: Point2D<Au>,
+}
+
+impl DetailedGlyph {
+ fn new(id: GlyphId, advance: Au, offset: Point2D<Au>) -> DetailedGlyph {
+ DetailedGlyph {
+ id: id,
+ advance: advance,
+ offset: offset,
+ }
+ }
+}
+
+#[deriving(PartialEq, Clone, Eq)]
+struct DetailedGlyphRecord {
+ // source string offset/GlyphEntry offset in the TextRun
+ entry_offset: CharIndex,
+ // offset into the detailed glyphs buffer
+ detail_offset: int,
+}
+
+impl PartialOrd for DetailedGlyphRecord {
+ fn partial_cmp(&self, other: &DetailedGlyphRecord) -> Option<Ordering> {
+ self.entry_offset.partial_cmp(&other.entry_offset)
+ }
+}
+
+impl Ord for DetailedGlyphRecord {
+ fn cmp(&self, other: &DetailedGlyphRecord) -> Ordering {
+ self.entry_offset.cmp(&other.entry_offset)
+ }
+}
+
+// Manages the lookup table for detailed glyphs. Sorting is deferred
+// until a lookup is actually performed; this matches the expected
+// usage pattern of setting/appending all the detailed glyphs, and
+// then querying without setting.
+struct DetailedGlyphStore {
+ // TODO(pcwalton): Allocation of this buffer is expensive. Consider a small-vector
+ // optimization.
+ detail_buffer: Vec<DetailedGlyph>,
+ // TODO(pcwalton): Allocation of this buffer is expensive. Consider a small-vector
+ // optimization.
+ detail_lookup: Vec<DetailedGlyphRecord>,
+ lookup_is_sorted: bool,
+}
+
+impl<'a> DetailedGlyphStore {
+ fn new() -> DetailedGlyphStore {
+ DetailedGlyphStore {
+ detail_buffer: vec!(), // TODO: default size?
+ detail_lookup: vec!(),
+ lookup_is_sorted: false,
+ }
+ }
+
+ fn add_detailed_glyphs_for_entry(&mut self, entry_offset: CharIndex, glyphs: &[DetailedGlyph]) {
+ let entry = DetailedGlyphRecord {
+ entry_offset: entry_offset,
+ detail_offset: self.detail_buffer.len() as int,
+ };
+
+ debug!("Adding entry[off={}] for detailed glyphs: {:?}", entry_offset, glyphs);
+
+ /* TODO: don't actually assert this until asserts are compiled
+ in/out based on severity, debug/release, etc. This assertion
+ would wreck the complexity of the lookup.
+
+ See Rust Issue #3647, #2228, #3627 for related information.
+
+ do self.detail_lookup.borrow |arr| {
+ assert !arr.contains(entry)
+ }
+ */
+
+ self.detail_lookup.push(entry);
+ self.detail_buffer.push_all(glyphs);
+ self.lookup_is_sorted = false;
+ }
+
+ fn get_detailed_glyphs_for_entry(&'a self, entry_offset: CharIndex, count: u16)
+ -> &'a [DetailedGlyph] {
+ debug!("Requesting detailed glyphs[n={}] for entry[off={}]", count, entry_offset);
+
+ // FIXME: Is this right? --pcwalton
+ // TODO: should fix this somewhere else
+ if count == 0 {
+ return self.detail_buffer.slice(0, 0);
+ }
+
+ assert!((count as uint) <= self.detail_buffer.len());
+ assert!(self.lookup_is_sorted);
+
+ let key = DetailedGlyphRecord {
+ entry_offset: entry_offset,
+ detail_offset: 0, // unused
+ };
+
+ let i = self.detail_lookup.as_slice().binary_search_index(&key)
+ .expect("Invalid index not found in detailed glyph lookup table!");
+
+ assert!(i + (count as uint) <= self.detail_buffer.len());
+ // return a slice into the buffer
+ self.detail_buffer.slice(i, i + count as uint)
+ }
+
+ fn get_detailed_glyph_with_index(&'a self,
+ entry_offset: CharIndex,
+ detail_offset: u16)
+ -> &'a DetailedGlyph {
+ assert!((detail_offset as uint) <= self.detail_buffer.len());
+ assert!(self.lookup_is_sorted);
+
+ let key = DetailedGlyphRecord {
+ entry_offset: entry_offset,
+ detail_offset: 0, // unused
+ };
+
+ let i = self.detail_lookup.as_slice().binary_search_index(&key)
+ .expect("Invalid index not found in detailed glyph lookup table!");
+
+ assert!(i + (detail_offset as uint) < self.detail_buffer.len());
+ &self.detail_buffer[i + (detail_offset as uint)]
+ }
+
+ fn ensure_sorted(&mut self) {
+ if self.lookup_is_sorted {
+ return;
+ }
+
+ // Sorting a unique vector is surprisingly hard. The follwing
+ // code is a good argument for using DVecs, but they require
+ // immutable locations thus don't play well with freezing.
+
+ // Thar be dragons here. You have been warned. (Tips accepted.)
+ let mut unsorted_records: Vec<DetailedGlyphRecord> = vec!();
+ mem::swap(&mut self.detail_lookup, &mut unsorted_records);
+ let mut mut_records : Vec<DetailedGlyphRecord> = unsorted_records;
+ mut_records.sort_by(|a, b| {
+ if a < b {
+ Less
+ } else {
+ Greater
+ }
+ });
+ let mut sorted_records = mut_records;
+ mem::swap(&mut self.detail_lookup, &mut sorted_records);
+
+ self.lookup_is_sorted = true;
+ }
+}
+
+// This struct is used by GlyphStore clients to provide new glyph data.
+// It should be allocated on the stack and passed by reference to GlyphStore.
+pub struct GlyphData {
+ id: GlyphId,
+ advance: Au,
+ offset: Point2D<Au>,
+ is_missing: bool,
+ cluster_start: bool,
+ ligature_start: bool,
+}
+
+impl GlyphData {
+ pub fn new(id: GlyphId,
+ advance: Au,
+ offset: Option<Point2D<Au>>,
+ is_missing: bool,
+ cluster_start: bool,
+ ligature_start: bool)
+ -> GlyphData {
+ GlyphData {
+ id: id,
+ advance: advance,
+ offset: offset.unwrap_or(Zero::zero()),
+ is_missing: is_missing,
+ cluster_start: cluster_start,
+ ligature_start: ligature_start,
+ }
+ }
+}
+
+// This enum is a proxy that's provided to GlyphStore clients when iterating
+// through glyphs (either for a particular TextRun offset, or all glyphs).
+// Rather than eagerly assembling and copying glyph data, it only retrieves
+// values as they are needed from the GlyphStore, using provided offsets.
+pub enum GlyphInfo<'a> {
+ SimpleGlyphInfo(&'a GlyphStore, CharIndex),
+ DetailGlyphInfo(&'a GlyphStore, CharIndex, u16),
+}
+
+impl<'a> GlyphInfo<'a> {
+ pub fn id(self) -> GlyphId {
+ match self {
+ SimpleGlyphInfo(store, entry_i) => store.entry_buffer[entry_i.to_uint()].id(),
+ DetailGlyphInfo(store, entry_i, detail_j) => {
+ store.detail_store.get_detailed_glyph_with_index(entry_i, detail_j).id
+ }
+ }
+ }
+
+ #[inline(always)]
+ // FIXME: Resolution conflicts with IteratorUtil trait so adding trailing _
+ pub fn advance(self) -> Au {
+ match self {
+ SimpleGlyphInfo(store, entry_i) => store.entry_buffer[entry_i.to_uint()].advance(),
+ DetailGlyphInfo(store, entry_i, detail_j) => {
+ store.detail_store.get_detailed_glyph_with_index(entry_i, detail_j).advance
+ }
+ }
+ }
+
+ pub fn offset(self) -> Option<Point2D<Au>> {
+ match self {
+ SimpleGlyphInfo(_, _) => None,
+ DetailGlyphInfo(store, entry_i, detail_j) => {
+ Some(store.detail_store.get_detailed_glyph_with_index(entry_i, detail_j).offset)
+ }
+ }
+ }
+}
+
+/// Stores the glyph data belonging to a text run.
+///
+/// Simple glyphs are stored inline in the `entry_buffer`, detailed glyphs are
+/// stored as pointers into the `detail_store`.
+///
+/// ~~~
+/// +- GlyphStore --------------------------------+
+/// | +---+---+---+---+---+---+---+ |
+/// | entry_buffer: | | s | | s | | s | s | | d = detailed
+/// | +-|-+---+-|-+---+-|-+---+---+ | s = simple
+/// | | | | |
+/// | | +---+-------+ |
+/// | | | |
+/// | +-V-+-V-+ |
+/// | detail_store: | d | d | |
+/// | +---+---+ |
+/// +---------------------------------------------+
+/// ~~~
+pub struct GlyphStore {
+ // TODO(pcwalton): Allocation of this buffer is expensive. Consider a small-vector
+ // optimization.
+ /// A buffer of glyphs within the text run, in the order in which they
+ /// appear in the input text
+ entry_buffer: Vec<GlyphEntry>,
+ /// A store of the detailed glyph data. Detailed glyphs contained in the
+ /// `entry_buffer` point to locations in this data structure.
+ detail_store: DetailedGlyphStore,
+
+ is_whitespace: bool,
+}
+
+int_range_index! {
+ #[deriving(Encodable)]
+ #[doc = "An index that refers to a character in a text run. This could \
+ point to the middle of a glyph."]
+ struct CharIndex(int)
+}
+
+impl<'a> GlyphStore {
+ // Initializes the glyph store, but doesn't actually shape anything.
+ // Use the set_glyph, set_glyphs() methods to store glyph data.
+ pub fn new(length: int, is_whitespace: bool) -> GlyphStore {
+ assert!(length > 0);
+
+ GlyphStore {
+ entry_buffer: Vec::from_elem(length as uint, GlyphEntry::initial()),
+ detail_store: DetailedGlyphStore::new(),
+ is_whitespace: is_whitespace,
+ }
+ }
+
+ pub fn char_len(&self) -> CharIndex {
+ CharIndex(self.entry_buffer.len() as int)
+ }
+
+ pub fn is_whitespace(&self) -> bool {
+ self.is_whitespace
+ }
+
+ pub fn finalize_changes(&mut self) {
+ self.detail_store.ensure_sorted();
+ }
+
+ pub fn add_glyph_for_char_index(&mut self, i: CharIndex, data: &GlyphData) {
+ fn glyph_is_compressible(data: &GlyphData) -> bool {
+ is_simple_glyph_id(data.id)
+ && is_simple_advance(data.advance)
+ && data.offset.is_zero()
+ && data.cluster_start // others are stored in detail buffer
+ }
+
+ assert!(data.ligature_start); // can't compress ligature continuation glyphs.
+ assert!(i < self.char_len());
+
+ let entry = match (data.is_missing, glyph_is_compressible(data)) {
+ (true, _) => GlyphEntry::missing(1),
+ (false, true) => GlyphEntry::simple(data.id, data.advance),
+ (false, false) => {
+ let glyph = [DetailedGlyph::new(data.id, data.advance, data.offset)];
+ self.detail_store.add_detailed_glyphs_for_entry(i, glyph);
+ GlyphEntry::complex(data.cluster_start, data.ligature_start, 1)
+ }
+ }.adapt_character_flags_of_entry(self.entry_buffer[i.to_uint()]);
+
+ *self.entry_buffer.get_mut(i.to_uint()) = entry;
+ }
+
+ pub fn add_glyphs_for_char_index(&mut self, i: CharIndex, data_for_glyphs: &[GlyphData]) {
+ assert!(i < self.char_len());
+ assert!(data_for_glyphs.len() > 0);
+
+ let glyph_count = data_for_glyphs.len() as int;
+
+ let first_glyph_data = data_for_glyphs[0];
+ let entry = match first_glyph_data.is_missing {
+ true => GlyphEntry::missing(glyph_count),
+ false => {
+ let glyphs_vec = Vec::from_fn(glyph_count as uint, |i| {
+ DetailedGlyph::new(data_for_glyphs[i].id,
+ data_for_glyphs[i].advance,
+ data_for_glyphs[i].offset)
+ });
+
+ self.detail_store.add_detailed_glyphs_for_entry(i, glyphs_vec.as_slice());
+ GlyphEntry::complex(first_glyph_data.cluster_start,
+ first_glyph_data.ligature_start,
+ glyph_count)
+ }
+ }.adapt_character_flags_of_entry(self.entry_buffer[i.to_uint()]);
+
+ debug!("Adding multiple glyphs[idx={}, count={}]: {:?}", i, glyph_count, entry);
+
+ *self.entry_buffer.get_mut(i.to_uint()) = entry;
+ }
+
+ // used when a character index has no associated glyph---for example, a ligature continuation.
+ pub fn add_nonglyph_for_char_index(&mut self, i: CharIndex, cluster_start: bool, ligature_start: bool) {
+ assert!(i < self.char_len());
+
+ let entry = GlyphEntry::complex(cluster_start, ligature_start, 0);
+ debug!("adding spacer for chracter without associated glyph[idx={}]", i);
+
+ *self.entry_buffer.get_mut(i.to_uint()) = entry;
+ }
+
+ pub fn iter_glyphs_for_char_index(&'a self, i: CharIndex) -> GlyphIterator<'a> {
+ self.iter_glyphs_for_char_range(&Range::new(i, CharIndex(1)))
+ }
+
+ #[inline]
+ pub fn iter_glyphs_for_char_range(&'a self, rang: &Range<CharIndex>) -> GlyphIterator<'a> {
+ if rang.begin() >= self.char_len() {
+ fail!("iter_glyphs_for_range: range.begin beyond length!");
+ }
+ if rang.end() > self.char_len() {
+ fail!("iter_glyphs_for_range: range.end beyond length!");
+ }
+
+ GlyphIterator {
+ store: self,
+ char_index: rang.begin(),
+ char_range: rang.each_index(),
+ glyph_range: None,
+ }
+ }
+
+ #[inline]
+ pub fn advance_for_char_range(&self, rang: &Range<CharIndex>) -> Au {
+ self.iter_glyphs_for_char_range(rang)
+ .fold(Au(0), |advance, (_, glyph)| advance + glyph.advance())
+ }
+
+ // getter methods
+ pub fn char_is_space(&self, i: CharIndex) -> bool {
+ assert!(i < self.char_len());
+ self.entry_buffer[i.to_uint()].char_is_space()
+ }
+
+ pub fn char_is_tab(&self, i: CharIndex) -> bool {
+ assert!(i < self.char_len());
+ self.entry_buffer[i.to_uint()].char_is_tab()
+ }
+
+ pub fn char_is_newline(&self, i: CharIndex) -> bool {
+ assert!(i < self.char_len());
+ self.entry_buffer[i.to_uint()].char_is_newline()
+ }
+
+ pub fn is_ligature_start(&self, i: CharIndex) -> bool {
+ assert!(i < self.char_len());
+ self.entry_buffer[i.to_uint()].is_ligature_start()
+ }
+
+ pub fn is_cluster_start(&self, i: CharIndex) -> bool {
+ assert!(i < self.char_len());
+ self.entry_buffer[i.to_uint()].is_cluster_start()
+ }
+
+ pub fn can_break_before(&self, i: CharIndex) -> BreakType {
+ assert!(i < self.char_len());
+ self.entry_buffer[i.to_uint()].can_break_before()
+ }
+
+ // setter methods
+ pub fn set_char_is_space(&mut self, i: CharIndex) {
+ assert!(i < self.char_len());
+ let entry = self.entry_buffer[i.to_uint()];
+ *self.entry_buffer.get_mut(i.to_uint()) = entry.set_char_is_space();
+ }
+
+ pub fn set_char_is_tab(&mut self, i: CharIndex) {
+ assert!(i < self.char_len());
+ let entry = self.entry_buffer[i.to_uint()];
+ *self.entry_buffer.get_mut(i.to_uint()) = entry.set_char_is_tab();
+ }
+
+ pub fn set_char_is_newline(&mut self, i: CharIndex) {
+ assert!(i < self.char_len());
+ let entry = self.entry_buffer[i.to_uint()];
+ *self.entry_buffer.get_mut(i.to_uint()) = entry.set_char_is_newline();
+ }
+
+ pub fn set_can_break_before(&mut self, i: CharIndex, t: BreakType) {
+ assert!(i < self.char_len());
+ let entry = self.entry_buffer[i.to_uint()];
+ *self.entry_buffer.get_mut(i.to_uint()) = entry.set_can_break_before(t);
+ }
+}
+
+/// An iterator over the glyphs in a character range in a `GlyphStore`.
+pub struct GlyphIterator<'a> {
+ store: &'a GlyphStore,
+ char_index: CharIndex,
+ char_range: EachIndex<int, CharIndex>,
+ glyph_range: Option<EachIndex<int, CharIndex>>,
+}
+
+impl<'a> GlyphIterator<'a> {
+ // Slow path when there is a glyph range.
+ #[inline(never)]
+ fn next_glyph_range(&mut self) -> Option<(CharIndex, GlyphInfo<'a>)> {
+ match self.glyph_range.get_mut_ref().next() {
+ Some(j) => Some((self.char_index,
+ DetailGlyphInfo(self.store, self.char_index, j.get() as u16 /* ??? */))),
+ None => {
+ // No more glyphs for current character. Try to get another.
+ self.glyph_range = None;
+ self.next()
+ }
+ }
+ }
+
+ // Slow path when there is a complex glyph.
+ #[inline(never)]
+ fn next_complex_glyph(&mut self, entry: &GlyphEntry, i: CharIndex)
+ -> Option<(CharIndex, GlyphInfo<'a>)> {
+ let glyphs = self.store.detail_store.get_detailed_glyphs_for_entry(i, entry.glyph_count());
+ self.glyph_range = Some(range::each_index(CharIndex(0), CharIndex(glyphs.len() as int)));
+ self.next()
+ }
+}
+
+impl<'a> Iterator<(CharIndex, GlyphInfo<'a>)> for GlyphIterator<'a> {
+ // I tried to start with something simpler and apply FlatMap, but the
+ // inability to store free variables in the FlatMap struct was problematic.
+ //
+ // This function consists of the fast path and is designed to be inlined into its caller. The
+ // slow paths, which should not be inlined, are `next_glyph_range()` and
+ // `next_complex_glyph()`.
+ #[inline(always)]
+ fn next(&mut self) -> Option<(CharIndex, GlyphInfo<'a>)> {
+ // Would use 'match' here but it borrows contents in a way that
+ // interferes with mutation.
+ if self.glyph_range.is_some() {
+ self.next_glyph_range()
+ } else {
+ // No glyph range. Look at next character.
+ self.char_range.next().and_then(|i| {
+ self.char_index = i;
+ assert!(i < self.store.char_len());
+ let entry = self.store.entry_buffer[i.to_uint()];
+ if entry.is_simple() {
+ Some((self.char_index, SimpleGlyphInfo(self.store, i)))
+ } else {
+ // Fall back to the slow path.
+ self.next_complex_glyph(&entry, i)
+ }
+ })
+ }
+ }
+}