From 9dd533ce015a3e7671fe8aadf49e105a18b57b21 Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Mon, 12 May 2014 13:43:10 -0700 Subject: [PATCH] Add character index type This is more self-documenting and may highlight errors in the future. --- src/components/gfx/display_list.rs | 3 +- src/components/gfx/font.rs | 10 +- src/components/gfx/gfx.rs | 1 + src/components/gfx/text/glyph.rs | 152 ++++++++++---------- src/components/gfx/text/shaping/harfbuzz.rs | 8 +- src/components/gfx/text/text_run.rs | 44 +++--- src/components/gfx/text/util.rs | 12 +- src/components/main/layout/box_.rs | 24 ++-- src/components/main/layout/text.rs | 53 +++---- 9 files changed, 161 insertions(+), 146 deletions(-) diff --git a/src/components/gfx/display_list.rs b/src/components/gfx/display_list.rs index 0a831f732d0..63ea5596e3c 100644 --- a/src/components/gfx/display_list.rs +++ b/src/components/gfx/display_list.rs @@ -16,6 +16,7 @@ use color::Color; use render_context::RenderContext; +use text::glyph::CharIndex; use text::TextRun; use collections::deque::Deque; @@ -395,7 +396,7 @@ pub struct TextDisplayItem { pub text_run: Arc<~TextRun>, /// The range of text within the text run. - pub range: Range, + pub range: Range, /// The color of the text. pub text_color: Color, diff --git a/src/components/gfx/font.rs b/src/components/gfx/font.rs index a90ad0ec822..4470b1a372a 100644 --- a/src/components/gfx/font.rs +++ b/src/components/gfx/font.rs @@ -23,7 +23,7 @@ use servo_util::geometry::Au; use platform::font_context::FontContextHandle; use platform::font::{FontHandle, FontTable}; use render_context::RenderContext; -use text::glyph::{GlyphStore, GlyphId}; +use text::glyph::{CharIndex, GlyphStore, GlyphId}; use text::shaping::ShaperMethods; use text::{Shaper, TextRun}; @@ -330,7 +330,7 @@ impl Font { pub fn draw_text_into_context(&mut self, rctx: &RenderContext, run: &~TextRun, - range: &Range, + range: &Range, baseline_origin: Point2D, color: Color) { use libc::types::common::c99::{uint16_t, uint32_t}; @@ -353,7 +353,7 @@ impl Font { let mut origin = baseline_origin.clone(); let mut azglyphs = vec!(); - azglyphs.reserve(range.length() as uint); + azglyphs.reserve(range.length().to_uint()); for (glyphs, _offset, slice_range) in run.iter_slices_for_range(range) { for (_i, glyph) in glyphs.iter_glyphs_for_char_range(&slice_range) { @@ -391,7 +391,7 @@ impl Font { } } - pub fn measure_text(&self, run: &TextRun, range: &Range) -> RunMetrics { + pub fn measure_text(&self, run: &TextRun, range: &Range) -> RunMetrics { // TODO(Issue #199): alter advance direction for RTL // TODO(Issue #98): using inter-char and inter-word spacing settings when measuring text let mut advance = Au(0); @@ -405,7 +405,7 @@ impl Font { pub fn measure_text_for_slice(&self, glyphs: &GlyphStore, - slice_range: &Range) + slice_range: &Range) -> RunMetrics { let mut advance = Au(0); for (_i, glyph) in glyphs.iter_glyphs_for_char_range(slice_range) { diff --git a/src/components/gfx/gfx.rs b/src/components/gfx/gfx.rs index dd331b66589..f88a4b6e61b 100644 --- a/src/components/gfx/gfx.rs +++ b/src/components/gfx/gfx.rs @@ -23,6 +23,7 @@ extern crate png; #[phase(syntax)] extern crate servo_macros = "macros"; extern crate servo_net = "net"; +#[phase(syntax, link)] extern crate servo_util = "util"; extern crate servo_msg = "msg"; extern crate style; diff --git a/src/components/gfx/text/glyph.rs b/src/components/gfx/text/glyph.rs index 5597ce963e1..da158f1a63d 100644 --- a/src/components/gfx/text/glyph.rs +++ b/src/components/gfx/text/glyph.rs @@ -4,7 +4,7 @@ use servo_util::vec::*; use servo_util::range; -use servo_util::range::{Range, EachIndex}; +use servo_util::range::{Range, RangeIndex, EachIndex}; use servo_util::geometry::Au; use std::cmp::{Ord, Eq}; @@ -273,7 +273,7 @@ impl DetailedGlyph { #[deriving(Eq, Clone)] struct DetailedGlyphRecord { // source string offset/GlyphEntry offset in the TextRun - entry_offset: int, + entry_offset: CharIndex, // offset into the detailed glyphs buffer detail_offset: int, } @@ -316,7 +316,7 @@ impl<'a> DetailedGlyphStore { } } - fn add_detailed_glyphs_for_entry(&mut self, entry_offset: int, glyphs: &[DetailedGlyph]) { + 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, @@ -340,7 +340,7 @@ impl<'a> DetailedGlyphStore { self.lookup_is_sorted = false; } - fn get_detailed_glyphs_for_entry(&'a self, entry_offset: int, count: u16) + 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); @@ -371,7 +371,7 @@ impl<'a> DetailedGlyphStore { } fn get_detailed_glyph_with_index(&'a self, - entry_offset: int, + entry_offset: CharIndex, detail_offset: u16) -> &'a DetailedGlyph { assert!((detail_offset as uint) <= self.detail_buffer.len()); @@ -460,14 +460,14 @@ impl GlyphData { // 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, int), - DetailGlyphInfo(&'a GlyphStore, int, u16), + 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.get(entry_i as uint).id(), + SimpleGlyphInfo(store, entry_i) => store.entry_buffer.get(entry_i.to_uint()).id(), DetailGlyphInfo(store, entry_i, detail_j) => { store.detail_store.get_detailed_glyph_with_index(entry_i, detail_j).id } @@ -478,7 +478,7 @@ impl<'a> GlyphInfo<'a> { // FIXME: Resolution conflicts with IteratorUtil trait so adding trailing _ pub fn advance(self) -> Au { match self { - SimpleGlyphInfo(store, entry_i) => store.entry_buffer.get(entry_i as uint).advance(), + SimpleGlyphInfo(store, entry_i) => store.entry_buffer.get(entry_i.to_uint()).advance(), DetailGlyphInfo(store, entry_i, detail_j) => { store.detail_store.get_detailed_glyph_with_index(entry_i, detail_j).advance } @@ -506,6 +506,12 @@ pub struct GlyphStore { is_whitespace: bool, } +range_index! { + #[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. @@ -519,8 +525,8 @@ impl<'a> GlyphStore { } } - pub fn char_len(&self) -> int { - self.entry_buffer.len() as int + pub fn char_len(&self) -> CharIndex { + CharIndex(self.entry_buffer.len() as int) } pub fn is_whitespace(&self) -> bool { @@ -531,7 +537,7 @@ impl<'a> GlyphStore { self.detail_store.ensure_sorted(); } - pub fn add_glyph_for_char_index(&mut self, i: int, data: &GlyphData) { + 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) @@ -540,7 +546,7 @@ impl<'a> GlyphStore { } assert!(data.ligature_start); // can't compress ligature continuation glyphs. - assert!(i < self.entry_buffer.len() as int); + assert!(i < CharIndex(self.entry_buffer.len() as int)); let entry = match (data.is_missing, glyph_is_compressible(data)) { (true, _) => GlyphEntry::missing(1), @@ -550,13 +556,13 @@ impl<'a> GlyphStore { 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.get(i as uint)); + }.adapt_character_flags_of_entry(*self.entry_buffer.get(i.to_uint())); - *self.entry_buffer.get_mut(i as uint) = entry; + *self.entry_buffer.get_mut(i.to_uint()) = entry; } - pub fn add_glyphs_for_char_index(&mut self, i: int, data_for_glyphs: &[GlyphData]) { - assert!(i < self.entry_buffer.len() as int); + pub fn add_glyphs_for_char_index(&mut self, i: CharIndex, data_for_glyphs: &[GlyphData]) { + assert!(i < CharIndex(self.entry_buffer.len() as int)); assert!(data_for_glyphs.len() > 0); let glyph_count = data_for_glyphs.len() as int; @@ -576,33 +582,33 @@ impl<'a> GlyphStore { first_glyph_data.ligature_start, glyph_count) } - }.adapt_character_flags_of_entry(*self.entry_buffer.get(i as uint)); + }.adapt_character_flags_of_entry(*self.entry_buffer.get(i.to_uint())); debug!("Adding multiple glyphs[idx={}, count={}]: {:?}", i, glyph_count, entry); - *self.entry_buffer.get_mut(i as uint) = 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: int, cluster_start: bool, ligature_start: bool) { - assert!(i < self.entry_buffer.len() as int); + pub fn add_nonglyph_for_char_index(&mut self, i: CharIndex, cluster_start: bool, ligature_start: bool) { + assert!(i < CharIndex(self.entry_buffer.len() as int)); 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 as uint) = entry; + *self.entry_buffer.get_mut(i.to_uint()) = entry; } - pub fn iter_glyphs_for_char_index(&'a self, i: int) -> GlyphIterator<'a> { - self.iter_glyphs_for_char_range(&Range::new(i as int, 1)) + 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) -> GlyphIterator<'a> { - if rang.begin() >= self.entry_buffer.len() as int { + pub fn iter_glyphs_for_char_range(&'a self, rang: &Range) -> GlyphIterator<'a> { + if rang.begin() >= CharIndex(self.entry_buffer.len() as int) { fail!("iter_glyphs_for_range: range.begin beyond length!"); } - if rang.end() > self.entry_buffer.len() as int { + if rang.end() > CharIndex(self.entry_buffer.len() as int) { fail!("iter_glyphs_for_range: range.end beyond length!"); } @@ -615,76 +621,76 @@ impl<'a> GlyphStore { } // getter methods - pub fn char_is_space(&self, i: int) -> bool { - assert!(i < self.entry_buffer.len() as int); - self.entry_buffer.get(i as uint).char_is_space() + pub fn char_is_space(&self, i: CharIndex) -> bool { + assert!(i < CharIndex(self.entry_buffer.len() as int)); + self.entry_buffer.get(i.to_uint()).char_is_space() } - pub fn char_is_tab(&self, i: int) -> bool { - assert!(i < self.entry_buffer.len() as int); - self.entry_buffer.get(i as uint).char_is_tab() + pub fn char_is_tab(&self, i: CharIndex) -> bool { + assert!(i < CharIndex(self.entry_buffer.len() as int)); + self.entry_buffer.get(i.to_uint()).char_is_tab() } - pub fn char_is_newline(&self, i: int) -> bool { - assert!(i < self.entry_buffer.len() as int); - self.entry_buffer.get(i as uint).char_is_newline() + pub fn char_is_newline(&self, i: CharIndex) -> bool { + assert!(i < CharIndex(self.entry_buffer.len() as int)); + self.entry_buffer.get(i.to_uint()).char_is_newline() } - pub fn is_ligature_start(&self, i: int) -> bool { - assert!(i < self.entry_buffer.len() as int); - self.entry_buffer.get(i as uint).is_ligature_start() + pub fn is_ligature_start(&self, i: CharIndex) -> bool { + assert!(i < CharIndex(self.entry_buffer.len() as int)); + self.entry_buffer.get(i.to_uint()).is_ligature_start() } - pub fn is_cluster_start(&self, i: int) -> bool { - assert!(i < self.entry_buffer.len() as int); - self.entry_buffer.get(i as uint).is_cluster_start() + pub fn is_cluster_start(&self, i: CharIndex) -> bool { + assert!(i < CharIndex(self.entry_buffer.len() as int)); + self.entry_buffer.get(i.to_uint()).is_cluster_start() } - pub fn can_break_before(&self, i: int) -> BreakType { - assert!(i < self.entry_buffer.len() as int); - self.entry_buffer.get(i as uint).can_break_before() + pub fn can_break_before(&self, i: CharIndex) -> BreakType { + assert!(i < CharIndex(self.entry_buffer.len() as int)); + self.entry_buffer.get(i.to_uint()).can_break_before() } // setter methods - pub fn set_char_is_space(&mut self, i: int) { - assert!(i < self.entry_buffer.len() as int); - let entry = *self.entry_buffer.get(i as uint); - *self.entry_buffer.get_mut(i as uint) = entry.set_char_is_space(); + pub fn set_char_is_space(&mut self, i: CharIndex) { + assert!(i < CharIndex(self.entry_buffer.len() as int)); + let entry = *self.entry_buffer.get(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: int) { - assert!(i < self.entry_buffer.len() as int); - let entry = *self.entry_buffer.get(i as uint); - *self.entry_buffer.get_mut(i as uint) = entry.set_char_is_tab(); + pub fn set_char_is_tab(&mut self, i: CharIndex) { + assert!(i < CharIndex(self.entry_buffer.len() as int)); + let entry = *self.entry_buffer.get(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: int) { - assert!(i < self.entry_buffer.len() as int); - let entry = *self.entry_buffer.get(i as uint); - *self.entry_buffer.get_mut(i as uint) = entry.set_char_is_newline(); + pub fn set_char_is_newline(&mut self, i: CharIndex) { + assert!(i < CharIndex(self.entry_buffer.len() as int)); + let entry = *self.entry_buffer.get(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: int, t: BreakType) { - assert!(i < self.entry_buffer.len() as int); - let entry = *self.entry_buffer.get(i as uint); - *self.entry_buffer.get_mut(i as uint) = entry.set_can_break_before(t); + pub fn set_can_break_before(&mut self, i: CharIndex, t: BreakType) { + assert!(i < CharIndex(self.entry_buffer.len() as int)); + let entry = *self.entry_buffer.get(i.to_uint()); + *self.entry_buffer.get_mut(i.to_uint()) = entry.set_can_break_before(t); } } pub struct GlyphIterator<'a> { store: &'a GlyphStore, - char_index: int, - char_range: EachIndex, - glyph_range: Option>, + char_index: CharIndex, + char_range: EachIndex, + glyph_range: Option>, } impl<'a> GlyphIterator<'a> { // Slow path when there is a glyph range. #[inline(never)] - fn next_glyph_range(&mut self) -> Option<(int, GlyphInfo<'a>)> { + 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 as u16))), + 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; @@ -695,15 +701,15 @@ impl<'a> GlyphIterator<'a> { // Slow path when there is a complex glyph. #[inline(never)] - fn next_complex_glyph(&mut self, entry: &GlyphEntry, i: int) - -> Option<(int, GlyphInfo<'a>)> { + 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(0, glyphs.len() as int)); + self.glyph_range = Some(range::each_index(CharIndex(0), CharIndex(glyphs.len() as int))); self.next() } } -impl<'a> Iterator<(int, GlyphInfo<'a>)> for GlyphIterator<'a> { +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. // @@ -711,7 +717,7 @@ impl<'a> Iterator<(int, GlyphInfo<'a>)> for GlyphIterator<'a> { // slow paths, which should not be inlined, are `next_glyph_range()` and // `next_complex_glyph()`. #[inline(always)] - fn next(&mut self) -> Option<(int, GlyphInfo<'a>)> { + 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() { @@ -721,8 +727,8 @@ impl<'a> Iterator<(int, GlyphInfo<'a>)> for GlyphIterator<'a> { match self.char_range.next() { Some(i) => { self.char_index = i; - assert!(i < self.store.entry_buffer.len() as int); - let entry = self.store.entry_buffer.get(i as uint); + assert!(i < CharIndex(self.store.entry_buffer.len() as int)); + let entry = self.store.entry_buffer.get(i.to_uint()); if entry.is_simple() { Some((self.char_index, SimpleGlyphInfo(self.store, i))) } else { diff --git a/src/components/gfx/text/shaping/harfbuzz.rs b/src/components/gfx/text/shaping/harfbuzz.rs index f40a39ff6ce..4a5111b9839 100644 --- a/src/components/gfx/text/shaping/harfbuzz.rs +++ b/src/components/gfx/text/shaping/harfbuzz.rs @@ -6,7 +6,7 @@ extern crate harfbuzz; use font::{Font, FontHandleMethods, FontTableMethods, FontTableTag}; use platform::font::FontTable; -use text::glyph::{GlyphStore, GlyphId, GlyphData}; +use text::glyph::{CharIndex, GlyphStore, GlyphId, GlyphData}; use text::shaping::ShaperMethods; use text::util::{float_to_fixed, fixed_to_float}; @@ -229,7 +229,7 @@ impl Shaper { // GlyphStore records are indexed by character, not byte offset. // so, we must be careful to increment this when saving glyph entries. - let mut char_idx = 0; + let mut char_idx = CharIndex(0); assert!(glyph_count <= char_max); @@ -435,7 +435,7 @@ impl Shaper { drop(range.ch); i = range.next as int; if i >= covered_byte_span.end() { break; } - char_idx += 1; + char_idx = char_idx + CharIndex(1); glyphs.add_nonglyph_for_char_index(char_idx, false, false); } } @@ -445,7 +445,7 @@ impl Shaper { glyph_span.reset(end, 0); let end = char_byte_span.end();; // FIXME: borrow checker workaround char_byte_span.reset(end, 0); - char_idx += 1; + char_idx = char_idx + CharIndex(1); } // this must be called after adding all glyph data; it sorts the diff --git a/src/components/gfx/text/text_run.rs b/src/components/gfx/text/text_run.rs index e4ff683f6d6..40620395d03 100644 --- a/src/components/gfx/text/text_run.rs +++ b/src/components/gfx/text/text_run.rs @@ -8,7 +8,7 @@ use servo_util::range::Range; use std::slice::Items; use style::computed_values::text_decoration; use sync::Arc; -use text::glyph::GlyphStore; +use text::glyph::{CharIndex, GlyphStore}; /// A text run. #[deriving(Clone)] @@ -23,14 +23,14 @@ pub struct TextRun { pub struct SliceIterator<'a> { glyph_iter: Items<'a, Arc>, - range: Range, - offset: int, + range: Range, + offset: CharIndex, } -impl<'a> Iterator<(&'a GlyphStore, int, Range)> for SliceIterator<'a> { +impl<'a> Iterator<(&'a GlyphStore, CharIndex, Range)> for SliceIterator<'a> { // inline(always) due to the inefficient rt failures messing up inline heuristics, I think. #[inline(always)] - fn next(&mut self) -> Option<(&'a GlyphStore, int, Range)> { + fn next(&mut self) -> Option<(&'a GlyphStore, CharIndex, Range)> { loop { let slice_glyphs = self.glyph_iter.next(); if slice_glyphs.is_none() { @@ -40,10 +40,10 @@ impl<'a> Iterator<(&'a GlyphStore, int, Range)> for SliceIterator<'a> { let slice_range = Range::new(self.offset, slice_glyphs.char_len()); let mut char_range = self.range.intersect(&slice_range); - char_range.shift_by(-(self.offset.to_int().unwrap())); + char_range.shift_by(-self.offset); let old_offset = self.offset; - self.offset += slice_glyphs.char_len(); + self.offset = self.offset + slice_glyphs.char_len(); if !char_range.is_empty() { return Some((&**slice_glyphs, old_offset, char_range)) } @@ -52,24 +52,24 @@ impl<'a> Iterator<(&'a GlyphStore, int, Range)> for SliceIterator<'a> { } pub struct LineIterator<'a> { - range: Range, - clump: Option>, + range: Range, + clump: Option>, slices: SliceIterator<'a>, } -impl<'a> Iterator> for LineIterator<'a> { - fn next(&mut self) -> Option> { +impl<'a> Iterator> for LineIterator<'a> { + fn next(&mut self) -> Option> { // Loop until we hit whitespace and are in a clump. loop { match self.slices.next() { Some((glyphs, offset, slice_range)) => { match (glyphs.is_whitespace(), self.clump) { (false, Some(ref mut c)) => { - c.extend_by(slice_range.length().to_int().unwrap()); + c.extend_by(slice_range.length()); } (false, None) => { let mut c = slice_range; - c.shift_by(offset.to_int().unwrap()); + c.shift_by(offset); self.clump = Some(c); } (true, None) => { /* chomp whitespace */ } @@ -165,8 +165,8 @@ impl<'a> TextRun { glyphs } - pub fn char_len(&self) -> int { - self.glyphs.iter().fold(0, |len, slice_glyphs| { + pub fn char_len(&self) -> CharIndex { + self.glyphs.iter().fold(CharIndex(0), |len, slice_glyphs| { len + slice_glyphs.char_len() }) } @@ -175,14 +175,14 @@ impl<'a> TextRun { &*self.glyphs } - pub fn range_is_trimmable_whitespace(&self, range: &Range) -> bool { + pub fn range_is_trimmable_whitespace(&self, range: &Range) -> bool { for (slice_glyphs, _, _) in self.iter_slices_for_range(range) { if !slice_glyphs.is_whitespace() { return false; } } true } - pub fn metrics_for_range(&self, range: &Range) -> RunMetrics { + pub fn metrics_for_range(&self, range: &Range) -> RunMetrics { // TODO(Issue #199): alter advance direction for RTL // TODO(Issue #98): using inter-char and inter-word spacing settings when measuring text let mut advance = Au(0); @@ -194,14 +194,14 @@ impl<'a> TextRun { RunMetrics::new(advance, self.font_metrics.ascent, self.font_metrics.descent) } - pub fn metrics_for_slice(&self, glyphs: &GlyphStore, slice_range: &Range) -> RunMetrics { + pub fn metrics_for_slice(&self, glyphs: &GlyphStore, slice_range: &Range) -> RunMetrics { let mut advance = Au(0); for (_i, glyph) in glyphs.iter_glyphs_for_char_range(slice_range) { advance = advance + glyph.advance(); } RunMetrics::new(advance, self.font_metrics.ascent, self.font_metrics.descent) } - pub fn min_width_for_range(&self, range: &Range) -> Au { + pub fn min_width_for_range(&self, range: &Range) -> Au { let mut max_piece_width = Au(0); debug!("iterating outer range {:?}", range); for (_, offset, slice_range) in self.iter_slices_for_range(range) { @@ -212,15 +212,15 @@ impl<'a> TextRun { max_piece_width } - pub fn iter_slices_for_range(&'a self, range: &Range) -> SliceIterator<'a> { + pub fn iter_slices_for_range(&'a self, range: &Range) -> SliceIterator<'a> { SliceIterator { glyph_iter: self.glyphs.iter(), range: *range, - offset: 0, + offset: CharIndex(0), } } - pub fn iter_natural_lines_for_range(&'a self, range: &Range) -> LineIterator<'a> { + pub fn iter_natural_lines_for_range(&'a self, range: &Range) -> LineIterator<'a> { LineIterator { range: *range, clump: None, diff --git a/src/components/gfx/text/util.rs b/src/components/gfx/text/util.rs index 6aaafe4b249..5c25a007337 100644 --- a/src/components/gfx/text/util.rs +++ b/src/components/gfx/text/util.rs @@ -2,6 +2,8 @@ * 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 text::glyph::CharIndex; + #[deriving(Eq)] pub enum CompressionMode { CompressNone, @@ -20,11 +22,13 @@ pub enum CompressionMode { // * Issue #114: record skipped and kept chars for mapping original to new text // // * Untracked: various edge cases for bidi, CJK, etc. -pub fn transform_text(text: &str, mode: CompressionMode, incoming_whitespace: bool, new_line_pos: &mut Vec) -> (~str, bool) { +pub fn transform_text(text: &str, mode: CompressionMode, + incoming_whitespace: bool, + new_line_pos: &mut Vec) -> (~str, bool) { let mut out_str: ~str = "".to_owned(); let out_whitespace = match mode { CompressNone | DiscardNewline => { - let mut new_line_index = 0; + let mut new_line_index = CharIndex(0); for ch in text.chars() { if is_discardable_char(ch, mode) { // TODO: record skipped char @@ -36,11 +40,11 @@ pub fn transform_text(text: &str, mode: CompressionMode, incoming_whitespace: bo // Save new-line's position for line-break // This value is relative(not absolute) new_line_pos.push(new_line_index); - new_line_index = 0; + new_line_index = CharIndex(0); } if ch != '\n' { - new_line_index += 1; + new_line_index = new_line_index + CharIndex(1); } out_str.push_char(ch); } diff --git a/src/components/main/layout/box_.rs b/src/components/main/layout/box_.rs index b96a28879cd..5b867e06bfc 100644 --- a/src/components/main/layout/box_.rs +++ b/src/components/main/layout/box_.rs @@ -28,6 +28,7 @@ use gfx::display_list::{LineDisplayItemClass, OpaqueNode, PseudoDisplayItemClass use gfx::display_list::{SolidColorDisplayItem, SolidColorDisplayItemClass, StackingLevel}; use gfx::display_list::{TextDecorations, TextDisplayItem, TextDisplayItemClass}; use gfx::font::FontStyle; +use gfx::text::glyph::CharIndex; use gfx::text::text_run::TextRun; use servo_msg::constellation_msg::{ConstellationChan, FrameRectMsg, PipelineId, SubpageId}; use servo_net::image::holder::{ImageHolder, LocalImageCacheHandle}; @@ -95,7 +96,7 @@ pub struct Box { /// New-line chracter(\n)'s positions(relative, not absolute) /// /// FIXME(#2260, pcwalton): This is very inefficient; remove. - pub new_line_pos: Vec, + pub new_line_pos: Vec, } /// Info specific to the kind of box. Keep this enum small. @@ -226,12 +227,12 @@ pub struct ScannedTextBoxInfo { pub run: Arc<~TextRun>, /// The range within the above text run that this represents. - pub range: Range, + pub range: Range, } impl ScannedTextBoxInfo { /// Creates the information specific to a scanned text box from a range and a text run. - pub fn new(run: Arc<~TextRun>, range: Range) -> ScannedTextBoxInfo { + pub fn new(run: Arc<~TextRun>, range: Range) -> ScannedTextBoxInfo { ScannedTextBoxInfo { run: run, range: range, @@ -1108,7 +1109,8 @@ impl Box { let cur_new_line_pos = new_line_pos.shift().unwrap(); let left_range = Range::new(text_box_info.range.begin(), cur_new_line_pos); - let right_range = Range::new(text_box_info.range.begin() + cur_new_line_pos + 1, text_box_info.range.length() - (cur_new_line_pos + 1)); + let right_range = Range::new(text_box_info.range.begin() + cur_new_line_pos + CharIndex(1), + text_box_info.range.length() - (cur_new_line_pos + CharIndex(1))); // Left box is for left text of first founded new-line character. let left_box = { @@ -1120,7 +1122,7 @@ impl Box { }; // Right box is for right text of first founded new-line character. - let right_box = if right_range.length() > 0 { + let right_box = if right_range.length() > CharIndex(0) { let new_text_box_info = ScannedTextBoxInfo::new(text_box_info.run.clone(), right_range); let new_metrics = new_text_box_info.run.metrics_for_range(&right_range); let mut new_box = self.transform(new_metrics.bounding_box.size, ScannedTextBox(new_text_box_info)); @@ -1145,8 +1147,8 @@ impl Box { ScannedTextBox(ref text_box_info) => { let mut pieces_processed_count: uint = 0; let mut remaining_width: Au = max_width; - let mut left_range = Range::new(text_box_info.range.begin(), 0); - let mut right_range: Option> = None; + let mut left_range = Range::new(text_box_info.range.begin(), CharIndex(0)); + let mut right_range: Option> = None; debug!("split_to_width: splitting text box (strlen={:u}, range={}, \ avail_width={})", @@ -1171,11 +1173,11 @@ impl Box { if starts_line && pieces_processed_count == 0 && glyphs.is_whitespace() { debug!("split_to_width: case=skipping leading trimmable whitespace"); - left_range.shift_by(slice_range.length() as int); + left_range.shift_by(slice_range.length()); } else { debug!("split_to_width: case=enlarging span"); remaining_width = remaining_width - advance; - left_range.extend_by(slice_range.length() as int); + left_range.extend_by(slice_range.length()); } } else { // The advance is more than the remaining width. @@ -1212,7 +1214,7 @@ impl Box { } } - let left_box = if left_range.length() > 0 { + let left_box = if left_range.length() > CharIndex(0) { let new_text_box_info = ScannedTextBoxInfo::new(text_box_info.run.clone(), left_range); let mut new_metrics = new_text_box_info.run.metrics_for_range(&left_range); new_metrics.bounding_box.size.height = self.border_box.size.height; @@ -1222,7 +1224,7 @@ impl Box { None }; - let right_box = right_range.map_or(None, |range: Range| { + let right_box = right_range.map_or(None, |range: Range| { let new_text_box_info = ScannedTextBoxInfo::new(text_box_info.run.clone(), range); let mut new_metrics = new_text_box_info.run.metrics_for_range(&range); diff --git a/src/components/main/layout/text.rs b/src/components/main/layout/text.rs index 1aa2b0c7866..7f38a617efd 100644 --- a/src/components/main/layout/text.rs +++ b/src/components/main/layout/text.rs @@ -10,6 +10,7 @@ use layout::inline::InlineBoxes; use gfx::font::{FontMetrics, FontStyle}; use gfx::font_context::FontContext; +use gfx::text::glyph::CharIndex; use gfx::text::text_run::TextRun; use gfx::text::util::{CompressWhitespaceNewline, transform_text, CompressNone}; use servo_util::geometry::Au; @@ -20,7 +21,7 @@ use style::computed_values::{font_family, line_height, white_space}; use sync::Arc; struct NewLinePositions { - new_line_pos: Vec, + new_line_pos: Vec, } // A helper function. @@ -31,7 +32,7 @@ fn can_coalesce_text_nodes(boxes: &[Box], left_i: uint, right_i: uint) -> bool { /// A stack-allocated object for scanning an inline flow into `TextRun`-containing `TextBox`es. pub struct TextRunScanner { - pub clump: Range, + pub clump: Range, } impl TextRunScanner { @@ -63,11 +64,11 @@ impl TextRunScanner { last_whitespace); } - self.clump.extend_by(1); + self.clump.extend_by(CharIndex(1)); } // Handle remaining clumps. - if self.clump.length() > 0 { + if self.clump.length() > CharIndex(0) { drop(self.flush_clump_to_list(font_context, old_boxes.as_slice(), &mut new_boxes, @@ -99,12 +100,12 @@ impl TextRunScanner { out_boxes: &mut Vec, last_whitespace: bool) -> bool { - assert!(self.clump.length() > 0); + assert!(self.clump.length() > CharIndex(0)); debug!("TextRunScanner: flushing boxes in range={}", self.clump); - let is_singleton = self.clump.length() == 1; + let is_singleton = self.clump.length() == CharIndex(1); - let is_text_clump = match in_boxes[self.clump.begin() as uint].specific { + let is_text_clump = match in_boxes[self.clump.begin().to_uint()].specific { UnscannedTextBox(_) => true, _ => false, }; @@ -117,11 +118,11 @@ impl TextRunScanner { (true, false) => { // FIXME(pcwalton): Stop cloning boxes, as above. debug!("TextRunScanner: pushing single non-text box in range: {}", self.clump); - let new_box = in_boxes[self.clump.begin() as uint].clone(); + let new_box = in_boxes[self.clump.begin().to_uint()].clone(); out_boxes.push(new_box) }, (true, true) => { - let old_box = &in_boxes[self.clump.begin() as uint]; + let old_box = &in_boxes[self.clump.begin().to_uint()]; let text = match old_box.specific { UnscannedTextBox(ref text_box_info) => &text_box_info.text, _ => fail!("Expected an unscanned text box!"), @@ -156,11 +157,11 @@ impl TextRunScanner { debug!("TextRunScanner: pushing single text box in range: {} ({})", self.clump, *text); - let range = Range::new(0, run.char_len()); + let range = Range::new(CharIndex(0), run.char_len()); let new_metrics = run.metrics_for_range(&range); let new_text_box_info = ScannedTextBoxInfo::new(Arc::new(run), range); let mut new_box = old_box.transform(new_metrics.bounding_box.size, - ScannedTextBox(new_text_box_info)); + ScannedTextBox(new_text_box_info)); new_box.new_line_pos = new_line_pos; out_boxes.push(new_box) } @@ -169,7 +170,7 @@ impl TextRunScanner { // TODO(#177): Text run creation must account for the renderability of text by // font group fonts. This is probably achieved by creating the font group above // and then letting `FontGroup` decide which `Font` to stick into the text run. - let in_box = &in_boxes[self.clump.begin() as uint]; + let in_box = &in_boxes[self.clump.begin().to_uint()]; let font_style = in_box.font_style(); let fontgroup = font_context.get_resolved_font_for_style(&font_style); let decoration = in_box.text_decoration(); @@ -184,12 +185,12 @@ impl TextRunScanner { // First, transform/compress text of all the nodes. let mut last_whitespace_in_clump = new_whitespace; - let transformed_strs: Vec<~str> = Vec::from_fn(self.clump.length() as uint, |i| { + let transformed_strs: Vec<~str> = Vec::from_fn(self.clump.length().to_uint(), |i| { // TODO(#113): We should be passing the compression context between calls to // `transform_text`, so that boxes starting and/or ending with whitespace can // be compressed correctly with respect to the text run. - let idx = i as int + self.clump.begin(); - let in_box = match in_boxes[idx as uint].specific { + let idx = CharIndex(i as int) + self.clump.begin(); + let in_box = match in_boxes[idx.to_uint()].specific { UnscannedTextBox(ref text_box_info) => &text_box_info.text, _ => fail!("Expected an unscanned text box!"), }; @@ -210,20 +211,20 @@ impl TextRunScanner { // Next, concatenate all of the transformed strings together, saving the new // character indices. let mut run_str: ~str = "".to_owned(); - let mut new_ranges: Vec> = vec![]; - let mut char_total = 0; + let mut new_ranges: Vec> = vec![]; + let mut char_total = CharIndex(0); for i in range(0, transformed_strs.len() as int) { - let added_chars = transformed_strs.get(i as uint).char_len() as int; + let added_chars = CharIndex(transformed_strs.get(i as uint).char_len() as int); new_ranges.push(Range::new(char_total, added_chars)); run_str.push_str(*transformed_strs.get(i as uint)); - char_total += added_chars; + char_total = char_total + added_chars; } // Now create the run. // TextRuns contain a cycle which is usually resolved by the teardown // sequence. If no clump takes ownership, however, it will leak. let clump = self.clump; - let run = if clump.length() != 0 && run_str.len() > 0 { + let run = if clump.length() != CharIndex(0) && run_str.len() > 0 { Some(Arc::new(~TextRun::new(&mut *fontgroup.borrow().fonts.get(0).borrow_mut(), run_str.clone(), decoration))) } else { @@ -234,25 +235,25 @@ impl TextRunScanner { debug!("TextRunScanner: pushing box(es) in range: {}", self.clump); for i in clump.each_index() { let logical_offset = i - self.clump.begin(); - let range = new_ranges.get(logical_offset as uint); - if range.length() == 0 { + let range = new_ranges.get(logical_offset.to_uint()); + if range.length() == CharIndex(0) { debug!("Elided an `UnscannedTextbox` because it was zero-length after \ - compression; {}", in_boxes[i as uint]); + compression; {}", in_boxes[i.to_uint()]); continue } let new_text_box_info = ScannedTextBoxInfo::new(run.get_ref().clone(), *range); let new_metrics = new_text_box_info.run.metrics_for_range(range); - let mut new_box = in_boxes[i as uint].transform(new_metrics.bounding_box.size, + let mut new_box = in_boxes[i.to_uint()].transform(new_metrics.bounding_box.size, ScannedTextBox(new_text_box_info)); - new_box.new_line_pos = new_line_positions.get(logical_offset as uint).new_line_pos.clone(); + new_box.new_line_pos = new_line_positions.get(logical_offset.to_uint()).new_line_pos.clone(); out_boxes.push(new_box) } } } // End of match. let end = self.clump.end(); // FIXME: borrow checker workaround - self.clump.reset(end, 0); + self.clump.reset(end, CharIndex(0)); new_whitespace } // End of `flush_clump_to_list`.