mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
Implement 'vertical-align' and fix 'line-height'.
This commit is contained in:
parent
0c50d4374f
commit
3b3f3f8b8a
2 changed files with 183 additions and 50 deletions
|
@ -30,7 +30,7 @@ use newcss::units::{Cursive, Fantasy, Monospace, SansSerif, Serif};
|
|||
use newcss::values::{CSSClearNone, CSSClearLeft, CSSClearRight, CSSClearBoth};
|
||||
use newcss::values::{CSSFontFamilyFamilyName, CSSFontFamilyGenericFamily};
|
||||
use newcss::values::{CSSFontSizeLength, CSSFontStyleItalic, CSSFontStyleNormal};
|
||||
use newcss::values::{CSSFontStyleOblique, CSSTextAlign, CSSTextDecoration, CSSLineHeight};
|
||||
use newcss::values::{CSSFontStyleOblique, CSSTextAlign, CSSTextDecoration, CSSLineHeight, CSSVerticalAlign};
|
||||
use newcss::values::{CSSTextDecorationNone, CSSFloatNone, CSSPositionStatic};
|
||||
use newcss::values::{CSSDisplayInlineBlock, CSSDisplayInlineTable};
|
||||
use script::dom::node::{AbstractNode, LayoutView};
|
||||
|
@ -817,6 +817,10 @@ impl RenderBox {
|
|||
self.nearest_ancestor_element().style().line_height()
|
||||
}
|
||||
|
||||
pub fn vertical_align(&self) -> CSSVerticalAlign {
|
||||
self.nearest_ancestor_element().style().vertical_align()
|
||||
}
|
||||
|
||||
/// Returns the text decoration of the computed style of the nearest `Element` node
|
||||
pub fn text_decoration(&self) -> CSSTextDecoration {
|
||||
/// Computes the propagated value of text-decoration, as specified in CSS 2.1 § 16.3.1
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use css::node_style::StyledNode;
|
||||
use std::cell::Cell;
|
||||
use layout::box::{CannotSplit, GenericRenderBoxClass, ImageRenderBoxClass, RenderBox};
|
||||
use layout::box::{SplitDidFit, SplitDidNotFit, TextRenderBoxClass};
|
||||
|
@ -17,9 +18,13 @@ use std::util;
|
|||
use geom::{Point2D, Rect, Size2D};
|
||||
use gfx::display_list::DisplayList;
|
||||
use gfx::geometry::Au;
|
||||
use newcss::values::{CSSTextAlignLeft, CSSTextAlignCenter, CSSTextAlignRight, CSSTextAlignJustify};
|
||||
use newcss::units::{Em, Px};
|
||||
use newcss::values::{CSSFontSizeLength};
|
||||
use newcss::values::{CSSTextAlignLeft, CSSTextAlignCenter, CSSTextAlignRight, CSSTextAlignJustify};
|
||||
use newcss::values::{CSSLineHeightNormal, CSSLineHeightNumber, CSSLineHeightLength, CSSLineHeightPercentage};
|
||||
use newcss::values::{CSSVerticalAlignBaseline, CSSVerticalAlignMiddle, CSSVerticalAlignSub, CSSVerticalAlignSuper,
|
||||
CSSVerticalAlignTextTop, CSSVerticalAlignTextBottom, CSSVerticalAlignTop, CSSVerticalAlignBottom,
|
||||
CSSVerticalAlignLength, CSSVerticalAlignPercentage};
|
||||
use servo_util::range::Range;
|
||||
use servo_util::tree::TreeNodeRef;
|
||||
use extra::container::Deque;
|
||||
|
@ -164,6 +169,16 @@ impl LineboxScanner {
|
|||
self.reset_linebox();
|
||||
}
|
||||
|
||||
fn calculate_line_height(&self, box: RenderBox, font_size: Au) -> Au {
|
||||
match box.line_height() {
|
||||
CSSLineHeightNormal => font_size.scale_by(1.14f),
|
||||
CSSLineHeightNumber(l) => font_size.scale_by(l),
|
||||
CSSLineHeightLength(Em(l)) => font_size.scale_by(l),
|
||||
CSSLineHeightLength(Px(l)) => Au::from_frac_px(l),
|
||||
CSSLineHeightPercentage(p) => font_size.scale_by(p / 100.0f)
|
||||
}
|
||||
}
|
||||
|
||||
fn box_height(&self, box: RenderBox) -> Au {
|
||||
match box {
|
||||
ImageRenderBoxClass(image_box) => {
|
||||
|
@ -180,13 +195,7 @@ impl LineboxScanner {
|
|||
// Compute the height based on the line-height and font size
|
||||
let text_bounds = run.metrics_for_range(range).bounding_box;
|
||||
let em_size = text_bounds.size.height;
|
||||
let line_height = match box.line_height() {
|
||||
CSSLineHeightNormal => em_size.scale_by(1.14f),
|
||||
CSSLineHeightNumber(l) => em_size.scale_by(l),
|
||||
CSSLineHeightLength(Em(l)) => em_size.scale_by(l),
|
||||
CSSLineHeightLength(Px(l)) => Au::from_frac_px(l),
|
||||
CSSLineHeightPercentage(p) => em_size.scale_by(p / 100.0f)
|
||||
};
|
||||
let line_height = self.calculate_line_height(box, em_size);
|
||||
|
||||
line_height
|
||||
}
|
||||
|
@ -654,78 +663,198 @@ impl InlineFlowData {
|
|||
}
|
||||
};
|
||||
|
||||
let mut topmost = Au(0);
|
||||
let mut bottommost = Au(0);
|
||||
// bottommost of boxes with 'top' value
|
||||
let mut bottommost_of_top = Au(0);
|
||||
// topmost of boxes with 'bottom' value
|
||||
let mut topmost_of_bottom = Au(0);
|
||||
|
||||
// Get the baseline offset, assuming that the tallest text box will determine
|
||||
// the baseline.
|
||||
let mut baseline_offset = Au(0);
|
||||
let mut max_height = Au(0);
|
||||
for box_i in line.range.eachi() {
|
||||
let cur_box = self.boxes[box_i];
|
||||
|
||||
match cur_box {
|
||||
let (top_from_base, bottom_from_base, ascent) = match cur_box {
|
||||
ImageRenderBoxClass(image_box) => {
|
||||
let size = image_box.image.get_size();
|
||||
let height = Au::from_px(size.unwrap_or_default(Size2D(0, 0)).height);
|
||||
let mut height = Au::from_px(size.unwrap_or_default(Size2D(0, 0)).height);
|
||||
|
||||
// TODO: margin, border, padding's top and bottom should be calculated in advance,
|
||||
// since baseline of image is bottom margin edge.
|
||||
let mut top = Au(0);
|
||||
let mut bottom = Au(0);
|
||||
do cur_box.with_model |model| {
|
||||
top = model.border.top + model.padding.top + model.margin.top;
|
||||
bottom = model.border.bottom + model.padding.bottom + model.margin.bottom;
|
||||
}
|
||||
let noncontent_height = top + bottom;
|
||||
height = height + noncontent_height;
|
||||
image_box.base.position.size.height = height;
|
||||
image_box.base.position.translate(&Point2D(Au(0), -height));
|
||||
|
||||
image_box.base.position.translate(&Point2D(Au(0), -height))
|
||||
}
|
||||
let ascent = height + bottom;
|
||||
(height, Au(0), ascent)
|
||||
},
|
||||
TextRenderBoxClass(text_box) => {
|
||||
|
||||
let range = &text_box.range;
|
||||
let run = &text_box.run;
|
||||
|
||||
// Compute the height based on the line-height and font size
|
||||
let text_bounds = run.metrics_for_range(range).bounding_box;
|
||||
let em_size = text_bounds.size.height;
|
||||
let line_height = match cur_box.line_height() {
|
||||
CSSLineHeightNormal => em_size.scale_by(1.14f),
|
||||
CSSLineHeightNumber(l) => em_size.scale_by(l),
|
||||
CSSLineHeightLength(Em(l)) => em_size.scale_by(l),
|
||||
CSSLineHeightLength(Px(l)) => Au::from_frac_px(l),
|
||||
CSSLineHeightPercentage(p) => em_size.scale_by(p / 100.0f)
|
||||
};
|
||||
let line_height = scanner.calculate_line_height(cur_box, em_size);
|
||||
|
||||
// If this is the current tallest box then use it for baseline
|
||||
// calculations.
|
||||
// TODO: this will need to take into account type of line-height
|
||||
// and the vertical-align value.
|
||||
if line_height > max_height {
|
||||
max_height = line_height;
|
||||
let linebox_height = line.bounds.size.height;
|
||||
// Offset from the top of the linebox is 1/2 of the leading + ascent
|
||||
baseline_offset = text_box.run.font.metrics.ascent +
|
||||
(linebox_height - em_size).scale_by(0.5f);
|
||||
}
|
||||
text_bounds.translate(&Point2D(text_box.base.position.origin.x, Au(0)))
|
||||
}
|
||||
// Find the top and bottom of the content area.
|
||||
// Those are used in text-top and text-bottom value of 'vertex-align'
|
||||
let text_ascent = text_box.run.font.metrics.ascent;
|
||||
|
||||
// Offset from the top of the box is 1/2 of the leading + ascent
|
||||
let text_offset = text_ascent + (line_height - em_size).scale_by(0.5f);
|
||||
text_bounds.translate(&Point2D(text_box.base.position.origin.x, Au(0)));
|
||||
|
||||
(text_offset, line_height - text_offset, text_ascent)
|
||||
},
|
||||
GenericRenderBoxClass(generic_box) => {
|
||||
generic_box.position
|
||||
}
|
||||
(generic_box.position.size.height, Au(0), generic_box.position.size.height)
|
||||
},
|
||||
// FIXME(pcwalton): This isn't very type safe!
|
||||
_ => {
|
||||
fail!(fmt!("Tried to assign height to unknown Box variant: %s",
|
||||
cur_box.debug_str()))
|
||||
}
|
||||
};
|
||||
let mut top_from_base = top_from_base;
|
||||
let mut bottom_from_base = bottom_from_base;
|
||||
|
||||
// TODO: We should find the top and bottom of the content area of parent box.
|
||||
// Those are used in text-top and text-bottom value of 'vertex-align'.
|
||||
// Assume that top is font_size of parent and bottom is 0.
|
||||
let mut parent_text_top = Au(0);
|
||||
let parent_text_bottom = Au(0);
|
||||
do cur_box.with_mut_base |base| {
|
||||
//get parent node
|
||||
let mut parent = base.node;
|
||||
match base.node.parent_node() {
|
||||
None => {},
|
||||
Some(parent_node) => parent = parent_node
|
||||
}
|
||||
let font_size = match parent.style().font_size() {
|
||||
CSSFontSizeLength(Px(length)) => length,
|
||||
// todo: this is based on a hard coded font size, should be the parent element's font size
|
||||
CSSFontSizeLength(Em(length)) => length * 16f,
|
||||
_ => 16f // px units
|
||||
};
|
||||
parent_text_top = Au::from_frac_px(font_size);
|
||||
}
|
||||
|
||||
let mut no_update_flag = false;
|
||||
let offset = match cur_box.vertical_align() {
|
||||
CSSVerticalAlignBaseline => {
|
||||
-ascent
|
||||
},
|
||||
CSSVerticalAlignMiddle => {
|
||||
// TODO: x-height value should be used from font info.
|
||||
let xheight = Au(0);
|
||||
-(xheight + scanner.box_height(cur_box)).scale_by(0.5)
|
||||
},
|
||||
CSSVerticalAlignSub => {
|
||||
// TODO: The proper position for subscripts should be used.
|
||||
// Lower the baseline to the proper position for subscripts
|
||||
let sub_offset = Au(0);
|
||||
(sub_offset - ascent)
|
||||
},
|
||||
CSSVerticalAlignSuper => {
|
||||
// TODO: The proper position for superscripts should be used.
|
||||
// Raise the baseline to the proper position for superscripts
|
||||
let super_offset = Au(0);
|
||||
(-super_offset - ascent)
|
||||
},
|
||||
CSSVerticalAlignTextTop => {
|
||||
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)
|
||||
},
|
||||
CSSVerticalAlignTextBottom => {
|
||||
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)
|
||||
},
|
||||
CSSVerticalAlignTop => {
|
||||
if bottommost_of_top < (top_from_base + bottom_from_base) {
|
||||
bottommost_of_top = top_from_base + bottom_from_base;
|
||||
}
|
||||
let offset_top = top_from_base - ascent;
|
||||
no_update_flag = true;
|
||||
offset_top
|
||||
},
|
||||
CSSVerticalAlignBottom => {
|
||||
if topmost_of_bottom < (top_from_base + bottom_from_base) {
|
||||
topmost_of_bottom = top_from_base + bottom_from_base;
|
||||
}
|
||||
let offset_bottom = -(bottom_from_base + ascent);
|
||||
no_update_flag = true;
|
||||
offset_bottom
|
||||
},
|
||||
CSSVerticalAlignLength(length) => {
|
||||
let length_offset = match length {
|
||||
Em(l) => Au::from_frac_px(cur_box.font_style().pt_size * l),
|
||||
Px(l) => Au::from_frac_px(l),
|
||||
};
|
||||
-(length_offset + ascent)
|
||||
},
|
||||
CSSVerticalAlignPercentage(p) => {
|
||||
let pt_size = cur_box.font_style().pt_size;
|
||||
let line_height = scanner.calculate_line_height(cur_box, Au::from_pt(pt_size));
|
||||
let percent_offset = line_height.scale_by(p / 100.0f);
|
||||
-(percent_offset + ascent)
|
||||
}
|
||||
};
|
||||
|
||||
if !no_update_flag && top_from_base > topmost {
|
||||
topmost = top_from_base;
|
||||
}
|
||||
if !no_update_flag && bottom_from_base > bottommost {
|
||||
bottommost = bottom_from_base;
|
||||
}
|
||||
|
||||
do cur_box.with_mut_base |base| {
|
||||
base.position.origin.y = line.bounds.origin.y + offset;
|
||||
}
|
||||
}
|
||||
|
||||
// Now go back and adjust the Y coordinates to match the baseline we determined.
|
||||
//Offset of boxes with 'bottom' value.
|
||||
topmost_of_bottom = topmost_of_bottom - bottommost;
|
||||
if topmost_of_bottom > topmost {
|
||||
topmost = topmost_of_bottom;
|
||||
}
|
||||
|
||||
//Offset of boxes with 'top' value.
|
||||
bottommost_of_top = bottommost_of_top - topmost;
|
||||
if bottommost_of_top > bottommost {
|
||||
//topmost_of_bottom = topmost_of_bottom - (bottommost_of_top - bottommost);
|
||||
bottommost = bottommost_of_top;
|
||||
}
|
||||
|
||||
let baseline_offset = topmost;
|
||||
for box_i in line.range.eachi() {
|
||||
let cur_box = self.boxes[box_i];
|
||||
|
||||
// TODO(#226): This is completely wrong. We need to use the element's `line-height`
|
||||
// when calculating line box height. Then we should go back over and set Y offsets
|
||||
// according to the `vertical-align` property of the containing block.
|
||||
let offset = match cur_box {
|
||||
TextRenderBoxClass(text_box) => {
|
||||
baseline_offset - text_box.run.font.metrics.ascent
|
||||
let adjust_offset = match cur_box.vertical_align() {
|
||||
CSSVerticalAlignTop => {
|
||||
Au(0)
|
||||
},
|
||||
_ => Au(0),
|
||||
CSSVerticalAlignBottom => {
|
||||
baseline_offset + bottommost
|
||||
},
|
||||
_ => {
|
||||
baseline_offset
|
||||
}
|
||||
};
|
||||
|
||||
do cur_box.with_mut_base |base| {
|
||||
base.position.origin.y = offset + line.bounds.origin.y;
|
||||
base.position.origin.y = base.position.origin.y + adjust_offset;
|
||||
}
|
||||
}
|
||||
} // End of `lines.each` loop.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue