SVGTextContentElement.cpp revision 2daae5fd11344eaa88a0d92b0f6d65f8d2255c00
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
37namespace WebCore {
38
39// Animated property definitions
40DEFINE_ANIMATED_ENUMERATION(SVGTextContentElement, SVGNames::lengthAdjustAttr, LengthAdjust, lengthAdjust)
41DEFINE_ANIMATED_BOOLEAN(SVGTextContentElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired)
42
43SVGTextContentElement::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
51void 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
59PassRefPtr<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
72unsigned SVGTextContentElement::getNumberOfChars() const
73{
74    document()->updateLayoutIgnorePendingStylesheets();
75    return SVGTextQuery(renderer()).numberOfCharacters();
76}
77
78float SVGTextContentElement::getComputedTextLength() const
79{
80    document()->updateLayoutIgnorePendingStylesheets();
81    return SVGTextQuery(renderer()).textLength();
82}
83
84float 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
97FloatPoint 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
109FloatPoint 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
121FloatRect 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
133float 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
145int SVGTextContentElement::getCharNumAtPosition(const FloatPoint& point) const
146{
147    document()->updateLayoutIgnorePendingStylesheets();
148    return SVGTextQuery(renderer()).characterNumberAtPosition(point);
149}
150
151void 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
182void 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
214void 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
236void 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
255void SVGTextContentElement::fillPassedAttributeToPropertyTypeMap(AttributeToPropertyTypeMap& attributeToPropertyTypeMap)
256{
257    SVGStyledElement::fillPassedAttributeToPropertyTypeMap(attributeToPropertyTypeMap);
258
259    attributeToPropertyTypeMap.set(SVGNames::textLengthAttr, AnimatedLength);
260    attributeToPropertyTypeMap.set(SVGNames::lengthAdjustAttr, AnimatedEnumeration);
261}
262
263bool 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
271SVGTextContentElement* 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