More TextRange refactoring.

This commit is contained in:
Brian J. Burg 2012-10-18 11:53:25 -07:00
parent efca6fedfc
commit 0f050e7835
5 changed files with 44 additions and 105 deletions

View file

@ -187,47 +187,47 @@ impl RenderBox : RenderBoxMethods {
let mut i : uint = 0; let mut i : uint = 0;
let mut remaining_width : au = max_width; let mut remaining_width : au = max_width;
let mut left_offset : uint = data.offset; let mut left_offset : uint = data.range.begin();
let mut left_length : uint = 0; let mut left_length : uint = 0;
let mut right_offset : Option<uint> = None; let mut right_offset : Option<uint> = None;
let mut right_length : Option<uint> = None; let mut right_length : Option<uint> = None;
debug!("split_to_width: splitting text box (strlen=%u, off=%u, len=%u, avail_width=%?)", debug!("split_to_width: splitting text box (strlen=%u, off=%u, len=%u, avail_width=%?)",
data.run.text.len(), data.offset, data.length, max_width); data.run.text.len(), data.range.begin(), data.range.length(), max_width);
do data.run.iter_indivisible_pieces_for_range(TextRange(data.offset, data.length)) |subrange| { do data.run.iter_indivisible_pieces_for_range(data.range) |piece_range| {
debug!("split_to_width: considering range (off=%u, len=%u, remain_width=%?)", debug!("split_to_width: considering range (off=%u, len=%u, remain_width=%?)",
subrange.begin(), subrange.length(), remaining_width); piece_range.begin(), piece_range.length(), remaining_width);
let metrics = data.run.metrics_for_range(subrange); let metrics = data.run.metrics_for_range(piece_range);
let advance = metrics.advance_width; let advance = metrics.advance_width;
let should_continue : bool; let should_continue : bool;
if advance <= remaining_width { if advance <= remaining_width {
should_continue = true; should_continue = true;
if starts_line && i == 0 && data.run.range_is_trimmable_whitespace(subrange) { if starts_line && i == 0 && data.run.range_is_trimmable_whitespace(piece_range) {
debug!("split_to_width: case=skipping leading trimmable whitespace"); debug!("split_to_width: case=skipping leading trimmable whitespace");
left_offset += subrange.length(); left_offset += piece_range.length();
} else { } else {
debug!("split_to_width: case=enlarging span"); debug!("split_to_width: case=enlarging span");
remaining_width -= advance; remaining_width -= advance;
left_length += subrange.length(); left_length += piece_range.length();
} }
} else { /* advance > remaining_width */ } else { /* advance > remaining_width */
should_continue = false; should_continue = false;
if data.run.range_is_trimmable_whitespace(subrange) { if data.run.range_is_trimmable_whitespace(piece_range) {
// if there are still things after the trimmable whitespace, create right chunk // if there are still things after the trimmable whitespace, create right chunk
if subrange.end() < data.offset + data.length { if piece_range.end() < data.range.end() {
debug!("split_to_width: case=skipping trimmable trailing whitespace, then split remainder"); debug!("split_to_width: case=skipping trimmable trailing whitespace, then split remainder");
right_offset = Some(subrange.end()); right_offset = Some(piece_range.end());
right_length = Some((data.offset + data.length) - subrange.end()); right_length = Some(data.range.end() - piece_range.end());
} else { } else {
debug!("split_to_width: case=skipping trimmable trailing whitespace"); debug!("split_to_width: case=skipping trimmable trailing whitespace");
} }
} else if subrange.begin() < data.length + data.offset { } else if piece_range.begin() < data.range.end() {
// still things left, create right chunk // still things left, create right chunk
right_offset = Some(subrange.begin()); right_offset = Some(piece_range.begin());
right_length = Some((data.offset + data.length) - subrange.begin()); right_length = Some(data.range.end() - piece_range.begin());
debug!("split_to_width: case=splitting remainder with right span: (off=%u, len=%u)", debug!("split_to_width: case=splitting remainder with right span: (off=%u, len=%u)",
subrange.begin(), (data.offset + data.length) - subrange.begin()); piece_range.begin(), data.range.end() - piece_range.begin());
} }
} }
i += 1; i += 1;
@ -236,13 +236,13 @@ impl RenderBox : RenderBoxMethods {
let left_box = if left_length > 0 { let left_box = if left_length > 0 {
Some(layout::text::adapt_textbox_with_range(self.d(), data.run, Some(layout::text::adapt_textbox_with_range(self.d(), data.run,
left_offset, left_length)) TextRange(left_offset, left_length)))
} else { None }; } else { None };
match (right_offset, right_length) { match (right_offset, right_length) {
(Some(right_off), Some(right_len)) => { (Some(right_off), Some(right_len)) => {
let right_box = layout::text::adapt_textbox_with_range(self.d(), data.run, let right_box = layout::text::adapt_textbox_with_range(self.d(), data.run,
right_off, right_len); TextRange(right_off, right_len));
return if i == 1 || left_box.is_none() { return if i == 1 || left_box.is_none() {
SplitDidNotFit(left_box, Some(right_box)) SplitDidNotFit(left_box, Some(right_box))
} else { } else {
@ -270,7 +270,7 @@ impl RenderBox : RenderBoxMethods {
// TODO: consult CSS 'width', margin, border. // TODO: consult CSS 'width', margin, border.
// TODO: If image isn't available, consult 'width'. // TODO: If image isn't available, consult 'width'.
ImageBox(_,i) => au::from_px(i.get_size().get_default(Size2D(0,0)).width), ImageBox(_,i) => au::from_px(i.get_size().get_default(Size2D(0,0)).width),
TextBox(_,d) => d.run.min_width_for_range(TextRange(d.offset, d.length)), TextBox(_,d) => d.run.min_width_for_range(d.range),
UnscannedTextBox(*) => fail ~"Shouldn't see unscanned boxes here." UnscannedTextBox(*) => fail ~"Shouldn't see unscanned boxes here."
} }
} }
@ -293,7 +293,7 @@ impl RenderBox : RenderBoxMethods {
// factor in min/pref widths of any text runs that it owns. // factor in min/pref widths of any text runs that it owns.
TextBox(_,d) => { TextBox(_,d) => {
let mut max_line_width: au = au(0); let mut max_line_width: au = au(0);
for d.run.iter_natural_lines_for_range(TextRange(d.offset, d.length)) |line_range| { for d.run.iter_natural_lines_for_range(d.range) |line_range| {
// if the line is a single newline, then len will be zero // if the line is a single newline, then len will be zero
if line_range.length() == 0 { loop } if line_range.length() == 0 { loop }
@ -442,7 +442,7 @@ impl RenderBox : RenderBoxMethods {
UnscannedTextBox(*) => fail ~"Shouldn't see unscanned boxes here.", UnscannedTextBox(*) => fail ~"Shouldn't see unscanned boxes here.",
TextBox(_,d) => { TextBox(_,d) => {
list.append_item(~dl::Text(copy abs_box_bounds, text_run::serialize(builder.ctx.font_cache, d.run), list.append_item(~dl::Text(copy abs_box_bounds, text_run::serialize(builder.ctx.font_cache, d.run),
d.offset, d.length)) d.range.begin(), d.range.length()))
}, },
// TODO: items for background, border, outline // TODO: items for background, border, outline
GenericBox(_) => { GenericBox(_) => {
@ -519,7 +519,7 @@ impl RenderBox : BoxedDebugMethods {
let repr = match self { let repr = match self {
@GenericBox(*) => ~"GenericBox", @GenericBox(*) => ~"GenericBox",
@ImageBox(*) => ~"ImageBox", @ImageBox(*) => ~"ImageBox",
@TextBox(_,d) => fmt!("TextBox(text=%s)", str::substr(d.run.text, d.offset, d.length)), @TextBox(_,d) => fmt!("TextBox(text=%s)", str::substr(d.run.text, d.range.begin(), d.range.length())),
@UnscannedTextBox(_,s) => fmt!("UnscannedTextBox(%s)", s) @UnscannedTextBox(_,s) => fmt!("UnscannedTextBox(%s)", s)
}; };

View file

@ -168,7 +168,8 @@ impl TextRunScanner {
let run = @TextRun(ctx.font_cache.get_test_font(), move transformed_text); let run = @TextRun(ctx.font_cache.get_test_font(), move transformed_text);
debug!("TextRunScanner: pushing single text box when start=%u,end=%u", debug!("TextRunScanner: pushing single text box when start=%u,end=%u",
self.clump_start, self.clump_end); self.clump_start, self.clump_end);
let new_box = layout::text::adapt_textbox_with_range(in_boxes[self.clump_start].d(), run, 0, run.text.len()); let new_box = layout::text::adapt_textbox_with_range(in_boxes[self.clump_start].d(), run,
TextRange(0, run.text.len()));
out_boxes.push(new_box); out_boxes.push(new_box);
}, },
(false, true) => { (false, true) => {
@ -221,7 +222,8 @@ impl TextRunScanner {
let run = @TextRun(ctx.font_cache.get_test_font(), move run_str); let run = @TextRun(ctx.font_cache.get_test_font(), move run_str);
debug!("TextRunScanner: pushing box(es) when start=%u,end=%u", debug!("TextRunScanner: pushing box(es) when start=%u,end=%u",
self.clump_start, self.clump_end); self.clump_start, self.clump_end);
let new_box = layout::text::adapt_textbox_with_range(in_boxes[self.clump_start].d(), run, 0, run.text.len()); let new_box = layout::text::adapt_textbox_with_range(in_boxes[self.clump_start].d(), run,
TextRange(0, run.text.len()));
out_boxes.push(new_box); out_boxes.push(new_box);
} }
} /* /match */ } /* /match */
@ -586,7 +588,7 @@ impl FlowContext : InlineLayout {
// adjust bounding box metric to box's horizontal offset // adjust bounding box metric to box's horizontal offset
// TODO: can we trust the leading provided by font metrics? // TODO: can we trust the leading provided by font metrics?
@TextBox(_, data) => { @TextBox(_, data) => {
let text_bounds = data.run.metrics_for_range(TextRange(data.offset, data.length)).bounding_box; let text_bounds = data.run.metrics_for_range(data.range).bounding_box;
text_bounds.translate(&Point2D(cur_box.d().position.origin.x, au(0))) text_bounds.translate(&Point2D(cur_box.d().position.origin.x, au(0)))
}, },
_ => fail fmt!("Tried to compute bounding box of unknown Box variant: %s", cur_box.debug_str()) _ => fail fmt!("Tried to compute bounding box of unknown Box variant: %s", cur_box.debug_str())

View file

@ -1,34 +1,27 @@
/** Text layout. */ /** Text layout. */
use au = gfx::geometry;
use au::au;
use geom::size::Size2D;
use servo_text::text_run::{TextRange, TextRun}; use servo_text::text_run::{TextRange, TextRun};
use servo_text::font_cache::FontCache;
use layout::box::{TextBox, RenderBox, RenderBoxData, UnscannedTextBox}; use layout::box::{TextBox, RenderBox, RenderBoxData, UnscannedTextBox};
use layout::context::LayoutContext;
pub struct TextBoxData { pub struct TextBoxData {
run: @TextRun, run: @TextRun,
offset: uint, range: TextRange,
length: uint
} }
pub fn TextBoxData(run: @TextRun, offset: uint, length: uint) -> TextBoxData { pub fn TextBoxData(run: @TextRun, range: TextRange) -> TextBoxData {
TextBoxData { TextBoxData {
run: run, run: run,
offset: offset, range: range,
length: length
} }
} }
pub fn adapt_textbox_with_range(box_data: &RenderBoxData, run: @TextRun, pub fn adapt_textbox_with_range(box_data: &RenderBoxData, run: @TextRun,
offset: uint, length: uint) -> @RenderBox { range: TextRange) -> @RenderBox {
debug!("Creating textbox with span: (strlen=%u, off=%u, len=%u) of textrun: %s", debug!("Creating textbox with span: (strlen=%u, off=%u, len=%u) of textrun: %s",
run.text.len(), offset, length, run.text); run.text.len(), range.begin(), range.length(), run.text);
let new_box_data = copy *box_data; let new_box_data = copy *box_data;
let new_text_data = TextBoxData(run, offset, length); let new_text_data = TextBoxData(run, range);
let metrics = run.metrics_for_range(TextRange(offset, length)); let metrics = run.metrics_for_range(range);
new_box_data.position.size = metrics.bounding_box.size; new_box_data.position.size = metrics.bounding_box.size;
@TextBox(move new_box_data, move new_text_data) @TextBox(move new_box_data, move new_text_data)
} }
@ -45,58 +38,3 @@ impl RenderBox : UnscannedMethods {
} }
} }
} }
/* The main reflow routine for text layout.
impl @RenderBox : TextLayout {
fn reflow_text(ctx: &LayoutContext) {
let d = match self {
@TextBox(_,d) => { d }
_ => { fail ~"expected text box in reflow_text!" }
};
// TODO: get font from textrun's TextStyle
let font = ctx.font_cache.get_test_font();
// Do line breaking.
let mut current = TextRun(font, d.text);
let mut lines = dvec::DVec();
let mut width_left = au::from_px(800);
let mut max_width = au(0);
while current.size().width > width_left {
let min_width = current.min_break_width();
debug!("line %d, current width %d, width left %d, min width %d",
lines.len() as int,
*current.size().width as int,
*width_left as int,
*min_width as int);
if min_width > width_left {
// Too bad, we couldn't break. Overflow.
break;
}
let (prev_line, next_line) = current.split(font, width_left);
let prev_width = prev_line.size().width;
if max_width < prev_width {
max_width = prev_width;
}
lines.push(move prev_line);
current = next_line;
}
let remaining_width = current.size().width;
if max_width < remaining_width {
max_width = remaining_width;
}
let line_count = 1 + (lines.len() as i32);
let total_height = au(*current.size().height * line_count);
lines.push(move current);
self.d().position.size = Size2D(max_width, total_height);
d.runs = move dvec::unwrap(lines);
}
}*/

View file

@ -9,7 +9,7 @@ use glyph::GlyphIndex;
use libc::{ c_int, c_double, c_ulong }; use libc::{ c_int, c_double, c_ulong };
use native_font::NativeFont; use native_font::NativeFont;
use ptr::{null, addr_of}; use ptr::{null, addr_of};
use text::text_run::TextRun; use text::text_run::{TextRun, TextRange};
use vec_to_ptr = vec::raw::to_ptr; use vec_to_ptr = vec::raw::to_ptr;
// Used to abstract over the shaper's choice of fixed int representation. // Used to abstract over the shaper's choice of fixed int representation.
@ -41,7 +41,7 @@ struct RunMetrics {
// Public API // Public API
pub trait FontMethods { pub trait FontMethods {
fn measure_text(run: &TextRun, offset: uint, length: uint) -> RunMetrics; fn measure_text(&TextRun, TextRange) -> RunMetrics;
fn buf(&self) -> @~[u8]; fn buf(&self) -> @~[u8];
// these are used to get glyphs and advances in the case that the // these are used to get glyphs and advances in the case that the
@ -51,15 +51,14 @@ pub trait FontMethods {
} }
pub impl Font : FontMethods { pub impl Font : FontMethods {
fn measure_text(run: &TextRun, offset: uint, length: uint) -> RunMetrics { fn measure_text(run: &TextRun, range: TextRange) -> RunMetrics {
assert offset < run.text.len(); assert range.is_valid_for_string(run.text);
assert offset + length <= run.text.len();
// TODO: alter advance direction for RTL // TODO: 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);
if length > 0 { if range.length() > 0 {
do run.glyphs.iter_glyphs_for_range(offset, length) |_i, glyph| { do run.glyphs.iter_glyphs_for_range(range.begin(), range.length()) |_i, glyph| {
advance += glyph.advance(); advance += glyph.advance();
} }
} }
@ -75,7 +74,7 @@ pub impl Font : FontMethods {
ascent: self.metrics.ascent, ascent: self.metrics.ascent,
descent: self.metrics.descent, descent: self.metrics.descent,
}; };
debug!("Measured text range '%s' with metrics:", run.text.substr(offset, length)); debug!("Measured text range '%s' with metrics:", run.text.substr(range.begin(), range.length()));
debug!("%?", metrics); debug!("%?", metrics);
return metrics; return metrics;

View file

@ -167,7 +167,7 @@ impl TextRun : TextRunMethods {
} }
fn metrics_for_range(&self, range: TextRange) -> RunMetrics { fn metrics_for_range(&self, range: TextRange) -> RunMetrics {
self.font.measure_text(self, range.begin(), range.length()) self.font.measure_text(self, range)
} }
fn min_width_for_range(&self, range: TextRange) -> au { fn min_width_for_range(&self, range: TextRange) -> au {
@ -175,7 +175,7 @@ impl TextRun : TextRunMethods {
let mut max_piece_width = au(0); let mut max_piece_width = au(0);
for self.iter_indivisible_pieces_for_range(range) |piece_range| { for self.iter_indivisible_pieces_for_range(range) |piece_range| {
let metrics = self.font.measure_text(self, piece_range.begin(), piece_range.length()); let metrics = self.font.measure_text(self, piece_range);
max_piece_width = au::max(max_piece_width, metrics.advance_width); max_piece_width = au::max(max_piece_width, metrics.advance_width);
} }
return max_piece_width; return max_piece_width;