Skip to content
Permalink
Browse files

Start implementing text in layout 2020

  • Loading branch information
nox committed Nov 21, 2019
1 parent 7e143c7 commit b3d30d284e97fbf2060d137034174bd872f3db97

Some generated files are not rendered by default. Learn more.

@@ -18,15 +18,19 @@ atomic_refcell = "0.1"
cssparser = "0.27"
euclid = "0.20"
gfx = {path = "../gfx"}
gfx_traits = {path = "../gfx_traits"}
ipc-channel = "0.12"
libc = "0.2"
msg = {path = "../msg"}
range = {path = "../range"}
rayon = "1"
rayon_croissant = "0.1.1"
script_layout_interface = {path = "../script_layout_interface"}
script_traits = {path = "../script_traits"}
serde = "1.0"
servo_arc = { path = "../servo_arc" }
servo_geometry = {path = "../geometry"}
style = {path = "../style", features = ["servo", "servo-layout-2020"]}
style_traits = {path = "../style_traits"}
unicode-script = {version = "0.3", features = ["harfbuzz"]}
webrender_api = {git = "https://github.com/servo/webrender", features = ["ipc"]}
@@ -6,7 +6,10 @@ use crate::fragments::{BoxFragment, Fragment};
use crate::geom::physical::{Rect, Vec2};
use crate::style_ext::ComputedValuesExt;
use app_units::Au;
use euclid::{self, SideOffsets2D};
use euclid::{Point2D, SideOffsets2D};
use gfx::text::glyph::GlyphStore;
use servo_geometry::MaxRect;
use std::sync::Arc;
use style::values::computed::{BorderStyle, Length};
use webrender_api::{self as wr, units, CommonItemProperties, PrimitiveFlags};

@@ -50,9 +53,30 @@ impl Fragment {
child.build_display_list(builder, is_contentful, &rect)
}
},
Fragment::Text(_) => {
Fragment::Text(t) => {
is_contentful.0 = true;
// FIXME
let rect = t
.content_rect
.to_physical(t.parent_style.writing_mode(), containing_block)
.translate(&containing_block.top_left);
let mut baseline_origin = rect.top_left.clone();
baseline_origin.y += t.ascent;
let common = CommonItemProperties {
clip_rect: rect.clone().into(),
clip_id: wr::ClipId::root(builder.pipeline_id),
spatial_id: wr::SpatialId::root_scroll_node(builder.pipeline_id),
hit_info: None,
// TODO(gw): Make use of the WR backface visibility functionality.
flags: PrimitiveFlags::default(),
};
let glyphs = glyphs(&t.glyphs, baseline_origin);
if glyphs.is_empty() {
return;
}
let color = t.parent_style.clone_color();
builder
.wr
.push_text(&common, rect.into(), &glyphs, t.font_key, rgba(color), None);
},
}
}
@@ -154,3 +178,28 @@ fn rgba(rgba: cssparser::RGBA) -> wr::ColorF {
rgba.alpha_f32(),
)
}

fn glyphs(glyph_runs: &[Arc<GlyphStore>], mut origin: Vec2<Length>) -> Vec<wr::GlyphInstance> {
use gfx_traits::ByteIndex;
use range::Range;

let mut glyphs = vec![];
for run in glyph_runs {
for glyph in run.iter_glyphs_for_byte_range(&Range::new(ByteIndex(0), run.len())) {
if !run.is_whitespace() {
let glyph_offset = glyph.offset().unwrap_or(Point2D::zero());
let point = units::LayoutPoint::new(
origin.x.px() + glyph_offset.x.to_f32_px(),
origin.y.px() + glyph_offset.y.to_f32_px(),
);
let glyph = wr::GlyphInstance {
index: glyph.id(),
point,
};
glyphs.push(glyph);
}
origin.x += Length::from(glyph.advance());
}
}
glyphs
}
@@ -5,7 +5,9 @@
use crate::context::LayoutContext;
use crate::flow::float::FloatBox;
use crate::flow::FlowChildren;
use crate::fragments::{AnonymousFragment, BoxFragment, CollapsedBlockMargins, Fragment};
use crate::fragments::{
AnonymousFragment, BoxFragment, CollapsedBlockMargins, Fragment, TextFragment,
};
use crate::geom::flow_relative::{Rect, Sides, Vec2};
use crate::positioned::{AbsolutelyPositionedBox, AbsolutelyPositionedFragment};
use crate::replaced::ReplacedContent;
@@ -286,7 +288,139 @@ impl<'box_tree> PartialInlineBoxFragment<'box_tree> {
}

