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