mirror of
https://github.com/servo/servo.git
synced 2025-08-04 13:10:20 +01:00
auto merge of #1365 : pcwalton/servo/minor-layout-refactoring, r=metajack
r? @metajack
This commit is contained in:
commit
6014bd3813
1 changed files with 256 additions and 242 deletions
|
@ -277,10 +277,56 @@ impl LineboxScanner {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns false only if we should break the line.
|
/// Performs float collision avoidance. This is called when adding a box is going to increase
|
||||||
fn try_append_to_line(&mut self, in_box: @Box, flow: &mut InlineFlow) -> bool {
|
/// the height, and because of that we will collide with some floats.
|
||||||
let line_is_empty: bool = self.pending_line.range.length() == 0;
|
///
|
||||||
|
/// We have two options here:
|
||||||
|
/// 1) Move the entire line so that it doesn't collide any more.
|
||||||
|
/// 2) Break the line and put the new box on the next line.
|
||||||
|
///
|
||||||
|
/// The problem with option 1 is that we might move the line and then wind up breaking anyway,
|
||||||
|
/// which violates the standard.
|
||||||
|
/// But option 2 is going to look weird sometimes.
|
||||||
|
///
|
||||||
|
/// So we'll try to move the line whenever we can, but break if we have to.
|
||||||
|
///
|
||||||
|
/// Returns false if and only if we should break the line.
|
||||||
|
fn avoid_floats(&mut self,
|
||||||
|
in_box: @Box,
|
||||||
|
flow: &mut InlineFlow,
|
||||||
|
new_height: Au,
|
||||||
|
line_is_empty: bool)
|
||||||
|
-> bool {
|
||||||
|
debug!("LineboxScanner: entering float collision avoider!");
|
||||||
|
|
||||||
|
// First predict where the next line is going to be.
|
||||||
|
let this_line_y = self.pending_line.bounds.origin.y;
|
||||||
|
let (next_line, first_box_width) = self.initial_line_placement(in_box, this_line_y, flow);
|
||||||
|
let next_green_zone = next_line.size;
|
||||||
|
|
||||||
|
let new_width = self.pending_line.bounds.size.width + first_box_width;
|
||||||
|
|
||||||
|
// Now, see if everything can fit at the new location.
|
||||||
|
if next_green_zone.width >= new_width && next_green_zone.height >= new_height {
|
||||||
|
debug!("LineboxScanner: case=adding box collides vertically with floats: moving line");
|
||||||
|
|
||||||
|
self.pending_line.bounds.origin = next_line.origin;
|
||||||
|
self.pending_line.green_zone = next_green_zone;
|
||||||
|
|
||||||
|
assert!(!line_is_empty, "Non-terminating line breaking");
|
||||||
|
self.work_list.push_front(in_box);
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!("LineboxScanner: case=adding box collides vertically with floats: breaking line");
|
||||||
|
self.work_list.push_front(in_box);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tries to append the given box to the line, splitting it if necessary. Returns false only if
|
||||||
|
/// we should break the line.
|
||||||
|
fn try_append_to_line(&mut self, in_box: @Box, flow: &mut InlineFlow) -> bool {
|
||||||
|
let line_is_empty = self.pending_line.range.length() == 0;
|
||||||
if line_is_empty {
|
if line_is_empty {
|
||||||
let (line_bounds, _) = self.initial_line_placement(in_box, self.cur_y, flow);
|
let (line_bounds, _) = self.initial_line_placement(in_box, self.cur_y, flow);
|
||||||
self.pending_line.bounds.origin = line_bounds.origin;
|
self.pending_line.bounds.origin = line_bounds.origin;
|
||||||
|
@ -294,132 +340,85 @@ impl LineboxScanner {
|
||||||
self.pending_line.green_zone,
|
self.pending_line.green_zone,
|
||||||
in_box.debug_str());
|
in_box.debug_str());
|
||||||
|
|
||||||
|
|
||||||
let green_zone = self.pending_line.green_zone;
|
let green_zone = self.pending_line.green_zone;
|
||||||
|
|
||||||
//assert!(green_zone.width >= self.pending_line.bounds.size.width &&
|
// NB: At this point, if `green_zone.width < self.pending_line.bounds.size.width` or
|
||||||
// green_zone.height >= self.pending_line.bounds.size.height,
|
// `green_zone.height < self.pending_line.bounds.size.height`, then we committed a line
|
||||||
// "Committed a line that overlaps with floats");
|
// that overlaps with floats.
|
||||||
|
|
||||||
let new_height = self.new_height_for_line(in_box);
|
let new_height = self.new_height_for_line(in_box);
|
||||||
if new_height > green_zone.height {
|
if new_height > green_zone.height {
|
||||||
debug!("LineboxScanner: entering float collision avoider!");
|
// Uh-oh. Float collision imminent. Enter the float collision avoider…
|
||||||
|
return self.avoid_floats(in_box, flow, new_height, line_is_empty)
|
||||||
// Uh-oh. Adding this box is going to increase the height,
|
|
||||||
// and because of that we will collide with some floats.
|
|
||||||
|
|
||||||
// We have two options here:
|
|
||||||
// 1) Move the entire line so that it doesn't collide any more.
|
|
||||||
// 2) Break the line and put the new box on the next line.
|
|
||||||
|
|
||||||
// The problem with option 1 is that we might move the line
|
|
||||||
// and then wind up breaking anyway, which violates the standard.
|
|
||||||
// But option 2 is going to look weird sometimes.
|
|
||||||
|
|
||||||
// So we'll try to move the line whenever we can, but break
|
|
||||||
// if we have to.
|
|
||||||
|
|
||||||
// First predict where the next line is going to be
|
|
||||||
let this_line_y = self.pending_line.bounds.origin.y;
|
|
||||||
let (next_line, first_box_width) = self.initial_line_placement(in_box, this_line_y, flow);
|
|
||||||
let next_green_zone = next_line.size;
|
|
||||||
|
|
||||||
let new_width = self.pending_line.bounds.size.width + first_box_width;
|
|
||||||
// Now, see if everything can fit at the new location.
|
|
||||||
if next_green_zone.width >= new_width && next_green_zone.height >= new_height{
|
|
||||||
debug!("LineboxScanner: case=adding box collides vertically with floats: moving line");
|
|
||||||
|
|
||||||
self.pending_line.bounds.origin = next_line.origin;
|
|
||||||
self.pending_line.green_zone = next_green_zone;
|
|
||||||
|
|
||||||
assert!(!line_is_empty, "Non-terminating line breaking");
|
|
||||||
self.work_list.push_front(in_box);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
debug!("LineboxScanner: case=adding box collides vertically with floats: breaking line");
|
|
||||||
self.work_list.push_front(in_box);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're not going to overflow the green zone vertically, we might still do so
|
// If we're not going to overflow the green zone vertically, we might still do so
|
||||||
// horizontally. We'll try to place the whole box on this line and break somewhere
|
// horizontally. We'll try to place the whole box on this line and break somewhere if it
|
||||||
// if it doesn't fit.
|
// doesn't fit.
|
||||||
|
|
||||||
let new_width = self.pending_line.bounds.size.width + in_box.position.get().size.width;
|
let new_width = self.pending_line.bounds.size.width + in_box.position.get().size.width;
|
||||||
|
|
||||||
if new_width <= green_zone.width {
|
if new_width <= green_zone.width {
|
||||||
debug!("LineboxScanner: case=box fits without splitting");
|
debug!("LineboxScanner: case=box fits without splitting");
|
||||||
self.push_box_to_line(in_box);
|
self.push_box_to_line(in_box);
|
||||||
return true;
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if !in_box.can_split() {
|
if !in_box.can_split() {
|
||||||
// TODO(Issue #224): signal that horizontal overflow happened?
|
// TODO(eatkinson, issue #224): Signal that horizontal overflow happened?
|
||||||
if line_is_empty {
|
if line_is_empty {
|
||||||
debug!("LineboxScanner: case=box can't split and line {:u} is empty, so \
|
debug!("LineboxScanner: case=box can't split and line {:u} is empty, so \
|
||||||
overflowing.",
|
overflowing.",
|
||||||
self.lines.len());
|
self.lines.len());
|
||||||
self.push_box_to_line(in_box);
|
self.push_box_to_line(in_box)
|
||||||
return true;
|
|
||||||
} else {
|
} else {
|
||||||
debug!("LineboxScanner: Case=box can't split, not appending.");
|
debug!("LineboxScanner: Case=box can't split, not appending.");
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let available_width = green_zone.width - self.pending_line.bounds.size.width;
|
|
||||||
|
|
||||||
match in_box.split_to_width(available_width, line_is_empty) {
|
|
||||||
CannotSplit(_) => {
|
|
||||||
error!("LineboxScanner: Tried to split unsplittable render box! {:s}",
|
|
||||||
in_box.debug_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
SplitDidFit(left, right) => {
|
|
||||||
debug!("LineboxScanner: case=split box did fit; deferring remainder box.");
|
|
||||||
match (left, right) {
|
|
||||||
(Some(left_box), Some(right_box)) => {
|
|
||||||
self.push_box_to_line(left_box);
|
|
||||||
self.work_list.push_front(right_box);
|
|
||||||
}
|
|
||||||
(Some(left_box), None) => self.push_box_to_line(left_box),
|
|
||||||
(None, Some(right_box)) => self.push_box_to_line(right_box),
|
|
||||||
(None, None) => error!("LineboxScanner: This split case makes no sense!"),
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
SplitDidNotFit(left, right) => {
|
|
||||||
if line_is_empty {
|
|
||||||
debug!("LineboxScanner: case=split box didn't fit and line {:u} is empty, so overflowing and deferring remainder box.",
|
|
||||||
self.lines.len());
|
|
||||||
// TODO(Issue #224): signal that horizontal overflow happened?
|
|
||||||
match (left, right) {
|
|
||||||
(Some(left_box), Some(right_box)) => {
|
|
||||||
self.push_box_to_line(left_box);
|
|
||||||
self.work_list.push_front(right_box);
|
|
||||||
}
|
|
||||||
(Some(left_box), None) => {
|
|
||||||
self.push_box_to_line(left_box);
|
|
||||||
}
|
|
||||||
(None, Some(right_box)) => {
|
|
||||||
self.push_box_to_line(right_box);
|
|
||||||
}
|
|
||||||
(None, None) => {
|
|
||||||
error!("LineboxScanner: This split case makes no sense!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
debug!("LineboxScanner: case=split box didn't fit, not appending and deferring original box.");
|
|
||||||
self.work_list.push_front(in_box);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return line_is_empty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let available_width = green_zone.width - self.pending_line.bounds.size.width;
|
||||||
|
let split = in_box.split_to_width(available_width, line_is_empty);
|
||||||
|
let (left, right) = match (split, line_is_empty) {
|
||||||
|
(CannotSplit(_), _) => {
|
||||||
|
error!("LineboxScanner: Tried to split unsplittable render box! {:s}",
|
||||||
|
in_box.debug_str());
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
(SplitDidNotFit(_, _), false) => {
|
||||||
|
debug!("LineboxScanner: case=split box didn't fit, not appending and deferring \
|
||||||
|
original box.");
|
||||||
|
self.work_list.push_front(in_box);
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
(SplitDidFit(left, right), _) => {
|
||||||
|
debug!("LineboxScanner: case=split box did fit; deferring remainder box.");
|
||||||
|
(left, right)
|
||||||
|
// Fall through to push boxes to the line.
|
||||||
|
}
|
||||||
|
(SplitDidNotFit(left, right), true) => {
|
||||||
|
// TODO(eatkinson, issue #224): Signal that horizontal overflow happened?
|
||||||
|
debug!("LineboxScanner: case=split box didn't fit and line {:u} is empty, so \
|
||||||
|
overflowing and deferring remainder box.",
|
||||||
|
self.lines.len());
|
||||||
|
(left, right)
|
||||||
|
// Fall though to push boxes to the line.
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match (left, right) {
|
||||||
|
(Some(left_box), Some(right_box)) => {
|
||||||
|
self.push_box_to_line(left_box);
|
||||||
|
self.work_list.push_front(right_box);
|
||||||
|
}
|
||||||
|
(Some(left_box), None) => self.push_box_to_line(left_box),
|
||||||
|
(None, Some(right_box)) => self.push_box_to_line(right_box),
|
||||||
|
(None, None) => error!("LineboxScanner: This split case makes no sense!"),
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
// unconditional push
|
// An unconditional push.
|
||||||
fn push_box_to_line(&mut self, box: @Box) {
|
fn push_box_to_line(&mut self, box: @Box) {
|
||||||
debug!("LineboxScanner: Pushing box {} to line {:u}", box.debug_id(), self.lines.len());
|
debug!("LineboxScanner: Pushing box {} to line {:u}", box.debug_id(), self.lines.len());
|
||||||
|
|
||||||
|
@ -511,6 +510,114 @@ impl InlineFlow {
|
||||||
// For now, don't traverse the subtree rooted here
|
// For now, don't traverse the subtree rooted here
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the relative offset from the baseline for this box, taking into account the value
|
||||||
|
/// of the CSS `vertical-align` property.
|
||||||
|
///
|
||||||
|
/// The extra boolean is set if and only if `biggest_top` and/or `biggest_bottom` were updated.
|
||||||
|
/// That is, if the box has a `top` or `bottom` value, true is returned.
|
||||||
|
fn relative_offset_from_baseline(cur_box: @Box,
|
||||||
|
ascent: Au,
|
||||||
|
parent_text_top: Au,
|
||||||
|
parent_text_bottom: Au,
|
||||||
|
top_from_base: &mut Au,
|
||||||
|
bottom_from_base: &mut Au,
|
||||||
|
biggest_top: &mut Au,
|
||||||
|
biggest_bottom: &mut Au)
|
||||||
|
-> (Au, bool) {
|
||||||
|
match cur_box.vertical_align() {
|
||||||
|
vertical_align::baseline => (-ascent, false),
|
||||||
|
vertical_align::middle => {
|
||||||
|
// TODO: x-height value should be used from font info.
|
||||||
|
let xheight = Au::new(0);
|
||||||
|
(-(xheight + cur_box.box_height()).scale_by(0.5), false)
|
||||||
|
},
|
||||||
|
vertical_align::sub => {
|
||||||
|
// TODO: The proper position for subscripts should be used.
|
||||||
|
// Lower the baseline to the proper position for subscripts
|
||||||
|
let sub_offset = Au::new(0);
|
||||||
|
(sub_offset - ascent, false)
|
||||||
|
},
|
||||||
|
vertical_align::super_ => {
|
||||||
|
// TODO: The proper position for superscripts should be used.
|
||||||
|
// Raise the baseline to the proper position for superscripts
|
||||||
|
let super_offset = Au::new(0);
|
||||||
|
(-super_offset - ascent, false)
|
||||||
|
},
|
||||||
|
vertical_align::text_top => {
|
||||||
|
let box_height = *top_from_base + *bottom_from_base;
|
||||||
|
let prev_bottom_from_base = *bottom_from_base;
|
||||||
|
*top_from_base = parent_text_top;
|
||||||
|
*bottom_from_base = box_height - *top_from_base;
|
||||||
|
(*bottom_from_base - prev_bottom_from_base - ascent, false)
|
||||||
|
},
|
||||||
|
vertical_align::text_bottom => {
|
||||||
|
let box_height = *top_from_base + *bottom_from_base;
|
||||||
|
let prev_bottom_from_base = *bottom_from_base;
|
||||||
|
*bottom_from_base = parent_text_bottom;
|
||||||
|
*top_from_base = box_height - *bottom_from_base;
|
||||||
|
(*bottom_from_base - prev_bottom_from_base - ascent, false)
|
||||||
|
},
|
||||||
|
vertical_align::top => {
|
||||||
|
if *biggest_top < (*top_from_base + *bottom_from_base) {
|
||||||
|
*biggest_top = *top_from_base + *bottom_from_base;
|
||||||
|
}
|
||||||
|
let offset_top = *top_from_base - ascent;
|
||||||
|
(offset_top, true)
|
||||||
|
},
|
||||||
|
vertical_align::bottom => {
|
||||||
|
if *biggest_bottom < (*top_from_base + *bottom_from_base) {
|
||||||
|
*biggest_bottom = *top_from_base + *bottom_from_base;
|
||||||
|
}
|
||||||
|
let offset_bottom = -(*bottom_from_base + ascent);
|
||||||
|
(offset_bottom, true)
|
||||||
|
},
|
||||||
|
vertical_align::Length(length) => (-(length + ascent), false),
|
||||||
|
vertical_align::Percentage(p) => {
|
||||||
|
let pt_size = cur_box.font_style().pt_size;
|
||||||
|
let line_height = cur_box.calculate_line_height(Au::from_pt(pt_size));
|
||||||
|
let percent_offset = line_height.scale_by(p);
|
||||||
|
(-(percent_offset + ascent), false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets box X positions based on alignment for one line.
|
||||||
|
fn set_horizontal_box_positions(boxes: &[@Box], line: &LineBox) {
|
||||||
|
// Figure out how much width we have.
|
||||||
|
let slack_width = Au::max(Au(0), line.green_zone.width - line.bounds.size.width);
|
||||||
|
|
||||||
|
// Get the text alignment.
|
||||||
|
//
|
||||||
|
// TODO(burg, issue #222): use 'text-align' property from `InlineFlow`'s block container,
|
||||||
|
// not from the style of the first box child.
|
||||||
|
let linebox_align = if line.range.begin() < boxes.len() {
|
||||||
|
let first_box = boxes[line.range.begin()];
|
||||||
|
first_box.nearest_ancestor_element().style().Text.text_align
|
||||||
|
} else {
|
||||||
|
// Nothing to lay out, so assume left alignment.
|
||||||
|
text_align::left
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set the box x positions based on that alignment.
|
||||||
|
let mut offset_x = line.bounds.origin.x;
|
||||||
|
offset_x = offset_x + match linebox_align {
|
||||||
|
// So sorry, but justified text is more complicated than shuffling linebox
|
||||||
|
// coordinates.
|
||||||
|
//
|
||||||
|
// TODO(burg, issue #213): Implement `text-align: justify`.
|
||||||
|
text_align::left | text_align::justify => Au(0),
|
||||||
|
text_align::center => slack_width.scale_by(0.5),
|
||||||
|
text_align::right => slack_width,
|
||||||
|
};
|
||||||
|
|
||||||
|
for i in line.range.eachi() {
|
||||||
|
let box = &boxes[i];
|
||||||
|
let size = box.position.get().size;
|
||||||
|
box.position.set(Rect(Point2D(offset_x, box.position.get().origin.y), size));
|
||||||
|
offset_x = offset_x + size.width;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Flow for InlineFlow {
|
impl Flow for InlineFlow {
|
||||||
|
@ -590,7 +697,8 @@ impl Flow for InlineFlow {
|
||||||
fn assign_height(&mut self, _: &mut LayoutContext) {
|
fn assign_height(&mut self, _: &mut LayoutContext) {
|
||||||
debug!("assign_height_inline: assigning height for flow {}", self.base.id);
|
debug!("assign_height_inline: assigning height for flow {}", self.base.id);
|
||||||
|
|
||||||
// Divide the boxes into lines
|
// Divide the boxes into lines.
|
||||||
|
//
|
||||||
// TODO(#226): Get the CSS `line-height` property from the containing block's style to
|
// TODO(#226): Get the CSS `line-height` property from the containing block's style to
|
||||||
// determine minimum linebox height.
|
// determine minimum linebox height.
|
||||||
//
|
//
|
||||||
|
@ -608,68 +716,20 @@ impl Flow for InlineFlow {
|
||||||
|
|
||||||
let mut line_height_offset = Au::new(0);
|
let mut line_height_offset = Au::new(0);
|
||||||
|
|
||||||
// Now, go through each line and lay out the boxes inside
|
// Now, go through each line and lay out the boxes inside.
|
||||||
for line in self.lines.mut_iter() {
|
for line in self.lines.mut_iter() {
|
||||||
// We need to distribute extra width based on text-align.
|
// Lay out boxes horizontally.
|
||||||
let mut slack_width = line.green_zone.width - line.bounds.size.width;
|
InlineFlow::set_horizontal_box_positions(self.boxes, line);
|
||||||
if slack_width < Au::new(0) {
|
|
||||||
slack_width = Au::new(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the text alignment.
|
|
||||||
// TODO(Issue #222): use 'text-align' property from InlineFlow's
|
|
||||||
// block container, not from the style of the first box child.
|
|
||||||
let linebox_align = if line.range.begin() < self.boxes.len() {
|
|
||||||
let first_box = self.boxes[line.range.begin()];
|
|
||||||
first_box.nearest_ancestor_element().style().Text.text_align
|
|
||||||
} else {
|
|
||||||
// Nothing to lay out, so assume left alignment.
|
|
||||||
text_align::left
|
|
||||||
};
|
|
||||||
|
|
||||||
// Set the box x positions
|
|
||||||
let mut offset_x = line.bounds.origin.x;
|
|
||||||
match linebox_align {
|
|
||||||
// So sorry, but justified text is more complicated than shuffling linebox coordinates.
|
|
||||||
// TODO(Issue #213): implement `text-align: justify`
|
|
||||||
text_align::left | text_align::justify => {
|
|
||||||
for i in line.range.eachi() {
|
|
||||||
let box = &self.boxes[i];
|
|
||||||
box.position.mutate().ptr.origin.x = offset_x;
|
|
||||||
offset_x = offset_x + box.position.get().size.width;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
text_align::center => {
|
|
||||||
offset_x = offset_x + slack_width.scale_by(0.5);
|
|
||||||
for i in line.range.eachi() {
|
|
||||||
let box = &self.boxes[i];
|
|
||||||
box.position.mutate().ptr.origin.x = offset_x;
|
|
||||||
offset_x = offset_x + box.position.get().size.width;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
text_align::right => {
|
|
||||||
offset_x = offset_x + slack_width;
|
|
||||||
for i in line.range.eachi() {
|
|
||||||
let box = &self.boxes[i];
|
|
||||||
box.position.mutate().ptr.origin.x = offset_x;
|
|
||||||
offset_x = offset_x + box.position.get().size.width;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Set the top y position of the current linebox.
|
// Set the top y position of the current linebox.
|
||||||
// `line_height_offset` is updated at the end of the previous loop.
|
// `line_height_offset` is updated at the end of the previous loop.
|
||||||
line.bounds.origin.y = line.bounds.origin.y + line_height_offset;
|
line.bounds.origin.y = line.bounds.origin.y + line_height_offset;
|
||||||
|
|
||||||
// Calculate the distance from baseline to the top of the linebox.
|
// Calculate the distance from baseline to the top and bottom of the linebox.
|
||||||
let mut topmost = Au::new(0);
|
let (mut topmost, mut bottommost) = (Au(0), Au(0));
|
||||||
// Calculate the distance from baseline to the bottom of the linebox.
|
// Calculate the biggest height among boxes with 'top' and 'bottom' values
|
||||||
let mut bottommost = Au::new(0);
|
// respectively.
|
||||||
|
let (mut biggest_top, mut biggest_bottom) = (Au(0), Au(0));
|
||||||
// Calculate the biggest height among boxes with 'top' value.
|
|
||||||
let mut biggest_top = Au::new(0);
|
|
||||||
// Calculate the biggest height among boxes with 'bottom' value.
|
|
||||||
let mut biggest_bottom = Au::new(0);
|
|
||||||
|
|
||||||
for box_i in line.range.eachi() {
|
for box_i in line.range.eachi() {
|
||||||
let cur_box = self.boxes[box_i];
|
let cur_box = self.boxes[box_i];
|
||||||
|
@ -679,8 +739,8 @@ impl Flow for InlineFlow {
|
||||||
ImageBox(ref image_box) => {
|
ImageBox(ref image_box) => {
|
||||||
let mut height = image_box.image_height(cur_box);
|
let mut height = image_box.image_height(cur_box);
|
||||||
|
|
||||||
// TODO: margin, border, padding's top and bottom should be calculated in advance,
|
// TODO: margin, border, padding's top and bottom should be calculated in
|
||||||
// since baseline of image is bottom margin edge.
|
// advance, since baseline of image is bottom margin edge.
|
||||||
let mut top;
|
let mut top;
|
||||||
let mut bottom;
|
let mut bottom;
|
||||||
{
|
{
|
||||||
|
@ -727,92 +787,45 @@ impl Flow for InlineFlow {
|
||||||
fail!("Unscanned text boxes should have been scanned by now.")
|
fail!("Unscanned text boxes should have been scanned by now.")
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut top_from_base = top_from_base;
|
let mut top_from_base = top_from_base;
|
||||||
let mut bottom_from_base = bottom_from_base;
|
let mut bottom_from_base = bottom_from_base;
|
||||||
|
|
||||||
// To calculate text-top and text-bottom value of 'vertical-align',
|
// To calculate text-top and text-bottom value of 'vertical-align',
|
||||||
// we should find the top and bottom of the content area of parent box.
|
// we should find the top and bottom of the content area of parent box.
|
||||||
// The content area is defined in "http://www.w3.org/TR/CSS2/visudet.html#inline-non-replaced".
|
// The content area is defined in:
|
||||||
// TODO: We should extract em-box info from font size of parent
|
// http://www.w3.org/TR/CSS2/visudet.html#inline-non-replaced
|
||||||
// and calcuate the distances from baseline to the top and the bottom of parent's content area.
|
//
|
||||||
|
// TODO: We should extract em-box info from the font size of the parent and
|
||||||
|
// calculate the distances from the baseline to the top and the bottom of the
|
||||||
|
// parent's content area.
|
||||||
|
|
||||||
// It should calculate the distance from baseline to the top of parent's content area.
|
// We should calculate the distance from baseline to the top of parent's content
|
||||||
// But, it is assumed now as font size of parent.
|
// area. But for now we assume it's the parent's font size.
|
||||||
let mut parent_text_top;
|
let mut parent_text_top;
|
||||||
// It should calculate the distance from baseline to the bottom of parent's content area.
|
|
||||||
// But, it is assumed now as 0.
|
|
||||||
let parent_text_bottom = Au::new(0);
|
|
||||||
// Get parent node
|
|
||||||
let parent = cur_box.node.parent_node();
|
|
||||||
|
|
||||||
|
// We should calculate the distance from baseline to the bottom of the parent's
|
||||||
|
// content area. But for now we assume it's zero.
|
||||||
|
let parent_text_bottom = Au::new(0);
|
||||||
|
|
||||||
|
let parent = cur_box.node.parent_node();
|
||||||
let font_size = parent.unwrap().style().Font.font_size;
|
let font_size = parent.unwrap().style().Font.font_size;
|
||||||
parent_text_top = font_size;
|
parent_text_top = font_size;
|
||||||
|
|
||||||
// This flag decides whether topmost and bottommost are updated or not.
|
// Calculate a relative offset from the baseline.
|
||||||
// That is, if the box has top or bottom value, no_update_flag becomes true.
|
//
|
||||||
let mut no_update_flag = false;
|
// The no-update flag decides whether `biggest_top` and `biggest_bottom` are
|
||||||
// Calculate a relative offset from baseline.
|
// updated or not. That is, if the box has a `top` or `bottom` value,
|
||||||
let offset = match cur_box.vertical_align() {
|
// `no_update_flag` becomes true.
|
||||||
vertical_align::baseline => {
|
let (offset, no_update_flag) =
|
||||||
-ascent
|
InlineFlow::relative_offset_from_baseline(cur_box,
|
||||||
},
|
ascent,
|
||||||
vertical_align::middle => {
|
parent_text_top,
|
||||||
// TODO: x-height value should be used from font info.
|
parent_text_bottom,
|
||||||
let xheight = Au::new(0);
|
&mut top_from_base,
|
||||||
-(xheight + cur_box.box_height()).scale_by(0.5)
|
&mut bottom_from_base,
|
||||||
},
|
&mut biggest_top,
|
||||||
vertical_align::sub => {
|
&mut biggest_bottom);
|
||||||
// TODO: The proper position for subscripts should be used.
|
|
||||||
// Lower the baseline to the proper position for subscripts
|
|
||||||
let sub_offset = Au::new(0);
|
|
||||||
(sub_offset - ascent)
|
|
||||||
},
|
|
||||||
vertical_align::super_ => {
|
|
||||||
// TODO: The proper position for superscripts should be used.
|
|
||||||
// Raise the baseline to the proper position for superscripts
|
|
||||||
let super_offset = Au::new(0);
|
|
||||||
(-super_offset - ascent)
|
|
||||||
},
|
|
||||||
vertical_align::text_top => {
|
|
||||||
let box_height = top_from_base + bottom_from_base;
|
|
||||||
let prev_bottom_from_base = bottom_from_base;
|
|
||||||
top_from_base = parent_text_top;
|
|
||||||
bottom_from_base = box_height - top_from_base;
|
|
||||||
(bottom_from_base - prev_bottom_from_base - ascent)
|
|
||||||
},
|
|
||||||
vertical_align::text_bottom => {
|
|
||||||
let box_height = top_from_base + bottom_from_base;
|
|
||||||
let prev_bottom_from_base = bottom_from_base;
|
|
||||||
bottom_from_base = parent_text_bottom;
|
|
||||||
top_from_base = box_height - bottom_from_base;
|
|
||||||
(bottom_from_base - prev_bottom_from_base - ascent)
|
|
||||||
},
|
|
||||||
vertical_align::top => {
|
|
||||||
if biggest_top < (top_from_base + bottom_from_base) {
|
|
||||||
biggest_top = top_from_base + bottom_from_base;
|
|
||||||
}
|
|
||||||
let offset_top = top_from_base - ascent;
|
|
||||||
no_update_flag = true;
|
|
||||||
offset_top
|
|
||||||
},
|
|
||||||
vertical_align::bottom => {
|
|
||||||
if biggest_bottom < (top_from_base + bottom_from_base) {
|
|
||||||
biggest_bottom = top_from_base + bottom_from_base;
|
|
||||||
}
|
|
||||||
let offset_bottom = -(bottom_from_base + ascent);
|
|
||||||
no_update_flag = true;
|
|
||||||
offset_bottom
|
|
||||||
},
|
|
||||||
vertical_align::Length(length) => {
|
|
||||||
-(length + ascent)
|
|
||||||
},
|
|
||||||
vertical_align::Percentage(p) => {
|
|
||||||
let pt_size = cur_box.font_style().pt_size;
|
|
||||||
let line_height = cur_box.calculate_line_height(Au::from_pt(pt_size));
|
|
||||||
let percent_offset = line_height.scale_by(p);
|
|
||||||
-(percent_offset + ascent)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// If the current box has 'top' or 'bottom' value, no_update_flag is true.
|
// If the current box has 'top' or 'bottom' value, no_update_flag is true.
|
||||||
// Otherwise, topmost and bottomost are updated.
|
// Otherwise, topmost and bottomost are updated.
|
||||||
|
@ -826,15 +839,15 @@ impl Flow for InlineFlow {
|
||||||
cur_box.position.mutate().ptr.origin.y = line.bounds.origin.y + offset;
|
cur_box.position.mutate().ptr.origin.y = line.bounds.origin.y + offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the distance from baseline to the top of the biggest box with 'bottom' value.
|
// Calculate the distance from baseline to the top of the biggest box with 'bottom'
|
||||||
// Then, if necessary, update the topmost.
|
// value. Then, if necessary, update the topmost.
|
||||||
let topmost_of_bottom = biggest_bottom - bottommost;
|
let topmost_of_bottom = biggest_bottom - bottommost;
|
||||||
if topmost_of_bottom > topmost {
|
if topmost_of_bottom > topmost {
|
||||||
topmost = topmost_of_bottom;
|
topmost = topmost_of_bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the distance from baseline to the bottom of the biggest box with 'top' value.
|
// Calculate the distance from baseline to the bottom of the biggest box with 'top'
|
||||||
// Then, if necessary, update the bottommost.
|
// value. Then, if necessary, update the bottommost.
|
||||||
let bottommost_of_top = biggest_top - topmost;
|
let bottommost_of_top = biggest_top - topmost;
|
||||||
if bottommost_of_top > bottommost {
|
if bottommost_of_top > bottommost {
|
||||||
bottommost = bottommost_of_top;
|
bottommost = bottommost_of_top;
|
||||||
|
@ -857,7 +870,8 @@ impl Flow for InlineFlow {
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is used to set the top y position of the next linebox in the next loop.
|
// This is used to set the top y position of the next linebox in the next loop.
|
||||||
line_height_offset = line_height_offset + topmost + bottommost - line.bounds.size.height;
|
line_height_offset = line_height_offset + topmost + bottommost -
|
||||||
|
line.bounds.size.height;
|
||||||
line.bounds.size.height = topmost + bottommost;
|
line.bounds.size.height = topmost + bottommost;
|
||||||
} // End of `lines.each` loop.
|
} // End of `lines.each` loop.
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue