1 /* 2 * Copyright (C) Research In Motion Limited 2010-2011. All rights reserved. 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Library General Public 6 * License as published by the Free Software Foundation; either 7 * version 2 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Library General Public License for more details. 13 * 14 * You should have received a copy of the GNU Library General Public License 15 * along with this library; see the file COPYING.LIB. If not, write to 16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 * Boston, MA 02110-1301, USA. 18 */ 19 20 #include "config.h" 21 22 #include "core/rendering/svg/SVGTextLayoutAttributesBuilder.h" 23 24 #include "core/rendering/svg/RenderSVGInlineText.h" 25 #include "core/rendering/svg/RenderSVGText.h" 26 #include "core/svg/SVGTextPositioningElement.h" 27 28 namespace WebCore { 29 30 SVGTextLayoutAttributesBuilder::SVGTextLayoutAttributesBuilder() 31 : m_textLength(0) 32 { 33 } 34 35 void SVGTextLayoutAttributesBuilder::buildLayoutAttributesForTextRenderer(RenderSVGInlineText* text) 36 { 37 ASSERT(text); 38 39 RenderSVGText* textRoot = RenderSVGText::locateRenderSVGTextAncestor(text); 40 if (!textRoot) 41 return; 42 43 if (m_textPositions.isEmpty()) { 44 m_characterDataMap.clear(); 45 46 m_textLength = 0; 47 UChar lastCharacter = ' '; 48 collectTextPositioningElements(textRoot, lastCharacter); 49 50 if (!m_textLength) 51 return; 52 53 buildCharacterDataMap(textRoot); 54 } 55 56 m_metricsBuilder.buildMetricsAndLayoutAttributes(textRoot, text, m_characterDataMap); 57 } 58 59 bool SVGTextLayoutAttributesBuilder::buildLayoutAttributesForForSubtree(RenderSVGText* textRoot) 60 { 61 ASSERT(textRoot); 62 63 m_characterDataMap.clear(); 64 65 if (m_textPositions.isEmpty()) { 66 m_textLength = 0; 67 UChar lastCharacter = ' '; 68 collectTextPositioningElements(textRoot, lastCharacter); 69 } 70 71 if (!m_textLength) 72 return false; 73 74 buildCharacterDataMap(textRoot); 75 m_metricsBuilder.buildMetricsAndLayoutAttributes(textRoot, 0, m_characterDataMap); 76 return true; 77 } 78 79 void SVGTextLayoutAttributesBuilder::rebuildMetricsForTextRenderer(RenderSVGInlineText* text) 80 { 81 ASSERT(text); 82 m_metricsBuilder.measureTextRenderer(text); 83 } 84 85 static inline void processRenderSVGInlineText(RenderSVGInlineText* text, unsigned& atCharacter, UChar& lastCharacter) 86 { 87 if (text->style()->whiteSpace() == PRE) { 88 atCharacter += text->textLength(); 89 return; 90 } 91 92 unsigned textLength = text->textLength(); 93 for (unsigned textPosition = 0; textPosition < textLength; ++textPosition) { 94 UChar currentCharacter = text->characterAt(textPosition); 95 if (currentCharacter == ' ' && lastCharacter == ' ') 96 continue; 97 98 lastCharacter = currentCharacter; 99 ++atCharacter; 100 } 101 } 102 103 void SVGTextLayoutAttributesBuilder::collectTextPositioningElements(RenderObject* start, UChar& lastCharacter) 104 { 105 ASSERT(!start->isSVGText() || m_textPositions.isEmpty()); 106 107 for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) { 108 if (child->isSVGInlineText()) { 109 processRenderSVGInlineText(toRenderSVGInlineText(child), m_textLength, lastCharacter); 110 continue; 111 } 112 113 if (!child->isSVGInline()) 114 continue; 115 116 SVGTextPositioningElement* element = SVGTextPositioningElement::elementFromRenderer(child); 117 unsigned atPosition = m_textPositions.size(); 118 if (element) 119 m_textPositions.append(TextPosition(element, m_textLength)); 120 121 collectTextPositioningElements(child, lastCharacter); 122 123 if (!element) 124 continue; 125 126 // Update text position, after we're back from recursion. 127 TextPosition& position = m_textPositions[atPosition]; 128 ASSERT(!position.length); 129 position.length = m_textLength - position.start; 130 } 131 } 132 133 void SVGTextLayoutAttributesBuilder::buildCharacterDataMap(RenderSVGText* textRoot) 134 { 135 SVGTextPositioningElement* outermostTextElement = SVGTextPositioningElement::elementFromRenderer(textRoot); 136 ASSERT(outermostTextElement); 137 138 // Grab outermost <text> element value lists and insert them in the character data map. 139 TextPosition wholeTextPosition(outermostTextElement, 0, m_textLength); 140 fillCharacterDataMap(wholeTextPosition); 141 142 // Handle x/y default attributes. 143 SVGCharacterDataMap::iterator it = m_characterDataMap.find(1); 144 if (it == m_characterDataMap.end()) { 145 SVGCharacterData data; 146 data.x = 0; 147 data.y = 0; 148 m_characterDataMap.set(1, data); 149 } else { 150 SVGCharacterData& data = it->value; 151 if (data.x == SVGTextLayoutAttributes::emptyValue()) 152 data.x = 0; 153 if (data.y == SVGTextLayoutAttributes::emptyValue()) 154 data.y = 0; 155 } 156 157 // Fill character data map using child text positioning elements in top-down order. 158 unsigned size = m_textPositions.size(); 159 for (unsigned i = 0; i < size; ++i) 160 fillCharacterDataMap(m_textPositions[i]); 161 } 162 163 static inline void updateCharacterData(unsigned i, float& lastRotation, SVGCharacterData& data, const SVGLengthContext& lengthContext, const SVGLengthList* xList, const SVGLengthList* yList, const SVGLengthList* dxList, const SVGLengthList* dyList, const SVGNumberList* rotateList) 164 { 165 if (xList) 166 data.x = xList->at(i).value(lengthContext); 167 if (yList) 168 data.y = yList->at(i).value(lengthContext); 169 if (dxList) 170 data.dx = dxList->at(i).value(lengthContext); 171 if (dyList) 172 data.dy = dyList->at(i).value(lengthContext); 173 if (rotateList) { 174 data.rotate = rotateList->at(i).value(); 175 lastRotation = data.rotate; 176 } 177 } 178 179 void SVGTextLayoutAttributesBuilder::fillCharacterDataMap(const TextPosition& position) 180 { 181 const SVGLengthList& xList = position.element->xCurrentValue(); 182 const SVGLengthList& yList = position.element->yCurrentValue(); 183 const SVGLengthList& dxList = position.element->dxCurrentValue(); 184 const SVGLengthList& dyList = position.element->dyCurrentValue(); 185 const SVGNumberList& rotateList = position.element->rotateCurrentValue(); 186 187 unsigned xListSize = xList.size(); 188 unsigned yListSize = yList.size(); 189 unsigned dxListSize = dxList.size(); 190 unsigned dyListSize = dyList.size(); 191 unsigned rotateListSize = rotateList.size(); 192 if (!xListSize && !yListSize && !dxListSize && !dyListSize && !rotateListSize) 193 return; 194 195 float lastRotation = SVGTextLayoutAttributes::emptyValue(); 196 SVGLengthContext lengthContext(position.element); 197 for (unsigned i = 0; i < position.length; ++i) { 198 const SVGLengthList* xListPtr = i < xListSize ? &xList : 0; 199 const SVGLengthList* yListPtr = i < yListSize ? &yList : 0; 200 const SVGLengthList* dxListPtr = i < dxListSize ? &dxList : 0; 201 const SVGLengthList* dyListPtr = i < dyListSize ? &dyList : 0; 202 const SVGNumberList* rotateListPtr = i < rotateListSize ? &rotateList : 0; 203 if (!xListPtr && !yListPtr && !dxListPtr && !dyListPtr && !rotateListPtr) 204 break; 205 206 SVGCharacterDataMap::iterator it = m_characterDataMap.find(position.start + i + 1); 207 if (it == m_characterDataMap.end()) { 208 SVGCharacterData data; 209 updateCharacterData(i, lastRotation, data, lengthContext, xListPtr, yListPtr, dxListPtr, dyListPtr, rotateListPtr); 210 m_characterDataMap.set(position.start + i + 1, data); 211 continue; 212 } 213 214 updateCharacterData(i, lastRotation, it->value, lengthContext, xListPtr, yListPtr, dxListPtr, dyListPtr, rotateListPtr); 215 } 216 217 // The last rotation value always spans the whole scope. 218 if (lastRotation == SVGTextLayoutAttributes::emptyValue()) 219 return; 220 221 for (unsigned i = rotateList.size(); i < position.length; ++i) { 222 SVGCharacterDataMap::iterator it = m_characterDataMap.find(position.start + i + 1); 223 if (it == m_characterDataMap.end()) { 224 SVGCharacterData data; 225 data.rotate = lastRotation; 226 m_characterDataMap.set(position.start + i + 1, data); 227 continue; 228 } 229 230 it->value.rotate = lastRotation; 231 } 232 } 233 234 } 235