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