• 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 
23 #if ENABLE(SVG)
24 #include "SVGTextContentElement.h"
25 
26 #include "CSSPropertyNames.h"
27 #include "CSSValueKeywords.h"
28 #include "Frame.h"
29 #include "RenderObject.h"
30 #include "RenderSVGResource.h"
31 #include "SVGDocumentExtensions.h"
32 #include "SVGNames.h"
33 #include "SVGTextQuery.h"
34 #include "SelectionController.h"
35 #include "XMLNames.h"
36 
37 namespace WebCore {
38 
39 // Animated property definitions
DEFINE_ANIMATED_ENUMERATION(SVGTextContentElement,SVGNames::lengthAdjustAttr,LengthAdjust,lengthAdjust)40 DEFINE_ANIMATED_ENUMERATION(SVGTextContentElement, SVGNames::lengthAdjustAttr, LengthAdjust, lengthAdjust)
41 DEFINE_ANIMATED_BOOLEAN(SVGTextContentElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired)
42 
43 SVGTextContentElement::SVGTextContentElement(const QualifiedName& tagName, Document* document)
44     : SVGStyledElement(tagName, document)
45     , m_specifiedTextLength(LengthModeOther)
46     , m_textLength(LengthModeOther)
47     , m_lengthAdjust(LENGTHADJUST_SPACING)
48 {
49 }
50 
synchronizeTextLength()51 void SVGTextContentElement::synchronizeTextLength()
52 {
53     if (!m_textLength.shouldSynchronize)
54         return;
55     AtomicString value(SVGPropertyTraits<SVGLength>::toString(m_specifiedTextLength));
56     SVGAnimatedPropertySynchronizer<true>::synchronize(this, SVGNames::textLengthAttr, value);
57 }
58 
textLengthAnimated()59 PassRefPtr<SVGAnimatedLength> SVGTextContentElement::textLengthAnimated()
60 {
61     DEFINE_STATIC_LOCAL(SVGLength, defaultTextLength, (LengthModeOther));
62     if (m_specifiedTextLength == defaultTextLength) {
63         ExceptionCode ec = 0;
64         m_textLength.value.newValueSpecifiedUnits(LengthTypeNumber, getComputedTextLength(), ec);
65         ASSERT(!ec);
66     }
67 
68     m_textLength.shouldSynchronize = true;
69     return SVGAnimatedProperty::lookupOrCreateWrapper<SVGAnimatedLength, SVGLength>(this, SVGNames::textLengthAttr, SVGNames::textLengthAttr.localName(), m_textLength.value);
70 }
71 
getNumberOfChars() const72 unsigned SVGTextContentElement::getNumberOfChars() const
73 {
74     document()->updateLayoutIgnorePendingStylesheets();
75     return SVGTextQuery(renderer()).numberOfCharacters();
76 }
77 
getComputedTextLength() const78 float SVGTextContentElement::getComputedTextLength() const
79 {
80     document()->updateLayoutIgnorePendingStylesheets();
81     return SVGTextQuery(renderer()).textLength();
82 }
83 
getSubStringLength(unsigned charnum,unsigned nchars,ExceptionCode & ec) const84 float SVGTextContentElement::getSubStringLength(unsigned charnum, unsigned nchars, ExceptionCode& ec) const
85 {
86     document()->updateLayoutIgnorePendingStylesheets();
87 
88     unsigned numberOfChars = getNumberOfChars();
89     if (charnum >= numberOfChars) {
90         ec = INDEX_SIZE_ERR;
91         return 0.0f;
92     }
93 
94     return SVGTextQuery(renderer()).subStringLength(charnum, nchars);
95 }
96 
getStartPositionOfChar(unsigned charnum,ExceptionCode & ec) const97 FloatPoint SVGTextContentElement::getStartPositionOfChar(unsigned charnum, ExceptionCode& ec) const
98 {
99     document()->updateLayoutIgnorePendingStylesheets();
100 
101     if (charnum > getNumberOfChars()) {
102         ec = INDEX_SIZE_ERR;
103         return FloatPoint();
104     }
105 
106     return SVGTextQuery(renderer()).startPositionOfCharacter(charnum);
107 }
108 
getEndPositionOfChar(unsigned charnum,ExceptionCode & ec) const109 FloatPoint SVGTextContentElement::getEndPositionOfChar(unsigned charnum, ExceptionCode& ec) const
110 {
111     document()->updateLayoutIgnorePendingStylesheets();
112 
113     if (charnum > getNumberOfChars()) {
114         ec = INDEX_SIZE_ERR;
115         return FloatPoint();
116     }
117 
118     return SVGTextQuery(renderer()).endPositionOfCharacter(charnum);
119 }
120 
getExtentOfChar(unsigned charnum,ExceptionCode & ec) const121 FloatRect SVGTextContentElement::getExtentOfChar(unsigned charnum, ExceptionCode& ec) const
122 {
123     document()->updateLayoutIgnorePendingStylesheets();
124 
125     if (charnum > getNumberOfChars()) {
126         ec = INDEX_SIZE_ERR;
127         return FloatRect();
128     }
129 
130     return SVGTextQuery(renderer()).extentOfCharacter(charnum);
131 }
132 
getRotationOfChar(unsigned charnum,ExceptionCode & ec) const133 float SVGTextContentElement::getRotationOfChar(unsigned charnum, ExceptionCode& ec) const
134 {
135     document()->updateLayoutIgnorePendingStylesheets();
136 
137     if (charnum > getNumberOfChars()) {
138         ec = INDEX_SIZE_ERR;
139         return 0.0f;
140     }
141 
142     return SVGTextQuery(renderer()).rotationOfCharacter(charnum);
143 }
144 
getCharNumAtPosition(const FloatPoint & point) const145 int SVGTextContentElement::getCharNumAtPosition(const FloatPoint& point) const
146 {
147     document()->updateLayoutIgnorePendingStylesheets();
148     return SVGTextQuery(renderer()).characterNumberAtPosition(point);
149 }
150 
selectSubString(unsigned charnum,unsigned nchars,ExceptionCode & ec) const151 void SVGTextContentElement::selectSubString(unsigned charnum, unsigned nchars, ExceptionCode& ec) const
152 {
153     unsigned numberOfChars = getNumberOfChars();
154     if (charnum >= numberOfChars) {
155         ec = INDEX_SIZE_ERR;
156         return;
157     }
158 
159     if (nchars > numberOfChars - charnum)
160         nchars = numberOfChars - charnum;
161 
162     ASSERT(document());
163     ASSERT(document()->frame());
164 
165     SelectionController* controller = document()->frame()->selection();
166     if (!controller)
167         return;
168 
169     // Find selection start
170     VisiblePosition start(firstPositionInNode(const_cast<SVGTextContentElement*>(this)));
171     for (unsigned i = 0; i < charnum; ++i)
172         start = start.next();
173 
174     // Find selection end
175     VisiblePosition end(start);
176     for (unsigned i = 0; i < nchars; ++i)
177         end = end.next();
178 
179     controller->setSelection(VisibleSelection(start, end));
180 }
181 
parseMappedAttribute(Attribute * attr)182 void SVGTextContentElement::parseMappedAttribute(Attribute* attr)
183 {
184     if (attr->name() == SVGNames::lengthAdjustAttr) {
185         if (attr->value() == "spacing")
186             setLengthAdjustBaseValue(LENGTHADJUST_SPACING);
187         else if (attr->value() == "spacingAndGlyphs")
188             setLengthAdjustBaseValue(LENGTHADJUST_SPACINGANDGLYPHS);
189     } else if (attr->name() == SVGNames::textLengthAttr) {
190         m_textLength.value = SVGLength(LengthModeOther, attr->value());
191         if (m_textLength.value.value(this) < 0)
192             document()->accessSVGExtensions()->reportError("A negative value for text attribute <textLength> is not allowed");
193     } else {
194         if (SVGTests::parseMappedAttribute(attr))
195             return;
196         if (SVGLangSpace::parseMappedAttribute(attr)) {
197             if (attr->name().matches(XMLNames::spaceAttr)) {
198                 DEFINE_STATIC_LOCAL(const AtomicString, preserveString, ("preserve"));
199 
200                 if (attr->value() == preserveString)
201                     addCSSProperty(attr, CSSPropertyWhiteSpace, CSSValuePre);
202                 else
203                     addCSSProperty(attr, CSSPropertyWhiteSpace, CSSValueNowrap);
204             }
205             return;
206         }
207         if (SVGExternalResourcesRequired::parseMappedAttribute(attr))
208             return;
209 
210         SVGStyledElement::parseMappedAttribute(attr);
211     }
212 }
213 
synchronizeProperty(const QualifiedName & attrName)214 void SVGTextContentElement::synchronizeProperty(const QualifiedName& attrName)
215 {
216     SVGStyledElement::synchronizeProperty(attrName);
217 
218     if (attrName == anyQName()) {
219         synchronizeLengthAdjust();
220         synchronizeTextLength();
221         synchronizeExternalResourcesRequired();
222         SVGTests::synchronizeProperties(this, attrName);
223         return;
224     }
225 
226     if (attrName == SVGNames::lengthAdjustAttr)
227         synchronizeLengthAdjust();
228     else if (attrName == SVGNames::textLengthAttr)
229         synchronizeTextLength();
230     else if (SVGExternalResourcesRequired::isKnownAttribute(attrName))
231         synchronizeExternalResourcesRequired();
232     else if (SVGTests::isKnownAttribute(attrName))
233         SVGTests::synchronizeProperties(this, attrName);
234 }
235 
svgAttributeChanged(const QualifiedName & attrName)236 void SVGTextContentElement::svgAttributeChanged(const QualifiedName& attrName)
237 {
238     SVGStyledElement::svgAttributeChanged(attrName);
239 
240     if (SVGTests::handleAttributeChange(this, attrName))
241         return;
242 
243     if (attrName == SVGNames::textLengthAttr)
244         m_specifiedTextLength = m_textLength.value;
245 
246     RenderObject* renderer = this->renderer();
247     if (!renderer)
248         return;
249 
250     if (attrName == SVGNames::textLengthAttr
251         || attrName == SVGNames::lengthAdjustAttr)
252         RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
253 }
254 
fillPassedAttributeToPropertyTypeMap(AttributeToPropertyTypeMap & attributeToPropertyTypeMap)255 void SVGTextContentElement::fillPassedAttributeToPropertyTypeMap(AttributeToPropertyTypeMap& attributeToPropertyTypeMap)
256 {
257     SVGStyledElement::fillPassedAttributeToPropertyTypeMap(attributeToPropertyTypeMap);
258 
259     attributeToPropertyTypeMap.set(SVGNames::textLengthAttr, AnimatedLength);
260     attributeToPropertyTypeMap.set(SVGNames::lengthAdjustAttr, AnimatedEnumeration);
261 }
262 
selfHasRelativeLengths() const263 bool SVGTextContentElement::selfHasRelativeLengths() const
264 {
265     // Any element of the <text> subtree is advertized as using relative lengths.
266     // On any window size change, we have to relayout the text subtree, as the
267     // effective 'on-screen' font size may change.
268     return true;
269 }
270 
elementFromRenderer(RenderObject * renderer)271 SVGTextContentElement* SVGTextContentElement::elementFromRenderer(RenderObject* renderer)
272 {
273     if (!renderer)
274         return 0;
275 
276     if (!renderer->isSVGText() && !renderer->isSVGInline())
277         return 0;
278 
279     Node* node = renderer->node();
280     ASSERT(node);
281     ASSERT(node->isSVGElement());
282 
283     if (!node->hasTagName(SVGNames::textTag)
284         && !node->hasTagName(SVGNames::tspanTag)
285 #if ENABLE(SVG_FONTS)
286         && !node->hasTagName(SVGNames::altGlyphTag)
287 #endif
288         && !node->hasTagName(SVGNames::trefTag)
289         && !node->hasTagName(SVGNames::textPathTag))
290         return 0;
291 
292     return static_cast<SVGTextContentElement*>(node);
293 }
294 
295 }
296 
297 #endif // ENABLE(SVG)
298