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 "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