diff options
author | Junyoung Cho <june0.cho@samsung.com> | 2013-08-22 19:05:21 +0900 |
---|---|---|
committer | Junyoung Cho <june0.cho@samsung.com> | 2013-08-22 19:15:38 +0900 |
commit | 3b3f3f8b8ad8f068b771174754cc2930f8d11cf0 (patch) | |
tree | 0a95f1ec14745d9a3bdf0e659cc8a2048bf215bd /src | |
parent | 0c50d4374f77a7d3b7a8a549859e537faae36b5e (diff) | |
download | servo-3b3f3f8b8ad8f068b771174754cc2930f8d11cf0.tar.gz servo-3b3f3f8b8ad8f068b771174754cc2930f8d11cf0.zip |
Implement 'vertical-align' and fix 'line-height'.
Diffstat (limited to 'src')
-rw-r--r-- | src/components/main/layout/box.rs | 6 | ||||
-rw-r--r-- | src/components/main/layout/inline.rs | 227 |
2 files changed, 183 insertions, 50 deletions
diff --git a/src/components/main/layout/box.rs b/src/components/main/layout/box.rs index 2d30a2d584b..844e37954f5 100644 --- a/src/components/main/layout/box.rs +++ b/src/components/main/layout/box.rs @@ -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 diff --git a/src/components/main/layout/inline.rs b/src/components/main/layout/inline.rs index 08119ee308c..60b940130a2 100644 --- a/src/components/main/layout/inline.rs +++ b/src/components/main/layout/inline.rs @@ -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; + } + } + + //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; } - // Now go back and adjust the Y coordinates to match the baseline we determined. + 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. |