1 /*
2 * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz>
3 * Copyright (C) 2006 Apple Computer Inc.
4 * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
5 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
6 * Copyright (C) 2011 Torch Mobile (Beijing) CO. Ltd. All rights reserved.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23
24 #include "third_party/blink/renderer/core/layout/svg/line/svg_root_inline_box.h"
25
26 #include "third_party/blink/renderer/core/layout/api/line_layout_api_shim.h"
27 #include "third_party/blink/renderer/core/layout/api/line_layout_block_flow.h"
28 #include "third_party/blink/renderer/core/layout/api/line_layout_svg_inline_text.h"
29 #include "third_party/blink/renderer/core/layout/svg/layout_svg_text.h"
30 #include "third_party/blink/renderer/core/layout/svg/line/svg_inline_flow_box.h"
31 #include "third_party/blink/renderer/core/layout/svg/line/svg_inline_text_box.h"
32 #include "third_party/blink/renderer/core/layout/svg/svg_text_layout_engine.h"
33 #include "third_party/blink/renderer/core/paint/svg_root_inline_box_painter.h"
34
35 namespace blink {
36
Paint(const PaintInfo & paint_info,const PhysicalOffset & paint_offset,LayoutUnit,LayoutUnit) const37 void SVGRootInlineBox::Paint(const PaintInfo& paint_info,
38 const PhysicalOffset& paint_offset,
39 LayoutUnit,
40 LayoutUnit) const {
41 SVGRootInlineBoxPainter(*this).Paint(paint_info, paint_offset);
42 }
43
MarkDirty()44 void SVGRootInlineBox::MarkDirty() {
45 for (InlineBox* child = FirstChild(); child; child = child->NextOnLine())
46 child->MarkDirty();
47 RootInlineBox::MarkDirty();
48 }
49
ComputePerCharacterLayoutInformation()50 void SVGRootInlineBox::ComputePerCharacterLayoutInformation() {
51 auto& text_root =
52 To<LayoutSVGText>(*LineLayoutAPIShim::LayoutObjectFrom(Block()));
53
54 const Vector<LayoutSVGInlineText*>& descendant_text_nodes =
55 text_root.DescendantTextNodes();
56 if (descendant_text_nodes.IsEmpty())
57 return;
58
59 if (text_root.NeedsReordering())
60 ReorderValueLists();
61
62 // Perform SVG text layout phase two (see SVGTextLayoutEngine for details).
63 SVGTextLayoutEngine character_layout(descendant_text_nodes);
64 character_layout.LayoutCharactersInTextBoxes(this);
65
66 // Perform SVG text layout phase three (see SVGTextChunkBuilder for details).
67 character_layout.FinishLayout();
68
69 // Perform SVG text layout phase four
70 // Position & resize all SVGInlineText/FlowBoxes in the inline box tree,
71 // resize the root box as well as the LayoutSVGText parent block.
72 LayoutInlineBoxes(*this);
73
74 // Let the HTML block space originate from the local SVG coordinate space.
75 LineLayoutBlockFlow parent_block = Block();
76 parent_block.SetLocation(LayoutPoint());
77 // The width could be any value, but set it so that a line box will mirror
78 // within the childRect when its coordinates are converted between physical
79 // block direction and flipped block direction, for ease of understanding of
80 // flipped coordinates. The height doesn't matter.
81 parent_block.SetSize(LayoutSize(X() * 2 + Width(), LayoutUnit()));
82
83 SetLineTopBottomPositions(LogicalTop(), LogicalBottom(), LogicalTop(),
84 LogicalBottom());
85 }
86
LayoutInlineBoxes(InlineBox & box)87 FloatRect SVGRootInlineBox::LayoutInlineBoxes(InlineBox& box) {
88 FloatRect rect;
89 if (auto* svg_inline_text_box = DynamicTo<SVGInlineTextBox>(box)) {
90 rect = svg_inline_text_box->CalculateBoundaries();
91 } else {
92 for (InlineBox* child = To<InlineFlowBox>(box).FirstChild(); child;
93 child = child->NextOnLine())
94 rect.Unite(LayoutInlineBoxes(*child));
95 }
96
97 LayoutRect logical_rect(EnclosingLayoutRect(rect));
98 if (!box.IsHorizontal())
99 logical_rect.SetSize(logical_rect.Size().TransposedSize());
100
101 box.SetX(logical_rect.X());
102 box.SetY(logical_rect.Y());
103 box.SetLogicalWidth(logical_rect.Width());
104 if (auto* svg_inline_text_box = DynamicTo<SVGInlineTextBox>(box))
105 svg_inline_text_box->SetLogicalHeight(logical_rect.Height());
106 else if (auto* svg_inline_flow_box = DynamicTo<SVGInlineFlowBox>(box))
107 svg_inline_flow_box->SetLogicalHeight(logical_rect.Height());
108 else
109 To<SVGRootInlineBox>(box).SetLogicalHeight(logical_rect.Height());
110
111 return rect;
112 }
113
ClosestLeafChildForPosition(const PhysicalOffset & point)114 InlineBox* SVGRootInlineBox::ClosestLeafChildForPosition(
115 const PhysicalOffset& point) {
116 InlineBox* first_leaf = FirstLeafChild();
117 InlineBox* last_leaf = LastLeafChild();
118 if (first_leaf == last_leaf)
119 return first_leaf;
120
121 // FIXME: Check for vertical text!
122 InlineBox* closest_leaf = nullptr;
123 for (InlineBox* leaf = first_leaf; leaf; leaf = leaf->NextLeafChild()) {
124 if (!leaf->IsSVGInlineTextBox())
125 continue;
126 if (point.top < leaf->Y())
127 continue;
128 if (point.left > leaf->Y() + leaf->VirtualLogicalHeight())
129 continue;
130
131 closest_leaf = leaf;
132 if (point.left < leaf->X() + leaf->LogicalWidth())
133 return leaf;
134 }
135
136 return closest_leaf ? closest_leaf : last_leaf;
137 }
138
SwapPositioningValuesInTextBoxes(SVGInlineTextBox * first_text_box,SVGInlineTextBox * last_text_box)139 static inline void SwapPositioningValuesInTextBoxes(
140 SVGInlineTextBox* first_text_box,
141 SVGInlineTextBox* last_text_box) {
142 LineLayoutSVGInlineText first_text_node =
143 LineLayoutSVGInlineText(first_text_box->GetLineLayoutItem());
144 SVGCharacterDataMap& first_character_data_map =
145 first_text_node.CharacterDataMap();
146 SVGCharacterDataMap::iterator it_first =
147 first_character_data_map.find(first_text_box->Start() + 1);
148 if (it_first == first_character_data_map.end())
149 return;
150 LineLayoutSVGInlineText last_text_node =
151 LineLayoutSVGInlineText(last_text_box->GetLineLayoutItem());
152 SVGCharacterDataMap& last_character_data_map =
153 last_text_node.CharacterDataMap();
154 SVGCharacterDataMap::iterator it_last =
155 last_character_data_map.find(last_text_box->Start() + 1);
156 if (it_last == last_character_data_map.end())
157 return;
158 // We only want to perform the swap if both inline boxes are absolutely
159 // positioned.
160 std::swap(it_first->value, it_last->value);
161 }
162
ReverseInlineBoxRangeAndValueListsIfNeeded(Vector<InlineBox * >::iterator first,Vector<InlineBox * >::iterator last)163 static inline void ReverseInlineBoxRangeAndValueListsIfNeeded(
164 Vector<InlineBox*>::iterator first,
165 Vector<InlineBox*>::iterator last) {
166 // This is a copy of std::reverse(first, last). It additionally assures
167 // that the metrics map within the layoutObjects belonging to the
168 // InlineBoxes are reordered as well.
169 while (true) {
170 if (first == last || first == --last)
171 return;
172
173 auto* first_text_box = DynamicTo<SVGInlineTextBox>(*first);
174 auto* last_text_box = DynamicTo<SVGInlineTextBox>(*last);
175 if (last_text_box && first_text_box) {
176 // Reordering is only necessary for BiDi text that is _absolutely_
177 // positioned.
178 if (first_text_box->Len() == 1 &&
179 first_text_box->Len() == last_text_box->Len())
180 SwapPositioningValuesInTextBoxes(first_text_box, last_text_box);
181 }
182
183 InlineBox* temp = *first;
184 *first = *last;
185 *last = temp;
186 ++first;
187 }
188 }
189
ReorderValueLists()190 void SVGRootInlineBox::ReorderValueLists() {
191 Vector<InlineBox*> leaf_boxes_in_logical_order;
192 CollectLeafBoxesInLogicalOrder(leaf_boxes_in_logical_order,
193 ReverseInlineBoxRangeAndValueListsIfNeeded);
194 }
195
NodeAtPoint(HitTestResult & result,const HitTestLocation & hit_test_location,const PhysicalOffset & accumulated_offset,LayoutUnit line_top,LayoutUnit line_bottom)196 bool SVGRootInlineBox::NodeAtPoint(HitTestResult& result,
197 const HitTestLocation& hit_test_location,
198 const PhysicalOffset& accumulated_offset,
199 LayoutUnit line_top,
200 LayoutUnit line_bottom) {
201 // Iterate the text boxes in reverse so that the top-most node will be considered first.
202 for (InlineBox* leaf = LastLeafChild(); leaf; leaf = leaf->PrevLeafChild()) {
203 if (!leaf->IsSVGInlineTextBox())
204 continue;
205 if (leaf->NodeAtPoint(result, hit_test_location, accumulated_offset,
206 line_top, line_bottom))
207 return true;
208 }
209
210 return false;
211 }
212
213 } // namespace blink
214