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/rendering/svg/SVGTextMetricsBuilder.h" 27 #include "core/svg/SVGTextPositioningElement.h" 28 29 namespace blink { 30 31 SVGTextLayoutAttributesBuilder::SVGTextLayoutAttributesBuilder() 32 : m_textLength(0) 33 { 34 } 35 36 void SVGTextLayoutAttributesBuilder::buildLayoutAttributesForTextRenderer(RenderSVGInlineText* text) 37 { 38 ASSERT(text); 39 40 RenderSVGText* textRoot = RenderSVGText::locateRenderSVGTextAncestor(text); 41 if (!textRoot) 42 return; 43 44 if (m_textPositions.isEmpty()) { 45 m_characterDataMap.clear(); 46 47 m_textLength = 0; 48 UChar lastCharacter = ' '; 49 collectTextPositioningElements(textRoot, lastCharacter); 50 51 if (!m_textLength) 52 return; 53 54 buildCharacterDataMap(textRoot); 55 } 56 57 SVGTextMetricsBuilder::buildMetricsAndLayoutAttributes(textRoot, text, m_characterDataMap); 58 } 59 60 bool SVGTextLayoutAttributesBuilder::buildLayoutAttributesForForSubtree(RenderSVGText* textRoot) 61 { 62 ASSERT(textRoot); 63 64 m_characterDataMap.clear(); 65 66 if (m_textPositions.isEmpty()) { 67 m_textLength = 0; 68 UChar lastCharacter = ' '; 69 collectTextPositioningElements(textRoot, lastCharacter); 70 } 71 72 if (!m_textLength) 73 return false; 74 75 buildCharacterDataMap(textRoot); 76 SVGTextMetricsBuilder::buildMetricsAndLayoutAttributes(textRoot, 0, m_characterDataMap); 77 return true; 78 } 79 80 void SVGTextLayoutAttributesBuilder::rebuildMetricsForTextRenderer(RenderSVGInlineText* text) 81 { 82 ASSERT(text); 83 SVGTextMetricsBuilder::measureTextRenderer(text); 84 } 85 86 static inline void processRenderSVGInlineText(RenderSVGInlineText* text, unsigned& atCharacter, UChar& lastCharacter) 87 { 88 if (text->style()->whiteSpace() == PRE) { 89 atCharacter += text->textLength(); 90 return; 91 } 92 93 unsigned textLength = text->textLength(); 94 for (unsigned textPosition = 0; textPosition < textLength; ++textPosition) { 95 UChar currentCharacter = text->characterAt(textPosition); 96 if (currentCharacter == ' ' && lastCharacter == ' ') 97 continue; 98 99 lastCharacter = currentCharacter; 100 ++atCharacter; 101 } 102 } 103 104 void SVGTextLayoutAttributesBuilder::collectTextPositioningElements(RenderObject* start, UChar& lastCharacter) 105 { 106 ASSERT(!start->isSVGText() || m_textPositions.isEmpty()); 107 108 for (RenderObject* child = start->slowFirstChild(); child; child = child->nextSibling()) { 109 if (child->isSVGInlineText()) { 110 processRenderSVGInlineText(toRenderSVGInlineText(child), m_textLength, lastCharacter); 111 continue; 112 } 113 114 if (!child->isSVGInline()) 115 continue; 116 117 SVGTextPositioningElement* element = SVGTextPositioningElement::elementFromRenderer(child); 118 unsigned atPosition = m_textPositions.size(); 119 if (element) 120 m_textPositions.append(TextPosition(element, m_textLength)); 121 122 collectTextPositioningElements(child, lastCharacter); 123 124 if (!element) 125 continue; 126 127 // Update text position, after we're back from recursion. 128 TextPosition& position = m_textPositions[atPosition]; 129 ASSERT(!position.length); 130 position.length = m_textLength - position.start; 131 } 132 } 133 134 void SVGTextLayoutAttributesBuilder::buildCharacterDataMap(RenderSVGText* textRoot) 135 { 136 SVGTextPositioningElement* outermostTextElement = SVGTextPositioningElement::elementFromRenderer(textRoot); 137 ASSERT(outermostTextElement); 138 139 // Grab outermost <text> element value lists and insert them in the character data map. 140 TextPosition wholeTextPosition(outermostTextElement, 0, m_textLength); 141 fillCharacterDataMap(wholeTextPosition); 142 143 // Handle x/y default attributes. 144 SVGCharacterDataMap::iterator it = m_characterDataMap.find(1); 145 if (it == m_characterDataMap.end()) { 146 SVGCharacterData data; 147 data.x = 0; 148 data.y = 0; 149 m_characterDataMap.set(1, data); 150 } else { 151 SVGCharacterData& data = it->value; 152 if (data.x == SVGTextLayoutAttributes::emptyValue()) 153 data.x = 0; 154 if (data.y == SVGTextLayoutAttributes::emptyValue()) 155 data.y = 0; 156 } 157 158 // Fill character data map using child text positioning elements in top-down order. 159 unsigned size = m_textPositions.size(); 160 for (unsigned i = 0; i < size; ++i) 161 fillCharacterDataMap(m_textPositions[i]); 162 } 163 164 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) 165 { 166 if (xList) 167 data.x = xList->at(i)->value(lengthContext); 168 if (yList) 169 data.y = yList->at(i)->value(lengthContext); 170 if (dxList) 171 data.dx = dxList->at(i)->value(lengthContext); 172 if (dyList) 173 data.dy = dyList->at(i)->value(lengthContext); 174 if (rotateList) { 175 data.rotate = rotateList->at(i)->value(); 176 lastRotation = data.rotate; 177 } 178 } 179 180 void SVGTextLayoutAttributesBuilder::fillCharacterDataMap(const TextPosition& position) 181 { 182 RefPtr<SVGLengthList> xList = position.element->x()->currentValue(); 183 RefPtr<SVGLengthList> yList = position.element->y()->currentValue(); 184 RefPtr<SVGLengthList> dxList = position.element->dx()->currentValue(); 185 RefPtr<SVGLengthList> dyList = position.element->dy()->currentValue(); 186 RefPtr<SVGNumberList> rotateList = position.element->rotate()->currentValue(); 187 188 unsigned xListSize = xList->length(); 189 unsigned yListSize = yList->length(); 190 unsigned dxListSize = dxList->length(); 191 unsigned dyListSize = dyList->length(); 192 unsigned rotateListSize = rotateList->length(); 193 if (!xListSize && !yListSize && !dxListSize && !dyListSize && !rotateListSize) 194 return; 195 196 float lastRotation = SVGTextLayoutAttributes::emptyValue(); 197 SVGLengthContext lengthContext(position.element); 198 for (unsigned i = 0; i < position.length; ++i) { 199 const SVGLengthList* xListPtr = i < xListSize ? xList.get() : 0; 200 const SVGLengthList* yListPtr = i < yListSize ? yList.get() : 0; 201 const SVGLengthList* dxListPtr = i < dxListSize ? dxList.get() : 0; 202 const SVGLengthList* dyListPtr = i < dyListSize ? dyList.get() : 0; 203 const SVGNumberList* rotateListPtr = i < rotateListSize ? rotateList.get() : 0; 204 if (!xListPtr && !yListPtr && !dxListPtr && !dyListPtr && !rotateListPtr) 205 break; 206 207 SVGCharacterDataMap::iterator it = m_characterDataMap.find(position.start + i + 1); 208 if (it == m_characterDataMap.end()) { 209 SVGCharacterData data; 210 updateCharacterData(i, lastRotation, data, lengthContext, xListPtr, yListPtr, dxListPtr, dyListPtr, rotateListPtr); 211 m_characterDataMap.set(position.start + i + 1, data); 212 continue; 213 } 214 215 updateCharacterData(i, lastRotation, it->value, lengthContext, xListPtr, yListPtr, dxListPtr, dyListPtr, rotateListPtr); 216 } 217 218 // The last rotation value always spans the whole scope. 219 if (lastRotation == SVGTextLayoutAttributes::emptyValue()) 220 return; 221 222 for (unsigned i = rotateList->length(); i < position.length; ++i) { 223 SVGCharacterDataMap::iterator it = m_characterDataMap.find(position.start + i + 1); 224 if (it == m_characterDataMap.end()) { 225 SVGCharacterData data; 226 data.rotate = lastRotation; 227 m_characterDataMap.set(position.start + i + 1, data); 228 continue; 229 } 230 231 it->value.rotate = lastRotation; 232 } 233 } 234 235 } 236