mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
layout: Share styles to inline box children via SharedInlineStyles
(#36896)
`TextRun`s use their parent style to render. Previously, these styles were cloned and stored directly in the box tree `TextRun` and resulting `TextFragment`s. This presents a problem for incremental layout. Wrapping the style in another layer of shared ownership and mutability will allow updating all `TextFragment`s during repaint-only incremental layout by simply updating the box tree styles of the original text parents. This adds a new set of borrows when accessing text styles, but also makes it so that during box tree block construction `InlineFormattingContext`s are created lazily and now `InlineFormattingContextBuilder::finish` consumes the builder, making the API make a bit more sense. This should also improve performance of box tree block construction slightly. Testing: This should not change observable behavior and thus is covered by existing WPT tests. Signed-off-by: Martin Robinson <mrobinson@igalia.com> Co-authored-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
parent
db83601b62
commit
a0dd2c1beb
24 changed files with 369 additions and 187 deletions
|
@ -13,7 +13,10 @@ use style::values::specified::text::TextTransformCase;
|
|||
use unicode_bidi::Level;
|
||||
|
||||
use super::text_run::TextRun;
|
||||
use super::{InlineBox, InlineBoxIdentifier, InlineBoxes, InlineFormattingContext, InlineItem};
|
||||
use super::{
|
||||
InlineBox, InlineBoxIdentifier, InlineBoxes, InlineFormattingContext, InlineItem,
|
||||
SharedInlineStyles,
|
||||
};
|
||||
use crate::PropagatedBoxTreeData;
|
||||
use crate::cell::ArcRefCell;
|
||||
use crate::context::LayoutContext;
|
||||
|
@ -25,6 +28,12 @@ use crate::style_ext::ComputedValuesExt;
|
|||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct InlineFormattingContextBuilder {
|
||||
/// A stack of [`SharedInlineStyles`] including one for the root, one for each inline box on the
|
||||
/// inline box stack, and importantly, one for every `display: contents` element that we are
|
||||
/// currently processing. Normally `display: contents` elements don't affect the structure of
|
||||
/// the [`InlineFormattingContext`], but the styles they provide do style their children.
|
||||
shared_inline_styles_stack: Vec<SharedInlineStyles>,
|
||||
|
||||
/// The collection of text strings that make up this [`InlineFormattingContext`] under
|
||||
/// construction.
|
||||
pub text_segments: Vec<String>,
|
||||
|
@ -63,7 +72,7 @@ pub(crate) struct InlineFormattingContextBuilder {
|
|||
/// The traversal is at all times as deep in the tree as this stack is,
|
||||
/// which is why the code doesn't need to keep track of the actual
|
||||
/// container root (see `handle_inline_level_element`).
|
||||
///
|
||||
//_
|
||||
/// When an inline box ends, it's removed from this stack.
|
||||
inline_box_stack: Vec<InlineBoxIdentifier>,
|
||||
|
||||
|
@ -83,10 +92,17 @@ pub(crate) struct InlineFormattingContextBuilder {
|
|||
}
|
||||
|
||||
impl InlineFormattingContextBuilder {
|
||||
pub(crate) fn new() -> Self {
|
||||
// For the purposes of `text-transform: capitalize` the start of the IFC is a word boundary.
|
||||
pub(crate) fn new(info: &NodeAndStyleInfo) -> Self {
|
||||
Self::new_for_shared_styles(vec![info.into()])
|
||||
}
|
||||
|
||||
pub(crate) fn new_for_shared_styles(
|
||||
shared_inline_styles_stack: Vec<SharedInlineStyles>,
|
||||
) -> Self {
|
||||
Self {
|
||||
// For the purposes of `text-transform: capitalize` the start of the IFC is a word boundary.
|
||||
on_word_boundary: true,
|
||||
shared_inline_styles_stack,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
@ -100,6 +116,13 @@ impl InlineFormattingContextBuilder {
|
|||
self.current_text_offset += string_to_push.len();
|
||||
}
|
||||
|
||||
fn shared_inline_styles(&self) -> SharedInlineStyles {
|
||||
self.shared_inline_styles_stack
|
||||
.last()
|
||||
.expect("Should always have at least one SharedInlineStyles")
|
||||
.clone()
|
||||
}
|
||||
|
||||
/// Return true if this [`InlineFormattingContextBuilder`] is empty for the purposes of ignoring
|
||||
/// during box tree construction. An IFC is empty if it only contains TextRuns with
|
||||
/// completely collapsible whitespace. When that happens it can be ignored completely.
|
||||
|
@ -179,6 +202,14 @@ impl InlineFormattingContextBuilder {
|
|||
) {
|
||||
self.push_control_character_string(inline_box.base.style.bidi_control_chars().0);
|
||||
|
||||
// Don't push a `SharedInlineStyles` if we are pushing this box when splitting
|
||||
// an IFC for a block-in-inline split. Shared styles are pushed as part of setting
|
||||
// up the second split of the IFC.
|
||||
if inline_box.is_first_split {
|
||||
self.shared_inline_styles_stack
|
||||
.push(inline_box.shared_inline_styles.clone());
|
||||
}
|
||||
|
||||
let (identifier, inline_box) = self.inline_boxes.start_inline_box(inline_box);
|
||||
let inline_level_box = ArcRefCell::new(InlineItem::StartInlineBox(inline_box));
|
||||
self.inline_items.push(inline_level_box.clone());
|
||||
|
@ -194,6 +225,8 @@ impl InlineFormattingContextBuilder {
|
|||
/// a single box tree items may be produced for a single inline box when that inline
|
||||
/// box is split around a block-level element.
|
||||
pub(crate) fn end_inline_box(&mut self) -> Vec<ArcRefCell<InlineItem>> {
|
||||
self.shared_inline_styles_stack.pop();
|
||||
|
||||
let (identifier, block_in_inline_splits) = self.end_inline_box_internal();
|
||||
let inline_level_box = self.inline_boxes.get(&identifier);
|
||||
{
|
||||
|
@ -272,8 +305,6 @@ impl InlineFormattingContextBuilder {
|
|||
}
|
||||
|
||||
let selection_range = info.get_selection_range();
|
||||
let selected_style = info.get_selected_style();
|
||||
|
||||
if let Some(last_character) = new_text.chars().next_back() {
|
||||
self.on_word_boundary = last_character.is_whitespace();
|
||||
self.last_inline_box_ended_with_collapsible_white_space =
|
||||
|
@ -295,14 +326,21 @@ impl InlineFormattingContextBuilder {
|
|||
.push(ArcRefCell::new(InlineItem::TextRun(ArcRefCell::new(
|
||||
TextRun::new(
|
||||
info.into(),
|
||||
info.style.clone(),
|
||||
self.shared_inline_styles(),
|
||||
new_range,
|
||||
selection_range,
|
||||
selected_style,
|
||||
),
|
||||
))));
|
||||
}
|
||||
|
||||
pub(crate) fn enter_display_contents(&mut self, shared_inline_styles: SharedInlineStyles) {
|
||||
self.shared_inline_styles_stack.push(shared_inline_styles);
|
||||
}
|
||||
|
||||
pub(crate) fn leave_display_contents(&mut self) {
|
||||
self.shared_inline_styles_stack.pop();
|
||||
}
|
||||
|
||||
pub(crate) fn split_around_block_and_finish(
|
||||
&mut self,
|
||||
layout_context: &LayoutContext,
|
||||
|
@ -318,7 +356,8 @@ impl InlineFormattingContextBuilder {
|
|||
// context. It has the same inline box structure as this builder, except the boxes are
|
||||
// marked as not being the first fragment. No inline content is carried over to this new
|
||||
// builder.
|
||||
let mut new_builder = InlineFormattingContextBuilder::new();
|
||||
let mut new_builder = Self::new_for_shared_styles(self.shared_inline_styles_stack.clone());
|
||||
|
||||
let block_in_inline_splits = std::mem::take(&mut self.block_in_inline_splits);
|
||||
for (identifier, historical_inline_boxes) in
|
||||
izip!(self.inline_box_stack.iter(), block_in_inline_splits)
|
||||
|
@ -356,7 +395,7 @@ impl InlineFormattingContextBuilder {
|
|||
|
||||
/// Finish the current inline formatting context, returning [`None`] if the context was empty.
|
||||
pub(crate) fn finish(
|
||||
&mut self,
|
||||
self,
|
||||
layout_context: &LayoutContext,
|
||||
propagated_data: PropagatedBoxTreeData,
|
||||
has_first_formatted_line: bool,
|
||||
|
@ -367,11 +406,9 @@ impl InlineFormattingContextBuilder {
|
|||
return None;
|
||||
}
|
||||
|
||||
let old_builder = std::mem::replace(self, InlineFormattingContextBuilder::new());
|
||||
assert!(old_builder.inline_box_stack.is_empty());
|
||||
|
||||
assert!(self.inline_box_stack.is_empty());
|
||||
Some(InlineFormattingContext::new_with_builder(
|
||||
old_builder,
|
||||
self,
|
||||
layout_context,
|
||||
propagated_data,
|
||||
has_first_formatted_line,
|
||||
|
|
|
@ -8,7 +8,10 @@ use app_units::Au;
|
|||
use fonts::FontMetrics;
|
||||
use malloc_size_of_derive::MallocSizeOf;
|
||||
|
||||
use super::{InlineContainerState, InlineContainerStateFlags, inline_container_needs_strut};
|
||||
use super::{
|
||||
InlineContainerState, InlineContainerStateFlags, SharedInlineStyles,
|
||||
inline_container_needs_strut,
|
||||
};
|
||||
use crate::ContainingBlock;
|
||||
use crate::cell::ArcRefCell;
|
||||
use crate::context::LayoutContext;
|
||||
|
@ -20,6 +23,9 @@ use crate::style_ext::{LayoutStyle, PaddingBorderMargin};
|
|||
#[derive(Debug, MallocSizeOf)]
|
||||
pub(crate) struct InlineBox {
|
||||
pub base: LayoutBoxBase,
|
||||
/// The [`SharedInlineStyles`] for this [`InlineBox`] that are used to share styles
|
||||
/// with all [`super::TextRun`] children.
|
||||
pub(super) shared_inline_styles: SharedInlineStyles,
|
||||
/// The identifier of this inline box in the containing [`super::InlineFormattingContext`].
|
||||
pub(super) identifier: InlineBoxIdentifier,
|
||||
/// Whether or not this is the first instance of an [`InlineBox`] before a possible
|
||||
|
@ -37,6 +43,7 @@ impl InlineBox {
|
|||
pub(crate) fn new(info: &NodeAndStyleInfo) -> Self {
|
||||
Self {
|
||||
base: LayoutBoxBase::new(info.into(), info.style.clone()),
|
||||
shared_inline_styles: info.into(),
|
||||
// This will be assigned later, when the box is actually added to the IFC.
|
||||
identifier: InlineBoxIdentifier::default(),
|
||||
is_first_split: true,
|
||||
|
@ -48,6 +55,7 @@ impl InlineBox {
|
|||
pub(crate) fn split_around_block(&self) -> Self {
|
||||
Self {
|
||||
base: LayoutBoxBase::new(self.base.base_fragment_info, self.base.style.clone()),
|
||||
shared_inline_styles: self.shared_inline_styles.clone(),
|
||||
is_first_split: false,
|
||||
is_last_split: false,
|
||||
..*self
|
||||
|
|
|
@ -7,7 +7,6 @@ use bitflags::bitflags;
|
|||
use fonts::{ByteIndex, FontMetrics, GlyphStore};
|
||||
use itertools::Either;
|
||||
use range::Range;
|
||||
use servo_arc::Arc;
|
||||
use style::Zero;
|
||||
use style::computed_values::position::T as Position;
|
||||
use style::computed_values::white_space_collapse::T as WhiteSpaceCollapse;
|
||||
|
@ -21,7 +20,7 @@ use unicode_bidi::{BidiInfo, Level};
|
|||
use webrender_api::FontInstanceKey;
|
||||
|
||||
use super::inline_box::{InlineBoxContainerState, InlineBoxIdentifier, InlineBoxTreePathToken};
|
||||
use super::{InlineFormattingContextLayout, LineBlockSizes};
|
||||
use super::{InlineFormattingContextLayout, LineBlockSizes, SharedInlineStyles};
|
||||
use crate::cell::ArcRefCell;
|
||||
use crate::fragment_tree::{BaseFragmentInfo, BoxFragment, Fragment, TextFragment};
|
||||
use crate::geom::{LogicalRect, LogicalVec2, PhysicalRect, ToLogical};
|
||||
|
@ -568,7 +567,7 @@ impl LineItemLayout<'_, '_> {
|
|||
self.current_state.fragments.push((
|
||||
Fragment::Text(ArcRefCell::new(TextFragment {
|
||||
base: text_item.base_fragment_info.into(),
|
||||
parent_style: text_item.parent_style,
|
||||
inline_styles: text_item.inline_styles.clone(),
|
||||
rect: PhysicalRect::zero(),
|
||||
font_metrics: text_item.font_metrics,
|
||||
font_key: text_item.font_key,
|
||||
|
@ -576,7 +575,6 @@ impl LineItemLayout<'_, '_> {
|
|||
text_decoration_line: text_item.text_decoration_line,
|
||||
justification_adjustment: self.justification_adjustment,
|
||||
selection_range: text_item.selection_range,
|
||||
selected_style: text_item.selected_style,
|
||||
})),
|
||||
content_rect,
|
||||
));
|
||||
|
@ -763,7 +761,7 @@ impl LineItem {
|
|||
|
||||
pub(super) struct TextRunLineItem {
|
||||
pub base_fragment_info: BaseFragmentInfo,
|
||||
pub parent_style: Arc<ComputedValues>,
|
||||
pub inline_styles: SharedInlineStyles,
|
||||
pub text: Vec<std::sync::Arc<GlyphStore>>,
|
||||
pub font_metrics: FontMetrics,
|
||||
pub font_key: FontInstanceKey,
|
||||
|
@ -771,13 +769,16 @@ pub(super) struct TextRunLineItem {
|
|||
/// The BiDi level of this [`TextRunLineItem`] to enable reordering.
|
||||
pub bidi_level: Level,
|
||||
pub selection_range: Option<Range<ByteIndex>>,
|
||||
pub selected_style: Arc<ComputedValues>,
|
||||
}
|
||||
|
||||
impl TextRunLineItem {
|
||||
fn trim_whitespace_at_end(&mut self, whitespace_trimmed: &mut Au) -> bool {
|
||||
if matches!(
|
||||
self.parent_style.get_inherited_text().white_space_collapse,
|
||||
self.inline_styles
|
||||
.style
|
||||
.borrow()
|
||||
.get_inherited_text()
|
||||
.white_space_collapse,
|
||||
WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
|
||||
) {
|
||||
return false;
|
||||
|
@ -803,7 +804,11 @@ impl TextRunLineItem {
|
|||
|
||||
fn trim_whitespace_at_start(&mut self, whitespace_trimmed: &mut Au) -> bool {
|
||||
if matches!(
|
||||
self.parent_style.get_inherited_text().white_space_collapse,
|
||||
self.inline_styles
|
||||
.style
|
||||
.borrow()
|
||||
.get_inherited_text()
|
||||
.white_space_collapse,
|
||||
WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
|
||||
) {
|
||||
return false;
|
||||
|
|
|
@ -118,6 +118,7 @@ use super::{
|
|||
};
|
||||
use crate::cell::ArcRefCell;
|
||||
use crate::context::LayoutContext;
|
||||
use crate::dom_traversal::NodeAndStyleInfo;
|
||||
use crate::flow::CollapsibleWithParentStartMargin;
|
||||
use crate::flow::float::{FloatBox, SequentialLayoutState};
|
||||
use crate::formatting_contexts::{
|
||||
|
@ -131,7 +132,7 @@ use crate::geom::{LogicalRect, LogicalVec2, ToLogical};
|
|||
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext};
|
||||
use crate::sizing::{ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult};
|
||||
use crate::style_ext::{ComputedValuesExt, PaddingBorderMargin};
|
||||
use crate::{ConstraintSpace, ContainingBlock, PropagatedBoxTreeData};
|
||||
use crate::{ConstraintSpace, ContainingBlock, PropagatedBoxTreeData, SharedStyle};
|
||||
|
||||
// From gfxFontConstants.h in Firefox.
|
||||
static FONT_SUBSCRIPT_OFFSET_RATIO: f32 = 0.20;
|
||||
|
@ -173,6 +174,25 @@ pub(crate) struct InlineFormattingContext {
|
|||
pub(super) has_right_to_left_content: bool,
|
||||
}
|
||||
|
||||
/// [`TextRun`] and `TextFragment`s need a handle on their parent inline box (or inline
|
||||
/// formatting context root)'s style. In order to implement incremental layout, these are
|
||||
/// wrapped in [`SharedStyle`]. This allows updating the parent box tree element without
|
||||
/// updating every single descendant box tree node and fragment.
|
||||
#[derive(Clone, Debug, MallocSizeOf)]
|
||||
pub(crate) struct SharedInlineStyles {
|
||||
pub style: SharedStyle,
|
||||
pub selected: SharedStyle,
|
||||
}
|
||||
|
||||
impl From<&NodeAndStyleInfo<'_>> for SharedInlineStyles {
|
||||
fn from(info: &NodeAndStyleInfo) -> Self {
|
||||
Self {
|
||||
style: SharedStyle::new(info.style.clone()),
|
||||
selected: SharedStyle::new(info.get_selected_style()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A collection of data used to cache [`FontMetrics`] in the [`InlineFormattingContext`]
|
||||
#[derive(Debug, MallocSizeOf)]
|
||||
pub(crate) struct FontKeyAndMetrics {
|
||||
|
@ -1313,7 +1333,7 @@ impl InlineFormattingContextLayout<'_> {
|
|||
) {
|
||||
let inline_advance = glyph_store.total_advance();
|
||||
let flags = if glyph_store.is_whitespace() {
|
||||
SegmentContentFlags::from(text_run.parent_style.get_inherited_text())
|
||||
SegmentContentFlags::from(text_run.inline_styles.style.borrow().get_inherited_text())
|
||||
} else {
|
||||
SegmentContentFlags::empty()
|
||||
};
|
||||
|
@ -1398,13 +1418,12 @@ impl InlineFormattingContextLayout<'_> {
|
|||
TextRunLineItem {
|
||||
text: vec![glyph_store],
|
||||
base_fragment_info: text_run.base_fragment_info,
|
||||
parent_style: text_run.parent_style.clone(),
|
||||
inline_styles: text_run.inline_styles.clone(),
|
||||
font_metrics,
|
||||
font_key: ifc_font_info.key,
|
||||
text_decoration_line: self.current_inline_container_state().text_decoration_line,
|
||||
bidi_level,
|
||||
selection_range,
|
||||
selected_style: text_run.selected_style.clone(),
|
||||
},
|
||||
));
|
||||
}
|
||||
|
@ -2363,8 +2382,9 @@ impl<'layout_data> ContentSizesComputation<'layout_data> {
|
|||
},
|
||||
InlineItem::TextRun(text_run) => {
|
||||
let text_run = &*text_run.borrow();
|
||||
let parent_style = text_run.inline_styles.style.borrow();
|
||||
for segment in text_run.shaped_text.iter() {
|
||||
let style_text = text_run.parent_style.get_inherited_text();
|
||||
let style_text = parent_style.get_inherited_text();
|
||||
let can_wrap = style_text.text_wrap_mode == TextWrapMode::Wrap;
|
||||
|
||||
// TODO: This should take account whether or not the first and last character prevent
|
||||
|
|
|
@ -26,7 +26,7 @@ use unicode_script::Script;
|
|||
use xi_unicode::linebreak_property;
|
||||
|
||||
use super::line_breaker::LineBreaker;
|
||||
use super::{FontKeyAndMetrics, InlineFormattingContextLayout};
|
||||
use super::{FontKeyAndMetrics, InlineFormattingContextLayout, SharedInlineStyles};
|
||||
use crate::fragment_tree::BaseFragmentInfo;
|
||||
|
||||
// These constants are the xi-unicode line breaking classes that are defined in
|
||||
|
@ -37,22 +37,6 @@ pub(crate) const XI_LINE_BREAKING_CLASS_ZW: u8 = 28;
|
|||
pub(crate) const XI_LINE_BREAKING_CLASS_WJ: u8 = 30;
|
||||
pub(crate) const XI_LINE_BREAKING_CLASS_ZWJ: u8 = 42;
|
||||
|
||||
/// <https://www.w3.org/TR/css-display-3/#css-text-run>
|
||||
#[derive(Debug, MallocSizeOf)]
|
||||
pub(crate) struct TextRun {
|
||||
pub base_fragment_info: BaseFragmentInfo,
|
||||
#[conditional_malloc_size_of]
|
||||
pub parent_style: Arc<ComputedValues>,
|
||||
pub text_range: Range<usize>,
|
||||
|
||||
/// The text of this [`TextRun`] with a font selected, broken into unbreakable
|
||||
/// segments, and shaped.
|
||||
pub shaped_text: Vec<TextRunSegment>,
|
||||
pub selection_range: Option<ServoRange<ByteIndex>>,
|
||||
#[conditional_malloc_size_of]
|
||||
pub selected_style: Arc<ComputedValues>,
|
||||
}
|
||||
|
||||
// There are two reasons why we might want to break at the start:
|
||||
//
|
||||
// 1. The line breaker told us that a break was necessary between two separate
|
||||
|
@ -334,21 +318,49 @@ impl TextRunSegment {
|
|||
}
|
||||
}
|
||||
|
||||
/// A single [`TextRun`] for the box tree. These are all descendants of
|
||||
/// [`super::InlineBox`] or the root of the [`super::InlineFormattingContext`]. During
|
||||
/// box tree construction, text is split into [`TextRun`]s based on their font, script,
|
||||
/// etc. When these are created text is already shaped.
|
||||
///
|
||||
/// <https://www.w3.org/TR/css-display-3/#css-text-run>
|
||||
#[derive(Debug, MallocSizeOf)]
|
||||
pub(crate) struct TextRun {
|
||||
/// The [`BaseFragmentInfo`] for this [`TextRun`]. Usually this comes from the
|
||||
/// original text node in the DOM for the text.
|
||||
pub base_fragment_info: BaseFragmentInfo,
|
||||
|
||||
/// The [`crate::SharedStyle`] from this [`TextRun`]s parent element. This is
|
||||
/// shared so that incremental layout can simply update the parent element and
|
||||
/// this [`TextRun`] will be updated automatically.
|
||||
pub inline_styles: SharedInlineStyles,
|
||||
|
||||
/// The range of text in [`super::InlineFormattingContext::text_content`] of the
|
||||
/// [`super::InlineFormattingContext`] that owns this [`TextRun`]. These are UTF-8 offsets.
|
||||
pub text_range: Range<usize>,
|
||||
|
||||
/// The text of this [`TextRun`] with a font selected, broken into unbreakable
|
||||
/// segments, and shaped.
|
||||
pub shaped_text: Vec<TextRunSegment>,
|
||||
|
||||
/// The selection range for the DOM text node that originated this [`TextRun`]. This
|
||||
/// comes directly from the DOM.
|
||||
pub selection_range: Option<ServoRange<ByteIndex>>,
|
||||
}
|
||||
|
||||
impl TextRun {
|
||||
pub(crate) fn new(
|
||||
base_fragment_info: BaseFragmentInfo,
|
||||
parent_style: Arc<ComputedValues>,
|
||||
inline_styles: SharedInlineStyles,
|
||||
text_range: Range<usize>,
|
||||
selection_range: Option<ServoRange<ByteIndex>>,
|
||||
selected_style: Arc<ComputedValues>,
|
||||
) -> Self {
|
||||
Self {
|
||||
base_fragment_info,
|
||||
parent_style,
|
||||
inline_styles,
|
||||
text_range,
|
||||
shaped_text: Vec::new(),
|
||||
selection_range,
|
||||
selected_style,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -360,11 +372,12 @@ impl TextRun {
|
|||
font_cache: &mut Vec<FontKeyAndMetrics>,
|
||||
bidi_info: &BidiInfo,
|
||||
) {
|
||||
let inherited_text_style = self.parent_style.get_inherited_text().clone();
|
||||
let parent_style = self.inline_styles.style.borrow().clone();
|
||||
let inherited_text_style = parent_style.get_inherited_text().clone();
|
||||
let letter_spacing = inherited_text_style
|
||||
.letter_spacing
|
||||
.0
|
||||
.resolve(self.parent_style.clone_font().font_size.computed_size());
|
||||
.resolve(parent_style.clone_font().font_size.computed_size());
|
||||
let letter_spacing = if letter_spacing.px() != 0. {
|
||||
Some(app_units::Au::from(letter_spacing))
|
||||
} else {
|
||||
|
@ -384,7 +397,13 @@ impl TextRun {
|
|||
let style_word_spacing: Option<Au> = specified_word_spacing.to_length().map(|l| l.into());
|
||||
|
||||
let segments = self
|
||||
.segment_text_by_font(formatting_context_text, font_context, font_cache, bidi_info)
|
||||
.segment_text_by_font(
|
||||
formatting_context_text,
|
||||
font_context,
|
||||
font_cache,
|
||||
bidi_info,
|
||||
&parent_style,
|
||||
)
|
||||
.into_iter()
|
||||
.map(|(mut segment, font)| {
|
||||
let word_spacing = style_word_spacing.unwrap_or_else(|| {
|
||||
|
@ -407,7 +426,7 @@ impl TextRun {
|
|||
};
|
||||
|
||||
segment.shape_text(
|
||||
&self.parent_style,
|
||||
&parent_style,
|
||||
formatting_context_text,
|
||||
linebreaker,
|
||||
&shaping_options,
|
||||
|
@ -430,8 +449,9 @@ impl TextRun {
|
|||
font_context: &FontContext,
|
||||
font_cache: &mut Vec<FontKeyAndMetrics>,
|
||||
bidi_info: &BidiInfo,
|
||||
parent_style: &Arc<ComputedValues>,
|
||||
) -> Vec<(TextRunSegment, FontRef)> {
|
||||
let font_group = font_context.font_group(self.parent_style.clone_font());
|
||||
let font_group = font_context.font_group(parent_style.clone_font());
|
||||
let mut current: Option<(TextRunSegment, FontRef)> = None;
|
||||
let mut results = Vec::new();
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue