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