auto merge of #2456 : bjz/servo/metrics, r=pcwalton

cc. @pcwalton
This commit is contained in:
bors-servo 2014-05-16 18:31:34 -04:00
commit e6f2178c54
4 changed files with 121 additions and 148 deletions

View file

@ -640,6 +640,12 @@ impl<'a> GlyphStore {
}
}
#[inline]
pub fn advance_for_char_range(&self, rang: &Range<CharIndex>) -> Au {
self.iter_glyphs_for_char_range(rang)
.fold(Au(0), |advance, (_, glyph)| advance + glyph.advance())
}
// getter methods
pub fn char_is_space(&self, i: CharIndex) -> bool {
assert!(i < CharIndex(self.entry_buffer.len() as int));

View file

@ -183,32 +183,41 @@ impl<'a> TextRun {
true
}
pub fn metrics_for_range(&self, range: &Range<CharIndex>) -> RunMetrics {
pub fn ascent(&self) -> Au {
self.font_metrics.ascent
}
pub fn descent(&self) -> Au {
self.font_metrics.descent
}
pub fn advance_for_range(&self, range: &Range<CharIndex>) -> Au {
// 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);
for (glyphs, _offset, slice_range) in self.iter_slices_for_range(range) {
for (_i, glyph) in glyphs.iter_glyphs_for_char_range(&slice_range) {
advance = advance + glyph.advance();
self.iter_slices_for_range(range)
.fold(Au(0), |advance, (glyphs, _, slice_range)| {
advance + glyphs.advance_for_char_range(&slice_range)
})
}
}
RunMetrics::new(advance, self.font_metrics.ascent, self.font_metrics.descent)
pub fn metrics_for_range(&self, range: &Range<CharIndex>) -> RunMetrics {
RunMetrics::new(self.advance_for_range(range),
self.font_metrics.ascent,
self.font_metrics.descent)
}
pub fn metrics_for_slice(&self, glyphs: &GlyphStore, slice_range: &Range<CharIndex>) -> 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)
RunMetrics::new(glyphs.advance_for_char_range(slice_range),
self.font_metrics.ascent,
self.font_metrics.descent)
}
pub fn min_width_for_range(&self, range: &Range<CharIndex>) -> Au {
let mut max_piece_width = Au(0);
debug!("iterating outer range {:?}", range);
for (_, offset, slice_range) in self.iter_slices_for_range(range) {
debug!("iterated on {:?}[{:?}]", offset, slice_range);
let metrics = self.metrics_for_range(&slice_range);
max_piece_width = Au::max(max_piece_width, metrics.advance_width);
max_piece_width = Au::max(max_piece_width, self.advance_for_range(&slice_range));
}
max_piece_width
}

View file

