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