impl TextRun {
fn layout(&self, _layout_context: &LayoutContext, _ifc: &mut InlineFormattingContextState) {
// TODO
fn layout(&self, layout_context: &LayoutContext, ifc: &mut InlineFormattingContextState) {
use gfx::font::{ShapingFlags, ShapingOptions};
use style::computed_values::text_rendering::T as TextRendering;
use style::computed_values::word_break::T as WordBreak;

let font_style = self.parent_style.clone_font();
let inherited_text_style = self.parent_style.get_inherited_text();
let letter_spacing = if inherited_text_style.letter_spacing.0.px() != 0. {
Some(app_units::Au::from(inherited_text_style.letter_spacing.0))
} else {
None
};

let mut flags = ShapingFlags::empty();
if letter_spacing.is_some() {
flags.insert(ShapingFlags::IGNORE_LIGATURES_SHAPING_FLAG);
}
if inherited_text_style.text_rendering == TextRendering::Optimizespeed {
flags.insert(ShapingFlags::IGNORE_LIGATURES_SHAPING_FLAG);
flags.insert(ShapingFlags::DISABLE_KERNING_SHAPING_FLAG)
}
if inherited_text_style.word_break == WordBreak::KeepAll {
flags.insert(ShapingFlags::KEEP_ALL_FLAG);
}

let shaping_options = gfx::font::ShapingOptions {
letter_spacing,
word_spacing: inherited_text_style.word_spacing.to_hash_key(),
script: unicode_script::Script::Common,
flags,
};

let (font_ascent, font_line_gap, font_key, runs) =
crate::context::with_thread_local_font_context(layout_context, |font_context| {
let font_group = font_context.font_group(font_style);
let font = font_group
.borrow_mut()
.first(font_context)
.expect("could not find font");
let mut font = font.borrow_mut();

let (runs, _break_at_start) = gfx::text::text_run::TextRun::break_and_shape(
&mut font,
&self.text,
&shaping_options,
&mut None,
);

(
font.metrics.ascent,
font.metrics.line_gap,
font.font_key,
runs,
)
});

let font_size = self.parent_style.get_font().font_size.size.0;
let mut runs = runs.iter();
loop {
let mut glyphs = vec![];
let mut advance_width = Length::zero();
let mut last_break_opportunity = None;
loop {
let next = runs.next();
if next
.as_ref()
.map_or(true, |run| run.glyph_store.is_whitespace())
{
if advance_width > ifc.containing_block.inline_size - ifc.inline_position {
if let Some((len, width, iter)) = last_break_opportunity.take() {
glyphs.truncate(len);
advance_width = width;
runs = iter;
}
break;
}
}
if let Some(run) = next {
if run.glyph_store.is_whitespace() {
last_break_opportunity = Some((glyphs.len(), advance_width, runs.clone()));
}
glyphs.push(run.glyph_store.clone());
advance_width += Length::from(run.glyph_store.total_advance());
} else {
break;
}
}
// https://www.w3.org/TR/CSS2/visudet.html#propdef-line-height
// 'normal':
// “set the used value to a "reasonable" value based on the font of the element.”
let line_height = font_size * 1.2;
let content_rect = Rect {
start_corner: Vec2 {
block: Length::zero(),
inline: ifc.inline_position - ifc.current_nesting_level.inline_start,
},
size: Vec2 {
block: line_height,
inline: advance_width,
},
};
ifc.inline_position += advance_width;
ifc.current_nesting_level
.max_block_size_of_fragments_so_far
.max_assign(line_height);
ifc.current_nesting_level
.fragments_so_far
.push(Fragment::Text(TextFragment {
parent_style: self.parent_style.clone(),
content_rect,
ascent: font_ascent.into(),
font_key,
glyphs,
}));
if runs.is_empty() {
break;
} else {
// New line
ifc.current_nesting_level.inline_start = Length::zero();
let mut nesting_level = &mut ifc.current_nesting_level;
for partial in ifc.partial_inline_boxes_stack.iter_mut().rev() {
partial.finish_layout(nesting_level, &mut ifc.inline_position, true);
partial.start_corner.inline = Length::zero();
partial.padding.inline_start = Length::zero();
partial.border.inline_start = Length::zero();
partial.margin.inline_start = Length::zero();
partial.parent_nesting_level.inline_start = Length::zero();
nesting_level = &mut partial.parent_nesting_level;
}
ifc.line_boxes
.finish_line(nesting_level, ifc.containing_block);
ifc.inline_position = Length::zero();
}
}
}
}
@@ -4,11 +4,13 @@

use crate::geom::flow_relative::{Rect, Sides};
use crate::style_ext::{Direction, WritingMode};
// use crate::text::ShapedSegment;
use servo_arc::Arc;
use gfx::text::glyph::GlyphStore;
use servo_arc::Arc as ServoArc;
use std::sync::Arc;
use style::properties::ComputedValues;
use style::values::computed::Length;
use style::Zero;
use webrender_api::FontInstanceKey;

pub(crate) enum Fragment {
Box(BoxFragment),
@@ -17,7 +19,7 @@ pub(crate) enum Fragment {
}

pub(crate) struct BoxFragment {
pub style: Arc<ComputedValues>,
pub style: ServoArc<ComputedValues>,
pub children: Vec<Fragment>,

/// From the containing block’s start corner…?
@@ -52,9 +54,11 @@ pub(crate) struct AnonymousFragment {
}

pub(crate) struct TextFragment {
pub parent_style: Arc<ComputedValues>,
pub parent_style: ServoArc<ComputedValues>,
pub content_rect: Rect<Length>,
// pub text: ShapedSegment,
pub ascent: Length,
pub font_key: FontInstanceKey,
pub glyphs: Vec<Arc<GlyphStore>>,
}

impl AnonymousFragment {
@@ -7,6 +7,7 @@
#![allow(unused_imports)]
#![allow(unused_variables)]
#![deny(unsafe_code)]
#![feature(exact_size_is_empty)]

#[macro_use]
extern crate serde;
@@ -11,7 +11,6 @@ ${helpers.predefined_type(
"font-family",
"FontFamily",
engines="gecko servo-2013 servo-2020",
servo_2020_pref="layout.2020.unimplemented",
initial_value="computed::FontFamily::serif()",
animation_value_type="discrete",
spec="https://drafts.csswg.org/css-fonts/#propdef-font-family",
@@ -22,7 +21,6 @@ ${helpers.predefined_type(
"font-style",
"FontStyle",
engines="gecko servo-2013 servo-2020",
servo_2020_pref="layout.2020.unimplemented",
initial_value="computed::FontStyle::normal()",
initial_specified_value="specified::FontStyle::normal()",
animation_value_type="FontStyle",
@@ -40,7 +38,6 @@ ${helpers.single_keyword_system(
"font-variant-caps",
"normal small-caps",
engines="gecko servo-2013 servo-2020",
servo_2020_pref="layout.2020.unimplemented",
extra_gecko_values="all-small-caps petite-caps all-petite-caps unicase titling-caps",
gecko_constant_prefix="NS_FONT_VARIANT_CAPS",
gecko_ffi_name="mFont.variantCaps",
@@ -54,7 +51,6 @@ ${helpers.predefined_type(
"font-weight",
"FontWeight",
engines="gecko servo-2013 servo-2020",
servo_2020_pref="layout.2020.unimplemented",
initial_value="computed::FontWeight::normal()",
initial_specified_value="specified::FontWeight::normal()",
animation_value_type="Number",
@@ -97,7 +93,6 @@ ${helpers.predefined_type(
"font-stretch",
"FontStretch",
engines="gecko servo-2013 servo-2020",
servo_2020_pref="layout.2020.unimplemented",
initial_value="computed::FontStretch::hundred()",
initial_specified_value="specified::FontStretch::normal()",
animation_value_type="Percentage",
@@ -21,7 +21,6 @@ ${helpers.predefined_type(
"LineHeight",
"computed::LineHeight::normal()",
engines="gecko servo-2013 servo-2020",
servo_2020_pref="layout.2020.unimplemented",
animation_value_type="LineHeight",
spec="https://drafts.csswg.org/css2/visudet.html#propdef-line-height",
servo_restyle_damage="reflow"
@@ -167,7 +166,7 @@ ${helpers.predefined_type(
"letter-spacing",
"LetterSpacing",
"computed::LetterSpacing::normal()",
engines="gecko servo-2013",
engines="gecko servo-2013 servo-2020",
animation_value_type="ComputedValue",
spec="https://drafts.csswg.org/css-text/#propdef-letter-spacing",
servo_restyle_damage="rebuild_and_reflow",
@@ -177,7 +176,7 @@ ${helpers.predefined_type(
"word-spacing",
"WordSpacing",
"computed::WordSpacing::zero()",
engines="gecko servo-2013",
engines="gecko servo-2013 servo-2020",
animation_value_type="ComputedValue",
spec="https://drafts.csswg.org/css-text/#propdef-word-spacing",
servo_restyle_damage="rebuild_and_reflow",
@@ -360,7 +359,7 @@ ${helpers.single_keyword(
${helpers.single_keyword(
"text-rendering",
"auto optimizespeed optimizelegibility geometricprecision",
engines="gecko servo-2013",
engines="gecko servo-2013 servo-2020",
gecko_enum_prefix="StyleTextRendering",
animation_value_type="discrete",
spec="https://www.w3.org/TR/SVG11/painting.html#TextRenderingProperty",
@@ -8,7 +8,6 @@
<%helpers:shorthand
name="font"
engines="gecko servo-2013 servo-2020"
servo_2020_pref="layout.2020.unimplemented"
sub_properties="
font-style
font-variant-caps

0 comments on commit b3d30d2

Please sign in to comment.
You can’t perform that action at this time.