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