Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial support for line heights calculations. #493

Merged
merged 3 commits into from May 30, 2013
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

@@ -17,9 +17,9 @@ config.mk
config.stamp
config.tmp
parser.out
src/components/servo/dom/bindings/codegen/*.rs
src/components/servo/dom/bindings/codegen/_cache/
src/components/servo/dom/bindings/codegen/test/*.rs
src/components/servo/dom/bindings/codegen/PrototypeList.h
src/components/servo/dom/bindings/codegen/UnionTypes.h
src/components/servo/dom/bindings/codegen/UnionConversions.h
src/components/main/dom/bindings/codegen/*.rs
src/components/main/dom/bindings/codegen/PrototypeList.h
src/components/main/dom/bindings/codegen/UnionConversions.h
src/components/main/dom/bindings/codegen/UnionTypes.h
src/components/main/dom/bindings/codegen/_cache/
src/components/main/dom/bindings/codegen/test/*.rs
@@ -157,6 +157,7 @@ impl FontHandleMethods for FontHandle {
let bounding_rect: CGRect = self.ctfont.bounding_box();
let ascent = Au::from_pt(self.ctfont.ascent() as float);
let descent = Au::from_pt(self.ctfont.descent() as float);
let em_size = Au::from_pt(self.ctfont.pt_size() as float);

let metrics = FontMetrics {
underline_size: Au::from_pt(self.ctfont.underline_thickness() as float),
@@ -168,9 +169,10 @@ impl FontHandleMethods for FontHandle {
underline_offset: Au::from_pt(self.ctfont.underline_position() as float),
leading: Au::from_pt(self.ctfont.leading() as float),
x_height: Au::from_pt(self.ctfont.x_height() as float),
em_size: ascent + descent,
ascent: ascent,
descent: descent,
// TODO: the ascent and descent need to be scaled by the pt_size, figure out why
em_size: em_size,
ascent: ascent * em_size / (ascent + descent),
descent: descent * em_size / (ascent + descent),
max_advance: Au::from_pt(bounding_rect.size.width as float)
};

@@ -26,7 +26,7 @@ use newcss::units::{Cursive, Em, Fantasy, Monospace, Pt, Px, SansSerif, Serif};
use newcss::values::{CSSBorderWidthLength, CSSBorderWidthMedium};
use newcss::values::{CSSFontFamilyFamilyName, CSSFontFamilyGenericFamily};
use newcss::values::{CSSFontSizeLength, CSSFontStyleItalic, CSSFontStyleNormal};
use newcss::values::{CSSFontStyleOblique, CSSTextAlign, CSSTextDecoration};
use newcss::values::{CSSFontStyleOblique, CSSTextAlign, CSSTextDecoration, CSSLineHeight};
use newcss::values::{CSSTextDecorationNone, CSSFloatNone, CSSPositionStatic};
use newcss::values::{CSSDisplayInlineBlock, CSSDisplayInlineTable};
use servo_net::image::holder::ImageHolder;
@@ -559,12 +559,10 @@ pub impl RenderBox {
return;
}

// Add the background to the list, if applicable.
self.paint_background_if_applicable(list, &absolute_box_bounds);

match *self {
UnscannedTextRenderBoxClass(*) => fail!(~"Shouldn't see unscanned boxes here."),
TextRenderBoxClass(text_box) => {

let nearest_ancestor_element = self.nearest_ancestor_element();
let color = nearest_ancestor_element.style().color().to_gfx_color();

@@ -604,10 +602,30 @@ pub impl RenderBox {
()
});
},
GenericRenderBoxClass(_) => {

// Add the background to the list, if applicable.
self.paint_background_if_applicable(list, &absolute_box_bounds);

GenericRenderBoxClass(_) => {}
// FIXME(pcwalton): This is a bit of an abuse of the logging infrastructure. We
// should have a real `SERVO_DEBUG` system.
debug!("%?", {
// FIXME: This should use `with_mut_ref` when that appears.
let mut this_list = list.take();
this_list.append_item(~DisplayItem::new_Border(&absolute_box_bounds,
Au::from_px(1),
rgb(0, 0, 200).to_gfx_color()));

list.put_back(this_list);
()
});

},
ImageRenderBoxClass(image_box) => {

// Add the background to the list, if applicable.
self.paint_background_if_applicable(list, &absolute_box_bounds);

match image_box.image.get_image() {
Some(image) => {
debug!("(building display list) building image box");
@@ -713,6 +731,9 @@ pub impl RenderBox {
fn font_style(&self) -> FontStyle {
let my_style = self.nearest_ancestor_element().style();

debug!("(font style) start: %?", self.nearest_ancestor_element().type_id());
self.dump();

// FIXME: Too much allocation here.
let font_families = do my_style.font_family().map |family| {
match *family {
@@ -728,12 +749,12 @@ pub impl RenderBox {
debug!("(font style) font families: `%s`", font_families);

let font_size = match my_style.font_size() {
CSSFontSizeLength(Px(length)) |
CSSFontSizeLength(Pt(length)) |
CSSFontSizeLength(Em(length)) => length,
_ => 16.0
CSSFontSizeLength(Px(length)) => length * 72f / 96f,
CSSFontSizeLength(Pt(length)) => length,
CSSFontSizeLength(Em(length)) => length * 16f,
_ => 12f // pt units
};
debug!("(font style) font size: `%f`", font_size);
debug!("(font style) font size: `%fpt`", font_size);

let (italic, oblique) = match my_style.font_style() {
CSSFontStyleNormal => (false, false),
@@ -756,6 +777,10 @@ pub impl RenderBox {
self.nearest_ancestor_element().style().text_align()
}

fn line_height(&self) -> CSSLineHeight {
self.nearest_ancestor_element().style().line_height()
}

/// Returns the text decoration of the computed style of the nearest `Element` node
fn text_decoration(&self) -> CSSTextDecoration {
/// Computes the propagated value of text-decoration, as specified in CSS 2.1 § 16.3.1
@@ -824,7 +849,7 @@ impl DebugMethods for RenderBox {
text_box.range.length()))
}
UnscannedTextRenderBoxClass(text_box) => {
fmt!("UnscannedTextRenderBox(%s)", text_box.text)
fmt!("UnscannedTextRenderBox(%?, %s)", text_box.base.node.type_id(), text_box.text)
}
};

@@ -23,6 +23,9 @@ use newcss::values::{CSSTextAlignCenter, CSSTextAlignJustify, CSSTextAlignLeft};
use newcss::values::{CSSTextAlignRight};
use newcss::values::CSSTextDecorationUnderline;
use newcss::values::CSSTextDecoration;
use newcss::units::{Em, Px, Pt};
use newcss::values::{CSSLineHeightNormal, CSSLineHeightNumber, CSSLineHeightLength, CSSLineHeightPercentage};

use servo_util::range::Range;
use std::deque::Deque;

@@ -276,27 +279,29 @@ impl TextRunScanner {
let underline = has_underline(old_box.text_decoration());

// TODO(#115): Use the actual CSS `white-space` property of the relevant style.
let compression = CompressWhitespaceNewline;
let compression = DiscardNewline;

let transformed_text = transform_text(text, compression);

// TODO(#177): Text run creation must account for the renderability of text by
// font group fonts. This is probably achieved by creating the font group above
// and then letting `FontGroup` decide which `Font` to stick into the text run.
let fontgroup = ctx.font_ctx.get_resolved_font_for_style(&font_style);
let run = @fontgroup.create_textrun(transformed_text, underline);

debug!("TextRunScanner: pushing single text box in range: %?", self.clump);
let new_box = do old_box.with_imm_base |old_box_base| {
let range = Range::new(0, run.char_len());
@mut adapt_textbox_with_range(*old_box_base, run, range)
};
if transformed_text.len() > 0 {
// TODO(#177): Text run creation must account for the renderability of text by
// font group fonts. This is probably achieved by creating the font group above
// and then letting `FontGroup` decide which `Font` to stick into the text run.
let fontgroup = ctx.font_ctx.get_resolved_font_for_style(&font_style);
let run = @fontgroup.create_textrun(transformed_text, underline);

debug!("TextRunScanner: pushing single text box in range: %?", self.clump);
let new_box = do old_box.with_imm_base |old_box_base| {
let range = Range::new(0, run.char_len());
@mut adapt_textbox_with_range(*old_box_base, run, range)
};

out_boxes.push(TextRenderBoxClass(new_box));
out_boxes.push(TextRenderBoxClass(new_box));
}
},
(false, true) => {
// TODO(#115): Use the actual CSS `white-space` property of the relevant style.
let compression = CompressWhitespaceNewline;
let compression = DiscardNewline;

// First, transform/compress text of all the nodes.
let transformed_strs: ~[~str] = do vec::from_fn(self.clump.length()) |i| {
@@ -306,7 +311,7 @@ impl TextRunScanner {
let idx = i + self.clump.begin();
transform_text(in_boxes[idx].raw_text(), compression)
};

// Next, concatenate all of the transformed strings together, saving the new
// character indices.
let mut run_str: ~str = ~"";
@@ -331,7 +336,7 @@ impl TextRunScanner {
// TextRuns contain a cycle which is usually resolved by the teardown
// sequence. If no clump takes ownership, however, it will leak.
let clump = self.clump;
let run = if clump.length() != 0 {
let run = if clump.length() != 0 && run_str.len() > 0 {
Some(@TextRun::new(fontgroup.fonts[0], run_str, underline))
} else {
None
@@ -751,72 +756,70 @@ impl InlineFlowData {
// TODO(#226): Get the CSS `line-height` property from each non-replaced inline element to
// determine its height for computing linebox height.

let line_height = Au::from_px(20);
let mut cur_y = Au(0);

for self.lines.eachi |i, line_span| {
debug!("assign_height_inline: processing line %u with box span: %?", i, line_span);

// These coordinates are relative to the left baseline.
let mut linebox_bounding_box = Au::zero_rect();
let mut linebox_height = Au(0);
let mut baseline_offset = Au(0);

let boxes = &mut self.boxes;

for line_span.eachi |box_i| {
let cur_box = boxes[box_i]; // FIXME: borrow checker workaround

// Compute the height of each box.
match cur_box {
// Compute the height and bounding box of each box.
let bounding_box = match cur_box {
ImageRenderBoxClass(image_box) => {
let size = image_box.image.get_size();
let height = Au::from_px(size.get_or_default(Size2D(0, 0)).height);
image_box.base.position.size.height = height;

image_box.base.position.translate(&Point2D(Au(0), -height))
}
TextRenderBoxClass(*) => {
// Text boxes are preinitialized.
TextRenderBoxClass(text_box) => {
// Compute the height based on the line-height and font size
let em_size = text_box.run.font.metrics.em_size;
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),
CSSLineHeightLength(Pt(l)) => Au::from_pt(l),
CSSLineHeightPercentage(p) => em_size.scale_by(p / 100.0f)
};

// 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 > linebox_height {
linebox_height = line_height;
// Offset from the top of the linebox
baseline_offset = text_box.run.font.metrics.ascent +
(linebox_height - em_size).scale_by(0.5f);
}

let range = &text_box.range;
let run = &text_box.run;
let text_bounds = run.metrics_for_range(range).bounding_box;
text_bounds.translate(&Point2D(text_box.base.position.origin.x, Au(0)))
}
GenericRenderBoxClass(generic_box) => {
// TODO(Issue #225): There will be different cases here for `inline-block`
// and other replaced content.
// FIXME(pcwalton): This seems clownshoes; can we remove?
generic_box.position.size.height = Au::from_px(30);
generic_box.position
}
// FIXME(pcwalton): This isn't very type safe!
_ => {
fail!(fmt!("Tried to assign height to unknown Box variant: %s",
cur_box.debug_str()))
}
}

// Compute the bounding rect with the left baseline as origin. Determining line box
// height is a matter of lining up ideal baselines and then taking the union of all
// these rects.
let bounding_box = match cur_box {
// Adjust to baseline coordinates.
//
// TODO(#227): Use left/right margins, border, padding for nonreplaced content,
// and also use top/bottom margins, border, padding for replaced or
// inline-block content.
//
// TODO(#225): Use height, width for `inline-block` and other replaced content.
ImageRenderBoxClass(*) | GenericRenderBoxClass(*) => {
let height = cur_box.position().size.height;
cur_box.position().translate(&Point2D(Au(0), -height))
},

// Adjust the bounding box metric to the box's horizontal offset.
//
// TODO: We can use font metrics directly instead of re-measuring for the
// bounding box.
TextRenderBoxClass(text_box) => {
let range = &text_box.range;
let run = &text_box.run;
let text_bounds = run.metrics_for_range(range).bounding_box;
text_bounds.translate(&Point2D(text_box.base.position.origin.x, Au(0)))
},

_ => {
fail!(fmt!("Tried to compute bounding box of unknown Box variant: %s",
cur_box.debug_str()))
}
};

debug!("assign_height_inline: bounding box for box b%d = %?",
@@ -828,40 +831,27 @@ impl InlineFlowData {
debug!("assign_height_inline: linebox bounding box = %?", linebox_bounding_box);
}

let linebox_height = linebox_bounding_box.size.height;
let baseline_offset = -linebox_bounding_box.origin.y;

// Now go back and adjust the Y coordinates to match the baseline we determined.
for line_span.eachi |box_i| {
let cur_box = 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 halfleading = match cur_box {
let offset = match cur_box {
TextRenderBoxClass(text_box) => {
//ad is the AD height as defined by CSS 2.1 § 10.8.1
let ad = text_box.run.font.metrics.ascent + text_box.run.font.metrics.descent;
(line_height - ad).scale_by(0.5)
baseline_offset - text_box.run.font.metrics.ascent
},
_ => Au(0),
};

//FIXME: when line-height is set on an inline element, the half leading
//distance can be negative.
let halfleading = Au::max(halfleading, Au(0));

let height = match cur_box {
TextRenderBoxClass(text_box) => text_box.run.font.metrics.ascent,
_ => cur_box.position().size.height
};

do cur_box.with_mut_base |base| {
base.position.origin.y = cur_y + halfleading + baseline_offset - height;
let height = base.position.size.height;
base.position.origin.y = offset + cur_y;
}
}

cur_y += Au::max(line_height, linebox_height);
cur_y += linebox_height;
} // End of `lines.each` loop.

self.common.position.size.height = cur_y;
@@ -0,0 +1,10 @@
#larger1 {
font-size: 20px;
line-height: 2;
}

#larger2 {
font-size: 30px;
line-height: 1;
}

@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="lineheight-simple.css"/>
</head>
<body>
<div>Regular font <span id="larger1">Even larger with line-height 2</span></div>
<div id="larger2">Large line 2!</div>
</body>
</html>
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.