1 /* 2 * Copyright (C) 2006 Oliver Hunt <ojh16 (at) student.canterbury.ac.nz> 3 * Copyright (C) 2006 Apple Computer Inc. 4 * Copyright (C) 2007 Nikolas Zimmermann <zimmermann (at) 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 "config.h" 25 #include "core/rendering/svg/SVGRootInlineBox.h" 26 27 #include "SVGNames.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" 32 #include "core/rendering/svg/SVGRenderingContext.h" 33 34 namespace WebCore { 35 36 void SVGRootInlineBox::paint(PaintInfo& paintInfo, const LayoutPoint&, LayoutUnit, LayoutUnit) 37 { 38 ASSERT(paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection); 39 ASSERT(!paintInfo.context->paintingDisabled()); 40 41 RenderObject* boxRenderer = renderer(); 42 ASSERT(boxRenderer); 43 44 bool isPrinting = renderer()->document().printing(); 45 bool hasSelection = !isPrinting && selectionState() != RenderObject::SelectionNone; 46 47 PaintInfo childPaintInfo(paintInfo); 48 if (hasSelection) { 49 for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) { 50 if (child->isSVGInlineTextBox()) 51 toSVGInlineTextBox(child)->paintSelectionBackground(childPaintInfo); 52 else if (child->isSVGInlineFlowBox()) 53 toSVGInlineFlowBox(child)->paintSelectionBackground(childPaintInfo); 54 } 55 } 56 57 SVGRenderingContext renderingContext(boxRenderer, paintInfo, SVGRenderingContext::SaveGraphicsContext); 58 if (renderingContext.isRenderingPrepared()) { 59 for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) { 60 if (child->isSVGInlineTextBox()) 61 SVGInlineFlowBox::computeTextMatchMarkerRectForRenderer(toRenderSVGInlineText(toSVGInlineTextBox(child)->textRenderer())); 62 63 child->paint(paintInfo, LayoutPoint(), 0, 0); 64 } 65 } 66 } 67 68 void SVGRootInlineBox::markDirty(bool dirty) 69 { 70 if (dirty) 71 for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) 72 child->markDirty(true); 73 RootInlineBox::markDirty(dirty); 74 } 75 76 void SVGRootInlineBox::computePerCharacterLayoutInformation() 77 { 78 RenderSVGText* textRoot = toRenderSVGText(block()); 79 ASSERT(textRoot); 80 81 Vector<SVGTextLayoutAttributes*>& layoutAttributes = textRoot->layoutAttributes(); 82 if (layoutAttributes.isEmpty()) 83 return; 84 85 if (textRoot->needsReordering()) 86 reorderValueLists(layoutAttributes); 87 88 // Perform SVG text layout phase two (see SVGTextLayoutEngine for details). 89 SVGTextLayoutEngine characterLayout(layoutAttributes); 90 layoutCharactersInTextBoxes(this, characterLayout); 91 92 // Perform SVG text layout phase three (see SVGTextChunkBuilder for details). 93 characterLayout.finishLayout(); 94 95 // Perform SVG text layout phase four 96 // Position & resize all SVGInlineText/FlowBoxes in the inline box tree, resize the root box as well as the RenderSVGText parent block. 97 FloatRect childRect; 98 layoutChildBoxes(this, &childRect); 99 layoutRootBox(childRect); 100 } 101 102 void SVGRootInlineBox::layoutCharactersInTextBoxes(InlineFlowBox* start, SVGTextLayoutEngine& characterLayout) 103 { 104 for (InlineBox* child = start->firstChild(); child; child = child->nextOnLine()) { 105 if (child->isSVGInlineTextBox()) { 106 ASSERT(child->renderer()); 107 ASSERT(child->renderer()->isSVGInlineText()); 108 characterLayout.layoutInlineTextBox(toSVGInlineTextBox(child)); 109 } else { 110 // Skip generated content. 111 Node* node = child->renderer()->node(); 112 if (!node) 113 continue; 114 115 SVGInlineFlowBox* flowBox = toSVGInlineFlowBox(child); 116 bool isTextPath = node->hasTagName(SVGNames::textPathTag); 117 if (isTextPath) { 118 // Build text chunks for all <textPath> children, using the line layout algorithm. 119 // This is needeed as text-anchor is just an additional startOffset for text paths. 120 SVGTextLayoutEngine lineLayout(characterLayout.layoutAttributes()); 121 layoutCharactersInTextBoxes(flowBox, lineLayout); 122 123 characterLayout.beginTextPathLayout(child->renderer(), lineLayout); 124 } 125 126 layoutCharactersInTextBoxes(flowBox, characterLayout); 127 128 if (isTextPath) 129 characterLayout.endTextPathLayout(); 130 } 131 } 132 } 133 134 void SVGRootInlineBox::layoutChildBoxes(InlineFlowBox* start, FloatRect* childRect) 135 { 136 for (InlineBox* child = start->firstChild(); child; child = child->nextOnLine()) { 137 FloatRect boxRect; 138 if (child->isSVGInlineTextBox()) { 139 ASSERT(child->renderer()); 140 ASSERT(child->renderer()->isSVGInlineText()); 141 142 SVGInlineTextBox* textBox = toSVGInlineTextBox(child); 143 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 SVGInlineFlowBox* flowBox = toSVGInlineFlowBox(child); 154 layoutChildBoxes(flowBox); 155 156 boxRect = flowBox->calculateBoundaries(); 157 flowBox->setX(boxRect.x()); 158 flowBox->setY(boxRect.y()); 159 flowBox->setLogicalWidth(boxRect.width()); 160 flowBox->setLogicalHeight(boxRect.height()); 161 } 162 if (childRect) 163 childRect->unite(boxRect); 164 } 165 } 166 167 void SVGRootInlineBox::layoutRootBox(const FloatRect& childRect) 168 { 169 RenderBlockFlow* parentBlock = block(); 170 ASSERT(parentBlock); 171 172 // Finally, assign the root block position, now that all content is laid out. 173 LayoutRect boundingRect = enclosingLayoutRect(childRect); 174 parentBlock->setLocation(boundingRect.location()); 175 parentBlock->setSize(boundingRect.size()); 176 177 // Position all children relative to the parent block. 178 for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) { 179 // Skip generated content. 180 if (!child->renderer()->node()) 181 continue; 182 child->adjustPosition(-childRect.x(), -childRect.y()); 183 } 184 185 // Position ourselves. 186 setX(0); 187 setY(0); 188 setLogicalWidth(childRect.width()); 189 setLogicalHeight(childRect.height()); 190 setLineTopBottomPositions(0, boundingRect.height(), 0, boundingRect.height()); 191 } 192 193 InlineBox* SVGRootInlineBox::closestLeafChildForPosition(const LayoutPoint& point) 194 { 195 InlineBox* firstLeaf = firstLeafChild(); 196 InlineBox* lastLeaf = lastLeafChild(); 197 if (firstLeaf == lastLeaf) 198 return firstLeaf; 199 200 // FIXME: Check for vertical text! 201 InlineBox* closestLeaf = 0; 202 for (InlineBox* leaf = firstLeaf; leaf; leaf = leaf->nextLeafChild()) { 203 if (!leaf->isSVGInlineTextBox()) 204 continue; 205 if (point.y() < leaf->y()) 206 continue; 207 if (point.y() > leaf->y() + leaf->virtualLogicalHeight()) 208 continue; 209 210 closestLeaf = leaf; 211 if (point.x() < leaf->left() + leaf->logicalWidth()) 212 return leaf; 213 } 214 215 return closestLeaf ? closestLeaf : lastLeaf; 216 } 217 218 static inline void swapItemsInLayoutAttributes(SVGTextLayoutAttributes* firstAttributes, SVGTextLayoutAttributes* lastAttributes, unsigned firstPosition, unsigned lastPosition) 219 { 220 SVGCharacterDataMap::iterator itFirst = firstAttributes->characterDataMap().find(firstPosition + 1); 221 SVGCharacterDataMap::iterator itLast = lastAttributes->characterDataMap().find(lastPosition + 1); 222 bool firstPresent = itFirst != firstAttributes->characterDataMap().end(); 223 bool lastPresent = itLast != lastAttributes->characterDataMap().end(); 224 if (!firstPresent && !lastPresent) 225 return; 226 227 if (firstPresent && lastPresent) { 228 std::swap(itFirst->value, itLast->value); 229 return; 230 } 231 232 if (firstPresent && !lastPresent) { 233 lastAttributes->characterDataMap().set(lastPosition + 1, itFirst->value); 234 return; 235 } 236 237 // !firstPresent && lastPresent 238 firstAttributes->characterDataMap().set(firstPosition + 1, itLast->value); 239 } 240 241 static inline void findFirstAndLastAttributesInVector(Vector<SVGTextLayoutAttributes*>& attributes, RenderSVGInlineText* firstContext, RenderSVGInlineText* lastContext, 242 SVGTextLayoutAttributes*& first, SVGTextLayoutAttributes*& last) 243 { 244 first = 0; 245 last = 0; 246 247 unsigned attributesSize = attributes.size(); 248 for (unsigned i = 0; i < attributesSize; ++i) { 249 SVGTextLayoutAttributes* current = attributes[i]; 250 if (!first && firstContext == current->context()) 251 first = current; 252 if (!last && lastContext == current->context()) 253 last = current; 254 if (first && last) 255 break; 256 } 257 258 ASSERT(first); 259 ASSERT(last); 260 } 261 262 static inline void reverseInlineBoxRangeAndValueListsIfNeeded(void* userData, Vector<InlineBox*>::iterator first, Vector<InlineBox*>::iterator last) 263 { 264 ASSERT(userData); 265 Vector<SVGTextLayoutAttributes*>& attributes = *reinterpret_cast<Vector<SVGTextLayoutAttributes*>*>(userData); 266 267 // 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. 268 while (true) { 269 if (first == last || first == --last) 270 return; 271 272 if (!(*last)->isSVGInlineTextBox() || !(*first)->isSVGInlineTextBox()) { 273 InlineBox* temp = *first; 274 *first = *last; 275 *last = temp; 276 ++first; 277 continue; 278 } 279 280 SVGInlineTextBox* firstTextBox = toSVGInlineTextBox(*first); 281 SVGInlineTextBox* lastTextBox = toSVGInlineTextBox(*last); 282 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()); 287 288 SVGTextLayoutAttributes* firstAttributes = 0; 289 SVGTextLayoutAttributes* lastAttributes = 0; 290 findFirstAndLastAttributesInVector(attributes, firstContext, lastContext, firstAttributes, lastAttributes); 291 swapItemsInLayoutAttributes(firstAttributes, lastAttributes, firstTextBox->start(), lastTextBox->start()); 292 } 293 294 InlineBox* temp = *first; 295 *first = *last; 296 *last = temp; 297 298 ++first; 299 } 300 } 301 302 void SVGRootInlineBox::reorderValueLists(Vector<SVGTextLayoutAttributes*>& attributes) 303 { 304 Vector<InlineBox*> leafBoxesInLogicalOrder; 305 collectLeafBoxesInLogicalOrder(leafBoxesInLogicalOrder, reverseInlineBoxRangeAndValueListsIfNeeded, &attributes); 306 } 307 308 } // namespace WebCore 309