mirror of
https://github.com/servo/servo.git
synced 2025-06-25 01:24:37 +01:00
auto merge of #532 : jrfeenst/servo/line-height3, r=pcwalton
Wikipedia, lipsum, and everything else I looked at seems fine now.
This commit is contained in:
commit
eb1e0230a9
8 changed files with 204 additions and 111 deletions
|
@ -112,9 +112,8 @@ pub impl Au {
|
|||
Rect(Point2D(z, z), Size2D(z, z))
|
||||
}
|
||||
|
||||
// assumes 72 points per inch, and 96 px per inch
|
||||
pub fn from_pt(f: float) -> Au {
|
||||
from_px((f / 72f * 96f) as int)
|
||||
from_px(pt_to_px(f) as int)
|
||||
}
|
||||
|
||||
pub fn from_frac_px(f: float) -> Au {
|
||||
|
@ -125,6 +124,16 @@ pub impl Au {
|
|||
pub fn max(x: Au, y: Au) -> Au { if *x > *y { x } else { y } }
|
||||
}
|
||||
|
||||
// assumes 72 points per inch, and 96 px per inch
|
||||
pub fn pt_to_px(f: float) -> float {
|
||||
f / 72f * 96f
|
||||
}
|
||||
|
||||
// assumes 72 points per inch, and 96 px per inch
|
||||
pub fn px_to_pt(f: float) -> float {
|
||||
f / 96f * 72f
|
||||
}
|
||||
|
||||
pub fn zero_rect() -> Rect<Au> {
|
||||
let z = Au(0);
|
||||
Rect(Point2D(z, z), Size2D(z, z))
|
||||
|
|
|
@ -219,7 +219,7 @@ impl FontHandleMethods for FontHandle {
|
|||
x_height: geometry::from_pt(0.0), //FIXME
|
||||
em_size: em_size,
|
||||
ascent: ascent,
|
||||
descent: descent,
|
||||
descent: -descent, // linux font's seem to use the opposite sign from mac
|
||||
max_advance: max_advance
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ use font::{CSSFontWeight, FontHandleMethods, FontMetrics, FontTableMethods};
|
|||
use font::{FontTableTag, FontWeight100, FontWeight200, FontWeight300, FontWeight400};
|
||||
use font::{FontWeight500, FontWeight600, FontWeight700, FontWeight800, FontWeight900};
|
||||
use font::{FractionalPixel, SpecifiedFontStyle};
|
||||
use geometry::Au;
|
||||
use geometry::{Au, px_to_pt};
|
||||
use platform::macos::font_context::FontContextHandle;
|
||||
use text::glyph::GlyphIndex;
|
||||
|
||||
|
@ -157,6 +157,9 @@ 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_frac_px(self.ctfont.pt_size() as float);
|
||||
|
||||
let scale = px_to_pt(self.ctfont.pt_size() as float) / (self.ctfont.ascent() as float + self.ctfont.descent() as float);
|
||||
|
||||
let metrics = FontMetrics {
|
||||
underline_size: Au::from_pt(self.ctfont.underline_thickness() as float),
|
||||
|
@ -168,9 +171,9 @@ 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,
|
||||
em_size: em_size,
|
||||
ascent: ascent.scale_by(scale),
|
||||
descent: descent.scale_by(scale),
|
||||
max_advance: Au::from_pt(bounding_rect.size.width as float)
|
||||
};
|
||||
|
||||
|
|
|
@ -28,15 +28,15 @@ impl Eq for CompressionMode {
|
|||
//
|
||||
// High level TODOs:
|
||||
//
|
||||
// * Issue #113: consider incoming text state (preceding spaces, arabic, etc)
|
||||
// * Issue #113: consider incoming text state (arabic, etc)
|
||||
// and propogate outgoing text state (dual of above)
|
||||
//
|
||||
// * Issue #114: record skipped and kept chars for mapping original to new text
|
||||
//
|
||||
// * Untracked: various edge cases for bidi, CJK, etc.
|
||||
pub fn transform_text(text: &str, mode: CompressionMode) -> ~str {
|
||||
pub fn transform_text(text: &str, mode: CompressionMode, incoming_whitespace: bool) -> (~str, bool) {
|
||||
let mut out_str: ~str = ~"";
|
||||
match mode {
|
||||
let out_whitespace = match mode {
|
||||
CompressNone | DiscardNewline => {
|
||||
for str::each_char(text) |ch: char| {
|
||||
if is_discardable_char(ch, mode) {
|
||||
|
@ -49,18 +49,14 @@ pub fn transform_text(text: &str, mode: CompressionMode) -> ~str {
|
|||
str::push_char(&mut out_str, ch);
|
||||
}
|
||||
}
|
||||
text.len() > 0 && is_in_whitespace(text.char_at_reverse(0), mode)
|
||||
},
|
||||
|
||||
CompressWhitespace | CompressWhitespaceNewline => {
|
||||
let mut in_whitespace: bool = false;
|
||||
let mut in_whitespace: bool = incoming_whitespace;
|
||||
for str::each_char(text) |ch: char| {
|
||||
// TODO: discard newlines between CJK chars
|
||||
let mut next_in_whitespace: bool = match (ch, mode) {
|
||||
(' ', _) => true,
|
||||
('\t', _) => true,
|
||||
('\n', CompressWhitespaceNewline) => true,
|
||||
(_, _) => false
|
||||
};
|
||||
let mut next_in_whitespace: bool = is_in_whitespace(ch, mode);
|
||||
|
||||
if !next_in_whitespace {
|
||||
if is_always_discardable_char(ch) {
|
||||
|
@ -82,10 +78,20 @@ pub fn transform_text(text: &str, mode: CompressionMode) -> ~str {
|
|||
// save whitespace context for next char
|
||||
in_whitespace = next_in_whitespace;
|
||||
} /* /for str::each_char */
|
||||
in_whitespace
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return out_str;
|
||||
return (out_str, out_whitespace);
|
||||
|
||||
fn is_in_whitespace(ch: char, mode: CompressionMode) -> bool {
|
||||
match (ch, mode) {
|
||||
(' ', _) => true,
|
||||
('\t', _) => true,
|
||||
('\n', CompressWhitespaceNewline) => true,
|
||||
(_, _) => false
|
||||
}
|
||||
}
|
||||
|
||||
fn is_discardable_char(ch: char, mode: CompressionMode) -> bool {
|
||||
if is_always_discardable_char(ch) {
|
||||
|
@ -143,7 +149,8 @@ fn test_transform_compress_none() {
|
|||
let mode = CompressNone;
|
||||
|
||||
for uint::range(0, test_strs.len()) |i| {
|
||||
assert!(transform_text(test_strs[i], mode) == test_strs[i]);
|
||||
(trimmed_str, _out) = transform_text(test_strs[i], mode, true);
|
||||
assert!(trimmed_str == test_strs[i])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -170,7 +177,8 @@ fn test_transform_discard_newline() {
|
|||
let mode = DiscardNewline;
|
||||
|
||||
for uint::range(0, test_strs.len()) |i| {
|
||||
assert!(transform_text(test_strs[i], mode) == oracle_strs[i]);
|
||||
(trimmed_str, _out) = transform_text(test_strs[i], mode, true);
|
||||
assert!(trimmed_str == oracle_strs[i])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -196,7 +204,8 @@ fn test_transform_compress_whitespace() {
|
|||
let mode = CompressWhitespace;
|
||||
|
||||
for uint::range(0, test_strs.len()) |i| {
|
||||
assert!(transform_text(test_strs[i], mode) == oracle_strs[i]);
|
||||
(trimmed_str, _out) = transform_text(test_strs[i], mode, true);
|
||||
assert!(trimmed_str == oracle_strs[i])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -210,7 +219,7 @@ fn test_transform_compress_whitespace_newline() {
|
|||
~"foo bar baz",
|
||||
~"foobarbaz\n\n"];
|
||||
|
||||
let oracle_strs : ~[~str] = ~[~" foo bar",
|
||||
let oracle_strs : ~[~str] = ~[~"foo bar",
|
||||
~"foo bar ",
|
||||
~"foo bar",
|
||||
~"foo bar",
|
||||
|
@ -222,6 +231,36 @@ fn test_transform_compress_whitespace_newline() {
|
|||
let mode = CompressWhitespaceNewline;
|
||||
|
||||
for uint::range(0, test_strs.len()) |i| {
|
||||
assert!(transform_text(test_strs[i], mode) == oracle_strs[i]);
|
||||
(trimmed_str, _out) = transform_text(test_strs[i], mode, true);
|
||||
assert!(trimmed_str == oracle_strs[i])
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transform_compress_whitespace_newline() {
|
||||
let test_strs : ~[~str] = ~[~" foo bar",
|
||||
~"\nfoo bar",
|
||||
~"foo bar ",
|
||||
~"foo\n bar",
|
||||
~"foo \nbar",
|
||||
~" foo bar \nbaz",
|
||||
~"foo bar baz",
|
||||
~"foobarbaz\n\n"];
|
||||
|
||||
let oracle_strs : ~[~str] = ~[~" foo bar",
|
||||
~" foo bar",
|
||||
~"foo bar ",
|
||||
~"foo bar",
|
||||
~"foo bar",
|
||||
~" foo bar baz",
|
||||
~"foo bar baz",
|
||||
~"foobarbaz "];
|
||||
|
||||
assert!(test_strs.len() == oracle_strs.len());
|
||||
let mode = CompressWhitespaceNewline;
|
||||
|
||||
for uint::range(0, test_strs.len()) |i| {
|
||||
(trimmed_str, _out) = transform_text(test_strs[i], mode, false);
|
||||
assert!(trimmed_str == oracle_strs[i])
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,14 +21,14 @@ use gfx::display_list::{DisplayList, ImageDisplayItem, ImageDisplayItemClass};
|
|||
use gfx::display_list::{SolidColorDisplayItem, SolidColorDisplayItemClass, TextDisplayItem};
|
||||
use gfx::display_list::{TextDisplayItemClass};
|
||||
use gfx::font::{FontStyle, FontWeight300};
|
||||
use gfx::geometry::Au;
|
||||
use gfx::geometry::{Au, pt_to_px};
|
||||
use gfx::text::text_run::TextRun;
|
||||
use newcss::color::rgb;
|
||||
use newcss::complete::CompleteStyle;
|
||||
use newcss::units::{Cursive, Em, Fantasy, Monospace, Pt, Px, SansSerif, Serif};
|
||||
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 script::dom::node::{AbstractNode, LayoutView};
|
||||
|
@ -559,9 +559,6 @@ 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) => {
|
||||
|
@ -625,11 +622,14 @@ pub impl RenderBox {
|
|||
()
|
||||
});
|
||||
},
|
||||
|
||||
GenericRenderBoxClass(_) => {
|
||||
// FIXME(pcwalton): This is somewhat of an abuse of the logging system.
|
||||
|
||||
// Add the background to the list, if applicable.
|
||||
self.paint_background_if_applicable(list, &absolute_box_bounds);
|
||||
|
||||
// FIXME(pcwalton): This is a bit of an abuse of the logging infrastructure. We
|
||||
// should have a real `SERVO_DEBUG` system.
|
||||
debug!("%?", {
|
||||
// Compute the text box bounds and draw a border surrounding them.
|
||||
do list.with_mut_ref |list| {
|
||||
let border_display_item = ~BorderDisplayItem {
|
||||
base: BaseDisplayItem {
|
||||
|
@ -637,14 +637,20 @@ pub impl RenderBox {
|
|||
extra: ExtraDisplayListData::new(*self),
|
||||
},
|
||||
width: Au::from_px(1),
|
||||
color: rgb(0, 0, 0).to_gfx_color(),
|
||||
color: rgb(0, 0, 200).to_gfx_color(),
|
||||
};
|
||||
list.append_item(BorderDisplayItemClass(border_display_item))
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
()
|
||||
});
|
||||
|
||||
},
|
||||
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");
|
||||
|
@ -708,6 +714,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 {
|
||||
|
@ -723,12 +732,13 @@ 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,
|
||||
CSSFontSizeLength(Pt(length)) => pt_to_px(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
|
||||
};
|
||||
debug!("(font style) font size: `%f`", font_size);
|
||||
debug!("(font style) font size: `%fpx`", font_size);
|
||||
|
||||
let (italic, oblique) = match my_style.font_style() {
|
||||
CSSFontStyleNormal => (false, false),
|
||||
|
@ -751,6 +761,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
|
||||
|
|
|
@ -20,6 +20,9 @@ use gfx::text::util::*;
|
|||
use newcss::values::{CSSTextAlignCenter, CSSTextAlignJustify, CSSTextAlignLeft};
|
||||
use newcss::values::{CSSTextAlignRight, CSSTextDecoration, CSSTextDecorationUnderline};
|
||||
use script::dom::node::{AbstractNode, LayoutView};
|
||||
use newcss::units::{Em, Px, Pt};
|
||||
use newcss::values::{CSSLineHeightNormal, CSSLineHeightNumber, CSSLineHeightLength, CSSLineHeightPercentage};
|
||||
|
||||
use servo_util::range::Range;
|
||||
use std::deque::Deque;
|
||||
|
||||
|
@ -191,17 +194,18 @@ impl TextRunScanner {
|
|||
assert!(inline.boxes.len() > 0);
|
||||
debug!("TextRunScanner: scanning %u boxes for text runs...", inline.boxes.len());
|
||||
|
||||
let mut last_whitespace = true;
|
||||
let mut out_boxes = ~[];
|
||||
for uint::range(0, flow.inline().boxes.len()) |box_i| {
|
||||
debug!("TextRunScanner: considering box: %?", flow.inline().boxes[box_i].debug_str());
|
||||
if box_i > 0 && !can_coalesce_text_nodes(flow.inline().boxes, box_i-1, box_i) {
|
||||
self.flush_clump_to_list(ctx, flow, &mut out_boxes);
|
||||
last_whitespace = self.flush_clump_to_list(ctx, flow, last_whitespace, &mut out_boxes);
|
||||
}
|
||||
self.clump.extend_by(1);
|
||||
}
|
||||
// handle remaining clumps
|
||||
if self.clump.length() > 0 {
|
||||
self.flush_clump_to_list(ctx, flow, &mut out_boxes);
|
||||
self.flush_clump_to_list(ctx, flow, last_whitespace, &mut out_boxes);
|
||||
}
|
||||
|
||||
debug!("TextRunScanner: swapping out boxes.");
|
||||
|
@ -238,7 +242,8 @@ impl TextRunScanner {
|
|||
fn flush_clump_to_list(&mut self,
|
||||
ctx: &mut LayoutContext,
|
||||
flow: FlowContext,
|
||||
out_boxes: &mut ~[RenderBox]) {
|
||||
last_whitespace: bool,
|
||||
out_boxes: &mut ~[RenderBox]) -> bool {
|
||||
let inline = &mut *flow.inline();
|
||||
let in_boxes = &inline.boxes;
|
||||
|
||||
|
@ -258,6 +263,8 @@ impl TextRunScanner {
|
|||
_ => false
|
||||
};
|
||||
|
||||
let mut new_whitespace = last_whitespace;
|
||||
|
||||
match (is_singleton, is_text_clump) {
|
||||
(false, false) => {
|
||||
fail!(~"WAT: can't coalesce non-text nodes in flush_clump_to_list()!")
|
||||
|
@ -275,8 +282,10 @@ impl TextRunScanner {
|
|||
// TODO(#115): Use the actual CSS `white-space` property of the relevant style.
|
||||
let compression = CompressWhitespaceNewline;
|
||||
|
||||
let transformed_text = transform_text(text, compression);
|
||||
let (transformed_text, whitespace) = transform_text(text, compression, last_whitespace);
|
||||
new_whitespace = whitespace;
|
||||
|
||||
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.
|
||||
|
@ -290,19 +299,24 @@ impl TextRunScanner {
|
|||
};
|
||||
|
||||
out_boxes.push(TextRenderBoxClass(new_box));
|
||||
}
|
||||
},
|
||||
(false, true) => {
|
||||
// TODO(#115): Use the actual CSS `white-space` property of the relevant style.
|
||||
let compression = CompressWhitespaceNewline;
|
||||
|
||||
// First, transform/compress text of all the nodes.
|
||||
let mut last_whitespace = true;
|
||||
let transformed_strs: ~[~str] = do vec::from_fn(self.clump.length()) |i| {
|
||||
// TODO(#113): We should be passing the compression context between calls to
|
||||
// `transform_text`, so that boxes starting and/or ending with whitespace can
|
||||
// be compressed correctly with respect to the text run.
|
||||
let idx = i + self.clump.begin();
|
||||
transform_text(in_boxes[idx].raw_text(), compression)
|
||||
let (new_str, new_whitespace) = transform_text(in_boxes[idx].raw_text(), compression, last_whitespace);
|
||||
last_whitespace = new_whitespace;
|
||||
new_str
|
||||
};
|
||||
new_whitespace = last_whitespace;
|
||||
|
||||
// Next, concatenate all of the transformed strings together, saving the new
|
||||
// character indices.
|
||||
|
@ -328,7 +342,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
|
||||
|
@ -373,6 +387,8 @@ impl TextRunScanner {
|
|||
|
||||
let end = self.clump.end(); // FIXME: borrow checker workaround
|
||||
self.clump.reset(end, 0);
|
||||
|
||||
new_whitespace
|
||||
} // End of `flush_clump_to_list`.
|
||||
}
|
||||
|
||||
|
@ -748,7 +764,6 @@ 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| {
|
||||
|
@ -756,64 +771,71 @@ impl InlineFlowData {
|
|||
|
||||
// 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;
|
||||
|
||||
if height > linebox_height {
|
||||
linebox_height = height;
|
||||
}
|
||||
TextRenderBoxClass(*) => {
|
||||
// Text boxes are preinitialized.
|
||||
|
||||
image_box.base.position.translate(&Point2D(Au(0), -height))
|
||||
}
|
||||
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),
|
||||
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 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)))
|
||||
}
|
||||
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);
|
||||
if generic_box.position.size.height > linebox_height {
|
||||
linebox_height = generic_box.position.size.height;
|
||||
}
|
||||
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 = %?",
|
||||
|
@ -825,9 +847,6 @@ 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];
|
||||
|
@ -835,30 +854,19 @@ impl InlineFlowData {
|
|||
// 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;
|
||||
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;
|
||||
|
|
10
src/test/html/lineheight-simple.css
Normal file
10
src/test/html/lineheight-simple.css
Normal file
|
@ -0,0 +1,10 @@
|
|||
#larger1 {
|
||||
font-size: 20px;
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
#larger2 {
|
||||
font-size: 30px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
10
src/test/html/lineheight-simple.html
Normal file
10
src/test/html/lineheight-simple.html
Normal file
|
@ -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>
|
Loading…
Add table
Add a link
Reference in a new issue