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 23#if ENABLE(SVG) 24#include "SVGPathElement.h" 25 26#include "Attribute.h" 27#include "RenderSVGPath.h" 28#include "RenderSVGResource.h" 29#include "SVGElementInstance.h" 30#include "SVGMPathElement.h" 31#include "SVGNames.h" 32#include "SVGPathSegArc.h" 33#include "SVGPathSegClosePath.h" 34#include "SVGPathSegCurvetoCubic.h" 35#include "SVGPathSegCurvetoCubicSmooth.h" 36#include "SVGPathSegCurvetoQuadratic.h" 37#include "SVGPathSegCurvetoQuadraticSmooth.h" 38#include "SVGPathSegLineto.h" 39#include "SVGPathSegLinetoHorizontal.h" 40#include "SVGPathSegLinetoVertical.h" 41#include "SVGPathSegList.h" 42#include "SVGPathSegListBuilder.h" 43#include "SVGPathSegListPropertyTearOff.h" 44#include "SVGPathSegMoveto.h" 45#include "SVGPathUtilities.h" 46#include "SVGSVGElement.h" 47 48namespace WebCore { 49 50// Define custom animated property 'd'. 51const SVGPropertyInfo* SVGPathElement::dPropertyInfo() 52{ 53 static const SVGPropertyInfo* s_propertyInfo = 0; 54 if (!s_propertyInfo) { 55 s_propertyInfo = new SVGPropertyInfo(AnimatedPath, 56 PropertyIsReadWrite, 57 SVGNames::dAttr, 58 SVGNames::dAttr.localName(), 59 &SVGPathElement::synchronizeD, 60 &SVGPathElement::lookupOrCreateDWrapper); 61 } 62 return s_propertyInfo; 63} 64 65// Animated property definitions 66DEFINE_ANIMATED_NUMBER(SVGPathElement, SVGNames::pathLengthAttr, PathLength, pathLength) 67DEFINE_ANIMATED_BOOLEAN(SVGPathElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired) 68 69BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGPathElement) 70 REGISTER_LOCAL_ANIMATED_PROPERTY(d) 71 REGISTER_LOCAL_ANIMATED_PROPERTY(pathLength) 72 REGISTER_LOCAL_ANIMATED_PROPERTY(externalResourcesRequired) 73 REGISTER_PARENT_ANIMATED_PROPERTIES(SVGStyledTransformableElement) 74 REGISTER_PARENT_ANIMATED_PROPERTIES(SVGTests) 75END_REGISTER_ANIMATED_PROPERTIES 76 77inline SVGPathElement::SVGPathElement(const QualifiedName& tagName, Document* document) 78 : SVGStyledTransformableElement(tagName, document) 79 , m_pathByteStream(SVGPathByteStream::create()) 80 , m_pathSegList(PathSegUnalteredRole) 81 , m_isAnimValObserved(false) 82{ 83 ASSERT(hasTagName(SVGNames::pathTag)); 84 registerAnimatedPropertiesForSVGPathElement(); 85} 86 87PassRefPtr<SVGPathElement> SVGPathElement::create(const QualifiedName& tagName, Document* document) 88{ 89 return adoptRef(new SVGPathElement(tagName, document)); 90} 91 92float SVGPathElement::getTotalLength() 93{ 94 float totalLength = 0; 95 getTotalLengthOfSVGPathByteStream(pathByteStream(), totalLength); 96 return totalLength; 97} 98 99FloatPoint SVGPathElement::getPointAtLength(float length) 100{ 101 FloatPoint point; 102 getPointAtLengthOfSVGPathByteStream(pathByteStream(), length, point); 103 return point; 104} 105 106unsigned SVGPathElement::getPathSegAtLength(float length) 107{ 108 unsigned pathSeg = 0; 109 getSVGPathSegAtLengthFromSVGPathByteStream(pathByteStream(), length, pathSeg); 110 return pathSeg; 111} 112 113PassRefPtr<SVGPathSegClosePath> SVGPathElement::createSVGPathSegClosePath(SVGPathSegRole role) 114{ 115 return SVGPathSegClosePath::create(this, role); 116} 117 118PassRefPtr<SVGPathSegMovetoAbs> SVGPathElement::createSVGPathSegMovetoAbs(float x, float y, SVGPathSegRole role) 119{ 120 return SVGPathSegMovetoAbs::create(this, role, x, y); 121} 122 123PassRefPtr<SVGPathSegMovetoRel> SVGPathElement::createSVGPathSegMovetoRel(float x, float y, SVGPathSegRole role) 124{ 125 return SVGPathSegMovetoRel::create(this, role, x, y); 126} 127 128PassRefPtr<SVGPathSegLinetoAbs> SVGPathElement::createSVGPathSegLinetoAbs(float x, float y, SVGPathSegRole role) 129{ 130 return SVGPathSegLinetoAbs::create(this, role, x, y); 131} 132 133PassRefPtr<SVGPathSegLinetoRel> SVGPathElement::createSVGPathSegLinetoRel(float x, float y, SVGPathSegRole role) 134{ 135 return SVGPathSegLinetoRel::create(this, role, x, y); 136} 137 138PassRefPtr<SVGPathSegCurvetoCubicAbs> SVGPathElement::createSVGPathSegCurvetoCubicAbs(float x, float y, float x1, float y1, float x2, float y2, SVGPathSegRole role) 139{ 140 return SVGPathSegCurvetoCubicAbs::create(this, role, x, y, x1, y1, x2, y2); 141} 142 143PassRefPtr<SVGPathSegCurvetoCubicRel> SVGPathElement::createSVGPathSegCurvetoCubicRel(float x, float y, float x1, float y1, float x2, float y2, SVGPathSegRole role) 144{ 145 return SVGPathSegCurvetoCubicRel::create(this, role, x, y, x1, y1, x2, y2); 146} 147 148PassRefPtr<SVGPathSegCurvetoQuadraticAbs> SVGPathElement::createSVGPathSegCurvetoQuadraticAbs(float x, float y, float x1, float y1, SVGPathSegRole role) 149{ 150 return SVGPathSegCurvetoQuadraticAbs::create(this, role, x, y, x1, y1); 151} 152 153PassRefPtr<SVGPathSegCurvetoQuadraticRel> SVGPathElement::createSVGPathSegCurvetoQuadraticRel(float x, float y, float x1, float y1, SVGPathSegRole role) 154{ 155 return SVGPathSegCurvetoQuadraticRel::create(this, role, x, y, x1, y1); 156} 157 158PassRefPtr<SVGPathSegArcAbs> SVGPathElement::createSVGPathSegArcAbs(float x, float y, float r1, float r2, float angle, bool largeArcFlag, bool sweepFlag, SVGPathSegRole role) 159{ 160 return SVGPathSegArcAbs::create(this, role, x, y, r1, r2, angle, largeArcFlag, sweepFlag); 161} 162 163PassRefPtr<SVGPathSegArcRel> SVGPathElement::createSVGPathSegArcRel(float x, float y, float r1, float r2, float angle, bool largeArcFlag, bool sweepFlag, SVGPathSegRole role) 164{ 165 return SVGPathSegArcRel::create(this, role, x, y, r1, r2, angle, largeArcFlag, sweepFlag); 166} 167 168PassRefPtr<SVGPathSegLinetoHorizontalAbs> SVGPathElement::createSVGPathSegLinetoHorizontalAbs(float x, SVGPathSegRole role) 169{ 170 return SVGPathSegLinetoHorizontalAbs::create(this, role, x); 171} 172 173PassRefPtr<SVGPathSegLinetoHorizontalRel> SVGPathElement::createSVGPathSegLinetoHorizontalRel(float x, SVGPathSegRole role) 174{ 175 return SVGPathSegLinetoHorizontalRel::create(this, role, x); 176} 177 178PassRefPtr<SVGPathSegLinetoVerticalAbs> SVGPathElement::createSVGPathSegLinetoVerticalAbs(float y, SVGPathSegRole role) 179{ 180 return SVGPathSegLinetoVerticalAbs::create(this, role, y); 181} 182 183PassRefPtr<SVGPathSegLinetoVerticalRel> SVGPathElement::createSVGPathSegLinetoVerticalRel(float y, SVGPathSegRole role) 184{ 185 return SVGPathSegLinetoVerticalRel::create(this, role, y); 186} 187 188PassRefPtr<SVGPathSegCurvetoCubicSmoothAbs> SVGPathElement::createSVGPathSegCurvetoCubicSmoothAbs(float x, float y, float x2, float y2, SVGPathSegRole role) 189{ 190 return SVGPathSegCurvetoCubicSmoothAbs::create(this, role, x, y, x2, y2); 191} 192 193PassRefPtr<SVGPathSegCurvetoCubicSmoothRel> SVGPathElement::createSVGPathSegCurvetoCubicSmoothRel(float x, float y, float x2, float y2, SVGPathSegRole role) 194{ 195 return SVGPathSegCurvetoCubicSmoothRel::create(this, role, x, y, x2, y2); 196} 197 198PassRefPtr<SVGPathSegCurvetoQuadraticSmoothAbs> SVGPathElement::createSVGPathSegCurvetoQuadraticSmoothAbs(float x, float y, SVGPathSegRole role) 199{ 200 return SVGPathSegCurvetoQuadraticSmoothAbs::create(this, role, x, y); 201} 202 203PassRefPtr<SVGPathSegCurvetoQuadraticSmoothRel> SVGPathElement::createSVGPathSegCurvetoQuadraticSmoothRel(float x, float y, SVGPathSegRole role) 204{ 205 return SVGPathSegCurvetoQuadraticSmoothRel::create(this, role, x, y); 206} 207 208bool SVGPathElement::isSupportedAttribute(const QualifiedName& attrName) 209{ 210 DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ()); 211 if (supportedAttributes.isEmpty()) { 212 SVGTests::addSupportedAttributes(supportedAttributes); 213 SVGLangSpace::addSupportedAttributes(supportedAttributes); 214 SVGExternalResourcesRequired::addSupportedAttributes(supportedAttributes); 215 supportedAttributes.add(SVGNames::dAttr); 216 supportedAttributes.add(SVGNames::pathLengthAttr); 217 } 218 return supportedAttributes.contains<QualifiedName, SVGAttributeHashTranslator>(attrName); 219} 220 221void SVGPathElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 222{ 223 if (!isSupportedAttribute(name)) { 224 SVGStyledTransformableElement::parseAttribute(name, value); 225 return; 226 } 227 228 if (name == SVGNames::dAttr) { 229 if (!buildSVGPathByteStreamFromString(value, m_pathByteStream.get(), UnalteredParsing)) 230 document()->accessSVGExtensions()->reportError("Problem parsing d=\"" + value + "\""); 231 return; 232 } 233 234 if (name == SVGNames::pathLengthAttr) { 235 setPathLengthBaseValue(value.toFloat()); 236 if (pathLengthBaseValue() < 0) 237 document()->accessSVGExtensions()->reportError("A negative value for path attribute <pathLength> is not allowed"); 238 return; 239 } 240 241 if (SVGTests::parseAttribute(name, value)) 242 return; 243 if (SVGLangSpace::parseAttribute(name, value)) 244 return; 245 if (SVGExternalResourcesRequired::parseAttribute(name, value)) 246 return; 247 248 ASSERT_NOT_REACHED(); 249} 250 251void SVGPathElement::svgAttributeChanged(const QualifiedName& attrName) 252{ 253 if (!isSupportedAttribute(attrName)) { 254 SVGStyledTransformableElement::svgAttributeChanged(attrName); 255 return; 256 } 257 258 SVGElementInstance::InvalidationGuard invalidationGuard(this); 259 260 if (SVGTests::handleAttributeChange(this, attrName)) 261 return; 262 263 RenderSVGPath* renderer = toRenderSVGPath(this->renderer()); 264 265 if (attrName == SVGNames::dAttr) { 266 if (m_pathSegList.shouldSynchronize && !SVGAnimatedProperty::lookupWrapper<SVGPathElement, SVGAnimatedPathSegListPropertyTearOff>(this, dPropertyInfo())->isAnimating()) { 267 SVGPathSegList newList(PathSegUnalteredRole); 268 buildSVGPathSegListFromByteStream(m_pathByteStream.get(), this, newList, UnalteredParsing); 269 m_pathSegList.value = newList; 270 } 271 272 if (renderer) 273 renderer->setNeedsShapeUpdate(); 274 275 invalidateMPathDependencies(); 276 } 277 278 if (renderer) 279 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer); 280} 281 282void SVGPathElement::invalidateMPathDependencies() 283{ 284 // <mpath> can only reference <path> but this dependency is not handled in 285 // markForLayoutAndParentResourceInvalidation so we update any mpath dependencies manually. 286 ASSERT(document()); 287 if (HashSet<SVGElement*>* dependencies = document()->accessSVGExtensions()->setOfElementsReferencingTarget(this)) { 288 HashSet<SVGElement*>::iterator end = dependencies->end(); 289 for (HashSet<SVGElement*>::iterator it = dependencies->begin(); it != end; ++it) { 290 if ((*it)->hasTagName(SVGNames::mpathTag)) 291 static_cast<SVGMPathElement*>(*it)->targetPathChanged(); 292 } 293 } 294} 295 296Node::InsertionNotificationRequest SVGPathElement::insertedInto(ContainerNode* rootParent) 297{ 298 SVGStyledTransformableElement::insertedInto(rootParent); 299 invalidateMPathDependencies(); 300 return InsertionDone; 301} 302 303void SVGPathElement::removedFrom(ContainerNode* rootParent) 304{ 305 SVGStyledTransformableElement::removedFrom(rootParent); 306 invalidateMPathDependencies(); 307} 308 309SVGPathByteStream* SVGPathElement::pathByteStream() const 310{ 311 SVGAnimatedProperty* property = SVGAnimatedProperty::lookupWrapper<SVGPathElement, SVGAnimatedPathSegListPropertyTearOff>(this, dPropertyInfo()); 312 if (!property || !property->isAnimating()) 313 return m_pathByteStream.get(); 314 return static_cast<SVGAnimatedPathSegListPropertyTearOff*>(property)->animatedPathByteStream(); 315} 316 317PassRefPtr<SVGAnimatedProperty> SVGPathElement::lookupOrCreateDWrapper(SVGElement* contextElement) 318{ 319 ASSERT(contextElement); 320 SVGPathElement* ownerType = toSVGPathElement(contextElement); 321 322 if (SVGAnimatedProperty* property = SVGAnimatedProperty::lookupWrapper<SVGPathElement, SVGAnimatedPathSegListPropertyTearOff>(ownerType, dPropertyInfo())) 323 return property; 324 325 // Build initial SVGPathSegList. 326 buildSVGPathSegListFromByteStream(ownerType->m_pathByteStream.get(), ownerType, ownerType->m_pathSegList.value, UnalteredParsing); 327 328 return SVGAnimatedProperty::lookupOrCreateWrapper<SVGPathElement, SVGAnimatedPathSegListPropertyTearOff, SVGPathSegList> 329 (ownerType, dPropertyInfo(), ownerType->m_pathSegList.value); 330} 331 332void SVGPathElement::synchronizeD(SVGElement* contextElement) 333{ 334 ASSERT(contextElement); 335 SVGPathElement* ownerType = toSVGPathElement(contextElement); 336 if (!ownerType->m_pathSegList.shouldSynchronize) 337 return; 338 ownerType->m_pathSegList.synchronize(ownerType, dPropertyInfo()->attributeName, ownerType->m_pathSegList.value.valueAsString()); 339} 340 341SVGPathSegListPropertyTearOff* SVGPathElement::pathSegList() 342{ 343 m_pathSegList.shouldSynchronize = true; 344 return static_cast<SVGPathSegListPropertyTearOff*>(static_pointer_cast<SVGAnimatedPathSegListPropertyTearOff>(lookupOrCreateDWrapper(this))->baseVal()); 345} 346 347SVGPathSegListPropertyTearOff* SVGPathElement::normalizedPathSegList() 348{ 349 // FIXME: https://bugs.webkit.org/show_bug.cgi?id=15412 - Implement normalized path segment lists! 350 return 0; 351} 352 353SVGPathSegListPropertyTearOff* SVGPathElement::animatedPathSegList() 354{ 355 m_pathSegList.shouldSynchronize = true; 356 m_isAnimValObserved = true; 357 return static_cast<SVGPathSegListPropertyTearOff*>(static_pointer_cast<SVGAnimatedPathSegListPropertyTearOff>(lookupOrCreateDWrapper(this))->animVal()); 358} 359 360SVGPathSegListPropertyTearOff* SVGPathElement::animatedNormalizedPathSegList() 361{ 362 // FIXME: https://bugs.webkit.org/show_bug.cgi?id=15412 - Implement normalized path segment lists! 363 return 0; 364} 365 366void SVGPathElement::pathSegListChanged(SVGPathSegRole role, ListModification listModification) 367{ 368 switch (role) { 369 case PathSegNormalizedRole: 370 // FIXME: https://bugs.webkit.org/show_bug.cgi?id=15412 - Implement normalized path segment lists! 371 break; 372 case PathSegUnalteredRole: 373 if (listModification == ListModificationAppend) { 374 ASSERT(!m_pathSegList.value.isEmpty()); 375 appendSVGPathByteStreamFromSVGPathSeg(m_pathSegList.value.last(), m_pathByteStream.get(), UnalteredParsing); 376 } else 377 buildSVGPathByteStreamFromSVGPathSegList(m_pathSegList.value, m_pathByteStream.get(), UnalteredParsing); 378 break; 379 case PathSegUndefinedRole: 380 return; 381 } 382 383 invalidateSVGAttributes(); 384 385 RenderSVGPath* renderer = toRenderSVGPath(this->renderer()); 386 if (!renderer) 387 return; 388 389 renderer->setNeedsShapeUpdate(); 390 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer); 391} 392 393FloatRect SVGPathElement::getBBox(StyleUpdateStrategy styleUpdateStrategy) 394{ 395 if (styleUpdateStrategy == AllowStyleUpdate) 396 this->document()->updateLayoutIgnorePendingStylesheets(); 397 398 RenderSVGPath* renderer = toRenderSVGPath(this->renderer()); 399 400 // FIXME: Eventually we should support getBBox for detached elements. 401 if (!renderer) 402 return FloatRect(); 403 404 return renderer->path().boundingRect(); 405} 406 407RenderObject* SVGPathElement::createRenderer(RenderArena* arena, RenderStyle*) 408{ 409 // By default, any subclass is expected to do path-based drawing 410 return new (arena) RenderSVGPath(this); 411} 412 413} 414 415#endif // ENABLE(SVG) 416