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 "core/svg/SVGTextContentElement.h" 23 24#include "bindings/core/v8/ExceptionMessages.h" 25#include "bindings/core/v8/ExceptionState.h" 26#include "bindings/core/v8/ExceptionStatePlaceholder.h" 27#include "core/CSSPropertyNames.h" 28#include "core/CSSValueKeywords.h" 29#include "core/SVGNames.h" 30#include "core/XMLNames.h" 31#include "core/editing/FrameSelection.h" 32#include "core/frame/LocalFrame.h" 33#include "core/rendering/RenderObject.h" 34#include "core/rendering/svg/RenderSVGResource.h" 35#include "core/rendering/svg/SVGTextQuery.h" 36 37namespace blink { 38 39template<> const SVGEnumerationStringEntries& getStaticStringEntries<SVGLengthAdjustType>() 40{ 41 DEFINE_STATIC_LOCAL(SVGEnumerationStringEntries, entries, ()); 42 if (entries.isEmpty()) { 43 entries.append(std::make_pair(SVGLengthAdjustSpacing, "spacing")); 44 entries.append(std::make_pair(SVGLengthAdjustSpacingAndGlyphs, "spacingAndGlyphs")); 45 } 46 return entries; 47} 48 49// SVGTextContentElement's 'textLength' attribute needs special handling. 50// It should return getComputedTextLength() when textLength is not specified manually. 51class SVGAnimatedTextLength FINAL : public SVGAnimatedLength { 52public: 53 static PassRefPtr<SVGAnimatedTextLength> create(SVGTextContentElement* contextElement) 54 { 55 return adoptRef(new SVGAnimatedTextLength(contextElement)); 56 } 57 58 virtual SVGLengthTearOff* baseVal() OVERRIDE 59 { 60 SVGTextContentElement* textContentElement = toSVGTextContentElement(contextElement()); 61 if (!textContentElement->textLengthIsSpecifiedByUser()) 62 baseValue()->newValueSpecifiedUnits(LengthTypeNumber, textContentElement->getComputedTextLength()); 63 64 return SVGAnimatedLength::baseVal(); 65 } 66 67private: 68 SVGAnimatedTextLength(SVGTextContentElement* contextElement) 69 : SVGAnimatedLength(contextElement, SVGNames::textLengthAttr, SVGLength::create(LengthModeOther), ForbidNegativeLengths) 70 { 71 } 72}; 73 74 75SVGTextContentElement::SVGTextContentElement(const QualifiedName& tagName, Document& document) 76 : SVGGraphicsElement(tagName, document) 77 , m_textLength(SVGAnimatedTextLength::create(this)) 78 , m_textLengthIsSpecifiedByUser(false) 79 , m_lengthAdjust(SVGAnimatedEnumeration<SVGLengthAdjustType>::create(this, SVGNames::lengthAdjustAttr, SVGLengthAdjustSpacing)) 80{ 81 addToPropertyMap(m_textLength); 82 addToPropertyMap(m_lengthAdjust); 83} 84 85unsigned SVGTextContentElement::getNumberOfChars() 86{ 87 document().updateLayoutIgnorePendingStylesheets(); 88 return SVGTextQuery(renderer()).numberOfCharacters(); 89} 90 91float SVGTextContentElement::getComputedTextLength() 92{ 93 document().updateLayoutIgnorePendingStylesheets(); 94 return SVGTextQuery(renderer()).textLength(); 95} 96 97float SVGTextContentElement::getSubStringLength(unsigned charnum, unsigned nchars, ExceptionState& exceptionState) 98{ 99 document().updateLayoutIgnorePendingStylesheets(); 100 101 unsigned numberOfChars = getNumberOfChars(); 102 if (charnum >= numberOfChars) { 103 exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexExceedsMaximumBound("charnum", charnum, getNumberOfChars())); 104 return 0.0f; 105 } 106 107 if (nchars > numberOfChars - charnum) 108 nchars = numberOfChars - charnum; 109 110 return SVGTextQuery(renderer()).subStringLength(charnum, nchars); 111} 112 113PassRefPtr<SVGPointTearOff> SVGTextContentElement::getStartPositionOfChar(unsigned charnum, ExceptionState& exceptionState) 114{ 115 document().updateLayoutIgnorePendingStylesheets(); 116 117 if (charnum > getNumberOfChars()) { 118 exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexExceedsMaximumBound("charnum", charnum, getNumberOfChars())); 119 return nullptr; 120 } 121 122 FloatPoint point = SVGTextQuery(renderer()).startPositionOfCharacter(charnum); 123 return SVGPointTearOff::create(SVGPoint::create(point), 0, PropertyIsNotAnimVal); 124} 125 126PassRefPtr<SVGPointTearOff> SVGTextContentElement::getEndPositionOfChar(unsigned charnum, ExceptionState& exceptionState) 127{ 128 document().updateLayoutIgnorePendingStylesheets(); 129 130 if (charnum > getNumberOfChars()) { 131 exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexExceedsMaximumBound("charnum", charnum, getNumberOfChars())); 132 return nullptr; 133 } 134 135 FloatPoint point = SVGTextQuery(renderer()).endPositionOfCharacter(charnum); 136 return SVGPointTearOff::create(SVGPoint::create(point), 0, PropertyIsNotAnimVal); 137} 138 139PassRefPtr<SVGRectTearOff> SVGTextContentElement::getExtentOfChar(unsigned charnum, ExceptionState& exceptionState) 140{ 141 document().updateLayoutIgnorePendingStylesheets(); 142 143 if (charnum > getNumberOfChars()) { 144 exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexExceedsMaximumBound("charnum", charnum, getNumberOfChars())); 145 return nullptr; 146 } 147 148 FloatRect rect = SVGTextQuery(renderer()).extentOfCharacter(charnum); 149 return SVGRectTearOff::create(SVGRect::create(rect), 0, PropertyIsNotAnimVal); 150} 151 152float SVGTextContentElement::getRotationOfChar(unsigned charnum, ExceptionState& exceptionState) 153{ 154 document().updateLayoutIgnorePendingStylesheets(); 155 156 if (charnum > getNumberOfChars()) { 157 exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexExceedsMaximumBound("charnum", charnum, getNumberOfChars())); 158 return 0.0f; 159 } 160 161 return SVGTextQuery(renderer()).rotationOfCharacter(charnum); 162} 163 164int SVGTextContentElement::getCharNumAtPosition(PassRefPtr<SVGPointTearOff> point, ExceptionState& exceptionState) 165{ 166 document().updateLayoutIgnorePendingStylesheets(); 167 return SVGTextQuery(renderer()).characterNumberAtPosition(point->target()->value()); 168} 169 170void SVGTextContentElement::selectSubString(unsigned charnum, unsigned nchars, ExceptionState& exceptionState) 171{ 172 unsigned numberOfChars = getNumberOfChars(); 173 if (charnum >= numberOfChars) { 174 exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexExceedsMaximumBound("charnum", charnum, getNumberOfChars())); 175 return; 176 } 177 178 if (nchars > numberOfChars - charnum) 179 nchars = numberOfChars - charnum; 180 181 ASSERT(document().frame()); 182 183 // Find selection start 184 VisiblePosition start(firstPositionInNode(const_cast<SVGTextContentElement*>(this))); 185 for (unsigned i = 0; i < charnum; ++i) 186 start = start.next(); 187 188 // Find selection end 189 VisiblePosition end(start); 190 for (unsigned i = 0; i < nchars; ++i) 191 end = end.next(); 192 193 document().frame()->selection().setSelection(VisibleSelection(start, end)); 194} 195 196bool SVGTextContentElement::isSupportedAttribute(const QualifiedName& attrName) 197{ 198 DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ()); 199 if (supportedAttributes.isEmpty()) { 200 supportedAttributes.add(SVGNames::lengthAdjustAttr); 201 supportedAttributes.add(SVGNames::textLengthAttr); 202 supportedAttributes.add(XMLNames::spaceAttr); 203 } 204 return supportedAttributes.contains<SVGAttributeHashTranslator>(attrName); 205} 206 207bool SVGTextContentElement::isPresentationAttribute(const QualifiedName& name) const 208{ 209 if (name.matches(XMLNames::spaceAttr)) 210 return true; 211 return SVGGraphicsElement::isPresentationAttribute(name); 212} 213 214void SVGTextContentElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style) 215{ 216 if (!isSupportedAttribute(name)) 217 SVGGraphicsElement::collectStyleForPresentationAttribute(name, value, style); 218 else if (name.matches(XMLNames::spaceAttr)) { 219 DEFINE_STATIC_LOCAL(const AtomicString, preserveString, ("preserve", AtomicString::ConstructFromLiteral)); 220 221 if (value == preserveString) 222 addPropertyToPresentationAttributeStyle(style, CSSPropertyWhiteSpace, CSSValuePre); 223 else 224 addPropertyToPresentationAttributeStyle(style, CSSPropertyWhiteSpace, CSSValueNowrap); 225 } 226} 227 228void SVGTextContentElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 229{ 230 SVGParsingError parseError = NoError; 231 232 if (!isSupportedAttribute(name)) 233 SVGGraphicsElement::parseAttribute(name, value); 234 else if (name == SVGNames::lengthAdjustAttr) { 235 m_lengthAdjust->setBaseValueAsString(value, parseError); 236 } else if (name == SVGNames::textLengthAttr) { 237 m_textLength->setBaseValueAsString(value, parseError); 238 } else if (name.matches(XMLNames::spaceAttr)) { 239 } else 240 ASSERT_NOT_REACHED(); 241 242 reportAttributeParsingError(parseError, name, value); 243} 244 245void SVGTextContentElement::svgAttributeChanged(const QualifiedName& attrName) 246{ 247 if (!isSupportedAttribute(attrName)) { 248 SVGGraphicsElement::svgAttributeChanged(attrName); 249 return; 250 } 251 252 if (attrName == SVGNames::textLengthAttr) 253 m_textLengthIsSpecifiedByUser = true; 254 255 SVGElement::InvalidationGuard invalidationGuard(this); 256 257 if (RenderObject* renderer = this->renderer()) 258 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer); 259} 260 261bool SVGTextContentElement::selfHasRelativeLengths() const 262{ 263 // Any element of the <text> subtree is advertized as using relative lengths. 264 // On any window size change, we have to relayout the text subtree, as the 265 // effective 'on-screen' font size may change. 266 return true; 267} 268 269SVGTextContentElement* SVGTextContentElement::elementFromRenderer(RenderObject* renderer) 270{ 271 if (!renderer) 272 return 0; 273 274 if (!renderer->isSVGText() && !renderer->isSVGInline()) 275 return 0; 276 277 SVGElement* element = toSVGElement(renderer->node()); 278 ASSERT(element); 279 return isSVGTextContentElement(*element) ? toSVGTextContentElement(element) : 0; 280} 281 282} 283