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