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 "SVGRootInlineBox.h"
28 #include "GraphicsContext.h"
29 #include "RenderSVGInlineText.h"
30 #include "RenderSVGText.h"
31 #include "SVGInlineFlowBox.h"
32 #include "SVGInlineTextBox.h"
34 #include "SVGRenderSupport.h"
35 #include "SVGTextPositioningElement.h"
39 void SVGRootInlineBox::paint(PaintInfo& paintInfo, const LayoutPoint&, LayoutUnit, LayoutUnit)
41 ASSERT(paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection);
42 ASSERT(!paintInfo.context->paintingDisabled());
44 RenderObject* boxRenderer = renderer();
47 bool isPrinting = renderer()->document()->printing();
48 bool hasSelection = !isPrinting && selectionState() != RenderObject::SelectionNone;
50 PaintInfo childPaintInfo(paintInfo);
52 for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) {
53 if (child->isSVGInlineTextBox())
54 static_cast<SVGInlineTextBox*>(child)->paintSelectionBackground(childPaintInfo);
55 else if (child->isSVGInlineFlowBox())
56 static_cast<SVGInlineFlowBox*>(child)->paintSelectionBackground(childPaintInfo);
60 GraphicsContextStateSaver stateSaver(*childPaintInfo.context);
62 if (SVGRenderSupport::prepareToRenderSVGContent(boxRenderer, childPaintInfo)) {
63 for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) {
64 if (child->isSVGInlineTextBox())
65 SVGInlineFlowBox::computeTextMatchMarkerRectForRenderer(toRenderSVGInlineText(static_cast<SVGInlineTextBox*>(child)->textRenderer()));
67 child->paint(childPaintInfo, LayoutPoint(), 0, 0);
71 SVGRenderSupport::finishRenderSVGContent(boxRenderer, childPaintInfo, paintInfo.context);
74 void SVGRootInlineBox::computePerCharacterLayoutInformation()
76 RenderSVGText* parentBlock = toRenderSVGText(block());
79 Vector<SVGTextLayoutAttributes>& attributes = parentBlock->layoutAttributes();
80 if (attributes.isEmpty())
83 if (parentBlock->needsReordering())
84 reorderValueLists(attributes);
86 // Perform SVG text layout phase two (see SVGTextLayoutEngine for details).
87 SVGTextLayoutEngine characterLayout(attributes);
88 layoutCharactersInTextBoxes(this, characterLayout);
90 // Perform SVG text layout phase three (see SVGTextChunkBuilder for details).
91 characterLayout.finishLayout();
93 // Perform SVG text layout phase four
94 // Position & resize all SVGInlineText/FlowBoxes in the inline box tree, resize the root box as well as the RenderSVGText parent block.
96 layoutChildBoxes(this, &childRect);
97 layoutRootBox(childRect);
100 void SVGRootInlineBox::layoutCharactersInTextBoxes(InlineFlowBox* start, SVGTextLayoutEngine& characterLayout)
102 for (InlineBox* child = start->firstChild(); child; child = child->nextOnLine()) {
103 if (child->isSVGInlineTextBox()) {
104 ASSERT(child->renderer());
105 ASSERT(child->renderer()->isSVGInlineText());
107 SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(child);
108 characterLayout.layoutInlineTextBox(textBox);
110 // Skip generated content.
111 Node* node = child->renderer()->node();
115 ASSERT(child->isInlineFlowBox());
117 SVGInlineFlowBox* flowBox = static_cast<SVGInlineFlowBox*>(child);
118 bool isTextPath = node->hasTagName(SVGNames::textPathTag);
120 // Build text chunks for all <textPath> children, using the line layout algorithm.
121 // This is needeed as text-anchor is just an additional startOffset for text paths.
122 RenderSVGText* parentBlock = toRenderSVGText(block());
125 SVGTextLayoutEngine lineLayout(parentBlock->layoutAttributes());
126 layoutCharactersInTextBoxes(flowBox, lineLayout);
128 characterLayout.beginTextPathLayout(child->renderer(), lineLayout);
131 layoutCharactersInTextBoxes(flowBox, characterLayout);
134 characterLayout.endTextPathLayout();
139 void SVGRootInlineBox::layoutChildBoxes(InlineFlowBox* start, FloatRect* childRect)
141 for (InlineBox* child = start->firstChild(); child; child = child->nextOnLine()) {
143 if (child->isSVGInlineTextBox()) {
144 ASSERT(child->renderer());
145 ASSERT(child->renderer()->isSVGInlineText());
147 SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(child);
148 boxRect = textBox->calculateBoundaries();
149 textBox->setX(boxRect.x());
150 textBox->setY(boxRect.y());
151 textBox->setLogicalWidth(boxRect.width());
152 textBox->setLogicalHeight(boxRect.height());
154 // Skip generated content.
155 if (!child->renderer()->node())
158 ASSERT(child->isInlineFlowBox());
160 SVGInlineFlowBox* flowBox = static_cast<SVGInlineFlowBox*>(child);
161 layoutChildBoxes(flowBox);
163 boxRect = flowBox->calculateBoundaries();
164 flowBox->setX(boxRect.x());
165 flowBox->setY(boxRect.y());
166 flowBox->setLogicalWidth(boxRect.width());
167 flowBox->setLogicalHeight(boxRect.height());
170 childRect->unite(boxRect);
174 void SVGRootInlineBox::layoutRootBox(const FloatRect& childRect)
176 RenderBlock* parentBlock = block();
179 // Finally, assign the root block position, now that all content is laid out.
180 IntRect roundedChildRect = enclosingIntRect(childRect);
181 parentBlock->setLocation(roundedChildRect.location());
182 parentBlock->setSize(roundedChildRect.size());
184 // Position all children relative to the parent block.
185 for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) {
186 // Skip generated content.
187 if (!child->renderer()->node())
189 child->adjustPosition(-childRect.x(), -childRect.y());
192 // Position ourselves.
195 setLogicalWidth(childRect.width());
196 setLogicalHeight(childRect.height());
197 setLineTopBottomPositions(0, roundedChildRect.height(), 0, roundedChildRect.height());
200 InlineBox* SVGRootInlineBox::closestLeafChildForPosition(const LayoutPoint& point)
202 InlineBox* firstLeaf = firstLeafChild();
203 InlineBox* lastLeaf = lastLeafChild();
204 if (firstLeaf == lastLeaf)
207 // FIXME: Check for vertical text!
208 InlineBox* closestLeaf = 0;
209 for (InlineBox* leaf = firstLeaf; leaf; leaf = leaf->nextLeafChild()) {
210 if (!leaf->isSVGInlineTextBox())
212 if (point.y() < leaf->y())
214 if (point.y() > leaf->y() + leaf->virtualLogicalHeight())
218 if (point.x() < leaf->left() + leaf->logicalWidth())
222 return closestLeaf ? closestLeaf : lastLeaf;
225 static inline void swapItemsInVector(Vector<float>& firstVector, Vector<float>& lastVector, unsigned first, unsigned last)
227 float temp = firstVector.at(first);
228 firstVector.at(first) = lastVector.at(last);
229 lastVector.at(last) = temp;
232 static inline void swapItemsInLayoutAttributes(SVGTextLayoutAttributes& firstAttributes, SVGTextLayoutAttributes& lastAttributes, unsigned firstPosition, unsigned lastPosition)
234 swapItemsInVector(firstAttributes.xValues(), lastAttributes.xValues(), firstPosition, lastPosition);
235 swapItemsInVector(firstAttributes.yValues(), lastAttributes.yValues(), firstPosition, lastPosition);
236 swapItemsInVector(firstAttributes.dxValues(), lastAttributes.dxValues(), firstPosition, lastPosition);
237 swapItemsInVector(firstAttributes.dyValues(), lastAttributes.dyValues(), firstPosition, lastPosition);
238 swapItemsInVector(firstAttributes.rotateValues(), lastAttributes.rotateValues(), firstPosition, lastPosition);
241 static inline void findFirstAndLastAttributesInVector(Vector<SVGTextLayoutAttributes>& attributes, RenderSVGInlineText* firstContext, RenderSVGInlineText* lastContext,
242 SVGTextLayoutAttributes*& first, SVGTextLayoutAttributes*& last)
247 unsigned attributesSize = attributes.size();
248 for (unsigned i = 0; i < attributesSize; ++i) {
249 SVGTextLayoutAttributes& current = attributes.at(i);
250 if (!first && firstContext == current.context())
252 if (!last && lastContext == current.context())
262 static inline void reverseInlineBoxRangeAndValueListsIfNeeded(void* userData, Vector<InlineBox*>::iterator first, Vector<InlineBox*>::iterator last)
265 Vector<SVGTextLayoutAttributes>& attributes = *reinterpret_cast<Vector<SVGTextLayoutAttributes>*>(userData);
267 // This is a copy of std::reverse(first, last). It additionally assure that the value lists within the InlineBoxes are reordered as well.
269 if (first == last || first == --last)
272 if (!(*last)->isSVGInlineTextBox() || !(*first)->isSVGInlineTextBox()) {
273 InlineBox* temp = *first;
280 SVGInlineTextBox* firstTextBox = static_cast<SVGInlineTextBox*>(*first);
281 SVGInlineTextBox* lastTextBox = static_cast<SVGInlineTextBox*>(*last);
283 // Reordering is only necessary for BiDi text that is _absolutely_ positioned.
284 if (firstTextBox->len() == 1 && firstTextBox->len() == lastTextBox->len()) {
285 RenderSVGInlineText* firstContext = toRenderSVGInlineText(firstTextBox->textRenderer());
286 RenderSVGInlineText* lastContext = toRenderSVGInlineText(lastTextBox->textRenderer());
288 SVGTextLayoutAttributes* firstAttributes = 0;
289 SVGTextLayoutAttributes* lastAttributes = 0;
290 findFirstAndLastAttributesInVector(attributes, firstContext, lastContext, firstAttributes, lastAttributes);
292 unsigned firstBoxPosition = firstTextBox->start();
293 unsigned firstBoxEnd = firstTextBox->end();
295 unsigned lastBoxPosition = lastTextBox->start();
296 unsigned lastBoxEnd = lastTextBox->end();
297 for (; firstBoxPosition <= firstBoxEnd && lastBoxPosition <= lastBoxEnd; ++lastBoxPosition, ++firstBoxPosition)
298 swapItemsInLayoutAttributes(*firstAttributes, *lastAttributes, firstBoxPosition, lastBoxPosition);
301 InlineBox* temp = *first;
309 void SVGRootInlineBox::reorderValueLists(Vector<SVGTextLayoutAttributes>& attributes)
311 Vector<InlineBox*> leafBoxesInLogicalOrder;
312 collectLeafBoxesInLogicalOrder(leafBoxesInLogicalOrder, reverseInlineBoxRangeAndValueListsIfNeeded, &attributes);
315 } // namespace WebCore
317 #endif // ENABLE(SVG)