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/LocalFrame.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 template<> const SVGEnumerationStringEntries& getStaticStringEntries<SVGLengthAdjustType>()
42 DEFINE_STATIC_LOCAL(SVGEnumerationStringEntries, entries, ());
43 if (entries.isEmpty()) {
44 entries.append(std::make_pair(SVGLengthAdjustUnknown, emptyString()));
45 entries.append(std::make_pair(SVGLengthAdjustSpacing, "spacing"));
46 entries.append(std::make_pair(SVGLengthAdjustSpacingAndGlyphs, "spacingAndGlyphs"));
51 // SVGTextContentElement's 'textLength' attribute needs special handling.
52 // It should return getComputedTextLength() when textLength is not specified manually.
53 class SVGAnimatedTextLength FINAL : public SVGAnimatedLength {
55 static PassRefPtr<SVGAnimatedTextLength> create(SVGTextContentElement* contextElement)
57 return adoptRef(new SVGAnimatedTextLength(contextElement));
60 virtual SVGLengthTearOff* baseVal() OVERRIDE
62 SVGTextContentElement* textContentElement = toSVGTextContentElement(contextElement());
63 if (!textContentElement->textLengthIsSpecifiedByUser())
64 baseValue()->newValueSpecifiedUnits(LengthTypeNumber, textContentElement->getComputedTextLength());
66 return SVGAnimatedLength::baseVal();
70 SVGAnimatedTextLength(SVGTextContentElement* contextElement)
71 : SVGAnimatedLength(contextElement, SVGNames::textLengthAttr, SVGLength::create(LengthModeOther), ForbidNegativeLengths)
77 SVGTextContentElement::SVGTextContentElement(const QualifiedName& tagName, Document& document)
78 : SVGGraphicsElement(tagName, document)
79 , m_textLength(SVGAnimatedTextLength::create(this))
80 , m_textLengthIsSpecifiedByUser(false)
81 , m_lengthAdjust(SVGAnimatedEnumeration<SVGLengthAdjustType>::create(this, SVGNames::lengthAdjustAttr, SVGLengthAdjustSpacing))
83 ScriptWrappable::init(this);
85 addToPropertyMap(m_textLength);
86 addToPropertyMap(m_lengthAdjust);
89 unsigned SVGTextContentElement::getNumberOfChars()
91 document().updateLayoutIgnorePendingStylesheets();
92 return SVGTextQuery(renderer()).numberOfCharacters();
95 float SVGTextContentElement::getComputedTextLength()
97 document().updateLayoutIgnorePendingStylesheets();
98 return SVGTextQuery(renderer()).textLength();
101 float SVGTextContentElement::getSubStringLength(unsigned charnum, unsigned nchars, ExceptionState& exceptionState)
103 document().updateLayoutIgnorePendingStylesheets();
105 unsigned numberOfChars = getNumberOfChars();
106 if (charnum >= numberOfChars) {
107 exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexExceedsMaximumBound("charnum", charnum, getNumberOfChars()));
111 if (nchars > numberOfChars - charnum)
112 nchars = numberOfChars - charnum;
114 return SVGTextQuery(renderer()).subStringLength(charnum, nchars);
117 PassRefPtr<SVGPointTearOff> SVGTextContentElement::getStartPositionOfChar(unsigned charnum, ExceptionState& exceptionState)
119 document().updateLayoutIgnorePendingStylesheets();
121 if (charnum > getNumberOfChars()) {
122 exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexExceedsMaximumBound("charnum", charnum, getNumberOfChars()));
126 FloatPoint point = SVGTextQuery(renderer()).startPositionOfCharacter(charnum);
127 return SVGPointTearOff::create(SVGPoint::create(point), 0, PropertyIsNotAnimVal);
130 PassRefPtr<SVGPointTearOff> SVGTextContentElement::getEndPositionOfChar(unsigned charnum, ExceptionState& exceptionState)
132 document().updateLayoutIgnorePendingStylesheets();
134 if (charnum > getNumberOfChars()) {
135 exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexExceedsMaximumBound("charnum", charnum, getNumberOfChars()));
139 FloatPoint point = SVGTextQuery(renderer()).endPositionOfCharacter(charnum);
140 return SVGPointTearOff::create(SVGPoint::create(point), 0, PropertyIsNotAnimVal);
143 PassRefPtr<SVGRectTearOff> SVGTextContentElement::getExtentOfChar(unsigned charnum, ExceptionState& exceptionState)
145 document().updateLayoutIgnorePendingStylesheets();
147 if (charnum > getNumberOfChars()) {
148 exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexExceedsMaximumBound("charnum", charnum, getNumberOfChars()));
152 FloatRect rect = SVGTextQuery(renderer()).extentOfCharacter(charnum);
153 return SVGRectTearOff::create(SVGRect::create(rect), 0, PropertyIsNotAnimVal);
156 float SVGTextContentElement::getRotationOfChar(unsigned charnum, ExceptionState& exceptionState)
158 document().updateLayoutIgnorePendingStylesheets();
160 if (charnum > getNumberOfChars()) {
161 exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexExceedsMaximumBound("charnum", charnum, getNumberOfChars()));
165 return SVGTextQuery(renderer()).rotationOfCharacter(charnum);
168 int SVGTextContentElement::getCharNumAtPosition(PassRefPtr<SVGPointTearOff> point, ExceptionState& exceptionState)
170 document().updateLayoutIgnorePendingStylesheets();
171 return SVGTextQuery(renderer()).characterNumberAtPosition(point->target()->value());
174 void SVGTextContentElement::selectSubString(unsigned charnum, unsigned nchars, ExceptionState& exceptionState)
176 unsigned numberOfChars = getNumberOfChars();
177 if (charnum >= numberOfChars) {
178 exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexExceedsMaximumBound("charnum", charnum, getNumberOfChars()));
182 if (nchars > numberOfChars - charnum)
183 nchars = numberOfChars - charnum;
185 ASSERT(document().frame());
187 // Find selection start
188 VisiblePosition start(firstPositionInNode(const_cast<SVGTextContentElement*>(this)));
189 for (unsigned i = 0; i < charnum; ++i)
190 start = start.next();
192 // Find selection end
193 VisiblePosition end(start);
194 for (unsigned i = 0; i < nchars; ++i)
197 document().frame()->selection().setSelection(VisibleSelection(start, end));
200 bool SVGTextContentElement::isSupportedAttribute(const QualifiedName& attrName)
202 DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ());
203 if (supportedAttributes.isEmpty()) {
204 supportedAttributes.add(SVGNames::lengthAdjustAttr);
205 supportedAttributes.add(SVGNames::textLengthAttr);
206 supportedAttributes.add(XMLNames::spaceAttr);
208 return supportedAttributes.contains<SVGAttributeHashTranslator>(attrName);
211 bool SVGTextContentElement::isPresentationAttribute(const QualifiedName& name) const
213 if (name.matches(XMLNames::spaceAttr))
215 return SVGGraphicsElement::isPresentationAttribute(name);
218 void SVGTextContentElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style)
220 if (!isSupportedAttribute(name))
221 SVGGraphicsElement::collectStyleForPresentationAttribute(name, value, style);
222 else if (name.matches(XMLNames::spaceAttr)) {
223 DEFINE_STATIC_LOCAL(const AtomicString, preserveString, ("preserve", AtomicString::ConstructFromLiteral));
225 if (value == preserveString)
226 addPropertyToPresentationAttributeStyle(style, CSSPropertyWhiteSpace, CSSValuePre);
228 addPropertyToPresentationAttributeStyle(style, CSSPropertyWhiteSpace, CSSValueNowrap);
232 void SVGTextContentElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
234 SVGParsingError parseError = NoError;
236 if (!isSupportedAttribute(name))
237 SVGGraphicsElement::parseAttribute(name, value);
238 else if (name == SVGNames::lengthAdjustAttr) {
239 m_lengthAdjust->setBaseValueAsString(value, parseError);
240 } else if (name == SVGNames::textLengthAttr) {
241 m_textLength->setBaseValueAsString(value, parseError);
242 } else if (name.matches(XMLNames::spaceAttr)) {
244 ASSERT_NOT_REACHED();
246 reportAttributeParsingError(parseError, name, value);
249 void SVGTextContentElement::svgAttributeChanged(const QualifiedName& attrName)
251 if (!isSupportedAttribute(attrName)) {
252 SVGGraphicsElement::svgAttributeChanged(attrName);
256 if (attrName == SVGNames::textLengthAttr)
257 m_textLengthIsSpecifiedByUser = true;
259 SVGElementInstance::InvalidationGuard invalidationGuard(this);
261 if (RenderObject* renderer = this->renderer())
262 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
265 bool SVGTextContentElement::selfHasRelativeLengths() const
267 // Any element of the <text> subtree is advertized as using relative lengths.
268 // On any window size change, we have to relayout the text subtree, as the
269 // effective 'on-screen' font size may change.
273 SVGTextContentElement* SVGTextContentElement::elementFromRenderer(RenderObject* renderer)
278 if (!renderer->isSVGText() && !renderer->isSVGInline())
281 SVGElement* element = toSVGElement(renderer->node());
283 return isSVGTextContentElement(*element) ? toSVGTextContentElement(element) : 0;