1 /* 2 * Copyright (C) 2004, 2005, 2007, 2008 Nikolas Zimmermann <zimmermann (at) kde.org> 3 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Rob Buis <buis (at) kde.org> 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Library General Public 7 * License as published by the Free Software Foundation; either 8 * version 2 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Library General Public License for more details. 14 * 15 * You should have received a copy of the GNU Library General Public License 16 * along with this library; see the file COPYING.LIB. If not, write to 17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18 * Boston, MA 02110-1301, USA. 19 */ 20 21 #include "config.h" 22 23 #if ENABLE(SVG) 24 #include "SVGTextContentElement.h" 25 26 #include "CSSPropertyNames.h" 27 #include "CSSValueKeywords.h" 28 #include "Frame.h" 29 #include "RenderObject.h" 30 #include "RenderSVGResource.h" 31 #include "SVGDocumentExtensions.h" 32 #include "SVGNames.h" 33 #include "SVGTextQuery.h" 34 #include "SelectionController.h" 35 #include "XMLNames.h" 36 37 namespace WebCore { 38 39 // Animated property definitions 40 DEFINE_ANIMATED_ENUMERATION(SVGTextContentElement, SVGNames::lengthAdjustAttr, LengthAdjust, lengthAdjust) 41 DEFINE_ANIMATED_BOOLEAN(SVGTextContentElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired) 42 43 SVGTextContentElement::SVGTextContentElement(const QualifiedName& tagName, Document* document) 44 : SVGStyledElement(tagName, document) 45 , m_specifiedTextLength(LengthModeOther) 46 , m_textLength(LengthModeOther) 47 , m_lengthAdjust(LENGTHADJUST_SPACING) 48 { 49 } 50 51 void SVGTextContentElement::synchronizeTextLength() 52 { 53 if (!m_textLength.shouldSynchronize) 54 return; 55 AtomicString value(SVGPropertyTraits<SVGLength>::toString(m_specifiedTextLength)); 56 SVGAnimatedPropertySynchronizer<true>::synchronize(this, SVGNames::textLengthAttr, value); 57 } 58 59 PassRefPtr<SVGAnimatedLength> SVGTextContentElement::textLengthAnimated() 60 { 61 DEFINE_STATIC_LOCAL(SVGLength, defaultTextLength, (LengthModeOther)); 62 if (m_specifiedTextLength == defaultTextLength) { 63 ExceptionCode ec = 0; 64 m_textLength.value.newValueSpecifiedUnits(LengthTypeNumber, getComputedTextLength(), ec); 65 ASSERT(!ec); 66 } 67 68 m_textLength.shouldSynchronize = true; 69 return SVGAnimatedProperty::lookupOrCreateWrapper<SVGAnimatedLength, SVGLength>(this, SVGNames::textLengthAttr, SVGNames::textLengthAttr.localName(), m_textLength.value); 70 } 71 72 unsigned SVGTextContentElement::getNumberOfChars() const 73 { 74 document()->updateLayoutIgnorePendingStylesheets(); 75 return SVGTextQuery(renderer()).numberOfCharacters(); 76 } 77 78 float SVGTextContentElement::getComputedTextLength() const 79 { 80 document()->updateLayoutIgnorePendingStylesheets(); 81 return SVGTextQuery(renderer()).textLength(); 82 } 83 84 float SVGTextContentElement::getSubStringLength(unsigned charnum, unsigned nchars, ExceptionCode& ec) const 85 { 86 document()->updateLayoutIgnorePendingStylesheets(); 87 88 unsigned numberOfChars = getNumberOfChars(); 89 if (charnum >= numberOfChars) { 90 ec = INDEX_SIZE_ERR; 91 return 0.0f; 92 } 93 94 return SVGTextQuery(renderer()).subStringLength(charnum, nchars); 95 } 96 97 FloatPoint SVGTextContentElement::getStartPositionOfChar(unsigned charnum, ExceptionCode& ec) const 98 { 99 document()->updateLayoutIgnorePendingStylesheets(); 100 101 if (charnum > getNumberOfChars()) { 102 ec = INDEX_SIZE_ERR; 103 return FloatPoint(); 104 } 105 106 return SVGTextQuery(renderer()).startPositionOfCharacter(charnum); 107 } 108 109 FloatPoint SVGTextContentElement::getEndPositionOfChar(unsigned charnum, ExceptionCode& ec) const 110 { 111 document()->updateLayoutIgnorePendingStylesheets(); 112 113 if (charnum > getNumberOfChars()) { 114 ec = INDEX_SIZE_ERR; 115 return FloatPoint(); 116 } 117 118 return SVGTextQuery(renderer()).endPositionOfCharacter(charnum); 119 } 120 121 FloatRect SVGTextContentElement::getExtentOfChar(unsigned charnum, ExceptionCode& ec) const 122 { 123 document()->updateLayoutIgnorePendingStylesheets(); 124 125 if (charnum > getNumberOfChars()) { 126 ec = INDEX_SIZE_ERR; 127 return FloatRect(); 128 } 129 130 return SVGTextQuery(renderer()).extentOfCharacter(charnum); 131 } 132 133 float SVGTextContentElement::getRotationOfChar(unsigned charnum, ExceptionCode& ec) const 134 { 135 document()->updateLayoutIgnorePendingStylesheets(); 136 137 if (charnum > getNumberOfChars()) { 138 ec = INDEX_SIZE_ERR; 139 return 0.0f; 140 } 141 142 return SVGTextQuery(renderer()).rotationOfCharacter(charnum); 143 } 144 145 int SVGTextContentElement::getCharNumAtPosition(const FloatPoint& point) const 146 { 147 document()->updateLayoutIgnorePendingStylesheets(); 148 return SVGTextQuery(renderer()).characterNumberAtPosition(point); 149 } 150 151 void SVGTextContentElement::selectSubString(unsigned charnum, unsigned nchars, ExceptionCode& ec) const 152 { 153 unsigned numberOfChars = getNumberOfChars(); 154 if (charnum >= numberOfChars) { 155 ec = INDEX_SIZE_ERR; 156 return; 157 } 158 159 if (nchars > numberOfChars - charnum) 160 nchars = numberOfChars - charnum; 161 162 ASSERT(document()); 163 ASSERT(document()->frame()); 164 165 SelectionController* controller = document()->frame()->selection(); 166 if (!controller) 167 return; 168 169 // Find selection start 170 VisiblePosition start(firstPositionInNode(const_cast<SVGTextContentElement*>(this))); 171 for (unsigned i = 0; i < charnum; ++i) 172 start = start.next(); 173 174 // Find selection end 175 VisiblePosition end(start); 176 for (unsigned i = 0; i < nchars; ++i) 177 end = end.next(); 178 179 controller->setSelection(VisibleSelection(start, end)); 180 } 181 182 void SVGTextContentElement::parseMappedAttribute(Attribute* attr) 183 { 184 if (attr->name() == SVGNames::lengthAdjustAttr) { 185 if (attr->value() == "spacing") 186 setLengthAdjustBaseValue(LENGTHADJUST_SPACING); 187 else if (attr->value() == "spacingAndGlyphs") 188 setLengthAdjustBaseValue(LENGTHADJUST_SPACINGANDGLYPHS); 189 } else if (attr->name() == SVGNames::textLengthAttr) { 190 m_textLength.value = SVGLength(LengthModeOther, attr->value()); 191 if (m_textLength.value.value(this) < 0) 192 document()->accessSVGExtensions()->reportError("A negative value for text attribute <textLength> is not allowed"); 193 } else { 194 if (SVGTests::parseMappedAttribute(attr)) 195 return; 196 if (SVGLangSpace::parseMappedAttribute(attr)) { 197 if (attr->name().matches(XMLNames::spaceAttr)) { 198 DEFINE_STATIC_LOCAL(const AtomicString, preserveString, ("preserve")); 199 200 if (attr->value() == preserveString) 201 addCSSProperty(attr, CSSPropertyWhiteSpace, CSSValuePre); 202 else 203 addCSSProperty(attr, CSSPropertyWhiteSpace, CSSValueNowrap); 204 } 205 return; 206 } 207 if (SVGExternalResourcesRequired::parseMappedAttribute(attr)) 208 return; 209 210 SVGStyledElement::parseMappedAttribute(attr); 211 } 212 } 213 214 void SVGTextContentElement::synchronizeProperty(const QualifiedName& attrName) 215 { 216 SVGStyledElement::synchronizeProperty(attrName); 217 218 if (attrName == anyQName()) { 219 synchronizeLengthAdjust(); 220 synchronizeTextLength(); 221 synchronizeExternalResourcesRequired(); 222 SVGTests::synchronizeProperties(this, attrName); 223 return; 224 } 225 226 if (attrName == SVGNames::lengthAdjustAttr) 227 synchronizeLengthAdjust(); 228 else if (attrName == SVGNames::textLengthAttr) 229 synchronizeTextLength(); 230 else if (SVGExternalResourcesRequired::isKnownAttribute(attrName)) 231 synchronizeExternalResourcesRequired(); 232 else if (SVGTests::isKnownAttribute(attrName)) 233 SVGTests::synchronizeProperties(this, attrName); 234 } 235 236 void SVGTextContentElement::svgAttributeChanged(const QualifiedName& attrName) 237 { 238 SVGStyledElement::svgAttributeChanged(attrName); 239 240 if (SVGTests::handleAttributeChange(this, attrName)) 241 return; 242 243 if (attrName == SVGNames::textLengthAttr) 244 m_specifiedTextLength = m_textLength.value; 245 246 RenderObject* renderer = this->renderer(); 247 if (!renderer) 248 return; 249 250 if (attrName == SVGNames::textLengthAttr 251 || attrName == SVGNames::lengthAdjustAttr) 252 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer); 253 } 254 255 void SVGTextContentElement::fillPassedAttributeToPropertyTypeMap(AttributeToPropertyTypeMap& attributeToPropertyTypeMap) 256 { 257 SVGStyledElement::fillPassedAttributeToPropertyTypeMap(attributeToPropertyTypeMap); 258 259 attributeToPropertyTypeMap.set(SVGNames::textLengthAttr, AnimatedLength); 260 attributeToPropertyTypeMap.set(SVGNames::lengthAdjustAttr, AnimatedEnumeration); 261 } 262 263 bool SVGTextContentElement::selfHasRelativeLengths() const 264 { 265 // Any element of the <text> subtree is advertized as using relative lengths. 266 // On any window size change, we have to relayout the text subtree, as the 267 // effective 'on-screen' font size may change. 268 return true; 269 } 270 271 SVGTextContentElement* SVGTextContentElement::elementFromRenderer(RenderObject* renderer) 272 { 273 if (!renderer) 274 return 0; 275 276 if (!renderer->isSVGText() && !renderer->isSVGInline()) 277 return 0; 278 279 Node* node = renderer->node(); 280 ASSERT(node); 281 ASSERT(node->isSVGElement()); 282 283 if (!node->hasTagName(SVGNames::textTag) 284 && !node->hasTagName(SVGNames::tspanTag) 285 #if ENABLE(SVG_FONTS) 286 && !node->hasTagName(SVGNames::altGlyphTag) 287 #endif 288 && !node->hasTagName(SVGNames::trefTag) 289 && !node->hasTagName(SVGNames::textPathTag)) 290 return 0; 291 292 return static_cast<SVGTextContentElement*>(node); 293 } 294 295 } 296 297 #endif // ENABLE(SVG) 298