@ -790,7 +790,7 @@ impl Box {
display_list.push(BorderDisplayItemClass(border_display_item));
// Draw a rectangle representing the baselines.
let ascent = text_box.run.metrics_for_range(&text_box.range).ascent;
let ascent = text_box.run.ascent();
let baseline = Rect(absolute_box_bounds.origin + Point2D(Au(0), ascent),
Size2D(absolute_box_bounds.size.width, Au(0)));
@ -1215,21 +1215,20 @@ impl Box {
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;
Some(self.transform(new_metrics.bounding_box.size,
ScannedTextBox(new_text_box_info)))
let width = new_text_box_info.run.advance_for_range(&left_range);
let height = self.border_box.size.height;
let size = Size2D(width, height);
Some(self.transform(size, ScannedTextBox(new_text_box_info)))
} else {
None
};
let right_box = right_range.map_or(None, |range: Range<CharIndex>| {
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);
new_metrics.bounding_box.size.height = self.border_box.size.height;
Some(self.transform(new_metrics.bounding_box.size,
ScannedTextBox(new_text_box_info)))
let right_box = right_range.map_or(None, |right_range: Range<CharIndex>| {
let new_text_box_info = ScannedTextBoxInfo::new(text_box_info.run.clone(), right_range);
let width = new_text_box_info.run.advance_for_range(&right_range);
let height = self.border_box.size.height;
let size = Size2D(width, height);
Some(self.transform(size, ScannedTextBox(new_text_box_info)))
});
if pieces_processed_count == 1 || left_box.is_none() {

View file

@ -18,6 +18,7 @@ use geom::{Point2D, Rect, SideOffsets2D, Size2D};
use gfx::display_list::ContentLevel;
use gfx::font::FontMetrics;
use gfx::font_context::FontContext;
use gfx::text::glyph::CharIndex;
use servo_util::geometry::Au;
use servo_util::geometry;
use servo_util::range;
@ -58,7 +59,22 @@ use sync::Arc;
/// left corner of the green zone is the same as that of the line, but
/// the green zone can be taller and wider than the line itself.
pub struct LineBox {
/// Consider the following HTML and rendered element with linebreaks:
pub range: Range<LineIndices>,
pub bounds: Rect<Au>,
pub green_zone: Size2D<Au>
}
int_range_index! {
#[doc = "The index of a box fragment in a flattened vector of DOM elements."]
struct FragmentIndex(int)
}
/// A line index consists of two indices: a fragment index that refers to the
/// index of a DOM fragment within a flattened inline element; and a glyph index
/// where the 0th glyph refers to the first glyph of that fragment.
///
/// For example, consider the following HTML and rendered element with
/// linebreaks:
///
/// ~~~html
/// <span>I <span>like truffles,</span> yes I do.</span>
@ -79,60 +95,44 @@ pub struct LineBox {
/// |------------|-------------|-------------|
/// | 'I like' | 'truffles,' | 'yes I do.' |
/// ~~~
pub range: Range<LineIndices>,
pub bounds: Rect<Au>,
pub green_zone: Size2D<Au>
}
int_range_index! {
#[doc = "The index of a box fragment into the flattened vector of DOM"]
#[doc = "elements."]
#[doc = ""]
#[doc = "For example, given the HTML below:"]
#[doc = ""]
#[doc = "~~~"]
#[doc = "<span>I <span>like truffles,</span> yes I do.</span>"]
#[doc = "~~~"]
#[doc = ""]
#[doc = "The fragments would be indexed as follows:"]
#[doc = ""]
#[doc = "~~~"]
#[doc = "| 0 | 1 | 2 |"]
#[doc = "|------|------------------|--------------|"]
#[doc = "| 'I ' | 'like truffles,' | ' yes I do.' |"]
#[doc = "~~~"]
struct FragmentIndex(int)
}
int_range_index! {
#[doc = "The index of a glyph in a single DOM fragment. Ligatures and"]
#[doc = "continuous runs of whitespace are treated as single glyphs."]
#[doc = "Non-breakable DOM fragments such as images are treated as"]
#[doc = "having a range length of `1`."]
#[doc = ""]
#[doc = "For example, given the HTML below:"]
#[doc = ""]
#[doc = "~~~"]
#[doc = "<span>like truffles,</span>"]
#[doc = "~~~"]
#[doc = ""]
#[doc = "The glyphs would be indexed as follows:"]
#[doc = ""]
#[doc = "~~~"]
#[doc = "| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |"]
#[doc = "|---|---|---|---|---|---|---|---|-----|---|----|----|"]
#[doc = "| l | i | k | e | | t | r | u | ffl | e | s | , |"]
#[doc = "~~~"]
struct GlyphIndex(int)
}
/// A line index consists of two indices: a fragment index that refers to the
/// index of a DOM fragment within a flattened inline element; and a glyph index
/// where the 0th glyph refers to the first glyph of that fragment.
#[deriving(Clone, Eq, Ord, TotalEq, TotalOrd, Zero)]
pub struct LineIndices {
/// The index of a box fragment into the flattened vector of DOM
/// elements.
///
/// For example, given the HTML below:
///
/// ~~~
/// <span>I <span>like truffles,</span> yes I do.</span>
/// ~~~
///
/// The fragments would be indexed as follows:
///
/// ~~~
/// | 0 | 1 | 2 |
/// |------|------------------|--------------|
/// | 'I ' | 'like truffles,' | ' yes I do.' |
/// ~~~
pub fragment_index: FragmentIndex,
pub glyph_index: GlyphIndex,
/// The index of a character in a DOM fragment. Ligatures and continuous
/// runs of whitespace are treated as single characters. Non-breakable DOM
/// fragments such as images are treated as having a range length of `1`.
///
/// For example, given the HTML below:
///
/// ~~~
/// <span>like truffles,</span>
/// ~~~
///
/// The characters would be indexed as follows:
///
/// ~~~
/// | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
/// |---|---|---|---|---|---|---|---|---|---|----|----|----|----|
/// | l | i | k | e | | t | r | u | f | f | l | e | s | , |
/// ~~~
pub char_index: CharIndex,
}
impl RangeIndex for LineIndices {}
@ -141,14 +141,14 @@ impl Add<LineIndices, LineIndices> for LineIndices {
fn add(&self, other: &LineIndices) -> LineIndices {
// TODO: use debug_assert! after rustc upgrade
if cfg!(not(ndebug)) {
assert!(other.fragment_index == num::zero() || other.glyph_index == num::zero(),
assert!(other.fragment_index == num::zero() || other.char_index == num::zero(),
"Attempted to add {} to {}. Both the fragment_index and \
glyph_index of the RHS are non-zero. This probably \
was a mistake!", self, other);
char_index of the RHS are non-zero. This probably was a \
mistake!", self, other);
}
LineIndices {
fragment_index: self.fragment_index + other.fragment_index,
glyph_index: self.glyph_index + other.glyph_index,
char_index: self.char_index + other.char_index,
}
}
}
@ -157,14 +157,14 @@ impl Sub<LineIndices, LineIndices> for LineIndices {
fn sub(&self, other: &LineIndices) -> LineIndices {
// TODO: use debug_assert! after rustc upgrade
if cfg!(not(ndebug)) {
assert!(other.fragment_index == num::zero() || other.glyph_index == num::zero(),
"Attempted to subtract {} from {}. Both the \
fragment_index and glyph_index of the RHS are non-zero. \
This probably was a mistake!", self, other);
assert!(other.fragment_index == num::zero() || other.char_index == num::zero(),
"Attempted to subtract {} from {}. Both the fragment_index \
and char_index of the RHS are non-zero. This probably was \
a mistake!", self, other);
}
LineIndices {
fragment_index: self.fragment_index - other.fragment_index,
glyph_index: self.glyph_index - other.glyph_index,
char_index: self.char_index - other.char_index,
}
}
}
@ -173,21 +173,21 @@ impl Neg<LineIndices> for LineIndices {
fn neg(&self) -> LineIndices {
// TODO: use debug_assert! after rustc upgrade
if cfg!(not(ndebug)) {
assert!(self.fragment_index == num::zero() || self.glyph_index == num::zero(),
assert!(self.fragment_index == num::zero() || self.char_index == num::zero(),
"Attempted to negate {}. Both the fragment_index and \
glyph_index are non-zero. This probably was a mistake!",
char_index are non-zero. This probably was a mistake!",
self);
}
LineIndices {
fragment_index: -self.fragment_index,
glyph_index: -self.glyph_index,
char_index: -self.char_index,
}
}
}
impl fmt::Show for LineIndices {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f.buf, "{}.{}", self.fragment_index, self.glyph_index)
write!(f.buf, "{}.{}", self.fragment_index, self.char_index)
}
}
@ -195,8 +195,8 @@ pub fn each_fragment_index(range: &Range<LineIndices>) -> EachIndex<int, Fragmen
range::each_index(range.begin().fragment_index, range.length().fragment_index)
}
pub fn each_glyph_index(range: &Range<LineIndices>) -> EachIndex<int, GlyphIndex> {
range::each_index(range.begin().glyph_index, range.length().glyph_index)
pub fn each_char_index(range: &Range<LineIndices>) -> EachIndex<int, CharIndex> {
range::each_index(range.begin().char_index, range.length().char_index)
}
struct LineboxScanner {
@ -331,7 +331,6 @@ impl LineboxScanner {
let first_box_size = first_box.border_box.size;
let splittable = first_box.can_split();
debug!("LineboxScanner: box size: {}, splittable: {}", first_box_size, splittable);
let line_is_empty: bool = self.pending_line.range.length() == num::zero();
// Initally, pretend a splittable box has 0 width.
// We will move it later if it has nonzero width
@ -342,7 +341,7 @@ impl LineboxScanner {
first_box_size.width
};
let mut info = PlacementInfo {
let info = PlacementInfo {
size: Size2D(placement_width, first_box_size.height),
ceiling: ceiling,
max_width: flow.base.position.size.width,
@ -368,49 +367,9 @@ impl LineboxScanner {
return (line_bounds, first_box_size.width);
}
// Otherwise, try and split the box
// FIXME(eatkinson): calling split_to_width here seems excessive and expensive.
// We should find a better abstraction or merge it with the call in
// try_append_to_line.
match first_box.split_to_width(line_bounds.size.width, line_is_empty) {
CannotSplit => {
error!("LineboxScanner: Tried to split unsplittable render box! {}",
first_box);
debug!("LineboxScanner: used to call split_to_width here");
return (line_bounds, first_box_size.width);
}
SplitDidFit(left, right) => {
debug!("LineboxScanner: case=box split and fit");
let actual_box_width = match (left, right) {
(Some(l_box), Some(_)) => l_box.border_box.size.width,
(Some(l_box), None) => l_box.border_box.size.width,
(None, Some(r_box)) => r_box.border_box.size.width,
(None, None) => fail!("This case makes no sense.")
};
return (line_bounds, actual_box_width);
}
SplitDidNotFit(left, right) => {
// The split didn't fit, but we might be able to
// push it down past floats.
debug!("LineboxScanner: case=box split and fit didn't fit; trying to push it down");
let actual_box_width = match (left, right) {
(Some(l_box), Some(_)) => l_box.border_box.size.width,
(Some(l_box), None) => l_box.border_box.size.width,
(None, Some(r_box)) => r_box.border_box.size.width,
(None, None) => fail!("This case makes no sense.")
};
info.size.width = actual_box_width;
let new_bounds = self.floats.place_between_floats(&info);
debug!("LineboxScanner: case=new line position: {}", new_bounds);
return (new_bounds, actual_box_width);
}
}
}
/// Performs float collision avoidance. This is called when adding a box is going to increase
/// the height, and because of that we will collide with some floats.
@ -587,14 +546,14 @@ impl LineboxScanner {
self.pending_line.range.reset(
LineIndices {
fragment_index: FragmentIndex(self.new_boxes.len() as int),
glyph_index: GlyphIndex(0) /* unused for now */,
char_index: CharIndex(0) /* unused for now */,
},
num::zero()
);
}
self.pending_line.range.extend_by(LineIndices {
fragment_index: FragmentIndex(1),
glyph_index: GlyphIndex(0) /* unused for now */ ,
char_index: CharIndex(0) /* unused for now */ ,
});
self.pending_line.bounds.size.width = self.pending_line.bounds.size.width +
box_.border_box.size.width;