Commit 7136fdaf authored by Kent Tamura's avatar Kent Tamura Committed by Commit Bot

RubyNG: Implement overhang behavior

This CL ports LayoutRubyRun::GetOverhang() to LayoutNG.
Overhang values are represented as negative margins of ruby run boxes.

Bug: 1069817
Change-Id: I487a633982d40211ee672d71caf00fadd88a07e6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2224895
Commit-Queue: Kent Tamura <tkent@chromium.org>
Reviewed-by: default avatarYoshifumi Inoue <yosin@chromium.org>
Reviewed-by: default avatarKoji Ishii <kojii@chromium.org>
Cr-Commit-Position: refs/heads/master@{#774436}
parent 36189508
......@@ -391,6 +391,8 @@ blink_core_sources("layout") {
"ng/inline/ng_physical_line_box_fragment.h",
"ng/inline/ng_physical_text_fragment.cc",
"ng/inline/ng_physical_text_fragment.h",
"ng/inline/ng_ruby_utils.cc",
"ng/inline/ng_ruby_utils.h",
"ng/inline/ng_text_fragment.cc",
"ng/inline/ng_text_fragment.h",
"ng/inline/ng_text_fragment_builder.cc",
......
......@@ -131,6 +131,9 @@ class CORE_EXPORT NGInlineItem {
bool IsImage() const {
return GetLayoutObject() && GetLayoutObject()->IsLayoutImage();
}
bool IsRubyRun() const {
return GetLayoutObject() && GetLayoutObject()->IsRubyRun();
}
void SetOffset(unsigned start, unsigned end) {
DCHECK_GE(end, start);
......
......@@ -8,6 +8,7 @@
#include "third_party/blink/renderer/core/layout/ng/inline/ng_bidi_paragraph.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_ruby_utils.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.h"
#include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h"
#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h"
......@@ -233,6 +234,9 @@ inline NGInlineItemResult* NGLineBreaker::AddItem(const NGInlineItem& item,
DCHECK_GE(offset_, item.StartOffset());
DCHECK_GE(end_offset, offset_);
DCHECK_LE(end_offset, item.EndOffset());
if (item.Type() != NGInlineItem::kCloseTag &&
item.Type() != NGInlineItem::kOpenTag)
pending_end_overhang_ = LayoutUnit();
NGInlineItemResults* item_results = line_info->MutableResults();
return &item_results->emplace_back(
&item, item_index_, offset_, end_offset, break_anywhere_if_overflow_,
......@@ -554,8 +558,14 @@ void NGLineBreaker::HandleText(const NGInlineItem& item,
return;
}
LayoutUnit end_overhang = pending_end_overhang_;
NGInlineItemResult* item_result = AddItem(item, line_info);
item_result->should_create_line_box = true;
// Try to commit |pending_end_overhang_| set by a prior item.
// |pending_end_overhang_| doesn't work well with bidi reordering. It's
// difficult to compute overhang after bidi reordering because it affect
// line breaking.
position_ -= CommitPendingEndOverhang(end_overhang, line_info);
if (auto_wrap_) {
if (mode_ == NGLineBreakerMode::kMinContent &&
......@@ -1405,6 +1415,19 @@ void NGLineBreaker::HandleAtomicInline(
auto_wrap_ && !(sticky_images_quirk_ && item.IsImage());
position_ += item_result->inline_size;
if (item.IsRubyRun()) {
NGAnnotationOverhang overhang = GetOverhang(*item_result);
pending_end_overhang_ = overhang.end;
if (CanApplyStartOverhang(*line_info, overhang.start)) {
DCHECK_EQ(item_result->margins.inline_start, LayoutUnit());
item_result->margins.inline_start = -overhang.start;
item_result->inline_size -= overhang.start;
position_ -= overhang.start;
}
}
trailing_whitespace_ = WhitespaceState::kNone;
MoveToNextOf(item);
}
......
......@@ -219,6 +219,7 @@ class CORE_EXPORT NGLineBreaker {
// that computes position in visual order, this position in logical order.
LayoutUnit position_;
LayoutUnit available_width_;
LayoutUnit pending_end_overhang_;
NGLineLayoutOpportunity line_opportunity_;
NGInlineNode node_;
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/layout/ng/inline/ng_ruby_utils.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.h"
#include "third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
namespace blink {
// See LayoutRubyRun::GetOverhang().
NGAnnotationOverhang GetOverhang(const NGInlineItemResult& item) {
DCHECK(RuntimeEnabledFeatures::LayoutNGRubyEnabled());
NGAnnotationOverhang overhang;
if (!item.layout_result)
return overhang;
const auto& run_fragment =
To<NGPhysicalContainerFragment>(item.layout_result->PhysicalFragment());
LayoutUnit start_overhang = LayoutUnit::Max();
LayoutUnit end_overhang = LayoutUnit::Max();
bool found_line = false;
const ComputedStyle* ruby_text_style = nullptr;
for (const auto& child_link : run_fragment.PostLayoutChildren()) {
const NGPhysicalFragment& child_fragment = *child_link.get();
const LayoutObject* layout_object = child_fragment.GetLayoutObject();
if (!layout_object)
continue;
if (layout_object->IsRubyText()) {
ruby_text_style = layout_object->Style();
continue;
}
if (layout_object->IsRubyBase()) {
const ComputedStyle& base_style = child_fragment.Style();
const WritingMode writing_mode = base_style.GetWritingMode();
const LayoutUnit base_inline_size =
NGFragment(writing_mode, child_fragment).InlineSize();
// RubyBase's inline_size is always same as RubyRun's inline_size.
// Overhang values are offsets from RubyBase's inline edges to
// the outmost text.
for (const auto& base_child_link :
To<NGPhysicalContainerFragment>(child_fragment)
.PostLayoutChildren()) {
found_line = true;
const LayoutUnit line_inline_size =
NGFragment(writing_mode, *base_child_link).InlineSize();
const LayoutUnit start =
base_child_link.offset
.ConvertToLogical(writing_mode, base_style.Direction(),
child_fragment.Size(),
base_child_link.get()->Size())
.inline_offset;
const LayoutUnit end = base_inline_size - start - line_inline_size;
start_overhang = std::min(start_overhang, start);
end_overhang = std::min(end_overhang, end);
}
}
}
if (!found_line || !ruby_text_style)
return overhang;
DCHECK_NE(start_overhang, LayoutUnit::Max());
DCHECK_NE(end_overhang, LayoutUnit::Max());
// We allow overhang up to the half of ruby text font size.
const LayoutUnit half_width_of_ruby_font =
LayoutUnit(ruby_text_style->FontSize()) / 2;
overhang.start = std::min(start_overhang, half_width_of_ruby_font);
overhang.end = std::min(end_overhang, half_width_of_ruby_font);
return overhang;
}
// See LayoutRubyRun::GetOverhang().
bool CanApplyStartOverhang(const NGLineInfo& line_info,
LayoutUnit& start_overhang) {
if (start_overhang <= LayoutUnit())
return false;
DCHECK(RuntimeEnabledFeatures::LayoutNGRubyEnabled());
const NGInlineItemResults& items = line_info.Results();
// Requires at least the current item and the previous item.
if (items.size() < 2)
return false;
// Find a previous item other than kOpenTag/kCloseTag.
// Searching items in the logical order doesn't work well with bidi
// reordering. However, it's difficult to compute overhang after bidi
// reordering because it affects line breaking.
wtf_size_t previous_index = items.size() - 2;
while ((items[previous_index].item->Type() == NGInlineItem::kOpenTag ||
items[previous_index].item->Type() == NGInlineItem::kCloseTag) &&
previous_index > 0)
--previous_index;
const NGInlineItemResult& previous_item = items[previous_index];
if (previous_item.item->Type() != NGInlineItem::kText)
return false;
const NGInlineItem& current_item = *items.back().item;
if (previous_item.item->Style()->FontSize() >
current_item.Style()->FontSize())
return false;
start_overhang = std::min(start_overhang, previous_item.inline_size);
return true;
}
// See LayoutRubyRun::GetOverhang().
LayoutUnit CommitPendingEndOverhang(LayoutUnit end_overhang,
NGLineInfo* line_info) {
if (end_overhang <= LayoutUnit())
return LayoutUnit();
DCHECK(RuntimeEnabledFeatures::LayoutNGRubyEnabled());
DCHECK(line_info);
NGInlineItemResults* items = line_info->MutableResults();
DCHECK_GE(items->size(), 2U);
const NGInlineItemResult& text_item = items->back();
DCHECK_EQ(text_item.item->Type(), NGInlineItem::kText);
wtf_size_t ruby_run_index = items->size() - 2;
while ((*items)[ruby_run_index].item->Type() != NGInlineItem::kAtomicInline) {
const auto type = (*items)[ruby_run_index].item->Type();
DCHECK(type == NGInlineItem::kOpenTag || type == NGInlineItem::kCloseTag)
<< type;
--ruby_run_index;
}
NGInlineItemResult& ruby_run_item = (*items)[ruby_run_index];
DCHECK(ruby_run_item.layout_result->PhysicalFragment().IsRubyRun());
if (ruby_run_item.item->Style()->FontSize() <
text_item.item->Style()->FontSize())
return LayoutUnit();
// Ideally we should refer to inline_size of |text_item| instead of the width
// of the NGInlineItem's ShapeResult. However it's impossible to compute
// inline_size of |text_item| before calling BreakText(), and BreakText()
// requires precise |position_| which takes |end_overhang| into account.
end_overhang = std::min(
end_overhang, LayoutUnit(text_item.item->TextShapeResult()->Width()));
DCHECK_EQ(ruby_run_item.margins.inline_end, LayoutUnit());
ruby_run_item.margins.inline_end = -end_overhang;
ruby_run_item.inline_size -= end_overhang;
return end_overhang;
}
} // namespace blink
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_RUBY_UTILS_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_RUBY_UTILS_H_
#include "third_party/blink/renderer/platform/geometry/layout_unit.h"
namespace blink {
class NGLineInfo;
struct NGInlineItemResult;
struct NGAnnotationOverhang {
LayoutUnit start;
LayoutUnit end;
};
// Returns overhang values of the specified NGInlineItemResult representing
// LayoutNGRubyRun.
//
// This is used by NGLineBreaker.
NGAnnotationOverhang GetOverhang(const NGInlineItemResult& item);
// Returns true if |start_overhang| is applied to a previous item, and
// clamp |start_overhang| to the width of the previous item.
//
// This is used by NGLineBreaker.
bool CanApplyStartOverhang(const NGLineInfo& line_info,
LayoutUnit& start_overhang);
// This should be called after NGInlineItemResult for a text is added in
// NGLineBreaker::HandleText().
//
// This function may update a NGInlineItemResult representing RubyRun
// in |line_info|
LayoutUnit CommitPendingEndOverhang(LayoutUnit end_overhang,
NGLineInfo* line_info);
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_RUBY_UTILS_H_
......@@ -142,6 +142,7 @@ class CORE_EXPORT NGPhysicalFragment
bool IsListMarker() const {
return IsCSSBox() && layout_object_->IsLayoutNGOutsideListMarker();
}
bool IsRubyRun() const { return layout_object_->IsRubyRun(); }
// Return true if this fragment is a container established by a fieldset
// element. Such a fragment contains an optional rendered legend fragment and
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment