1/* 2 * Copyright (C) 2004, 2005, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org> 3 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Rob Buis <buis@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 "FrameSelection.h" 30#include "RenderObject.h" 31#include "RenderSVGResource.h" 32#include "RenderSVGText.h" 33#include "SVGDocumentExtensions.h" 34#include "SVGElementInstance.h" 35#include "SVGNames.h" 36#include "SVGTextQuery.h" 37#include "XMLNames.h" 38 39namespace WebCore { 40 41// Define custom animated property 'textLength'. 42const SVGPropertyInfo* SVGTextContentElement::textLengthPropertyInfo() 43{ 44 static const SVGPropertyInfo* s_propertyInfo = 0; 45 if (!s_propertyInfo) { 46 s_propertyInfo = new SVGPropertyInfo(AnimatedLength, 47 PropertyIsReadWrite, 48 SVGNames::textLengthAttr, 49 SVGNames::textLengthAttr.localName(), 50 &SVGTextContentElement::synchronizeTextLength, 51 &SVGTextContentElement::lookupOrCreateTextLengthWrapper); 52 } 53 return s_propertyInfo; 54} 55 56// Animated property definitions 57DEFINE_ANIMATED_ENUMERATION(SVGTextContentElement, SVGNames::lengthAdjustAttr, LengthAdjust, lengthAdjust, SVGLengthAdjustType) 58DEFINE_ANIMATED_BOOLEAN(SVGTextContentElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired) 59 60BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGTextContentElement) 61 REGISTER_LOCAL_ANIMATED_PROPERTY(textLength) 62 REGISTER_LOCAL_ANIMATED_PROPERTY(lengthAdjust) 63 REGISTER_LOCAL_ANIMATED_PROPERTY(externalResourcesRequired) 64 REGISTER_PARENT_ANIMATED_PROPERTIES(SVGStyledElement) 65 REGISTER_PARENT_ANIMATED_PROPERTIES(SVGTests) 66END_REGISTER_ANIMATED_PROPERTIES 67 68SVGTextContentElement::SVGTextContentElement(const QualifiedName& tagName, Document* document) 69 : SVGStyledElement(tagName, document) 70 , m_textLength(LengthModeOther) 71 , m_specifiedTextLength(LengthModeOther) 72 , m_lengthAdjust(SVGLengthAdjustSpacing) 73{ 74 registerAnimatedPropertiesForSVGTextContentElement(); 75} 76 77void SVGTextContentElement::synchronizeTextLength(SVGElement* contextElement) 78{ 79 ASSERT(contextElement); 80 SVGTextContentElement* ownerType = toSVGTextContentElement(contextElement); 81 if (!ownerType->m_textLength.shouldSynchronize) 82 return; 83 AtomicString value(SVGPropertyTraits<SVGLength>::toString(ownerType->m_specifiedTextLength)); 84 ownerType->m_textLength.synchronize(ownerType, textLengthPropertyInfo()->attributeName, value); 85} 86 87PassRefPtr<SVGAnimatedProperty> SVGTextContentElement::lookupOrCreateTextLengthWrapper(SVGElement* contextElement) 88{ 89 ASSERT(contextElement); 90 SVGTextContentElement* ownerType = toSVGTextContentElement(contextElement); 91 return SVGAnimatedProperty::lookupOrCreateWrapper<SVGTextContentElement, SVGAnimatedLength, SVGLength> 92 (ownerType, textLengthPropertyInfo(), ownerType->m_textLength.value); 93} 94 95PassRefPtr<SVGAnimatedLength> SVGTextContentElement::textLengthAnimated() 96{ 97 DEFINE_STATIC_LOCAL(SVGLength, defaultTextLength, (LengthModeOther)); 98 if (m_specifiedTextLength == defaultTextLength) 99 m_textLength.value.newValueSpecifiedUnits(LengthTypeNumber, getComputedTextLength(), ASSERT_NO_EXCEPTION); 100 101 m_textLength.shouldSynchronize = true; 102 return static_pointer_cast<SVGAnimatedLength>(lookupOrCreateTextLengthWrapper(this)); 103 104} 105 106unsigned SVGTextContentElement::getNumberOfChars() 107{ 108 document()->updateLayoutIgnorePendingStylesheets(); 109 return SVGTextQuery(renderer()).numberOfCharacters(); 110} 111 112float SVGTextContentElement::getComputedTextLength() 113{ 114 document()->updateLayoutIgnorePendingStylesheets(); 115 return SVGTextQuery(renderer()).textLength(); 116} 117 118float SVGTextContentElement::getSubStringLength(unsigned charnum, unsigned nchars, ExceptionCode& ec) 119{ 120 document()->updateLayoutIgnorePendingStylesheets(); 121 122 unsigned numberOfChars = getNumberOfChars(); 123 if (charnum >= numberOfChars) { 124 ec = INDEX_SIZE_ERR; 125 return 0.0f; 126 } 127 128 return SVGTextQuery(renderer()).subStringLength(charnum, nchars); 129} 130 131FloatPoint SVGTextContentElement::getStartPositionOfChar(unsigned charnum, ExceptionCode& ec) 132{ 133 document()->updateLayoutIgnorePendingStylesheets(); 134 135 if (charnum > getNumberOfChars()) { 136 ec = INDEX_SIZE_ERR; 137 return FloatPoint(); 138 } 139 140 return SVGTextQuery(renderer()).startPositionOfCharacter(charnum); 141} 142 143FloatPoint SVGTextContentElement::getEndPositionOfChar(unsigned charnum, ExceptionCode& ec) 144{ 145 document()->updateLayoutIgnorePendingStylesheets(); 146 147 if (charnum > getNumberOfChars()) { 148 ec = INDEX_SIZE_ERR; 149 return FloatPoint(); 150 } 151 152 return SVGTextQuery(renderer()).endPositionOfCharacter(charnum); 153} 154 155FloatRect SVGTextContentElement::getExtentOfChar(unsigned charnum, ExceptionCode& ec) 156{ 157 document()->updateLayoutIgnorePendingStylesheets(); 158 159 if (charnum > getNumberOfChars()) { 160 ec = INDEX_SIZE_ERR; 161 return FloatRect(); 162 } 163 164 return SVGTextQuery(renderer()).extentOfCharacter(charnum); 165} 166 167float SVGTextContentElement::getRotationOfChar(unsigned charnum, ExceptionCode& ec) 168{ 169 document()->updateLayoutIgnorePendingStylesheets(); 170 171 if (charnum > getNumberOfChars()) { 172 ec = INDEX_SIZE_ERR; 173 return 0.0f; 174 } 175 176 return SVGTextQuery(renderer()).rotationOfCharacter(charnum); 177} 178 179int SVGTextContentElement::getCharNumAtPosition(const FloatPoint& point) 180{ 181 document()->updateLayoutIgnorePendingStylesheets(); 182 return SVGTextQuery(renderer()).characterNumberAtPosition(point); 183} 184 185void SVGTextContentElement::selectSubString(unsigned charnum, unsigned nchars, ExceptionCode& ec) 186{ 187 unsigned numberOfChars = getNumberOfChars(); 188 if (charnum >= numberOfChars) { 189 ec = INDEX_SIZE_ERR; 190 return; 191 } 192 193 if (nchars > numberOfChars - charnum) 194 nchars = numberOfChars - charnum; 195 196 ASSERT(document()); 197 ASSERT(document()->frame()); 198 199 FrameSelection* selection = document()->frame()->selection(); 200 if (!selection) 201 return; 202 203 // Find selection start 204 VisiblePosition start(firstPositionInNode(const_cast<SVGTextContentElement*>(this))); 205 for (unsigned i = 0; i < charnum; ++i) 206 start = start.next(); 207 208 // Find selection end 209 VisiblePosition end(start); 210 for (unsigned i = 0; i < nchars; ++i) 211 end = end.next(); 212 213 selection->setSelection(VisibleSelection(start, end)); 214} 215 216bool SVGTextContentElement::isSupportedAttribute(const QualifiedName& attrName) 217{ 218 DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ()); 219 if (supportedAttributes.isEmpty()) { 220 SVGTests::addSupportedAttributes(supportedAttributes); 221 SVGLangSpace::addSupportedAttributes(supportedAttributes); 222 SVGExternalResourcesRequired::addSupportedAttributes(supportedAttributes); 223 supportedAttributes.add(SVGNames::lengthAdjustAttr); 224 supportedAttributes.add(SVGNames::textLengthAttr); 225 } 226 return supportedAttributes.contains<QualifiedName, SVGAttributeHashTranslator>(attrName); 227} 228 229bool SVGTextContentElement::isPresentationAttribute(const QualifiedName& name) const 230{ 231 if (name.matches(XMLNames::spaceAttr)) 232 return true; 233 return SVGStyledElement::isPresentationAttribute(name); 234} 235 236void SVGTextContentElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style) 237{ 238 if (!isSupportedAttribute(name)) 239 SVGStyledElement::collectStyleForPresentationAttribute(name, value, style); 240 else if (name.matches(XMLNames::spaceAttr)) { 241 DEFINE_STATIC_LOCAL(const AtomicString, preserveString, ("preserve", AtomicString::ConstructFromLiteral)); 242 243 if (value == preserveString) 244 addPropertyToPresentationAttributeStyle(style, CSSPropertyWhiteSpace, CSSValuePre); 245 else 246 addPropertyToPresentationAttributeStyle(style, CSSPropertyWhiteSpace, CSSValueNowrap); 247 } 248} 249 250void SVGTextContentElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 251{ 252 SVGParsingError parseError = NoError; 253 254 if (!isSupportedAttribute(name)) 255 SVGStyledElement::parseAttribute(name, value); 256 else if (name == SVGNames::lengthAdjustAttr) { 257 SVGLengthAdjustType propertyValue = SVGPropertyTraits<SVGLengthAdjustType>::fromString(value); 258 if (propertyValue > 0) 259 setLengthAdjustBaseValue(propertyValue); 260 } else if (name == SVGNames::textLengthAttr) { 261 m_textLength.value = SVGLength::construct(LengthModeOther, value, parseError, ForbidNegativeLengths); 262 } else if (SVGTests::parseAttribute(name, value) 263 || SVGExternalResourcesRequired::parseAttribute(name, value)) { 264 } else if (SVGLangSpace::parseAttribute(name, value)) { 265 } else 266 ASSERT_NOT_REACHED(); 267 268 reportAttributeParsingError(parseError, name, value); 269} 270 271void SVGTextContentElement::svgAttributeChanged(const QualifiedName& attrName) 272{ 273 if (!isSupportedAttribute(attrName)) { 274 SVGStyledElement::svgAttributeChanged(attrName); 275 return; 276 } 277 278 SVGElementInstance::InvalidationGuard invalidationGuard(this); 279 280 if (SVGTests::handleAttributeChange(this, attrName)) 281 return; 282 283 if (attrName == SVGNames::textLengthAttr) 284 m_specifiedTextLength = m_textLength.value; 285 286 if (RenderObject* renderer = this->renderer()) 287 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer); 288} 289 290bool SVGTextContentElement::selfHasRelativeLengths() const 291{ 292 // Any element of the <text> subtree is advertized as using relative lengths. 293 // On any window size change, we have to relayout the text subtree, as the 294 // effective 'on-screen' font size may change. 295 return true; 296} 297 298SVGTextContentElement* SVGTextContentElement::elementFromRenderer(RenderObject* renderer) 299{ 300 if (!renderer) 301 return 0; 302 303 if (!renderer->isSVGText() && !renderer->isSVGInline()) 304 return 0; 305 306 Node* node = renderer->node(); 307 ASSERT(node); 308 ASSERT(node->isSVGElement()); 309 310 if (!node->hasTagName(SVGNames::textTag) 311 && !node->hasTagName(SVGNames::tspanTag) 312#if ENABLE(SVG_FONTS) 313 && !node->hasTagName(SVGNames::altGlyphTag) 314#endif 315 && !node->hasTagName(SVGNames::trefTag) 316 && !node->hasTagName(SVGNames::textPathTag)) 317 return 0; 318 319 return static_cast<SVGTextContentElement*>(node); 320} 321 322} 323 324#endif // ENABLE(SVG) 325