Home | History | Annotate | Download | only in svg
      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