mirror of
https://github.com/servo/servo.git
synced 2025-08-05 21:50:18 +01:00
auto merge of #932 : june0cho/servo/text_deco, r=metajack
Implement 'overline' and 'line-through' in 'text-decoration' property. Add a test case.
This commit is contained in:
commit
77a09b2003
8 changed files with 98 additions and 30 deletions
|
@ -26,6 +26,8 @@ use servo_net::image::base::Image;
|
||||||
use servo_util::range::Range;
|
use servo_util::range::Range;
|
||||||
use extra::arc::Arc;
|
use extra::arc::Arc;
|
||||||
|
|
||||||
|
use newcss::values::{CSSTextDecorationUnderline, CSSTextDecorationOverline, CSSTextDecorationLineThrough};
|
||||||
|
|
||||||
/// A list of rendering operations to be performed.
|
/// A list of rendering operations to be performed.
|
||||||
pub struct DisplayList<E> {
|
pub struct DisplayList<E> {
|
||||||
list: ~[DisplayItem<E>]
|
list: ~[DisplayItem<E>]
|
||||||
|
@ -135,14 +137,31 @@ impl<E> DisplayItem<E> {
|
||||||
baseline_origin,
|
baseline_origin,
|
||||||
text.color);
|
text.color);
|
||||||
|
|
||||||
if new_run.underline {
|
let width = text.base.bounds.size.width;
|
||||||
// TODO(eatkinson): Use the font metrics to properly position the underline
|
let underline_size = font.metrics.underline_size;
|
||||||
// bar.
|
let underline_offset = font.metrics.underline_offset;
|
||||||
let width = text.base.bounds.size.width;
|
let strikeout_size = font.metrics.strikeout_size;
|
||||||
let underline_size = font.metrics.underline_size;
|
let strikeout_offset = font.metrics.strikeout_offset;
|
||||||
let underline_bounds = Rect(Point2D(baseline_origin.x, baseline_origin.y),
|
|
||||||
Size2D(width, underline_size));
|
match new_run.decoration {
|
||||||
render_context.draw_solid_color(&underline_bounds, text.color);
|
CSSTextDecorationUnderline => {
|
||||||
|
let underline_y = baseline_origin.y - underline_offset;
|
||||||
|
let underline_bounds = Rect(Point2D(baseline_origin.x, underline_y),
|
||||||
|
Size2D(width, underline_size));
|
||||||
|
render_context.draw_solid_color(&underline_bounds, text.color);
|
||||||
|
},
|
||||||
|
CSSTextDecorationOverline => {
|
||||||
|
let overline_bounds = Rect(Point2D(baseline_origin.x, origin.y),
|
||||||
|
Size2D(width, underline_size));
|
||||||
|
render_context.draw_solid_color(&overline_bounds, text.color);
|
||||||
|
},
|
||||||
|
CSSTextDecorationLineThrough => {
|
||||||
|
let strikeout_y = baseline_origin.y - strikeout_offset;
|
||||||
|
let strikeout_bounds = Rect(Point2D(baseline_origin.x, strikeout_y),
|
||||||
|
Size2D(width, strikeout_size));
|
||||||
|
render_context.draw_solid_color(&strikeout_bounds, text.color);
|
||||||
|
},
|
||||||
|
_ => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,8 @@ use servo_util::time;
|
||||||
use servo_util::time::profile;
|
use servo_util::time::profile;
|
||||||
use servo_util::time::ProfilerChan;
|
use servo_util::time::ProfilerChan;
|
||||||
|
|
||||||
|
use newcss::values::CSSTextDecoration;
|
||||||
|
|
||||||
// FontHandle encapsulates access to the platform's font API,
|
// FontHandle encapsulates access to the platform's font API,
|
||||||
// e.g. quartz, FreeType. It provides access to metrics and tables
|
// e.g. quartz, FreeType. It provides access to metrics and tables
|
||||||
// needed by the text shaper as well as access to the underlying font
|
// needed by the text shaper as well as access to the underlying font
|
||||||
|
@ -80,6 +82,8 @@ pub trait FontTableMethods {
|
||||||
pub struct FontMetrics {
|
pub struct FontMetrics {
|
||||||
underline_size: Au,
|
underline_size: Au,
|
||||||
underline_offset: Au,
|
underline_offset: Au,
|
||||||
|
strikeout_size: Au,
|
||||||
|
strikeout_offset: Au,
|
||||||
leading: Au,
|
leading: Au,
|
||||||
x_height: Au,
|
x_height: Au,
|
||||||
em_size: Au,
|
em_size: Au,
|
||||||
|
@ -191,11 +195,11 @@ impl FontGroup {
|
||||||
self.fonts = ~[];
|
self.fonts = ~[];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_textrun(&self, text: ~str, underline: bool) -> TextRun {
|
pub fn create_textrun(&self, text: ~str, decoration: CSSTextDecoration) -> TextRun {
|
||||||
assert!(self.fonts.len() > 0);
|
assert!(self.fonts.len() > 0);
|
||||||
|
|
||||||
// TODO(Issue #177): Actually fall back through the FontGroup when a font is unsuitable.
|
// TODO(Issue #177): Actually fall back through the FontGroup when a font is unsuitable.
|
||||||
return TextRun::new(self.fonts[0], text, underline);
|
return TextRun::new(self.fonts[0], text, decoration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -219,6 +219,7 @@ impl FontHandleMethods for FontHandle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[fixed_stack_segment]
|
||||||
fn get_metrics(&self) -> FontMetrics {
|
fn get_metrics(&self) -> FontMetrics {
|
||||||
/* TODO(Issue #76): complete me */
|
/* TODO(Issue #76): complete me */
|
||||||
let face = self.get_face_rec();
|
let face = self.get_face_rec();
|
||||||
|
@ -230,11 +231,26 @@ impl FontHandleMethods for FontHandle {
|
||||||
let descent = self.font_units_to_au(face.descender as float);
|
let descent = self.font_units_to_au(face.descender as float);
|
||||||
let max_advance = self.font_units_to_au(face.max_advance_width as float);
|
let max_advance = self.font_units_to_au(face.max_advance_width as float);
|
||||||
|
|
||||||
|
let mut strikeout_size = geometry::from_pt(0.0);
|
||||||
|
let mut strikeout_offset = geometry::from_pt(0.0);
|
||||||
|
let mut x_height = geometry::from_pt(0.0);
|
||||||
|
unsafe {
|
||||||
|
let os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2) as *TT_OS2;
|
||||||
|
let valid = os2.is_not_null() && (*os2).version != 0xffff;
|
||||||
|
if valid {
|
||||||
|
strikeout_size = self.font_units_to_au((*os2).yStrikeoutSize as float);
|
||||||
|
strikeout_offset = self.font_units_to_au((*os2).yStrikeoutPosition as float);
|
||||||
|
x_height = self.font_units_to_au((*os2).sxHeight as float);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return FontMetrics {
|
return FontMetrics {
|
||||||
underline_size: underline_size,
|
underline_size: underline_size,
|
||||||
underline_offset: underline_offset,
|
underline_offset: underline_offset,
|
||||||
|
strikeout_size: strikeout_size,
|
||||||
|
strikeout_offset: strikeout_offset,
|
||||||
leading: geometry::from_pt(0.0), //FIXME
|
leading: geometry::from_pt(0.0), //FIXME
|
||||||
x_height: geometry::from_pt(0.0), //FIXME
|
x_height: x_height,
|
||||||
em_size: em_size,
|
em_size: em_size,
|
||||||
ascent: ascent,
|
ascent: ascent,
|
||||||
descent: -descent, // linux font's seem to use the opposite sign from mac
|
descent: -descent, // linux font's seem to use the opposite sign from mac
|
||||||
|
|
|
@ -219,6 +219,7 @@ impl FontHandleMethods for FontHandle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[fixed_stack_segment]
|
||||||
fn get_metrics(&self) -> FontMetrics {
|
fn get_metrics(&self) -> FontMetrics {
|
||||||
/* TODO(Issue #76): complete me */
|
/* TODO(Issue #76): complete me */
|
||||||
let face = self.get_face_rec();
|
let face = self.get_face_rec();
|
||||||
|
@ -230,11 +231,26 @@ impl FontHandleMethods for FontHandle {
|
||||||
let descent = self.font_units_to_au(face.descender as float);
|
let descent = self.font_units_to_au(face.descender as float);
|
||||||
let max_advance = self.font_units_to_au(face.max_advance_width as float);
|
let max_advance = self.font_units_to_au(face.max_advance_width as float);
|
||||||
|
|
||||||
|
let mut strikeout_size = geometry::from_pt(0.0);
|
||||||
|
let mut strikeout_offset = geometry::from_pt(0.0);
|
||||||
|
let mut x_height = geometry::from_pt(0.0);
|
||||||
|
unsafe {
|
||||||
|
let os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2) as *TT_OS2;
|
||||||
|
let valid = os2.is_not_null() && (*os2).version != 0xffff;
|
||||||
|
if valid {
|
||||||
|
strikeout_size = self.font_units_to_au((*os2).yStrikeoutSize as float);
|
||||||
|
strikeout_offset = self.font_units_to_au((*os2).yStrikeoutPosition as float);
|
||||||
|
x_height = self.font_units_to_au((*os2).sxHeight as float);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return FontMetrics {
|
return FontMetrics {
|
||||||
underline_size: underline_size,
|
underline_size: underline_size,
|
||||||
underline_offset: underline_offset,
|
underline_offset: underline_offset,
|
||||||
|
strikeout_size: strikeout_size,
|
||||||
|
strikeout_offset: strikeout_offset,
|
||||||
leading: geometry::from_pt(0.0), //FIXME
|
leading: geometry::from_pt(0.0), //FIXME
|
||||||
x_height: geometry::from_pt(0.0), //FIXME
|
x_height: x_height,
|
||||||
em_size: em_size,
|
em_size: em_size,
|
||||||
ascent: ascent,
|
ascent: ascent,
|
||||||
descent: -descent, // linux font's seem to use the opposite sign from mac
|
descent: -descent, // linux font's seem to use the opposite sign from mac
|
||||||
|
|
|
@ -13,6 +13,7 @@ use font::{FontTableTag, FontWeight100, FontWeight200, FontWeight300, FontWeight
|
||||||
use font::{FontWeight500, FontWeight600, FontWeight700, FontWeight800, FontWeight900};
|
use font::{FontWeight500, FontWeight600, FontWeight700, FontWeight800, FontWeight900};
|
||||||
use font::{FractionalPixel, SpecifiedFontStyle};
|
use font::{FractionalPixel, SpecifiedFontStyle};
|
||||||
use geometry::{Au, px_to_pt};
|
use geometry::{Au, px_to_pt};
|
||||||
|
use geometry;
|
||||||
use platform::macos::font_context::FontContextHandle;
|
use platform::macos::font_context::FontContextHandle;
|
||||||
use text::glyph::GlyphIndex;
|
use text::glyph::GlyphIndex;
|
||||||
|
|
||||||
|
@ -171,6 +172,8 @@ impl FontHandleMethods for FontHandle {
|
||||||
// see also: https://bugs.webkit.org/show_bug.cgi?id=16768
|
// see also: https://bugs.webkit.org/show_bug.cgi?id=16768
|
||||||
// see also: https://bugreports.qt-project.org/browse/QTBUG-13364
|
// see also: https://bugreports.qt-project.org/browse/QTBUG-13364
|
||||||
underline_offset: Au::from_pt(self.ctfont.underline_position() as float),
|
underline_offset: Au::from_pt(self.ctfont.underline_position() as float),
|
||||||
|
strikeout_size: geometry::from_pt(0.0), // FIXME(Issue #942)
|
||||||
|
strikeout_offset: geometry::from_pt(0.0), // FIXME(Issue #942)
|
||||||
leading: Au::from_pt(self.ctfont.leading() as float),
|
leading: Au::from_pt(self.ctfont.leading() as float),
|
||||||
x_height: Au::from_pt(self.ctfont.x_height() as float),
|
x_height: Au::from_pt(self.ctfont.x_height() as float),
|
||||||
em_size: em_size,
|
em_size: em_size,
|
||||||
|
|
|
@ -10,12 +10,13 @@ use text::glyph::GlyphStore;
|
||||||
use font::{Font, FontDescriptor, RunMetrics};
|
use font::{Font, FontDescriptor, RunMetrics};
|
||||||
use servo_util::range::Range;
|
use servo_util::range::Range;
|
||||||
use extra::arc::Arc;
|
use extra::arc::Arc;
|
||||||
|
use newcss::values::CSSTextDecoration;
|
||||||
|
|
||||||
/// A text run.
|
/// A text run.
|
||||||
pub struct TextRun {
|
pub struct TextRun {
|
||||||
text: ~str,
|
text: ~str,
|
||||||
font: @mut Font,
|
font: @mut Font,
|
||||||
underline: bool,
|
decoration: CSSTextDecoration,
|
||||||
glyphs: ~[Arc<GlyphStore>],
|
glyphs: ~[Arc<GlyphStore>],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +24,7 @@ pub struct TextRun {
|
||||||
pub struct SendableTextRun {
|
pub struct SendableTextRun {
|
||||||
text: ~str,
|
text: ~str,
|
||||||
font: FontDescriptor,
|
font: FontDescriptor,
|
||||||
underline: bool,
|
decoration: CSSTextDecoration,
|
||||||
priv glyphs: ~[Arc<GlyphStore>],
|
priv glyphs: ~[Arc<GlyphStore>],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +38,7 @@ impl SendableTextRun {
|
||||||
TextRun {
|
TextRun {
|
||||||
text: self.text.clone(),
|
text: self.text.clone(),
|
||||||
font: font,
|
font: font,
|
||||||
underline: self.underline,
|
decoration: self.decoration,
|
||||||
glyphs: self.glyphs.clone(),
|
glyphs: self.glyphs.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,13 +117,13 @@ impl<'self> Iterator<Range> for LineIterator<'self> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'self> TextRun {
|
impl<'self> TextRun {
|
||||||
pub fn new(font: @mut Font, text: ~str, underline: bool) -> TextRun {
|
pub fn new(font: @mut Font, text: ~str, decoration: CSSTextDecoration) -> TextRun {
|
||||||
let glyphs = TextRun::break_and_shape(font, text);
|
let glyphs = TextRun::break_and_shape(font, text);
|
||||||
|
|
||||||
let run = TextRun {
|
let run = TextRun {
|
||||||
text: text,
|
text: text,
|
||||||
font: font,
|
font: font,
|
||||||
underline: underline,
|
decoration: decoration,
|
||||||
glyphs: glyphs,
|
glyphs: glyphs,
|
||||||
};
|
};
|
||||||
return run;
|
return run;
|
||||||
|
@ -191,7 +192,7 @@ impl<'self> TextRun {
|
||||||
SendableTextRun {
|
SendableTextRun {
|
||||||
text: self.text.clone(),
|
text: self.text.clone(),
|
||||||
font: self.font.get_descriptor(),
|
font: self.font.get_descriptor(),
|
||||||
underline: self.underline,
|
decoration: self.decoration,
|
||||||
glyphs: self.glyphs.clone(),
|
glyphs: self.glyphs.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,6 @@ use layout::box::{RenderBox, RenderBoxBase, TextRenderBox};
|
||||||
use layout::box::{TextRenderBoxClass, UnscannedTextRenderBoxClass};
|
use layout::box::{TextRenderBoxClass, UnscannedTextRenderBoxClass};
|
||||||
use layout::context::LayoutContext;
|
use layout::context::LayoutContext;
|
||||||
use layout::flow::FlowContext;
|
use layout::flow::FlowContext;
|
||||||
use newcss::values::{CSSTextDecoration, CSSTextDecorationUnderline};
|
|
||||||
use servo_util::range::Range;
|
use servo_util::range::Range;
|
||||||
|
|
||||||
|
|
||||||
|
@ -128,13 +127,6 @@ impl TextRunScanner {
|
||||||
let inline = flow.inline();
|
let inline = flow.inline();
|
||||||
let in_boxes = &inline.boxes;
|
let in_boxes = &inline.boxes;
|
||||||
|
|
||||||
fn has_underline(decoration: CSSTextDecoration) -> bool{
|
|
||||||
match decoration {
|
|
||||||
CSSTextDecorationUnderline => true,
|
|
||||||
_ => false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assert!(self.clump.length() > 0);
|
assert!(self.clump.length() > 0);
|
||||||
|
|
||||||
debug!("TextRunScanner: flushing boxes in range=%?", self.clump);
|
debug!("TextRunScanner: flushing boxes in range=%?", self.clump);
|
||||||
|
@ -158,7 +150,7 @@ impl TextRunScanner {
|
||||||
let old_box = in_boxes[self.clump.begin()];
|
let old_box = in_boxes[self.clump.begin()];
|
||||||
let text = old_box.raw_text();
|
let text = old_box.raw_text();
|
||||||
let font_style = old_box.font_style();
|
let font_style = old_box.font_style();
|
||||||
let underline = has_underline(old_box.text_decoration());
|
let decoration = old_box.text_decoration();
|
||||||
|
|
||||||
// TODO(#115): Use the actual CSS `white-space` property of the relevant style.
|
// TODO(#115): Use the actual CSS `white-space` property of the relevant style.
|
||||||
let compression = CompressWhitespaceNewline;
|
let compression = CompressWhitespaceNewline;
|
||||||
|
@ -171,7 +163,7 @@ impl TextRunScanner {
|
||||||
// font group fonts. This is probably achieved by creating the font group above
|
// 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.
|
// 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 fontgroup = ctx.font_ctx.get_resolved_font_for_style(&font_style);
|
||||||
let run = @fontgroup.create_textrun(transformed_text, underline);
|
let run = @fontgroup.create_textrun(transformed_text, decoration);
|
||||||
|
|
||||||
debug!("TextRunScanner: pushing single text box in range: %? (%?)", self.clump, text);
|
debug!("TextRunScanner: pushing single text box in range: %? (%?)", self.clump, text);
|
||||||
let new_box = do old_box.with_base |old_box_base| {
|
let new_box = do old_box.with_base |old_box_base| {
|
||||||
|
@ -220,13 +212,13 @@ impl TextRunScanner {
|
||||||
// and then letting `FontGroup` decide which `Font` to stick into the text run.
|
// and then letting `FontGroup` decide which `Font` to stick into the text run.
|
||||||
let font_style = in_boxes[self.clump.begin()].font_style();
|
let font_style = in_boxes[self.clump.begin()].font_style();
|
||||||
let fontgroup = ctx.font_ctx.get_resolved_font_for_style(&font_style);
|
let fontgroup = ctx.font_ctx.get_resolved_font_for_style(&font_style);
|
||||||
let underline = has_underline(in_boxes[self.clump.begin()].text_decoration());
|
let decoration = in_boxes[self.clump.begin()].text_decoration();
|
||||||
|
|
||||||
// TextRuns contain a cycle which is usually resolved by the teardown
|
// TextRuns contain a cycle which is usually resolved by the teardown
|
||||||
// sequence. If no clump takes ownership, however, it will leak.
|
// sequence. If no clump takes ownership, however, it will leak.
|
||||||
let clump = self.clump;
|
let clump = self.clump;
|
||||||
let run = if clump.length() != 0 && run_str.len() > 0 {
|
let run = if clump.length() != 0 && run_str.len() > 0 {
|
||||||
Some(@TextRun::new(fontgroup.fonts[0], run_str, underline))
|
Some(@TextRun::new(fontgroup.fonts[0], run_str, decoration))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
17
src/test/html/text_deco_simple.html
Normal file
17
src/test/html/text_deco_simple.html
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title></title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p style="text-decoration:none;"> none </p>
|
||||||
|
<p style="text-decoration:underline;"> text underline </p>
|
||||||
|
<p style="text-decoration:overline;"> text underline </p>
|
||||||
|
<p style="text-decoration:line-through;"> text underline </p>
|
||||||
|
<p>
|
||||||
|
<p style="font-size:40px; text-decoration:underline; font-family:Verdana;"> text underline pqrstg </p>
|
||||||
|
<p style="text-decoration:overline;"> text overline xxxxxxxxXXXXXXX</p>
|
||||||
|
<p style="font-size:50px; text-decoration:line-through;"> text line-through xxxxxxxxXXXXX</p>
|
||||||
|
<p style="text-decoration:blink;"> text blink </p>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Add table
Add a link
Reference in a new issue