• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "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'.
textLengthPropertyInfo()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
DEFINE_ANIMATED_ENUMERATION(SVGTextContentElement,SVGNames::lengthAdjustAttr,LengthAdjust,lengthAdjust,SVGLengthAdjustType)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 
synchronizeTextLength(SVGElement * contextElement)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 
lookupOrCreateTextLengthWrapper(SVGElement * contextElement)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 
textLength()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 
getNumberOfChars()104 unsigned SVGTextContentElement::getNumberOfChars()
105 {
106     document().updateLayoutIgnorePendingStylesheets();
107     return SVGTextQuery(renderer()).numberOfCharacters();
108 }
109 
getComputedTextLength()110 float SVGTextContentElement::getComputedTextLength()
111 {
112     document().updateLayoutIgnorePendingStylesheets();
113     return SVGTextQuery(renderer()).textLength();
114 }
115 
getSubStringLength(unsigned charnum,unsigned nchars,ExceptionState & exceptionState)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 
getStartPositionOfChar(unsigned charnum,ExceptionState & exceptionState)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 
getEndPositionOfChar(unsigned charnum,ExceptionState & exceptionState)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 
getExtentOfChar(unsigned charnum,ExceptionState & exceptionState)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 
getRotationOfChar(unsigned charnum,ExceptionState & exceptionState)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 
getCharNumAtPosition(const SVGPoint & point)180 int SVGTextContentElement::getCharNumAtPosition(const SVGPoint& point)
181 {
182     document().updateLayoutIgnorePendingStylesheets();
183     return SVGTextQuery(renderer()).characterNumberAtPosition(point);
184 }
185 
selectSubString(unsigned charnum,unsigned nchars,ExceptionState & exceptionState)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 
isSupportedAttribute(const QualifiedName & attrName)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 
isPresentationAttribute(const QualifiedName & name) const224 bool SVGTextContentElement::isPresentationAttribute(const QualifiedName& name) const
225 {
226     if (name.matches(XMLNames::spaceAttr))
227         return true;
228     return SVGGraphicsElement::isPresentationAttribute(name);
229 }
230 
collectStyleForPresentationAttribute(const QualifiedName & name,const AtomicString & value,MutableStylePropertySet * style)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 
parseAttribute(const QualifiedName & name,const AtomicString & value)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 
svgAttributeChanged(const QualifiedName & attrName)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 
selfHasRelativeLengths() const281 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 
elementFromRenderer(RenderObject * renderer)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