1/*
2 * Copyright (C) 2004, 2005, 2006, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3 * Copyright (C) 2004, 2005, 2006, 2007 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/SVGPathElement.h"
23
24#include "core/rendering/svg/RenderSVGPath.h"
25#include "core/rendering/svg/RenderSVGResource.h"
26#include "core/svg/SVGDocumentExtensions.h"
27#include "core/svg/SVGMPathElement.h"
28#include "core/svg/SVGPathSegArcAbs.h"
29#include "core/svg/SVGPathSegArcRel.h"
30#include "core/svg/SVGPathSegClosePath.h"
31#include "core/svg/SVGPathSegCurvetoCubicAbs.h"
32#include "core/svg/SVGPathSegCurvetoCubicRel.h"
33#include "core/svg/SVGPathSegCurvetoCubicSmoothAbs.h"
34#include "core/svg/SVGPathSegCurvetoCubicSmoothRel.h"
35#include "core/svg/SVGPathSegCurvetoQuadraticAbs.h"
36#include "core/svg/SVGPathSegCurvetoQuadraticRel.h"
37#include "core/svg/SVGPathSegCurvetoQuadraticSmoothAbs.h"
38#include "core/svg/SVGPathSegCurvetoQuadraticSmoothRel.h"
39#include "core/svg/SVGPathSegLinetoAbs.h"
40#include "core/svg/SVGPathSegLinetoHorizontalAbs.h"
41#include "core/svg/SVGPathSegLinetoHorizontalRel.h"
42#include "core/svg/SVGPathSegLinetoRel.h"
43#include "core/svg/SVGPathSegLinetoVerticalAbs.h"
44#include "core/svg/SVGPathSegLinetoVerticalRel.h"
45#include "core/svg/SVGPathSegMovetoAbs.h"
46#include "core/svg/SVGPathSegMovetoRel.h"
47#include "core/svg/SVGPathUtilities.h"
48#include "core/svg/SVGPointTearOff.h"
49
50namespace blink {
51
52inline SVGPathElement::SVGPathElement(Document& document)
53    : SVGGeometryElement(SVGNames::pathTag, document)
54    , m_pathLength(SVGAnimatedNumber::create(this, SVGNames::pathLengthAttr, SVGNumber::create()))
55    , m_pathSegList(SVGAnimatedPath::create(this, SVGNames::dAttr))
56{
57    addToPropertyMap(m_pathLength);
58    addToPropertyMap(m_pathSegList);
59}
60
61DEFINE_NODE_FACTORY(SVGPathElement)
62
63float SVGPathElement::getTotalLength()
64{
65    float totalLength = 0;
66    getTotalLengthOfSVGPathByteStream(pathByteStream(), totalLength);
67    return totalLength;
68}
69
70PassRefPtr<SVGPointTearOff> SVGPathElement::getPointAtLength(float length)
71{
72    FloatPoint point;
73    getPointAtLengthOfSVGPathByteStream(pathByteStream(), length, point);
74    return SVGPointTearOff::create(SVGPoint::create(point), 0, PropertyIsNotAnimVal);
75}
76
77unsigned SVGPathElement::getPathSegAtLength(float length)
78{
79    unsigned pathSeg = 0;
80    getSVGPathSegAtLengthFromSVGPathByteStream(pathByteStream(), length, pathSeg);
81    return pathSeg;
82}
83
84PassRefPtr<SVGPathSegClosePath> SVGPathElement::createSVGPathSegClosePath()
85{
86    return SVGPathSegClosePath::create(0);
87}
88
89PassRefPtr<SVGPathSegMovetoAbs> SVGPathElement::createSVGPathSegMovetoAbs(float x, float y)
90{
91    return SVGPathSegMovetoAbs::create(0, x, y);
92}
93
94PassRefPtr<SVGPathSegMovetoRel> SVGPathElement::createSVGPathSegMovetoRel(float x, float y)
95{
96    return SVGPathSegMovetoRel::create(0, x, y);
97}
98
99PassRefPtr<SVGPathSegLinetoAbs> SVGPathElement::createSVGPathSegLinetoAbs(float x, float y)
100{
101    return SVGPathSegLinetoAbs::create(0, x, y);
102}
103
104PassRefPtr<SVGPathSegLinetoRel> SVGPathElement::createSVGPathSegLinetoRel(float x, float y)
105{
106    return SVGPathSegLinetoRel::create(0, x, y);
107}
108
109PassRefPtr<SVGPathSegCurvetoCubicAbs> SVGPathElement::createSVGPathSegCurvetoCubicAbs(float x, float y, float x1, float y1, float x2, float y2)
110{
111    return SVGPathSegCurvetoCubicAbs::create(0, x, y, x1, y1, x2, y2);
112}
113
114PassRefPtr<SVGPathSegCurvetoCubicRel> SVGPathElement::createSVGPathSegCurvetoCubicRel(float x, float y, float x1, float y1, float x2, float y2)
115{
116    return SVGPathSegCurvetoCubicRel::create(0, x, y, x1, y1, x2, y2);
117}
118
119PassRefPtr<SVGPathSegCurvetoQuadraticAbs> SVGPathElement::createSVGPathSegCurvetoQuadraticAbs(float x, float y, float x1, float y1)
120{
121    return SVGPathSegCurvetoQuadraticAbs::create(0, x, y, x1, y1);
122}
123
124PassRefPtr<SVGPathSegCurvetoQuadraticRel> SVGPathElement::createSVGPathSegCurvetoQuadraticRel(float x, float y, float x1, float y1)
125{
126    return SVGPathSegCurvetoQuadraticRel::create(0, x, y, x1, y1);
127}
128
129PassRefPtr<SVGPathSegArcAbs> SVGPathElement::createSVGPathSegArcAbs(float x, float y, float r1, float r2, float angle, bool largeArcFlag, bool sweepFlag)
130{
131    return SVGPathSegArcAbs::create(0, x, y, r1, r2, angle, largeArcFlag, sweepFlag);
132}
133
134PassRefPtr<SVGPathSegArcRel> SVGPathElement::createSVGPathSegArcRel(float x, float y, float r1, float r2, float angle, bool largeArcFlag, bool sweepFlag)
135{
136    return SVGPathSegArcRel::create(0, x, y, r1, r2, angle, largeArcFlag, sweepFlag);
137}
138
139PassRefPtr<SVGPathSegLinetoHorizontalAbs> SVGPathElement::createSVGPathSegLinetoHorizontalAbs(float x)
140{
141    return SVGPathSegLinetoHorizontalAbs::create(0, x);
142}
143
144PassRefPtr<SVGPathSegLinetoHorizontalRel> SVGPathElement::createSVGPathSegLinetoHorizontalRel(float x)
145{
146    return SVGPathSegLinetoHorizontalRel::create(0, x);
147}
148
149PassRefPtr<SVGPathSegLinetoVerticalAbs> SVGPathElement::createSVGPathSegLinetoVerticalAbs(float y)
150{
151    return SVGPathSegLinetoVerticalAbs::create(0, y);
152}
153
154PassRefPtr<SVGPathSegLinetoVerticalRel> SVGPathElement::createSVGPathSegLinetoVerticalRel(float y)
155{
156    return SVGPathSegLinetoVerticalRel::create(0, y);
157}
158
159PassRefPtr<SVGPathSegCurvetoCubicSmoothAbs> SVGPathElement::createSVGPathSegCurvetoCubicSmoothAbs(float x, float y, float x2, float y2)
160{
161    return SVGPathSegCurvetoCubicSmoothAbs::create(0, x, y, x2, y2);
162}
163
164PassRefPtr<SVGPathSegCurvetoCubicSmoothRel> SVGPathElement::createSVGPathSegCurvetoCubicSmoothRel(float x, float y, float x2, float y2)
165{
166    return SVGPathSegCurvetoCubicSmoothRel::create(0, x, y, x2, y2);
167}
168
169PassRefPtr<SVGPathSegCurvetoQuadraticSmoothAbs> SVGPathElement::createSVGPathSegCurvetoQuadraticSmoothAbs(float x, float y)
170{
171    return SVGPathSegCurvetoQuadraticSmoothAbs::create(0, x, y);
172}
173
174PassRefPtr<SVGPathSegCurvetoQuadraticSmoothRel> SVGPathElement::createSVGPathSegCurvetoQuadraticSmoothRel(float x, float y)
175{
176    return SVGPathSegCurvetoQuadraticSmoothRel::create(0, x, y);
177}
178
179bool SVGPathElement::isSupportedAttribute(const QualifiedName& attrName)
180{
181    DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ());
182    if (supportedAttributes.isEmpty()) {
183        supportedAttributes.add(SVGNames::dAttr);
184        supportedAttributes.add(SVGNames::pathLengthAttr);
185    }
186    return supportedAttributes.contains<SVGAttributeHashTranslator>(attrName);
187}
188
189void SVGPathElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
190{
191    if (!isSupportedAttribute(name)) {
192        SVGGeometryElement::parseAttribute(name, value);
193        return;
194    }
195
196    SVGParsingError parseError = NoError;
197
198    if (name == SVGNames::dAttr) {
199        m_pathSegList->setBaseValueAsString(value, parseError);
200    } else if (name == SVGNames::pathLengthAttr) {
201        m_pathLength->setBaseValueAsString(value, parseError);
202        if (parseError == NoError && m_pathLength->baseValue()->value() < 0)
203            document().accessSVGExtensions().reportError("A negative value for path attribute <pathLength> is not allowed");
204    } else {
205        ASSERT_NOT_REACHED();
206    }
207
208    reportAttributeParsingError(parseError, name, value);
209}
210
211void SVGPathElement::svgAttributeChanged(const QualifiedName& attrName)
212{
213    if (!isSupportedAttribute(attrName)) {
214        SVGGeometryElement::svgAttributeChanged(attrName);
215        return;
216    }
217
218    SVGElement::InvalidationGuard invalidationGuard(this);
219
220    RenderSVGShape* renderer = toRenderSVGShape(this->renderer());
221
222    if (attrName == SVGNames::dAttr) {
223        if (renderer)
224            renderer->setNeedsShapeUpdate();
225
226        invalidateMPathDependencies();
227    }
228
229    if (renderer)
230        RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
231}
232
233void SVGPathElement::invalidateMPathDependencies()
234{
235    // <mpath> can only reference <path> but this dependency is not handled in
236    // markForLayoutAndParentResourceInvalidation so we update any mpath dependencies manually.
237    if (SVGElementSet* dependencies = setOfIncomingReferences()) {
238        SVGElementSet::iterator end = dependencies->end();
239        for (SVGElementSet::iterator it = dependencies->begin(); it != end; ++it) {
240            if (isSVGMPathElement(**it))
241                toSVGMPathElement(*it)->targetPathChanged();
242        }
243    }
244}
245
246Node::InsertionNotificationRequest SVGPathElement::insertedInto(ContainerNode* rootParent)
247{
248    SVGGeometryElement::insertedInto(rootParent);
249    invalidateMPathDependencies();
250    return InsertionDone;
251}
252
253void SVGPathElement::removedFrom(ContainerNode* rootParent)
254{
255    SVGGeometryElement::removedFrom(rootParent);
256    invalidateMPathDependencies();
257}
258
259void SVGPathElement::pathSegListChanged(ListModification listModification)
260{
261    m_pathSegList->baseValue()->clearByteStream();
262
263    invalidateSVGAttributes();
264
265    RenderSVGShape* renderer = toRenderSVGShape(this->renderer());
266    if (!renderer)
267        return;
268
269    renderer->setNeedsShapeUpdate();
270    RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
271}
272
273FloatRect SVGPathElement::getBBox()
274{
275    // By default, getBBox() returns objectBoundingBox but that will include
276    // markers so we override it to return just the path's bounding rect.
277
278    document().updateLayoutIgnorePendingStylesheets();
279
280    // FIXME: Eventually we should support getBBox for detached elements.
281    if (!renderer())
282        return FloatRect();
283
284    RenderSVGShape* renderer = toRenderSVGShape(this->renderer());
285    return renderer->path().boundingRect();
286}
287
288} // namespace blink
289