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.
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.
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.
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.
25 #include "core/rendering/svg/SVGRootInlineBox.h"
27 #include "core/paint/SVGRootInlineBoxPainter.h"
28 #include "core/rendering/svg/RenderSVGInlineText.h"
29 #include "core/rendering/svg/RenderSVGText.h"
30 #include "core/rendering/svg/SVGInlineFlowBox.h"
31 #include "core/rendering/svg/SVGInlineTextBox.h"
35 void SVGRootInlineBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit, LayoutUnit)
37 SVGRootInlineBoxPainter(*this).paint(paintInfo, paintOffset);
40 void SVGRootInlineBox::markDirty()
42 for (InlineBox* child = firstChild(); child; child = child->nextOnLine())
44 RootInlineBox::markDirty();
47 void SVGRootInlineBox::computePerCharacterLayoutInformation()
49 RenderSVGText& textRoot = toRenderSVGText(block());
51 Vector<SVGTextLayoutAttributes*>& layoutAttributes = textRoot.layoutAttributes();
52 if (layoutAttributes.isEmpty())
55 if (textRoot.needsReordering())
56 reorderValueLists(layoutAttributes);
58 // Perform SVG text layout phase two (see SVGTextLayoutEngine for details).
59 SVGTextLayoutEngine characterLayout(layoutAttributes);
60 layoutCharactersInTextBoxes(this, characterLayout);
62 // Perform SVG text layout phase three (see SVGTextChunkBuilder for details).
63 characterLayout.finishLayout();
65 // Perform SVG text layout phase four
66 // Position & resize all SVGInlineText/FlowBoxes in the inline box tree, resize the root box as well as the RenderSVGText parent block.
68 layoutChildBoxes(this, &childRect);
69 layoutRootBox(childRect);
72 void SVGRootInlineBox::layoutCharactersInTextBoxes(InlineFlowBox* start, SVGTextLayoutEngine& characterLayout)
74 for (InlineBox* child = start->firstChild(); child; child = child->nextOnLine()) {
75 if (child->isSVGInlineTextBox()) {
76 ASSERT(child->renderer().isSVGInlineText());
77 characterLayout.layoutInlineTextBox(toSVGInlineTextBox(child));
79 // Skip generated content.
80 Node* node = child->renderer().node();
84 SVGInlineFlowBox* flowBox = toSVGInlineFlowBox(child);
85 bool isTextPath = isSVGTextPathElement(*node);
87 // Build text chunks for all <textPath> children, using the line layout algorithm.
88 // This is needeed as text-anchor is just an additional startOffset for text paths.
89 SVGTextLayoutEngine lineLayout(characterLayout.layoutAttributes());
90 layoutCharactersInTextBoxes(flowBox, lineLayout);
92 characterLayout.beginTextPathLayout(&child->renderer(), lineLayout);
95 layoutCharactersInTextBoxes(flowBox, characterLayout);
98 characterLayout.endTextPathLayout();
103 void SVGRootInlineBox::layoutChildBoxes(InlineFlowBox* start, FloatRect* childRect)
105 for (InlineBox* child = start->firstChild(); child; child = child->nextOnLine()) {
107 if (child->isSVGInlineTextBox()) {
108 ASSERT(child->renderer().isSVGInlineText());
110 SVGInlineTextBox* textBox = toSVGInlineTextBox(child);
111 boxRect = textBox->calculateBoundaries();
112 textBox->setX(boxRect.x());
113 textBox->setY(boxRect.y());
114 textBox->setLogicalWidth(boxRect.width());
115 textBox->setLogicalHeight(boxRect.height());
117 // Skip generated content.
118 if (!child->renderer().node())
121 SVGInlineFlowBox* flowBox = toSVGInlineFlowBox(child);
122 layoutChildBoxes(flowBox);
124 boxRect = flowBox->calculateBoundaries();
125 flowBox->setX(boxRect.x());
126 flowBox->setY(boxRect.y());
127 flowBox->setLogicalWidth(boxRect.width());
128 flowBox->setLogicalHeight(boxRect.height());
131 childRect->unite(boxRect);
135 void SVGRootInlineBox::layoutRootBox(const FloatRect& childRect)
137 RenderBlockFlow& parentBlock = block();
139 // Finally, assign the root block position, now that all content is laid out.
140 LayoutRect boundingRect = enclosingLayoutRect(childRect);
141 parentBlock.setLocation(boundingRect.location());
142 parentBlock.setSize(boundingRect.size());
144 // Position all children relative to the parent block.
145 for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) {
146 // Skip generated content.
147 if (!child->renderer().node())
149 child->adjustPosition(-childRect.x(), -childRect.y());
152 // Position ourselves.
155 setLogicalWidth(childRect.width());
156 setLogicalHeight(childRect.height());
157 setLineTopBottomPositions(0, boundingRect.height(), 0, boundingRect.height());
160 InlineBox* SVGRootInlineBox::closestLeafChildForPosition(const LayoutPoint& point)
162 InlineBox* firstLeaf = firstLeafChild();
163 InlineBox* lastLeaf = lastLeafChild();
164 if (firstLeaf == lastLeaf)
167 // FIXME: Check for vertical text!
168 InlineBox* closestLeaf = 0;
169 for (InlineBox* leaf = firstLeaf; leaf; leaf = leaf->nextLeafChild()) {
170 if (!leaf->isSVGInlineTextBox())
172 if (point.y() < leaf->y())
174 if (point.y() > leaf->y() + leaf->virtualLogicalHeight())
178 if (point.x() < leaf->left() + leaf->logicalWidth())
182 return closestLeaf ? closestLeaf : lastLeaf;
185 static inline void swapItemsInLayoutAttributes(SVGTextLayoutAttributes* firstAttributes, SVGTextLayoutAttributes* lastAttributes, unsigned firstPosition, unsigned lastPosition)
187 SVGCharacterDataMap::iterator itFirst = firstAttributes->characterDataMap().find(firstPosition + 1);
188 SVGCharacterDataMap::iterator itLast = lastAttributes->characterDataMap().find(lastPosition + 1);
189 bool firstPresent = itFirst != firstAttributes->characterDataMap().end();
190 bool lastPresent = itLast != lastAttributes->characterDataMap().end();
191 // We only want to perform the swap if both inline boxes are absolutely
193 if (!firstPresent || !lastPresent)
195 std::swap(itFirst->value, itLast->value);
198 static inline void findFirstAndLastAttributesInVector(Vector<SVGTextLayoutAttributes*>& attributes, RenderSVGInlineText* firstContext, RenderSVGInlineText* lastContext,
199 SVGTextLayoutAttributes*& first, SVGTextLayoutAttributes*& last)
204 unsigned attributesSize = attributes.size();
205 for (unsigned i = 0; i < attributesSize; ++i) {
206 SVGTextLayoutAttributes* current = attributes[i];
207 if (!first && firstContext == current->context())
209 if (!last && lastContext == current->context())
219 static inline void reverseInlineBoxRangeAndValueListsIfNeeded(void* userData, Vector<InlineBox*>::iterator first, Vector<InlineBox*>::iterator last)
222 Vector<SVGTextLayoutAttributes*>& attributes = *reinterpret_cast<Vector<SVGTextLayoutAttributes*>*>(userData);
224 // This is a copy of std::reverse(first, last). It additionally assures that the metrics map within the renderers belonging to the InlineBoxes are reordered as well.
226 if (first == last || first == --last)
229 if (!(*last)->isSVGInlineTextBox() || !(*first)->isSVGInlineTextBox()) {
230 InlineBox* temp = *first;
237 SVGInlineTextBox* firstTextBox = toSVGInlineTextBox(*first);
238 SVGInlineTextBox* lastTextBox = toSVGInlineTextBox(*last);
240 // Reordering is only necessary for BiDi text that is _absolutely_ positioned.
241 if (firstTextBox->len() == 1 && firstTextBox->len() == lastTextBox->len()) {
242 RenderSVGInlineText& firstContext = toRenderSVGInlineText(firstTextBox->renderer());
243 RenderSVGInlineText& lastContext = toRenderSVGInlineText(lastTextBox->renderer());
245 SVGTextLayoutAttributes* firstAttributes = 0;
246 SVGTextLayoutAttributes* lastAttributes = 0;
247 findFirstAndLastAttributesInVector(attributes, &firstContext, &lastContext, firstAttributes, lastAttributes);
248 swapItemsInLayoutAttributes(firstAttributes, lastAttributes, firstTextBox->start(), lastTextBox->start());
251 InlineBox* temp = *first;
259 void SVGRootInlineBox::reorderValueLists(Vector<SVGTextLayoutAttributes*>& attributes)
261 Vector<InlineBox*> leafBoxesInLogicalOrder;
262 collectLeafBoxesInLogicalOrder(leafBoxesInLogicalOrder, reverseInlineBoxRangeAndValueListsIfNeeded, &attributes);