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>
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.
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.
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.
22 #include "core/svg/SVGTextContentElement.h"
24 #include "CSSPropertyNames.h"
25 #include "CSSValueKeywords.h"
28 #include "bindings/v8/ExceptionMessages.h"
29 #include "bindings/v8/ExceptionState.h"
30 #include "bindings/v8/ExceptionStatePlaceholder.h"
31 #include "core/editing/FrameSelection.h"
32 #include "core/frame/Frame.h"
33 #include "core/rendering/RenderObject.h"
34 #include "core/rendering/svg/RenderSVGResource.h"
35 #include "core/rendering/svg/SVGTextQuery.h"
36 #include "core/svg/SVGElementInstance.h"
40 // Animated property definitions
42 // SVGTextContentElement's 'textLength' attribute needs special handling.
43 // It should return getComputedTextLength() when textLength is not specified manually.
44 class SVGAnimatedTextLength FINAL : public SVGAnimatedLength {
46 static PassRefPtr<SVGAnimatedTextLength> create(SVGTextContentElement* contextElement)
48 return adoptRef(new SVGAnimatedTextLength(contextElement));
51 virtual SVGLengthTearOff* baseVal() OVERRIDE
53 SVGTextContentElement* textContentElement = toSVGTextContentElement(contextElement());
54 if (!textContentElement->textLengthIsSpecifiedByUser())
55 baseValue()->newValueSpecifiedUnits(LengthTypeNumber, textContentElement->getComputedTextLength());
57 return SVGAnimatedLength::baseVal();
61 SVGAnimatedTextLength(SVGTextContentElement* contextElement)
62 : SVGAnimatedLength(contextElement, SVGNames::textLengthAttr, SVGLength::create(LengthModeOther))
67 DEFINE_ANIMATED_ENUMERATION(SVGTextContentElement, SVGNames::lengthAdjustAttr, LengthAdjust, lengthAdjust, SVGLengthAdjustType)
69 BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGTextContentElement)
70 REGISTER_LOCAL_ANIMATED_PROPERTY(lengthAdjust)
71 REGISTER_PARENT_ANIMATED_PROPERTIES(SVGGraphicsElement)
72 END_REGISTER_ANIMATED_PROPERTIES
74 SVGTextContentElement::SVGTextContentElement(const QualifiedName& tagName, Document& document)
75 : SVGGraphicsElement(tagName, document)
76 , m_textLength(SVGAnimatedTextLength::create(this))
77 , m_textLengthIsSpecifiedByUser(false)
78 , m_lengthAdjust(SVGLengthAdjustSpacing)
80 ScriptWrappable::init(this);
82 addToPropertyMap(m_textLength);
83 registerAnimatedPropertiesForSVGTextContentElement();
86 unsigned SVGTextContentElement::getNumberOfChars()
88 document().updateLayoutIgnorePendingStylesheets();
89 return SVGTextQuery(renderer()).numberOfCharacters();
92 float SVGTextContentElement::getComputedTextLength()
94 document().updateLayoutIgnorePendingStylesheets();
95 return SVGTextQuery(renderer()).textLength();
98 float SVGTextContentElement::getSubStringLength(unsigned charnum, unsigned nchars, ExceptionState& exceptionState)
100 document().updateLayoutIgnorePendingStylesheets();
102 unsigned numberOfChars = getNumberOfChars();
103 if (charnum >= numberOfChars) {
104 exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexExceedsMaximumBound("charnum", charnum, getNumberOfChars()));
108 if (nchars > numberOfChars - charnum)
109 nchars = numberOfChars - charnum;
111 return SVGTextQuery(renderer()).subStringLength(charnum, nchars);
114 PassRefPtr<SVGPointTearOff> SVGTextContentElement::getStartPositionOfChar(unsigned charnum, ExceptionState& exceptionState)
116 document().updateLayoutIgnorePendingStylesheets();
118 if (charnum > getNumberOfChars()) {
119 exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexExceedsMaximumBound("charnum", charnum, getNumberOfChars()));
123 FloatPoint point = SVGTextQuery(renderer()).startPositionOfCharacter(charnum);
124 return SVGPointTearOff::create(SVGPoint::create(point), 0, PropertyIsNotAnimVal);
127 PassRefPtr<SVGPointTearOff> SVGTextContentElement::getEndPositionOfChar(unsigned charnum, ExceptionState& exceptionState)
129 document().updateLayoutIgnorePendingStylesheets();
131 if (charnum > getNumberOfChars()) {
132 exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexExceedsMaximumBound("charnum", charnum, getNumberOfChars()));
136 FloatPoint point = SVGTextQuery(renderer()).endPositionOfCharacter(charnum);
137 return SVGPointTearOff::create(SVGPoint::create(point), 0, PropertyIsNotAnimVal);
140 PassRefPtr<SVGRectTearOff> SVGTextContentElement::getExtentOfChar(unsigned charnum, ExceptionState& exceptionState)
142 document().updateLayoutIgnorePendingStylesheets();
144 if (charnum > getNumberOfChars()) {
145 exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexExceedsMaximumBound("charnum", charnum, getNumberOfChars()));
149 FloatRect rect = SVGTextQuery(renderer()).extentOfCharacter(charnum);
150 return SVGRectTearOff::create(SVGRect::create(rect), 0, PropertyIsNotAnimVal);
153 float SVGTextContentElement::getRotationOfChar(unsigned charnum, ExceptionState& exceptionState)
155 document().updateLayoutIgnorePendingStylesheets();
157 if (charnum > getNumberOfChars()) {
158 exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexExceedsMaximumBound("charnum", charnum, getNumberOfChars()));
162 return SVGTextQuery(renderer()).rotationOfCharacter(charnum);
165 int SVGTextContentElement::getCharNumAtPosition(PassRefPtr<SVGPointTearOff> point, ExceptionState& exceptionState)
167 document().updateLayoutIgnorePendingStylesheets();
168 return SVGTextQuery(renderer()).characterNumberAtPosition(point->target()->value());
171 void SVGTextContentElement::selectSubString(unsigned charnum, unsigned nchars, ExceptionState& exceptionState)
173 unsigned numberOfChars = getNumberOfChars();
174 if (charnum >= numberOfChars) {
175 exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexExceedsMaximumBound("charnum", charnum, getNumberOfChars()));
179 if (nchars > numberOfChars - charnum)
180 nchars = numberOfChars - charnum;
182 ASSERT(document().frame());
184 // Find selection start
185 VisiblePosition start(firstPositionInNode(const_cast<SVGTextContentElement*>(this)));
186 for (unsigned i = 0; i < charnum; ++i)
187 start = start.next();
189 // Find selection end
190 VisiblePosition end(start);
191 for (unsigned i = 0; i < nchars; ++i)
194 document().frame()->selection().setSelection(VisibleSelection(start, end));
197 bool SVGTextContentElement::isSupportedAttribute(const QualifiedName& attrName)
199 DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ());
200 if (supportedAttributes.isEmpty()) {
201 supportedAttributes.add(SVGNames::lengthAdjustAttr);
202 supportedAttributes.add(SVGNames::textLengthAttr);
203 supportedAttributes.add(XMLNames::spaceAttr);
205 return supportedAttributes.contains<SVGAttributeHashTranslator>(attrName);
208 bool SVGTextContentElement::isPresentationAttribute(const QualifiedName& name) const
210 if (name.matches(XMLNames::spaceAttr))
212 return SVGGraphicsElement::isPresentationAttribute(name);
215 void SVGTextContentElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style)
217 if (!isSupportedAttribute(name))
218 SVGGraphicsElement::collectStyleForPresentationAttribute(name, value, style);
219 else if (name.matches(XMLNames::spaceAttr)) {
220 DEFINE_STATIC_LOCAL(const AtomicString, preserveString, ("preserve", AtomicString::ConstructFromLiteral));
222 if (value == preserveString)
223 addPropertyToPresentationAttributeStyle(style, CSSPropertyWhiteSpace, CSSValuePre);
225 addPropertyToPresentationAttributeStyle(style, CSSPropertyWhiteSpace, CSSValueNowrap);
229 void SVGTextContentElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
231 SVGParsingError parseError = NoError;
233 if (!isSupportedAttribute(name))
234 SVGGraphicsElement::parseAttribute(name, value);
235 else if (name == SVGNames::lengthAdjustAttr) {
236 SVGLengthAdjustType propertyValue = SVGPropertyTraits<SVGLengthAdjustType>::fromString(value);
237 if (propertyValue > 0)
238 setLengthAdjustBaseValue(propertyValue);
239 } else if (name == SVGNames::textLengthAttr) {
240 m_textLength->setBaseValueAsString(value, ForbidNegativeLengths, parseError);
241 } else if (name.matches(XMLNames::spaceAttr)) {
243 ASSERT_NOT_REACHED();
245 reportAttributeParsingError(parseError, name, value);
248 void SVGTextContentElement::svgAttributeChanged(const QualifiedName& attrName)
250 if (!isSupportedAttribute(attrName)) {
251 SVGGraphicsElement::svgAttributeChanged(attrName);
255 if (attrName == SVGNames::textLengthAttr)
256 m_textLengthIsSpecifiedByUser = true;
258 SVGElementInstance::InvalidationGuard invalidationGuard(this);
260 if (RenderObject* renderer = this->renderer())
261 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
264 bool SVGTextContentElement::selfHasRelativeLengths() const
266 // Any element of the <text> subtree is advertized as using relative lengths.
267 // On any window size change, we have to relayout the text subtree, as the
268 // effective 'on-screen' font size may change.
272 SVGTextContentElement* SVGTextContentElement::elementFromRenderer(RenderObject* renderer)
277 if (!renderer->isSVGText() && !renderer->isSVGInline())
280 SVGElement* element = toSVGElement(renderer->node());
283 if (!element->isTextContent())
286 return toSVGTextContentElement(element);