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

Start implementing text in layout 2020 #24822

Merged
merged 6 commits into from Nov 25, 2019
Merged
Changes from 1 commit
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

Start implementing text in layout 2020

  • Loading branch information
nox committed Nov 25, 2019
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",
This conversation was marked as resolved by SimonSapin

This comment has been minimized.

Copy link
@SimonSapin

SimonSapin Nov 22, 2019

Member

Can/should we also add support for the font shorthand now?

This comment has been minimized.

Copy link
@nox

nox Nov 25, 2019

Author Member

Good idea.

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",
This conversation was marked as resolved by nox

This comment has been minimized.

Copy link
@SimonSapin

SimonSapin Nov 25, 2019

Member

Do we have layout code for this one? (Or in fact for all properties enabled here?)

This comment has been minimized.

Copy link
@nox

nox Nov 25, 2019

Author Member

We pass that in the font style struct, so kinda, but not really.

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
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.