SVGTextContentElement.cpp revision cad810f21b803229eb11403f9209855525a25d57
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 "SVGNames.h"
31#include "SVGTextQuery.h"
32#include "SelectionController.h"
33#include "XMLNames.h"
34
35namespace WebCore {
36
37// Animated property definitions
38DEFINE_ANIMATED_LENGTH(SVGTextContentElement, SVGNames::textLengthAttr, TextLength, textLength)
39DEFINE_ANIMATED_ENUMERATION(SVGTextContentElement, SVGNames::lengthAdjustAttr, LengthAdjust, lengthAdjust)
40DEFINE_ANIMATED_BOOLEAN(SVGTextContentElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired)
41
42SVGTextContentElement::SVGTextContentElement(const QualifiedName& tagName, Document* document)
43    : SVGStyledElement(tagName, document)
44    , m_textLength(LengthModeOther)
45    , m_lengthAdjust(LENGTHADJUST_SPACING)
46{
47}
48
49unsigned SVGTextContentElement::getNumberOfChars() const
50{
51    document()->updateLayoutIgnorePendingStylesheets();
52    return SVGTextQuery(renderer()).numberOfCharacters();
53}
54
55float SVGTextContentElement::getComputedTextLength() const
56{
57    document()->updateLayoutIgnorePendingStylesheets();
58    return SVGTextQuery(renderer()).textLength();
59}
60
61float SVGTextContentElement::getSubStringLength(unsigned charnum, unsigned nchars, ExceptionCode& ec) const
62{
63    document()->updateLayoutIgnorePendingStylesheets();
64
65    unsigned numberOfChars = getNumberOfChars();
66    if (charnum >= numberOfChars) {
67        ec = INDEX_SIZE_ERR;
68        return 0.0f;
69    }
70
71    return SVGTextQuery(renderer()).subStringLength(charnum, nchars);
72}
73
74FloatPoint SVGTextContentElement::getStartPositionOfChar(unsigned charnum, ExceptionCode& ec) const
75{
76    document()->updateLayoutIgnorePendingStylesheets();
77
78    if (charnum > getNumberOfChars()) {
79        ec = INDEX_SIZE_ERR;
80        return FloatPoint();
81    }
82
83    return SVGTextQuery(renderer()).startPositionOfCharacter(charnum);
84}
85
86FloatPoint SVGTextContentElement::getEndPositionOfChar(unsigned charnum, ExceptionCode& ec) const
87{
88    document()->updateLayoutIgnorePendingStylesheets();
89
90    if (charnum > getNumberOfChars()) {
91        ec = INDEX_SIZE_ERR;
92        return FloatPoint();
93    }
94
95    return SVGTextQuery(renderer()).endPositionOfCharacter(charnum);
96}
97
98FloatRect SVGTextContentElement::getExtentOfChar(unsigned charnum, ExceptionCode& ec) const
99{
100    document()->updateLayoutIgnorePendingStylesheets();
101
102    if (charnum > getNumberOfChars()) {
103        ec = INDEX_SIZE_ERR;
104        return FloatRect();
105    }
106
107    return SVGTextQuery(renderer()).extentOfCharacter(charnum);
108}
109
110float SVGTextContentElement::getRotationOfChar(unsigned charnum, ExceptionCode& ec) const
111{
112    document()->updateLayoutIgnorePendingStylesheets();
113
114    if (charnum > getNumberOfChars()) {
115        ec = INDEX_SIZE_ERR;
116        return 0.0f;
117    }
118
119    return SVGTextQuery(renderer()).rotationOfCharacter(charnum);
120}
121
122int SVGTextContentElement::getCharNumAtPosition(const FloatPoint& point) const
123{
124    document()->updateLayoutIgnorePendingStylesheets();
125    return SVGTextQuery(renderer()).characterNumberAtPosition(point);
126}
127
128void SVGTextContentElement::selectSubString(unsigned charnum, unsigned nchars, ExceptionCode& ec) const
129{
130    unsigned numberOfChars = getNumberOfChars();
131    if (charnum >= numberOfChars) {
132        ec = INDEX_SIZE_ERR;
133        return;
134    }
135
136    if (nchars > numberOfChars - charnum)
137        nchars = numberOfChars - charnum;
138
139    ASSERT(document());
140    ASSERT(document()->frame());
141
142    SelectionController* controller = document()->frame()->selection();
143    if (!controller)
144        return;
145
146    // Find selection start
147    VisiblePosition start(const_cast<SVGTextContentElement*>(this), 0, SEL_DEFAULT_AFFINITY);
148    for (unsigned i = 0; i < charnum; ++i)
149        start = start.next();
150
151    // Find selection end
152    VisiblePosition end(start);
153    for (unsigned i = 0; i < nchars; ++i)
154        end = end.next();
155
156    controller->setSelection(VisibleSelection(start, end));
157}
158
159void SVGTextContentElement::parseMappedAttribute(Attribute* attr)
160{
161    if (attr->name() == SVGNames::lengthAdjustAttr) {
162        if (attr->value() == "spacing")
163            setLengthAdjustBaseValue(LENGTHADJUST_SPACING);
164        else if (attr->value() == "spacingAndGlyphs")
165            setLengthAdjustBaseValue(LENGTHADJUST_SPACINGANDGLYPHS);
166    } else if (attr->name() == SVGNames::textLengthAttr) {
167        setTextLengthBaseValue(SVGLength(LengthModeOther, attr->value()));
168        if (textLengthBaseValue().value(this) < 0.0)
169            document()->accessSVGExtensions()->reportError("A negative value for text attribute <textLength> is not allowed");
170    } else {
171        if (SVGTests::parseMappedAttribute(attr))
172            return;
173        if (SVGLangSpace::parseMappedAttribute(attr)) {
174            if (attr->name().matches(XMLNames::spaceAttr)) {
175                DEFINE_STATIC_LOCAL(const AtomicString, preserveString, ("preserve"));
176
177                if (attr->value() == preserveString)
178                    addCSSProperty(attr, CSSPropertyWhiteSpace, CSSValuePre);
179                else
180                    addCSSProperty(attr, CSSPropertyWhiteSpace, CSSValueNowrap);
181            }
182            return;
183        }
184        if (SVGExternalResourcesRequired::parseMappedAttribute(attr))
185            return;
186
187        SVGStyledElement::parseMappedAttribute(attr);
188    }
189}
190
191void SVGTextContentElement::synchronizeProperty(const QualifiedName& attrName)
192{
193    SVGStyledElement::synchronizeProperty(attrName);
194
195    if (attrName == anyQName()) {
196        synchronizeLengthAdjust();
197        synchronizeTextLength();
198        synchronizeExternalResourcesRequired();
199        SVGTests::synchronizeProperties(this, attrName);
200        return;
201    }
202
203    if (attrName == SVGNames::lengthAdjustAttr)
204        synchronizeLengthAdjust();
205    else if (attrName == SVGNames::textLengthAttr)
206        synchronizeTextLength();
207    else if (SVGExternalResourcesRequired::isKnownAttribute(attrName))
208        synchronizeExternalResourcesRequired();
209    else if (SVGTests::isKnownAttribute(attrName))
210        SVGTests::synchronizeProperties(this, attrName);
211}
212
213void SVGTextContentElement::svgAttributeChanged(const QualifiedName& attrName)
214{
215    SVGStyledElement::svgAttributeChanged(attrName);
216
217    if (SVGTests::handleAttributeChange(this, attrName))
218        return;
219
220    // FIXME: also handle attribute changes for lengthAdjust and textLength
221}
222
223bool SVGTextContentElement::isKnownAttribute(const QualifiedName& attrName)
224{
225    return attrName.matches(SVGNames::lengthAdjustAttr)
226            || attrName.matches(SVGNames::textLengthAttr)
227            || SVGLangSpace::isKnownAttribute(attrName)
228            || SVGExternalResourcesRequired::isKnownAttribute(attrName)
229            || SVGStyledElement::isKnownAttribute(attrName);
230}
231
232bool SVGTextContentElement::selfHasRelativeLengths() const
233{
234    return textLength().isRelative();
235}
236
237SVGTextContentElement* SVGTextContentElement::elementFromRenderer(RenderObject* renderer)
238{
239    if (!renderer)
240        return 0;
241
242    if (!renderer->isSVGText() && !renderer->isSVGInline())
243        return 0;
244
245    Node* node = renderer->node();
246    ASSERT(node);
247    ASSERT(node->isSVGElement());
248
249    if (!node->hasTagName(SVGNames::textTag)
250        && !node->hasTagName(SVGNames::tspanTag)
251#if ENABLE(SVG_FONTS)
252        && !node->hasTagName(SVGNames::altGlyphTag)
253#endif
254        && !node->hasTagName(SVGNames::trefTag)
255        && !node->hasTagName(SVGNames::textPathTag))
256        return 0;
257
258    return static_cast<SVGTextContentElement*>(node);
259}
260
261}
262
263#endif // ENABLE(SVG)
264