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 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23 #include "config.h"
24 #include "SVGRootInlineBox.h"
25
26 #if ENABLE(SVG)
27 #include "GraphicsContext.h"
28 #include "RenderSVGInlineText.h"
29 #include "RenderSVGText.h"
30 #include "SVGInlineFlowBox.h"
31 #include "SVGInlineTextBox.h"
32 #include "SVGNames.h"
33 #include "SVGRenderSupport.h"
34 #include "SVGTextPositioningElement.h"
35
36 namespace WebCore {
37
paint(PaintInfo & paintInfo,int,int,int,int)38 void SVGRootInlineBox::paint(PaintInfo& paintInfo, int, int, int, int)
39 {
40 ASSERT(paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection);
41 ASSERT(!paintInfo.context->paintingDisabled());
42
43 RenderObject* boxRenderer = renderer();
44 ASSERT(boxRenderer);
45
46 bool isPrinting = renderer()->document()->printing();
47 bool hasSelection = !isPrinting && selectionState() != RenderObject::SelectionNone;
48
49 PaintInfo childPaintInfo(paintInfo);
50 if (hasSelection) {
51 for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) {
52 if (child->isSVGInlineTextBox())
53 static_cast<SVGInlineTextBox*>(child)->paintSelectionBackground(childPaintInfo);
54 else if (child->isSVGInlineFlowBox())
55 static_cast<SVGInlineFlowBox*>(child)->paintSelectionBackground(childPaintInfo);
56 }
57 }
58
59 childPaintInfo.context->save();
60
61 if (SVGRenderSupport::prepareToRenderSVGContent(boxRenderer, childPaintInfo)) {
62 for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) {
63 if (child->isSVGInlineTextBox())
64 SVGInlineFlowBox::computeTextMatchMarkerRectForRenderer(toRenderSVGInlineText(static_cast<SVGInlineTextBox*>(child)->textRenderer()));
65
66 child->paint(childPaintInfo, 0, 0, 0, 0);
67 }
68 }
69
70 SVGRenderSupport::finishRenderSVGContent(boxRenderer, childPaintInfo, paintInfo.context);
71 childPaintInfo.context->restore();
72 }
73
computePerCharacterLayoutInformation()74 void SVGRootInlineBox::computePerCharacterLayoutInformation()
75 {
76 RenderSVGText* parentBlock = toRenderSVGText(block());
77 ASSERT(parentBlock);
78
79 Vector<SVGTextLayoutAttributes>& attributes = parentBlock->layoutAttributes();
80 if (parentBlock->needsReordering())
81 reorderValueLists(attributes);
82
83 // Perform SVG text layout phase two (see SVGTextLayoutEngine for details).
84 SVGTextLayoutEngine characterLayout(attributes);
85 layoutCharactersInTextBoxes(this, characterLayout);
86
87 // Perform SVG text layout phase three (see SVGTextChunkBuilder for details).
88 characterLayout.finishLayout();
89
90 // Perform SVG text layout phase four
91 // Position & resize all SVGInlineText/FlowBoxes in the inline box tree, resize the root box as well as the RenderSVGText parent block.
92 layoutChildBoxes(this);
93 layoutRootBox();
94 }
95
layoutCharactersInTextBoxes(InlineFlowBox * start,SVGTextLayoutEngine & characterLayout)96 void SVGRootInlineBox::layoutCharactersInTextBoxes(InlineFlowBox* start, SVGTextLayoutEngine& characterLayout)
97 {
98 for (InlineBox* child = start->firstChild(); child; child = child->nextOnLine()) {
99 if (child->isSVGInlineTextBox()) {
100 ASSERT(child->renderer());
101 ASSERT(child->renderer()->isSVGInlineText());
102
103 SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(child);
104 characterLayout.layoutInlineTextBox(textBox);
105 } else {
106 // Skip generated content.
107 Node* node = child->renderer()->node();
108 if (!node)
109 continue;
110
111 ASSERT(child->isInlineFlowBox());
112
113 SVGInlineFlowBox* flowBox = static_cast<SVGInlineFlowBox*>(child);
114 bool isTextPath = node->hasTagName(SVGNames::textPathTag);
115 if (isTextPath) {
116 // Build text chunks for all <textPath> children, using the line layout algorithm.
117 // This is needeed as text-anchor is just an additional startOffset for text paths.
118 RenderSVGText* parentBlock = toRenderSVGText(block());
119 ASSERT(parentBlock);
120
121 SVGTextLayoutEngine lineLayout(parentBlock->layoutAttributes());
122 layoutCharactersInTextBoxes(flowBox, lineLayout);
123
124 characterLayout.beginTextPathLayout(child->renderer(), lineLayout);
125 }
126
127 layoutCharactersInTextBoxes(flowBox, characterLayout);
128
129 if (isTextPath)
130 characterLayout.endTextPathLayout();
131 }
132 }
133 }
134
layoutChildBoxes(InlineFlowBox * start)135 void SVGRootInlineBox::layoutChildBoxes(InlineFlowBox* start)
136 {
137 for (InlineBox* child = start->firstChild(); child; child = child->nextOnLine()) {
138 if (child->isSVGInlineTextBox()) {
139 ASSERT(child->renderer());
140 ASSERT(child->renderer()->isSVGInlineText());
141
142 SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(child);
143 IntRect boxRect = textBox->calculateBoundaries();
144 textBox->setX(boxRect.x());
145 textBox->setY(boxRect.y());
146 textBox->setLogicalWidth(boxRect.width());
147 textBox->setLogicalHeight(boxRect.height());
148 } else {
149 // Skip generated content.
150 if (!child->renderer()->node())
151 continue;
152
153 ASSERT(child->isInlineFlowBox());
154
155 SVGInlineFlowBox* flowBox = static_cast<SVGInlineFlowBox*>(child);
156 layoutChildBoxes(flowBox);
157
158 IntRect boxRect = flowBox->calculateBoundaries();
159 flowBox->setX(boxRect.x());
160 flowBox->setY(boxRect.y());
161 flowBox->setLogicalWidth(boxRect.width());
162 flowBox->setLogicalHeight(boxRect.height());
163 }
164 }
165 }
166
layoutRootBox()167 void SVGRootInlineBox::layoutRootBox()
168 {
169 RenderBlock* parentBlock = block();
170 ASSERT(parentBlock);
171
172 IntRect childRect;
173 for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) {
174 // Skip generated content.
175 if (!child->renderer()->node())
176 continue;
177 childRect.unite(child->calculateBoundaries());
178 }
179
180 int xBlock = childRect.x();
181 int yBlock = childRect.y();
182 int widthBlock = childRect.width();
183 int heightBlock = childRect.height();
184
185 // Finally, assign the root block position, now that all content is laid out.
186 parentBlock->setLocation(xBlock, yBlock);
187 parentBlock->setWidth(widthBlock);
188 parentBlock->setHeight(heightBlock);
189
190 // Position all children relative to the parent block.
191 for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) {
192 // Skip generated content.
193 if (!child->renderer()->node())
194 continue;
195 child->adjustPosition(-xBlock, -yBlock);
196 }
197
198 // Position ourselves.
199 setX(0);
200 setY(0);
201 setLogicalWidth(widthBlock);
202 setLogicalHeight(heightBlock);
203 setBlockLogicalHeight(heightBlock);
204 setLineTopBottomPositions(0, heightBlock);
205 }
206
closestLeafChildForPosition(const IntPoint & point)207 InlineBox* SVGRootInlineBox::closestLeafChildForPosition(const IntPoint& point)
208 {
209 InlineBox* firstLeaf = firstLeafChild();
210 InlineBox* lastLeaf = lastLeafChild();
211 if (firstLeaf == lastLeaf)
212 return firstLeaf;
213
214 // FIXME: Check for vertical text!
215 InlineBox* closestLeaf = 0;
216 for (InlineBox* leaf = firstLeaf; leaf; leaf = leaf->nextLeafChild()) {
217 if (!leaf->isSVGInlineTextBox())
218 continue;
219 if (point.y() < leaf->m_y)
220 continue;
221 if (point.y() > leaf->m_y + leaf->virtualLogicalHeight())
222 continue;
223
224 closestLeaf = leaf;
225 if (point.x() < leaf->m_x + leaf->m_logicalWidth)
226 return leaf;
227 }
228
229 return closestLeaf ? closestLeaf : lastLeaf;
230 }
231
swapItemsInVector(Vector<float> & firstVector,Vector<float> & lastVector,unsigned first,unsigned last)232 static inline void swapItemsInVector(Vector<float>& firstVector, Vector<float>& lastVector, unsigned first, unsigned last)
233 {
234 float temp = firstVector.at(first);
235 firstVector.at(first) = lastVector.at(last);
236 lastVector.at(last) = temp;
237 }
238
swapItemsInLayoutAttributes(SVGTextLayoutAttributes & firstAttributes,SVGTextLayoutAttributes & lastAttributes,unsigned firstPosition,unsigned lastPosition)239 static inline void swapItemsInLayoutAttributes(SVGTextLayoutAttributes& firstAttributes, SVGTextLayoutAttributes& lastAttributes, unsigned firstPosition, unsigned lastPosition)
240 {
241 swapItemsInVector(firstAttributes.xValues(), lastAttributes.xValues(), firstPosition, lastPosition);
242 swapItemsInVector(firstAttributes.yValues(), lastAttributes.yValues(), firstPosition, lastPosition);
243 swapItemsInVector(firstAttributes.dxValues(), lastAttributes.dxValues(), firstPosition, lastPosition);
244 swapItemsInVector(firstAttributes.dyValues(), lastAttributes.dyValues(), firstPosition, lastPosition);
245 swapItemsInVector(firstAttributes.rotateValues(), lastAttributes.rotateValues(), firstPosition, lastPosition);
246 }
247
findFirstAndLastAttributesInVector(Vector<SVGTextLayoutAttributes> & attributes,RenderSVGInlineText * firstContext,RenderSVGInlineText * lastContext,SVGTextLayoutAttributes * & first,SVGTextLayoutAttributes * & last)248 static inline void findFirstAndLastAttributesInVector(Vector<SVGTextLayoutAttributes>& attributes, RenderSVGInlineText* firstContext, RenderSVGInlineText* lastContext,
249 SVGTextLayoutAttributes*& first, SVGTextLayoutAttributes*& last)
250 {
251 first = 0;
252 last = 0;
253
254 unsigned attributesSize = attributes.size();
255 for (unsigned i = 0; i < attributesSize; ++i) {
256 SVGTextLayoutAttributes& current = attributes.at(i);
257 if (!first && firstContext == current.context())
258 first = ¤t;
259 if (!last && lastContext == current.context())
260 last = ¤t;
261 if (first && last)
262 break;
263 }
264
265 ASSERT(first);
266 ASSERT(last);
267 }
268
reverseInlineBoxRangeAndValueListsIfNeeded(void * userData,Vector<InlineBox * >::iterator first,Vector<InlineBox * >::iterator last)269 static inline void reverseInlineBoxRangeAndValueListsIfNeeded(void* userData, Vector<InlineBox*>::iterator first, Vector<InlineBox*>::iterator last)
270 {
271 ASSERT(userData);
272 Vector<SVGTextLayoutAttributes>& attributes = *reinterpret_cast<Vector<SVGTextLayoutAttributes>*>(userData);
273
274 // This is a copy of std::reverse(first, last). It additionally assure that the value lists within the InlineBoxes are reordered as well.
275 while (true) {
276 if (first == last || first == --last)
277 return;
278
279 if (!(*last)->isSVGInlineTextBox() || !(*first)->isSVGInlineTextBox()) {
280 InlineBox* temp = *first;
281 *first = *last;
282 *last = temp;
283 ++first;
284 continue;
285 }
286
287 SVGInlineTextBox* firstTextBox = static_cast<SVGInlineTextBox*>(*first);
288 SVGInlineTextBox* lastTextBox = static_cast<SVGInlineTextBox*>(*last);
289
290 // Reordering is only necessary for BiDi text that is _absolutely_ positioned.
291 if (firstTextBox->len() == 1 && firstTextBox->len() == lastTextBox->len()) {
292 RenderSVGInlineText* firstContext = toRenderSVGInlineText(firstTextBox->textRenderer());
293 RenderSVGInlineText* lastContext = toRenderSVGInlineText(lastTextBox->textRenderer());
294
295 SVGTextLayoutAttributes* firstAttributes = 0;
296 SVGTextLayoutAttributes* lastAttributes = 0;
297 findFirstAndLastAttributesInVector(attributes, firstContext, lastContext, firstAttributes, lastAttributes);
298
299 unsigned firstBoxPosition = firstTextBox->start();
300 unsigned firstBoxEnd = firstTextBox->end();
301
302 unsigned lastBoxPosition = lastTextBox->start();
303 unsigned lastBoxEnd = lastTextBox->end();
304 for (; firstBoxPosition <= firstBoxEnd && lastBoxPosition <= lastBoxEnd; ++lastBoxPosition, ++firstBoxPosition)
305 swapItemsInLayoutAttributes(*firstAttributes, *lastAttributes, firstBoxPosition, lastBoxPosition);
306 }
307
308 InlineBox* temp = *first;
309 *first = *last;
310 *last = temp;
311
312 ++first;
313 }
314 }
315
reorderValueLists(Vector<SVGTextLayoutAttributes> & attributes)316 void SVGRootInlineBox::reorderValueLists(Vector<SVGTextLayoutAttributes>& attributes)
317 {
318 Vector<InlineBox*> leafBoxesInLogicalOrder;
319 collectLeafBoxesInLogicalOrder(leafBoxesInLogicalOrder, reverseInlineBoxRangeAndValueListsIfNeeded, &attributes);
320 }
321
322 } // namespace WebCore
323
324 #endif // ENABLE(SVG)
325