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