Add character index type

This is more self-documenting and may highlight errors in the future.
This commit is contained in:
Brendan Zabarauskas 2014-05-12 13:43:10 -07:00
parent 9df9f07cfd
commit 9dd533ce01
9 changed files with 161 additions and 146 deletions

View file

@ -16,6 +16,7 @@
use color::Color; use color::Color;
use render_context::RenderContext; use render_context::RenderContext;
use text::glyph::CharIndex;
use text::TextRun; use text::TextRun;
use collections::deque::Deque; use collections::deque::Deque;
@ -395,7 +396,7 @@ pub struct TextDisplayItem {
pub text_run: Arc<~TextRun>, pub text_run: Arc<~TextRun>,
/// The range of text within the text run. /// The range of text within the text run.
pub range: Range<int>, pub range: Range<CharIndex>,
/// The color of the text. /// The color of the text.
pub text_color: Color, pub text_color: Color,

View file

@ -23,7 +23,7 @@ use servo_util::geometry::Au;
use platform::font_context::FontContextHandle; use platform::font_context::FontContextHandle;
use platform::font::{FontHandle, FontTable}; use platform::font::{FontHandle, FontTable};
use render_context::RenderContext; use render_context::RenderContext;
use text::glyph::{GlyphStore, GlyphId}; use text::glyph::{CharIndex, GlyphStore, GlyphId};
use text::shaping::ShaperMethods; use text::shaping::ShaperMethods;
use text::{Shaper, TextRun}; use text::{Shaper, TextRun};
@ -330,7 +330,7 @@ impl Font {
pub fn draw_text_into_context(&mut self, pub fn draw_text_into_context(&mut self,
rctx: &RenderContext, rctx: &RenderContext,
run: &~TextRun, run: &~TextRun,
range: &Range<int>, range: &Range<CharIndex>,
baseline_origin: Point2D<Au>, baseline_origin: Point2D<Au>,
color: Color) { color: Color) {
use libc::types::common::c99::{uint16_t, uint32_t}; use libc::types::common::c99::{uint16_t, uint32_t};
@ -353,7 +353,7 @@ impl Font {
let mut origin = baseline_origin.clone(); let mut origin = baseline_origin.clone();
let mut azglyphs = vec!(); 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 (glyphs, _offset, slice_range) in run.iter_slices_for_range(range) {
for (_i, glyph) in glyphs.iter_glyphs_for_char_range(&slice_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<int>) -> RunMetrics { pub fn measure_text(&self, run: &TextRun, range: &Range<CharIndex>) -> RunMetrics {
// TODO(Issue #199): alter advance direction for RTL // TODO(Issue #199): alter advance direction for RTL
// TODO(Issue #98): using inter-char and inter-word spacing settings when measuring text // TODO(Issue #98): using inter-char and inter-word spacing settings when measuring text
let mut advance = Au(0); let mut advance = Au(0);
@ -405,7 +405,7 @@ impl Font {
pub fn measure_text_for_slice(&self, pub fn measure_text_for_slice(&self,
glyphs: &GlyphStore, glyphs: &GlyphStore,
slice_range: &Range<int>) slice_range: &Range<CharIndex>)
-> RunMetrics { -> RunMetrics {
let mut advance = Au(0); let mut advance = Au(0);
for (_i, glyph) in glyphs.iter_glyphs_for_char_range(slice_range) { for (_i, glyph) in glyphs.iter_glyphs_for_char_range(slice_range) {

View file

@ -23,6 +23,7 @@ extern crate png;
#[phase(syntax)] #[phase(syntax)]
extern crate servo_macros = "macros"; extern crate servo_macros = "macros";
extern crate servo_net = "net"; extern crate servo_net = "net";
#[phase(syntax, link)]
extern crate servo_util = "util"; extern crate servo_util = "util";
extern crate servo_msg = "msg"; extern crate servo_msg = "msg";
extern crate style; extern crate style;

View file

@ -4,7 +4,7 @@
use servo_util::vec::*; use servo_util::vec::*;
use servo_util::range; use servo_util::range;
use servo_util::range::{Range, EachIndex}; use servo_util::range::{Range, RangeIndex, EachIndex};
use servo_util::geometry::Au; use servo_util::geometry::Au;
use std::cmp::{Ord, Eq}; use std::cmp::{Ord, Eq};
@ -273,7 +273,7 @@ impl DetailedGlyph {
#[deriving(Eq, Clone)] #[deriving(Eq, Clone)]
struct DetailedGlyphRecord { struct DetailedGlyphRecord {
// source string offset/GlyphEntry offset in the TextRun // source string offset/GlyphEntry offset in the TextRun
entry_offset: int, entry_offset: CharIndex,
// offset into the detailed glyphs buffer // offset into the detailed glyphs buffer
detail_offset: int, 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 { let entry = DetailedGlyphRecord {
entry_offset: entry_offset, entry_offset: entry_offset,
detail_offset: self.detail_buffer.len() as int, detail_offset: self.detail_buffer.len() as int,
@ -340,7 +340,7 @@ impl<'a> DetailedGlyphStore {
self.lookup_is_sorted = false; 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] { -> &'a [DetailedGlyph] {
debug!("Requesting detailed glyphs[n={}] for entry[off={}]", count, entry_offset); 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, fn get_detailed_glyph_with_index(&'a self,
entry_offset: int, entry_offset: CharIndex,
detail_offset: u16) detail_offset: u16)
-> &'a DetailedGlyph { -> &'a DetailedGlyph {
assert!((detail_offset as uint) <= self.detail_buffer.len()); 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 // Rather than eagerly assembling and copying glyph data, it only retrieves
// values as they are needed from the GlyphStore, using provided offsets. // values as they are needed from the GlyphStore, using provided offsets.
pub enum GlyphInfo<'a> { pub enum GlyphInfo<'a> {
SimpleGlyphInfo(&'a GlyphStore, int), SimpleGlyphInfo(&'a GlyphStore, CharIndex),
DetailGlyphInfo(&'a GlyphStore, int, u16), DetailGlyphInfo(&'a GlyphStore, CharIndex, u16),
} }
impl<'a> GlyphInfo<'a> { impl<'a> GlyphInfo<'a> {
pub fn id(self) -> GlyphId { pub fn id(self) -> GlyphId {
match self { 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) => { DetailGlyphInfo(store, entry_i, detail_j) => {
store.detail_store.get_detailed_glyph_with_index(entry_i, detail_j).id 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 _ // FIXME: Resolution conflicts with IteratorUtil trait so adding trailing _
pub fn advance(self) -> Au { pub fn advance(self) -> Au {
match self { 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) => { DetailGlyphInfo(store, entry_i, detail_j) => {
store.detail_store.get_detailed_glyph_with_index(entry_i, detail_j).advance store.detail_store.get_detailed_glyph_with_index(entry_i, detail_j).advance
} }
@ -506,6 +506,12 @@ pub struct GlyphStore {
is_whitespace: bool, 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 { impl<'a> GlyphStore {
// Initializes the glyph store, but doesn't actually shape anything. // Initializes the glyph store, but doesn't actually shape anything.
// Use the set_glyph, set_glyphs() methods to store glyph data. // Use the set_glyph, set_glyphs() methods to store glyph data.
@ -519,8 +525,8 @@ impl<'a> GlyphStore {
} }
} }
pub fn char_len(&self) -> int { pub fn char_len(&self) -> CharIndex {
self.entry_buffer.len() as int CharIndex(self.entry_buffer.len() as int)
} }
pub fn is_whitespace(&self) -> bool { pub fn is_whitespace(&self) -> bool {
@ -531,7 +537,7 @@ impl<'a> GlyphStore {
self.detail_store.ensure_sorted(); 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 { fn glyph_is_compressible(data: &GlyphData) -> bool {
is_simple_glyph_id(data.id) is_simple_glyph_id(data.id)
&& is_simple_advance(data.advance) && is_simple_advance(data.advance)
@ -540,7 +546,7 @@ impl<'a> GlyphStore {
} }
assert!(data.ligature_start); // can't compress ligature continuation glyphs. 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)) { let entry = match (data.is_missing, glyph_is_compressible(data)) {
(true, _) => GlyphEntry::missing(1), (true, _) => GlyphEntry::missing(1),
@ -550,13 +556,13 @@ impl<'a> GlyphStore {
self.detail_store.add_detailed_glyphs_for_entry(i, glyph); self.detail_store.add_detailed_glyphs_for_entry(i, glyph);
GlyphEntry::complex(data.cluster_start, data.ligature_start, 1) 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]) { pub fn add_glyphs_for_char_index(&mut self, i: CharIndex, data_for_glyphs: &[GlyphData]) {
assert!(i < self.entry_buffer.len() as int); assert!(i < CharIndex(self.entry_buffer.len() as int));
assert!(data_for_glyphs.len() > 0); assert!(data_for_glyphs.len() > 0);
let glyph_count = data_for_glyphs.len() as int; let glyph_count = data_for_glyphs.len() as int;
@ -576,33 +582,33 @@ impl<'a> GlyphStore {
first_glyph_data.ligature_start, first_glyph_data.ligature_start,
glyph_count) 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); 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. // 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) { pub fn add_nonglyph_for_char_index(&mut self, i: CharIndex, cluster_start: bool, ligature_start: bool) {
assert!(i < self.entry_buffer.len() as int); assert!(i < CharIndex(self.entry_buffer.len() as int));
let entry = GlyphEntry::complex(cluster_start, ligature_start, 0); let entry = GlyphEntry::complex(cluster_start, ligature_start, 0);
debug!("adding spacer for chracter without associated glyph[idx={}]", i); 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> { pub fn iter_glyphs_for_char_index(&'a self, i: CharIndex) -> GlyphIterator<'a> {
self.iter_glyphs_for_char_range(&Range::new(i as int, 1)) self.iter_glyphs_for_char_range(&Range::new(i, CharIndex(1)))
} }
#[inline] #[inline]
pub fn iter_glyphs_for_char_range(&'a self, rang: &Range<int>) -> GlyphIterator<'a> { pub fn iter_glyphs_for_char_range(&'a self, rang: &Range<CharIndex>) -> GlyphIterator<'a> {
if rang.begin() >= self.entry_buffer.len() as int { if rang.begin() >= CharIndex(self.entry_buffer.len() as int) {
fail!("iter_glyphs_for_range: range.begin beyond length!"); 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!"); fail!("iter_glyphs_for_range: range.end beyond length!");
} }
@ -615,76 +621,76 @@ impl<'a> GlyphStore {
} }
// getter methods // getter methods
pub fn char_is_space(&self, i: int) -> bool { pub fn char_is_space(&self, i: CharIndex) -> bool {
assert!(i < self.entry_buffer.len() as int); assert!(i < CharIndex(self.entry_buffer.len() as int));
self.entry_buffer.get(i as uint).char_is_space() self.entry_buffer.get(i.to_uint()).char_is_space()
} }
pub fn char_is_tab(&self, i: int) -> bool { pub fn char_is_tab(&self, i: CharIndex) -> bool {
assert!(i < self.entry_buffer.len() as int); assert!(i < CharIndex(self.entry_buffer.len() as int));
self.entry_buffer.get(i as uint).char_is_tab() self.entry_buffer.get(i.to_uint()).char_is_tab()
} }
pub fn char_is_newline(&self, i: int) -> bool { pub fn char_is_newline(&self, i: CharIndex) -> bool {
assert!(i < self.entry_buffer.len() as int); assert!(i < CharIndex(self.entry_buffer.len() as int));
self.entry_buffer.get(i as uint).char_is_newline() self.entry_buffer.get(i.to_uint()).char_is_newline()
} }
pub fn is_ligature_start(&self, i: int) -> bool { pub fn is_ligature_start(&self, i: CharIndex) -> bool {
assert!(i < self.entry_buffer.len() as int); assert!(i < CharIndex(self.entry_buffer.len() as int));
self.entry_buffer.get(i as uint).is_ligature_start() self.entry_buffer.get(i.to_uint()).is_ligature_start()
} }
pub fn is_cluster_start(&self, i: int) -> bool { pub fn is_cluster_start(&self, i: CharIndex) -> bool {
assert!(i < self.entry_buffer.len() as int); assert!(i < CharIndex(self.entry_buffer.len() as int));
self.entry_buffer.get(i as uint).is_cluster_start() self.entry_buffer.get(i.to_uint()).is_cluster_start()
} }
pub fn can_break_before(&self, i: int) -> BreakType { pub fn can_break_before(&self, i: CharIndex) -> BreakType {
assert!(i < self.entry_buffer.len() as int); assert!(i < CharIndex(self.entry_buffer.len() as int));
self.entry_buffer.get(i as uint).can_break_before() self.entry_buffer.get(i.to_uint()).can_break_before()
} }
// setter methods // setter methods
pub fn set_char_is_space(&mut self, i: int) { pub fn set_char_is_space(&mut self, i: CharIndex) {
assert!(i < self.entry_buffer.len() as int); assert!(i < CharIndex(self.entry_buffer.len() as int));
let entry = *self.entry_buffer.get(i as uint); let entry = *self.entry_buffer.get(i.to_uint());
*self.entry_buffer.get_mut(i as uint) = entry.set_char_is_space(); *self.entry_buffer.get_mut(i.to_uint()) = entry.set_char_is_space();
} }
pub fn set_char_is_tab(&mut self, i: int) { pub fn set_char_is_tab(&mut self, i: CharIndex) {
assert!(i < self.entry_buffer.len() as int); assert!(i < CharIndex(self.entry_buffer.len() as int));
let entry = *self.entry_buffer.get(i as uint); let entry = *self.entry_buffer.get(i.to_uint());
*self.entry_buffer.get_mut(i as uint) = entry.set_char_is_tab(); *self.entry_buffer.get_mut(i.to_uint()) = entry.set_char_is_tab();
} }
pub fn set_char_is_newline(&mut self, i: int) { pub fn set_char_is_newline(&mut self, i: CharIndex) {
assert!(i < self.entry_buffer.len() as int); assert!(i < CharIndex(self.entry_buffer.len() as int));
let entry = *self.entry_buffer.get(i as uint); let entry = *self.entry_buffer.get(i.to_uint());
*self.entry_buffer.get_mut(i as uint) = entry.set_char_is_newline(); *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) { pub fn set_can_break_before(&mut self, i: CharIndex, t: BreakType) {
assert!(i < self.entry_buffer.len() as int); assert!(i < CharIndex(self.entry_buffer.len() as int));
let entry = *self.entry_buffer.get(i as uint); let entry = *self.entry_buffer.get(i.to_uint());
*self.entry_buffer.get_mut(i as uint) = entry.set_can_break_before(t); *self.entry_buffer.get_mut(i.to_uint()) = entry.set_can_break_before(t);
} }
} }
pub struct GlyphIterator<'a> { pub struct GlyphIterator<'a> {
store: &'a GlyphStore, store: &'a GlyphStore,
char_index: int, char_index: CharIndex,
char_range: EachIndex<int, int>, char_range: EachIndex<int, CharIndex>,
glyph_range: Option<EachIndex<int, int>>, glyph_range: Option<EachIndex<int, CharIndex>>,
} }
impl<'a> GlyphIterator<'a> { impl<'a> GlyphIterator<'a> {
// Slow path when there is a glyph range. // Slow path when there is a glyph range.
#[inline(never)] #[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() { match self.glyph_range.get_mut_ref().next() {
Some(j) => Some((self.char_index, 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 => { None => {
// No more glyphs for current character. Try to get another. // No more glyphs for current character. Try to get another.
self.glyph_range = None; self.glyph_range = None;
@ -695,15 +701,15 @@ impl<'a> GlyphIterator<'a> {
// Slow path when there is a complex glyph. // Slow path when there is a complex glyph.
#[inline(never)] #[inline(never)]
fn next_complex_glyph(&mut self, entry: &GlyphEntry, i: int) fn next_complex_glyph(&mut self, entry: &GlyphEntry, i: CharIndex)
-> Option<(int, GlyphInfo<'a>)> { -> Option<(CharIndex, GlyphInfo<'a>)> {
let glyphs = self.store.detail_store.get_detailed_glyphs_for_entry(i, entry.glyph_count()); 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() 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 // I tried to start with something simpler and apply FlatMap, but the
// inability to store free variables in the FlatMap struct was problematic. // 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 // slow paths, which should not be inlined, are `next_glyph_range()` and
// `next_complex_glyph()`. // `next_complex_glyph()`.
#[inline(always)] #[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 // Would use 'match' here but it borrows contents in a way that
// interferes with mutation. // interferes with mutation.
if self.glyph_range.is_some() { if self.glyph_range.is_some() {
@ -721,8 +727,8 @@ impl<'a> Iterator<(int, GlyphInfo<'a>)> for GlyphIterator<'a> {
match self.char_range.next() { match self.char_range.next() {
Some(i) => { Some(i) => {
self.char_index = i; self.char_index = i;
assert!(i < self.store.entry_buffer.len() as int); assert!(i < CharIndex(self.store.entry_buffer.len() as int));
let entry = self.store.entry_buffer.get(i as uint); let entry = self.store.entry_buffer.get(i.to_uint());
if entry.is_simple() { if entry.is_simple() {
Some((self.char_index, SimpleGlyphInfo(self.store, i))) Some((self.char_index, SimpleGlyphInfo(self.store, i)))
} else { } else {

View file

@ -6,7 +6,7 @@ extern crate harfbuzz;
use font::{Font, FontHandleMethods, FontTableMethods, FontTableTag}; use font::{Font, FontHandleMethods, FontTableMethods, FontTableTag};
use platform::font::FontTable; use platform::font::FontTable;
use text::glyph::{GlyphStore, GlyphId, GlyphData}; use text::glyph::{CharIndex, GlyphStore, GlyphId, GlyphData};
use text::shaping::ShaperMethods; use text::shaping::ShaperMethods;
use text::util::{float_to_fixed, fixed_to_float}; use text::util::{float_to_fixed, fixed_to_float};
@ -229,7 +229,7 @@ impl Shaper {
// GlyphStore records are indexed by character, not byte offset. // GlyphStore records are indexed by character, not byte offset.
// so, we must be careful to increment this when saving glyph entries. // 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); assert!(glyph_count <= char_max);
@ -435,7 +435,7 @@ impl Shaper {
drop(range.ch); drop(range.ch);
i = range.next as int; i = range.next as int;
if i >= covered_byte_span.end() { break; } 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); glyphs.add_nonglyph_for_char_index(char_idx, false, false);
} }
} }
@ -445,7 +445,7 @@ impl Shaper {
glyph_span.reset(end, 0); glyph_span.reset(end, 0);
let end = char_byte_span.end();; // FIXME: borrow checker workaround let end = char_byte_span.end();; // FIXME: borrow checker workaround
char_byte_span.reset(end, 0); 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 // this must be called after adding all glyph data; it sorts the

View file

@ -8,7 +8,7 @@ use servo_util::range::Range;
use std::slice::Items; use std::slice::Items;
use style::computed_values::text_decoration; use style::computed_values::text_decoration;
use sync::Arc; use sync::Arc;
use text::glyph::GlyphStore; use text::glyph::{CharIndex, GlyphStore};
/// A text run. /// A text run.
#[deriving(Clone)] #[deriving(Clone)]
@ -23,14 +23,14 @@ pub struct TextRun {
pub struct SliceIterator<'a> { pub struct SliceIterator<'a> {
glyph_iter: Items<'a, Arc<GlyphStore>>, glyph_iter: Items<'a, Arc<GlyphStore>>,
range: Range<int>, range: Range<CharIndex>,
offset: int, offset: CharIndex,
} }
impl<'a> Iterator<(&'a GlyphStore, int, Range<int>)> for SliceIterator<'a> { impl<'a> Iterator<(&'a GlyphStore, CharIndex, Range<CharIndex>)> for SliceIterator<'a> {
// inline(always) due to the inefficient rt failures messing up inline heuristics, I think. // inline(always) due to the inefficient rt failures messing up inline heuristics, I think.
#[inline(always)] #[inline(always)]
fn next(&mut self) -> Option<(&'a GlyphStore, int, Range<int>)> { fn next(&mut self) -> Option<(&'a GlyphStore, CharIndex, Range<CharIndex>)> {
loop { loop {
let slice_glyphs = self.glyph_iter.next(); let slice_glyphs = self.glyph_iter.next();
if slice_glyphs.is_none() { if slice_glyphs.is_none() {
@ -40,10 +40,10 @@ impl<'a> Iterator<(&'a GlyphStore, int, Range<int>)> for SliceIterator<'a> {
let slice_range = Range::new(self.offset, slice_glyphs.char_len()); let slice_range = Range::new(self.offset, slice_glyphs.char_len());
let mut char_range = self.range.intersect(&slice_range); 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; let old_offset = self.offset;
self.offset += slice_glyphs.char_len(); self.offset = self.offset + slice_glyphs.char_len();
if !char_range.is_empty() { if !char_range.is_empty() {
return Some((&**slice_glyphs, old_offset, char_range)) return Some((&**slice_glyphs, old_offset, char_range))
} }
@ -52,24 +52,24 @@ impl<'a> Iterator<(&'a GlyphStore, int, Range<int>)> for SliceIterator<'a> {
} }
pub struct LineIterator<'a> { pub struct LineIterator<'a> {
range: Range<int>, range: Range<CharIndex>,
clump: Option<Range<int>>, clump: Option<Range<CharIndex>>,
slices: SliceIterator<'a>, slices: SliceIterator<'a>,
} }
impl<'a> Iterator<Range<int>> for LineIterator<'a> { impl<'a> Iterator<Range<CharIndex>> for LineIterator<'a> {
fn next(&mut self) -> Option<Range<int>> { fn next(&mut self) -> Option<Range<CharIndex>> {
// Loop until we hit whitespace and are in a clump. // Loop until we hit whitespace and are in a clump.
loop { loop {
match self.slices.next() { match self.slices.next() {
Some((glyphs, offset, slice_range)) => { Some((glyphs, offset, slice_range)) => {
match (glyphs.is_whitespace(), self.clump) { match (glyphs.is_whitespace(), self.clump) {
(false, Some(ref mut c)) => { (false, Some(ref mut c)) => {
c.extend_by(slice_range.length().to_int().unwrap()); c.extend_by(slice_range.length());
} }
(false, None) => { (false, None) => {
let mut c = slice_range; let mut c = slice_range;
c.shift_by(offset.to_int().unwrap()); c.shift_by(offset);
self.clump = Some(c); self.clump = Some(c);
} }
(true, None) => { /* chomp whitespace */ } (true, None) => { /* chomp whitespace */ }
@ -165,8 +165,8 @@ impl<'a> TextRun {
glyphs glyphs
} }
pub fn char_len(&self) -> int { pub fn char_len(&self) -> CharIndex {
self.glyphs.iter().fold(0, |len, slice_glyphs| { self.glyphs.iter().fold(CharIndex(0), |len, slice_glyphs| {
len + slice_glyphs.char_len() len + slice_glyphs.char_len()
}) })
} }
@ -175,14 +175,14 @@ impl<'a> TextRun {
&*self.glyphs &*self.glyphs
} }
pub fn range_is_trimmable_whitespace(&self, range: &Range<int>) -> bool { pub fn range_is_trimmable_whitespace(&self, range: &Range<CharIndex>) -> bool {
for (slice_glyphs, _, _) in self.iter_slices_for_range(range) { for (slice_glyphs, _, _) in self.iter_slices_for_range(range) {
if !slice_glyphs.is_whitespace() { return false; } if !slice_glyphs.is_whitespace() { return false; }
} }
true true
} }
pub fn metrics_for_range(&self, range: &Range<int>) -> RunMetrics { pub fn metrics_for_range(&self, range: &Range<CharIndex>) -> RunMetrics {
// TODO(Issue #199): alter advance direction for RTL // TODO(Issue #199): alter advance direction for RTL
// TODO(Issue #98): using inter-char and inter-word spacing settings when measuring text // TODO(Issue #98): using inter-char and inter-word spacing settings when measuring text
let mut advance = Au(0); let mut advance = Au(0);
@ -194,14 +194,14 @@ impl<'a> TextRun {
RunMetrics::new(advance, self.font_metrics.ascent, self.font_metrics.descent) RunMetrics::new(advance, self.font_metrics.ascent, self.font_metrics.descent)
} }
pub fn metrics_for_slice(&self, glyphs: &GlyphStore, slice_range: &Range<int>) -> RunMetrics { pub fn metrics_for_slice(&self, glyphs: &GlyphStore, slice_range: &Range<CharIndex>) -> RunMetrics {
let mut advance = Au(0); let mut advance = Au(0);
for (_i, glyph) in glyphs.iter_glyphs_for_char_range(slice_range) { for (_i, glyph) in glyphs.iter_glyphs_for_char_range(slice_range) {
advance = advance + glyph.advance(); advance = advance + glyph.advance();
} }
RunMetrics::new(advance, self.font_metrics.ascent, self.font_metrics.descent) RunMetrics::new(advance, self.font_metrics.ascent, self.font_metrics.descent)
} }
pub fn min_width_for_range(&self, range: &Range<int>) -> Au { pub fn min_width_for_range(&self, range: &Range<CharIndex>) -> Au {
let mut max_piece_width = Au(0); let mut max_piece_width = Au(0);
debug!("iterating outer range {:?}", range); debug!("iterating outer range {:?}", range);
for (_, offset, slice_range) in self.iter_slices_for_range(range) { for (_, offset, slice_range) in self.iter_slices_for_range(range) {
@ -212,15 +212,15 @@ impl<'a> TextRun {
max_piece_width max_piece_width
} }
pub fn iter_slices_for_range(&'a self, range: &Range<int>) -> SliceIterator<'a> { pub fn iter_slices_for_range(&'a self, range: &Range<CharIndex>) -> SliceIterator<'a> {
SliceIterator { SliceIterator {
glyph_iter: self.glyphs.iter(), glyph_iter: self.glyphs.iter(),
range: *range, range: *range,
offset: 0, offset: CharIndex(0),
} }
} }
pub fn iter_natural_lines_for_range(&'a self, range: &Range<int>) -> LineIterator<'a> { pub fn iter_natural_lines_for_range(&'a self, range: &Range<CharIndex>) -> LineIterator<'a> {
LineIterator { LineIterator {
range: *range, range: *range,
clump: None, clump: None,

View file

@ -2,6 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use text::glyph::CharIndex;
#[deriving(Eq)] #[deriving(Eq)]
pub enum CompressionMode { pub enum CompressionMode {
CompressNone, CompressNone,
@ -20,11 +22,13 @@ pub enum CompressionMode {
// * Issue #114: record skipped and kept chars for mapping original to new text // * Issue #114: record skipped and kept chars for mapping original to new text
// //
// * Untracked: various edge cases for bidi, CJK, etc. // * Untracked: various edge cases for bidi, CJK, etc.
pub fn transform_text(text: &str, mode: CompressionMode, incoming_whitespace: bool, new_line_pos: &mut Vec<int>) -> (~str, bool) { pub fn transform_text(text: &str, mode: CompressionMode,
incoming_whitespace: bool,
new_line_pos: &mut Vec<CharIndex>) -> (~str, bool) {
let mut out_str: ~str = "".to_owned(); let mut out_str: ~str = "".to_owned();
let out_whitespace = match mode { let out_whitespace = match mode {
CompressNone | DiscardNewline => { CompressNone | DiscardNewline => {
let mut new_line_index = 0; let mut new_line_index = CharIndex(0);
for ch in text.chars() { for ch in text.chars() {
if is_discardable_char(ch, mode) { if is_discardable_char(ch, mode) {
// TODO: record skipped char // 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 // Save new-line's position for line-break
// This value is relative(not absolute) // This value is relative(not absolute)
new_line_pos.push(new_line_index); new_line_pos.push(new_line_index);
new_line_index = 0; new_line_index = CharIndex(0);
} }
if ch != '\n' { if ch != '\n' {
new_line_index += 1; new_line_index = new_line_index + CharIndex(1);
} }
out_str.push_char(ch); out_str.push_char(ch);
} }

View file

@ -28,6 +28,7 @@ use gfx::display_list::{LineDisplayItemClass, OpaqueNode, PseudoDisplayItemClass
use gfx::display_list::{SolidColorDisplayItem, SolidColorDisplayItemClass, StackingLevel}; use gfx::display_list::{SolidColorDisplayItem, SolidColorDisplayItemClass, StackingLevel};
use gfx::display_list::{TextDecorations, TextDisplayItem, TextDisplayItemClass}; use gfx::display_list::{TextDecorations, TextDisplayItem, TextDisplayItemClass};
use gfx::font::FontStyle; use gfx::font::FontStyle;
use gfx::text::glyph::CharIndex;
use gfx::text::text_run::TextRun; use gfx::text::text_run::TextRun;
use servo_msg::constellation_msg::{ConstellationChan, FrameRectMsg, PipelineId, SubpageId}; use servo_msg::constellation_msg::{ConstellationChan, FrameRectMsg, PipelineId, SubpageId};
use servo_net::image::holder::{ImageHolder, LocalImageCacheHandle}; use servo_net::image::holder::{ImageHolder, LocalImageCacheHandle};
@ -95,7 +96,7 @@ pub struct Box {
/// New-line chracter(\n)'s positions(relative, not absolute) /// New-line chracter(\n)'s positions(relative, not absolute)
/// ///
/// FIXME(#2260, pcwalton): This is very inefficient; remove. /// FIXME(#2260, pcwalton): This is very inefficient; remove.
pub new_line_pos: Vec<int>, pub new_line_pos: Vec<CharIndex>,
} }
/// Info specific to the kind of box. Keep this enum small. /// Info specific to the kind of box. Keep this enum small.
@ -226,12 +227,12 @@ pub struct ScannedTextBoxInfo {
pub run: Arc<~TextRun>, pub run: Arc<~TextRun>,
/// The range within the above text run that this represents. /// The range within the above text run that this represents.
pub range: Range<int>, pub range: Range<CharIndex>,
} }
impl ScannedTextBoxInfo { impl ScannedTextBoxInfo {
/// Creates the information specific to a scanned text box from a range and a text run. /// Creates the information specific to a scanned text box from a range and a text run.
pub fn new(run: Arc<~TextRun>, range: Range<int>) -> ScannedTextBoxInfo { pub fn new(run: Arc<~TextRun>, range: Range<CharIndex>) -> ScannedTextBoxInfo {
ScannedTextBoxInfo { ScannedTextBoxInfo {
run: run, run: run,
range: range, range: range,
@ -1108,7 +1109,8 @@ impl Box {
let cur_new_line_pos = new_line_pos.shift().unwrap(); 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 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. // Left box is for left text of first founded new-line character.
let left_box = { let left_box = {
@ -1120,7 +1122,7 @@ impl Box {
}; };
// Right box is for right text of first founded new-line character. // 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_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 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)); 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) => { ScannedTextBox(ref text_box_info) => {
let mut pieces_processed_count: uint = 0; let mut pieces_processed_count: uint = 0;
let mut remaining_width: Au = max_width; let mut remaining_width: Au = max_width;
let mut left_range = Range::new(text_box_info.range.begin(), 0); let mut left_range = Range::new(text_box_info.range.begin(), CharIndex(0));
let mut right_range: Option<Range<int>> = None; let mut right_range: Option<Range<CharIndex>> = None;
debug!("split_to_width: splitting text box (strlen={:u}, range={}, \ debug!("split_to_width: splitting text box (strlen={:u}, range={}, \
avail_width={})", avail_width={})",
@ -1171,11 +1173,11 @@ impl Box {
if starts_line && pieces_processed_count == 0 && glyphs.is_whitespace() { if starts_line && pieces_processed_count == 0 && glyphs.is_whitespace() {
debug!("split_to_width: case=skipping leading trimmable 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 { } else {
debug!("split_to_width: case=enlarging span"); debug!("split_to_width: case=enlarging span");
remaining_width = remaining_width - advance; remaining_width = remaining_width - advance;
left_range.extend_by(slice_range.length() as int); left_range.extend_by(slice_range.length());
} }
} else { } else {
// The advance is more than the remaining width. // 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 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); 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; new_metrics.bounding_box.size.height = self.border_box.size.height;
@ -1222,7 +1224,7 @@ impl Box {
None None
}; };
let right_box = right_range.map_or(None, |range: Range<int>| { let right_box = right_range.map_or(None, |range: Range<CharIndex>| {
let new_text_box_info = ScannedTextBoxInfo::new(text_box_info.run.clone(), let new_text_box_info = ScannedTextBoxInfo::new(text_box_info.run.clone(),
range); range);
let mut new_metrics = new_text_box_info.run.metrics_for_range(&range); let mut new_metrics = new_text_box_info.run.metrics_for_range(&range);

View file

@ -10,6 +10,7 @@ use layout::inline::InlineBoxes;
use gfx::font::{FontMetrics, FontStyle}; use gfx::font::{FontMetrics, FontStyle};
use gfx::font_context::FontContext; use gfx::font_context::FontContext;
use gfx::text::glyph::CharIndex;
use gfx::text::text_run::TextRun; use gfx::text::text_run::TextRun;
use gfx::text::util::{CompressWhitespaceNewline, transform_text, CompressNone}; use gfx::text::util::{CompressWhitespaceNewline, transform_text, CompressNone};
use servo_util::geometry::Au; use servo_util::geometry::Au;
@ -20,7 +21,7 @@ use style::computed_values::{font_family, line_height, white_space};
use sync::Arc; use sync::Arc;
struct NewLinePositions { struct NewLinePositions {
new_line_pos: Vec<int>, new_line_pos: Vec<CharIndex>,
} }
// A helper function. // 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. /// A stack-allocated object for scanning an inline flow into `TextRun`-containing `TextBox`es.
pub struct TextRunScanner { pub struct TextRunScanner {
pub clump: Range<int>, pub clump: Range<CharIndex>,
} }
impl TextRunScanner { impl TextRunScanner {
@ -63,11 +64,11 @@ impl TextRunScanner {
last_whitespace); last_whitespace);
} }
self.clump.extend_by(1); self.clump.extend_by(CharIndex(1));
} }
// Handle remaining clumps. // Handle remaining clumps.
if self.clump.length() > 0 { if self.clump.length() > CharIndex(0) {
drop(self.flush_clump_to_list(font_context, drop(self.flush_clump_to_list(font_context,
old_boxes.as_slice(), old_boxes.as_slice(),
&mut new_boxes, &mut new_boxes,
@ -99,12 +100,12 @@ impl TextRunScanner {
out_boxes: &mut Vec<Box>, out_boxes: &mut Vec<Box>,
last_whitespace: bool) last_whitespace: bool)
-> bool { -> bool {
assert!(self.clump.length() > 0); assert!(self.clump.length() > CharIndex(0));
debug!("TextRunScanner: flushing boxes in range={}", self.clump); 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, UnscannedTextBox(_) => true,
_ => false, _ => false,
}; };
@ -117,11 +118,11 @@ impl TextRunScanner {
(true, false) => { (true, false) => {
// FIXME(pcwalton): Stop cloning boxes, as above. // FIXME(pcwalton): Stop cloning boxes, as above.
debug!("TextRunScanner: pushing single non-text box in range: {}", self.clump); 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) out_boxes.push(new_box)
}, },
(true, true) => { (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 { let text = match old_box.specific {
UnscannedTextBox(ref text_box_info) => &text_box_info.text, UnscannedTextBox(ref text_box_info) => &text_box_info.text,
_ => fail!("Expected an unscanned text box!"), _ => fail!("Expected an unscanned text box!"),
@ -156,11 +157,11 @@ impl TextRunScanner {
debug!("TextRunScanner: pushing single text box in range: {} ({})", debug!("TextRunScanner: pushing single text box in range: {} ({})",
self.clump, self.clump,
*text); *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_metrics = run.metrics_for_range(&range);
let new_text_box_info = ScannedTextBoxInfo::new(Arc::new(run), range); let new_text_box_info = ScannedTextBoxInfo::new(Arc::new(run), range);
let mut new_box = old_box.transform(new_metrics.bounding_box.size, 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; new_box.new_line_pos = new_line_pos;
out_boxes.push(new_box) out_boxes.push(new_box)
} }
@ -169,7 +170,7 @@ impl TextRunScanner {
// TODO(#177): Text run creation must account for the renderability of text by // 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 // 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. // 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 font_style = in_box.font_style();
let fontgroup = font_context.get_resolved_font_for_style(&font_style); let fontgroup = font_context.get_resolved_font_for_style(&font_style);
let decoration = in_box.text_decoration(); let decoration = in_box.text_decoration();
@ -184,12 +185,12 @@ impl TextRunScanner {
// First, transform/compress text of all the nodes. // First, transform/compress text of all the nodes.
let mut last_whitespace_in_clump = new_whitespace; 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 // TODO(#113): We should be passing the compression context between calls to
// `transform_text`, so that boxes starting and/or ending with whitespace can // `transform_text`, so that boxes starting and/or ending with whitespace can
// be compressed correctly with respect to the text run. // be compressed correctly with respect to the text run.
let idx = i as int + self.clump.begin(); let idx = CharIndex(i as int) + self.clump.begin();
let in_box = match in_boxes[idx as uint].specific { let in_box = match in_boxes[idx.to_uint()].specific {
UnscannedTextBox(ref text_box_info) => &text_box_info.text, UnscannedTextBox(ref text_box_info) => &text_box_info.text,
_ => fail!("Expected an unscanned text box!"), _ => fail!("Expected an unscanned text box!"),
}; };
@ -210,20 +211,20 @@ impl TextRunScanner {
// Next, concatenate all of the transformed strings together, saving the new // Next, concatenate all of the transformed strings together, saving the new
// character indices. // character indices.
let mut run_str: ~str = "".to_owned(); let mut run_str: ~str = "".to_owned();
let mut new_ranges: Vec<Range<int>> = vec![]; let mut new_ranges: Vec<Range<CharIndex>> = vec![];
let mut char_total = 0; let mut char_total = CharIndex(0);
for i in range(0, transformed_strs.len() as int) { 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)); new_ranges.push(Range::new(char_total, added_chars));
run_str.push_str(*transformed_strs.get(i as uint)); run_str.push_str(*transformed_strs.get(i as uint));
char_total += added_chars; char_total = char_total + added_chars;
} }
// Now create the run. // Now create the run.
// TextRuns contain a cycle which is usually resolved by the teardown // TextRuns contain a cycle which is usually resolved by the teardown
// sequence. If no clump takes ownership, however, it will leak. // sequence. If no clump takes ownership, however, it will leak.
let clump = self.clump; 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(), Some(Arc::new(~TextRun::new(&mut *fontgroup.borrow().fonts.get(0).borrow_mut(),
run_str.clone(), decoration))) run_str.clone(), decoration)))
} else { } else {
@ -234,25 +235,25 @@ impl TextRunScanner {
debug!("TextRunScanner: pushing box(es) in range: {}", self.clump); debug!("TextRunScanner: pushing box(es) in range: {}", self.clump);
for i in clump.each_index() { for i in clump.each_index() {
let logical_offset = i - self.clump.begin(); let logical_offset = i - self.clump.begin();
let range = new_ranges.get(logical_offset as uint); let range = new_ranges.get(logical_offset.to_uint());
if range.length() == 0 { if range.length() == CharIndex(0) {
debug!("Elided an `UnscannedTextbox` because it was zero-length after \ debug!("Elided an `UnscannedTextbox` because it was zero-length after \
compression; {}", in_boxes[i as uint]); compression; {}", in_boxes[i.to_uint()]);
continue continue
} }
let new_text_box_info = ScannedTextBoxInfo::new(run.get_ref().clone(), *range); 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 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)); 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) out_boxes.push(new_box)
} }
} }
} // End of match. } // End of match.
let end = self.clump.end(); // FIXME: borrow checker workaround let end = self.clump.end(); // FIXME: borrow checker workaround
self.clump.reset(end, 0); self.clump.reset(end, CharIndex(0));
new_whitespace new_whitespace
} // End of `flush_clump_to_list`. } // End of `flush_clump_to_list`.