Home | History | Annotate | Download | only in svg
      1 /*
      2  * Copyright (C) 2004, 2005, 2007, 2009 Apple Inc. All rights reserved.
      3  *           (C) 2005 Rob Buis <buis (at) kde.org>
      4  *           (C) 2006 Alexander Kellett <lypanov (at) kde.org>
      5  * Copyright (C) Research In Motion Limited 2010. All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
     17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
     20  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     21  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     23  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     24  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     26  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 #include "config.h"
     30 
     31 #include "core/rendering/svg/SVGRenderTreeAsText.h"
     32 
     33 #include "SVGNames.h"
     34 #include "core/rendering/InlineTextBox.h"
     35 #include "core/rendering/RenderTreeAsText.h"
     36 #include "core/rendering/svg/RenderSVGGradientStop.h"
     37 #include "core/rendering/svg/RenderSVGImage.h"
     38 #include "core/rendering/svg/RenderSVGInlineText.h"
     39 #include "core/rendering/svg/RenderSVGResourceClipper.h"
     40 #include "core/rendering/svg/RenderSVGResourceFilter.h"
     41 #include "core/rendering/svg/RenderSVGResourceLinearGradient.h"
     42 #include "core/rendering/svg/RenderSVGResourceMarker.h"
     43 #include "core/rendering/svg/RenderSVGResourceMasker.h"
     44 #include "core/rendering/svg/RenderSVGResourcePattern.h"
     45 #include "core/rendering/svg/RenderSVGResourceRadialGradient.h"
     46 #include "core/rendering/svg/RenderSVGResourceSolidColor.h"
     47 #include "core/rendering/svg/RenderSVGRoot.h"
     48 #include "core/rendering/svg/RenderSVGShape.h"
     49 #include "core/rendering/svg/RenderSVGText.h"
     50 #include "core/rendering/svg/SVGInlineTextBox.h"
     51 #include "core/rendering/svg/SVGRootInlineBox.h"
     52 #include "core/svg/LinearGradientAttributes.h"
     53 #include "core/svg/PatternAttributes.h"
     54 #include "core/svg/RadialGradientAttributes.h"
     55 #include "core/svg/SVGCircleElement.h"
     56 #include "core/svg/SVGEllipseElement.h"
     57 #include "core/svg/SVGLineElement.h"
     58 #include "core/svg/SVGLinearGradientElement.h"
     59 #include "core/svg/SVGPathElement.h"
     60 #include "core/svg/SVGPathUtilities.h"
     61 #include "core/svg/SVGPatternElement.h"
     62 #include "core/svg/SVGPointList.h"
     63 #include "core/svg/SVGPolyElement.h"
     64 #include "core/svg/SVGRadialGradientElement.h"
     65 #include "core/svg/SVGRectElement.h"
     66 #include "core/svg/SVGStopElement.h"
     67 #include "platform/graphics/GraphicsTypes.h"
     68 
     69 #include <math.h>
     70 #include <memory>
     71 
     72 namespace WebCore {
     73 
     74 /** class + iomanip to help streaming list separators, i.e. ", " in string "a, b, c, d"
     75  * Can be used in cases where you don't know which item in the list is the first
     76  * one to be printed, but still want to avoid strings like ", b, c".
     77  */
     78 class TextStreamSeparator {
     79 public:
     80     TextStreamSeparator(const String& s)
     81         : m_separator(s)
     82         , m_needToSeparate(false)
     83     {
     84     }
     85 
     86 private:
     87     friend TextStream& operator<<(TextStream&, TextStreamSeparator&);
     88 
     89     String m_separator;
     90     bool m_needToSeparate;
     91 };
     92 
     93 TextStream& operator<<(TextStream& ts, TextStreamSeparator& sep)
     94 {
     95     if (sep.m_needToSeparate)
     96         ts << sep.m_separator;
     97     else
     98         sep.m_needToSeparate = true;
     99     return ts;
    100 }
    101 
    102 template<typename ValueType>
    103 static void writeNameValuePair(TextStream& ts, const char* name, ValueType value)
    104 {
    105     ts << " [" << name << "=" << value << "]";
    106 }
    107 
    108 template<typename ValueType>
    109 static void writeNameAndQuotedValue(TextStream& ts, const char* name, ValueType value)
    110 {
    111     ts << " [" << name << "=\"" << value << "\"]";
    112 }
    113 
    114 static void writeIfNotEmpty(TextStream& ts, const char* name, const String& value)
    115 {
    116     if (!value.isEmpty())
    117         writeNameValuePair(ts, name, value);
    118 }
    119 
    120 template<typename ValueType>
    121 static void writeIfNotDefault(TextStream& ts, const char* name, ValueType value, ValueType defaultValue)
    122 {
    123     if (value != defaultValue)
    124         writeNameValuePair(ts, name, value);
    125 }
    126 
    127 TextStream& operator<<(TextStream& ts, const AffineTransform& transform)
    128 {
    129     if (transform.isIdentity())
    130         ts << "identity";
    131     else
    132         ts << "{m=(("
    133            << transform.a() << "," << transform.b()
    134            << ")("
    135            << transform.c() << "," << transform.d()
    136            << ")) t=("
    137            << transform.e() << "," << transform.f()
    138            << ")}";
    139 
    140     return ts;
    141 }
    142 
    143 static TextStream& operator<<(TextStream& ts, const WindRule rule)
    144 {
    145     switch (rule) {
    146     case RULE_NONZERO:
    147         ts << "NON-ZERO";
    148         break;
    149     case RULE_EVENODD:
    150         ts << "EVEN-ODD";
    151         break;
    152     }
    153 
    154     return ts;
    155 }
    156 
    157 static TextStream& operator<<(TextStream& ts, const SVGUnitTypes::SVGUnitType& unitType)
    158 {
    159     ts << SVGPropertyTraits<SVGUnitTypes::SVGUnitType>::toString(unitType);
    160     return ts;
    161 }
    162 
    163 static TextStream& operator<<(TextStream& ts, const SVGMarkerUnitsType& markerUnit)
    164 {
    165     ts << SVGPropertyTraits<SVGMarkerUnitsType>::toString(markerUnit);
    166     return ts;
    167 }
    168 
    169 TextStream& operator<<(TextStream& ts, const Color& c)
    170 {
    171     return ts << c.nameForRenderTreeAsText();
    172 }
    173 
    174 // FIXME: Maybe this should be in KCanvasRenderingStyle.cpp
    175 static TextStream& operator<<(TextStream& ts, const DashArray& a)
    176 {
    177     ts << "{";
    178     DashArray::const_iterator end = a.end();
    179     for (DashArray::const_iterator it = a.begin(); it != end; ++it) {
    180         if (it != a.begin())
    181             ts << ", ";
    182         ts << *it;
    183     }
    184     ts << "}";
    185     return ts;
    186 }
    187 
    188 // FIXME: Maybe this should be in GraphicsTypes.cpp
    189 static TextStream& operator<<(TextStream& ts, LineCap style)
    190 {
    191     switch (style) {
    192     case ButtCap:
    193         ts << "BUTT";
    194         break;
    195     case RoundCap:
    196         ts << "ROUND";
    197         break;
    198     case SquareCap:
    199         ts << "SQUARE";
    200         break;
    201     }
    202     return ts;
    203 }
    204 
    205 // FIXME: Maybe this should be in GraphicsTypes.cpp
    206 static TextStream& operator<<(TextStream& ts, LineJoin style)
    207 {
    208     switch (style) {
    209     case MiterJoin:
    210         ts << "MITER";
    211         break;
    212     case RoundJoin:
    213         ts << "ROUND";
    214         break;
    215     case BevelJoin:
    216         ts << "BEVEL";
    217         break;
    218     }
    219     return ts;
    220 }
    221 
    222 static TextStream& operator<<(TextStream& ts, const SVGSpreadMethodType& type)
    223 {
    224     ts << SVGPropertyTraits<SVGSpreadMethodType>::toString(type).upper();
    225     return ts;
    226 }
    227 
    228 static void writeSVGPaintingResource(TextStream& ts, RenderSVGResource* resource)
    229 {
    230     if (resource->resourceType() == SolidColorResourceType) {
    231         ts << "[type=SOLID] [color=" << static_cast<RenderSVGResourceSolidColor*>(resource)->color() << "]";
    232         return;
    233     }
    234 
    235     // All other resources derive from RenderSVGResourceContainer
    236     RenderSVGResourceContainer* container = static_cast<RenderSVGResourceContainer*>(resource);
    237     SVGElement* element = container->element();
    238     ASSERT(element);
    239 
    240     if (resource->resourceType() == PatternResourceType)
    241         ts << "[type=PATTERN]";
    242     else if (resource->resourceType() == LinearGradientResourceType)
    243         ts << "[type=LINEAR-GRADIENT]";
    244     else if (resource->resourceType() == RadialGradientResourceType)
    245         ts << "[type=RADIAL-GRADIENT]";
    246 
    247     ts << " [id=\"" << element->getIdAttribute() << "\"]";
    248 }
    249 
    250 static void writeStyle(TextStream& ts, const RenderObject& object)
    251 {
    252     const RenderStyle* style = object.style();
    253     const SVGRenderStyle* svgStyle = style->svgStyle();
    254 
    255     if (!object.localTransform().isIdentity())
    256         writeNameValuePair(ts, "transform", object.localTransform());
    257     writeIfNotDefault(ts, "image rendering", style->imageRendering(), RenderStyle::initialImageRendering());
    258     writeIfNotDefault(ts, "opacity", style->opacity(), RenderStyle::initialOpacity());
    259     if (object.isSVGShape()) {
    260         const RenderSVGShape& shape = static_cast<const RenderSVGShape&>(object);
    261         ASSERT(shape.element());
    262 
    263         Color fallbackColor;
    264         if (RenderSVGResource* strokePaintingResource = RenderSVGResource::strokePaintingResource(const_cast<RenderSVGShape*>(&shape), shape.style(), fallbackColor)) {
    265             TextStreamSeparator s(" ");
    266             ts << " [stroke={" << s;
    267             writeSVGPaintingResource(ts, strokePaintingResource);
    268 
    269             SVGLengthContext lengthContext(shape.element());
    270             double dashOffset = svgStyle->strokeDashOffset().value(lengthContext);
    271             double strokeWidth = svgStyle->strokeWidth().value(lengthContext);
    272             const Vector<SVGLength>& dashes = svgStyle->strokeDashArray();
    273 
    274             DashArray dashArray;
    275             const Vector<SVGLength>::const_iterator end = dashes.end();
    276             for (Vector<SVGLength>::const_iterator it = dashes.begin(); it != end; ++it)
    277                 dashArray.append((*it).value(lengthContext));
    278 
    279             writeIfNotDefault(ts, "opacity", svgStyle->strokeOpacity(), 1.0f);
    280             writeIfNotDefault(ts, "stroke width", strokeWidth, 1.0);
    281             writeIfNotDefault(ts, "miter limit", svgStyle->strokeMiterLimit(), 4.0f);
    282             writeIfNotDefault(ts, "line cap", svgStyle->capStyle(), ButtCap);
    283             writeIfNotDefault(ts, "line join", svgStyle->joinStyle(), MiterJoin);
    284             writeIfNotDefault(ts, "dash offset", dashOffset, 0.0);
    285             if (!dashArray.isEmpty())
    286                 writeNameValuePair(ts, "dash array", dashArray);
    287 
    288             ts << "}]";
    289         }
    290 
    291         if (RenderSVGResource* fillPaintingResource = RenderSVGResource::fillPaintingResource(const_cast<RenderSVGShape*>(&shape), shape.style(), fallbackColor)) {
    292             TextStreamSeparator s(" ");
    293             ts << " [fill={" << s;
    294             writeSVGPaintingResource(ts, fillPaintingResource);
    295 
    296             writeIfNotDefault(ts, "opacity", svgStyle->fillOpacity(), 1.0f);
    297             writeIfNotDefault(ts, "fill rule", svgStyle->fillRule(), RULE_NONZERO);
    298             ts << "}]";
    299         }
    300         writeIfNotDefault(ts, "clip rule", svgStyle->clipRule(), RULE_NONZERO);
    301     }
    302 
    303     writeIfNotEmpty(ts, "start marker", svgStyle->markerStartResource());
    304     writeIfNotEmpty(ts, "middle marker", svgStyle->markerMidResource());
    305     writeIfNotEmpty(ts, "end marker", svgStyle->markerEndResource());
    306 }
    307 
    308 static TextStream& writePositionAndStyle(TextStream& ts, const RenderObject& object)
    309 {
    310     ts << " " << enclosingIntRect(const_cast<RenderObject&>(object).absoluteClippedOverflowRect());
    311     writeStyle(ts, object);
    312     return ts;
    313 }
    314 
    315 static TextStream& operator<<(TextStream& ts, const RenderSVGShape& shape)
    316 {
    317     writePositionAndStyle(ts, shape);
    318 
    319     SVGElement* svgElement = shape.element();
    320     SVGLengthContext lengthContext(svgElement);
    321 
    322     if (svgElement->hasTagName(SVGNames::rectTag)) {
    323         SVGRectElement* element = toSVGRectElement(svgElement);
    324         writeNameValuePair(ts, "x", element->xCurrentValue().value(lengthContext));
    325         writeNameValuePair(ts, "y", element->yCurrentValue().value(lengthContext));
    326         writeNameValuePair(ts, "width", element->widthCurrentValue().value(lengthContext));
    327         writeNameValuePair(ts, "height", element->heightCurrentValue().value(lengthContext));
    328     } else if (svgElement->hasTagName(SVGNames::lineTag)) {
    329         SVGLineElement* element = toSVGLineElement(svgElement);
    330         writeNameValuePair(ts, "x1", element->x1CurrentValue().value(lengthContext));
    331         writeNameValuePair(ts, "y1", element->y1CurrentValue().value(lengthContext));
    332         writeNameValuePair(ts, "x2", element->x2CurrentValue().value(lengthContext));
    333         writeNameValuePair(ts, "y2", element->y2CurrentValue().value(lengthContext));
    334     } else if (svgElement->hasTagName(SVGNames::ellipseTag)) {
    335         SVGEllipseElement* element = toSVGEllipseElement(svgElement);
    336         writeNameValuePair(ts, "cx", element->cxCurrentValue().value(lengthContext));
    337         writeNameValuePair(ts, "cy", element->cyCurrentValue().value(lengthContext));
    338         writeNameValuePair(ts, "rx", element->rxCurrentValue().value(lengthContext));
    339         writeNameValuePair(ts, "ry", element->ryCurrentValue().value(lengthContext));
    340     } else if (svgElement->hasTagName(SVGNames::circleTag)) {
    341         SVGCircleElement* element = toSVGCircleElement(svgElement);
    342         writeNameValuePair(ts, "cx", element->cxCurrentValue().value(lengthContext));
    343         writeNameValuePair(ts, "cy", element->cyCurrentValue().value(lengthContext));
    344         writeNameValuePair(ts, "r", element->rCurrentValue().value(lengthContext));
    345     } else if (svgElement->hasTagName(SVGNames::polygonTag) || svgElement->hasTagName(SVGNames::polylineTag)) {
    346         writeNameAndQuotedValue(ts, "points", toSVGPolyElement(svgElement)->pointsCurrentValue().valueAsString());
    347     } else if (svgElement->hasTagName(SVGNames::pathTag)) {
    348         String pathString;
    349         // FIXME: We should switch to UnalteredParsing here - this will affect the path dumping output of dozens of tests.
    350         buildStringFromByteStream(toSVGPathElement(svgElement)->pathByteStream(), pathString, NormalizedParsing);
    351         writeNameAndQuotedValue(ts, "data", pathString);
    352     } else
    353         ASSERT_NOT_REACHED();
    354     return ts;
    355 }
    356 
    357 static TextStream& operator<<(TextStream& ts, const RenderSVGRoot& root)
    358 {
    359     return writePositionAndStyle(ts, root);
    360 }
    361 
    362 static void writeRenderSVGTextBox(TextStream& ts, const RenderSVGText& text)
    363 {
    364     SVGRootInlineBox* box = toSVGRootInlineBox(text.firstRootBox());
    365     if (!box)
    366         return;
    367 
    368     ts << " " << enclosingIntRect(FloatRect(text.location(), FloatSize(box->logicalWidth(), box->logicalHeight())));
    369 
    370     // FIXME: Remove this hack, once the new text layout engine is completly landed. We want to preserve the old layout test results for now.
    371     ts << " contains 1 chunk(s)";
    372 
    373     if (text.parent() && (text.parent()->style()->visitedDependentColor(CSSPropertyColor) != text.style()->visitedDependentColor(CSSPropertyColor)))
    374         writeNameValuePair(ts, "color", text.resolveColor(CSSPropertyColor).nameForRenderTreeAsText());
    375 }
    376 
    377 static inline void writeSVGInlineTextBox(TextStream& ts, SVGInlineTextBox* textBox, int indent)
    378 {
    379     Vector<SVGTextFragment>& fragments = textBox->textFragments();
    380     if (fragments.isEmpty())
    381         return;
    382 
    383     RenderSVGInlineText* textRenderer = toRenderSVGInlineText(textBox->textRenderer());
    384     ASSERT(textRenderer);
    385 
    386     const SVGRenderStyle* svgStyle = textRenderer->style()->svgStyle();
    387     String text = textBox->textRenderer()->text();
    388 
    389     unsigned fragmentsSize = fragments.size();
    390     for (unsigned i = 0; i < fragmentsSize; ++i) {
    391         SVGTextFragment& fragment = fragments.at(i);
    392         writeIndent(ts, indent + 1);
    393 
    394         unsigned startOffset = fragment.characterOffset;
    395         unsigned endOffset = fragment.characterOffset + fragment.length;
    396 
    397         // FIXME: Remove this hack, once the new text layout engine is completly landed. We want to preserve the old layout test results for now.
    398         ts << "chunk 1 ";
    399         ETextAnchor anchor = svgStyle->textAnchor();
    400         bool isVerticalText = svgStyle->isVerticalWritingMode();
    401         if (anchor == TA_MIDDLE) {
    402             ts << "(middle anchor";
    403             if (isVerticalText)
    404                 ts << ", vertical";
    405             ts << ") ";
    406         } else if (anchor == TA_END) {
    407             ts << "(end anchor";
    408             if (isVerticalText)
    409                 ts << ", vertical";
    410             ts << ") ";
    411         } else if (isVerticalText)
    412             ts << "(vertical) ";
    413         startOffset -= textBox->start();
    414         endOffset -= textBox->start();
    415         // </hack>
    416 
    417         ts << "text run " << i + 1 << " at (" << fragment.x << "," << fragment.y << ")";
    418         ts << " startOffset " << startOffset << " endOffset " << endOffset;
    419         if (isVerticalText)
    420             ts << " height " << fragment.height;
    421         else
    422             ts << " width " << fragment.width;
    423 
    424         if (!textBox->isLeftToRightDirection() || textBox->dirOverride()) {
    425             ts << (textBox->isLeftToRightDirection() ? " LTR" : " RTL");
    426             if (textBox->dirOverride())
    427                 ts << " override";
    428         }
    429 
    430         ts << ": " << quoteAndEscapeNonPrintables(text.substring(fragment.characterOffset, fragment.length)) << "\n";
    431     }
    432 }
    433 
    434 static inline void writeSVGInlineTextBoxes(TextStream& ts, const RenderText& text, int indent)
    435 {
    436     for (InlineTextBox* box = text.firstTextBox(); box; box = box->nextTextBox()) {
    437         if (!box->isSVGInlineTextBox())
    438             continue;
    439 
    440         writeSVGInlineTextBox(ts, toSVGInlineTextBox(box), indent);
    441     }
    442 }
    443 
    444 static void writeStandardPrefix(TextStream& ts, const RenderObject& object, int indent)
    445 {
    446     writeIndent(ts, indent);
    447     ts << object.renderName();
    448 
    449     if (object.node())
    450         ts << " {" << object.node()->nodeName() << "}";
    451 }
    452 
    453 static void writeChildren(TextStream& ts, const RenderObject& object, int indent)
    454 {
    455     for (RenderObject* child = object.firstChild(); child; child = child->nextSibling())
    456         write(ts, *child, indent + 1);
    457 }
    458 
    459 static inline void writeCommonGradientProperties(TextStream& ts, SVGSpreadMethodType spreadMethod, const AffineTransform& gradientTransform, SVGUnitTypes::SVGUnitType gradientUnits)
    460 {
    461     writeNameValuePair(ts, "gradientUnits", gradientUnits);
    462 
    463     if (spreadMethod != SVGSpreadMethodPad)
    464         ts << " [spreadMethod=" << spreadMethod << "]";
    465 
    466     if (!gradientTransform.isIdentity())
    467         ts << " [gradientTransform=" << gradientTransform << "]";
    468 }
    469 
    470 void writeSVGResourceContainer(TextStream& ts, const RenderObject& object, int indent)
    471 {
    472     writeStandardPrefix(ts, object, indent);
    473 
    474     Element* element = toElement(object.node());
    475     const AtomicString& id = element->getIdAttribute();
    476     writeNameAndQuotedValue(ts, "id", id);
    477 
    478     RenderSVGResourceContainer* resource = toRenderSVGResourceContainer(const_cast<RenderObject*>(&object));
    479     ASSERT(resource);
    480 
    481     if (resource->resourceType() == MaskerResourceType) {
    482         RenderSVGResourceMasker* masker = toRenderSVGResourceMasker(resource);
    483         writeNameValuePair(ts, "maskUnits", masker->maskUnits());
    484         writeNameValuePair(ts, "maskContentUnits", masker->maskContentUnits());
    485         ts << "\n";
    486     } else if (resource->resourceType() == FilterResourceType) {
    487         RenderSVGResourceFilter* filter = toRenderSVGResourceFilter(resource);
    488         writeNameValuePair(ts, "filterUnits", filter->filterUnits());
    489         writeNameValuePair(ts, "primitiveUnits", filter->primitiveUnits());
    490         ts << "\n";
    491         // Creating a placeholder filter which is passed to the builder.
    492         FloatRect dummyRect;
    493         RefPtr<SVGFilter> dummyFilter = SVGFilter::create(AffineTransform(), dummyRect, dummyRect, dummyRect, true);
    494         if (RefPtr<SVGFilterBuilder> builder = filter->buildPrimitives(dummyFilter.get())) {
    495             if (FilterEffect* lastEffect = builder->lastEffect())
    496                 lastEffect->externalRepresentation(ts, indent + 1);
    497         }
    498     } else if (resource->resourceType() == ClipperResourceType) {
    499         writeNameValuePair(ts, "clipPathUnits", toRenderSVGResourceClipper(resource)->clipPathUnits());
    500         ts << "\n";
    501     } else if (resource->resourceType() == MarkerResourceType) {
    502         RenderSVGResourceMarker* marker = toRenderSVGResourceMarker(resource);
    503         writeNameValuePair(ts, "markerUnits", marker->markerUnits());
    504         ts << " [ref at " << marker->referencePoint() << "]";
    505         ts << " [angle=";
    506         if (marker->angle() == -1)
    507             ts << "auto" << "]\n";
    508         else
    509             ts << marker->angle() << "]\n";
    510     } else if (resource->resourceType() == PatternResourceType) {
    511         RenderSVGResourcePattern* pattern = static_cast<RenderSVGResourcePattern*>(resource);
    512 
    513         // Dump final results that are used for rendering. No use in asking SVGPatternElement for its patternUnits(), as it may
    514         // link to other patterns using xlink:href, we need to build the full inheritance chain, aka. collectPatternProperties()
    515         PatternAttributes attributes;
    516         toSVGPatternElement(pattern->element())->collectPatternAttributes(attributes);
    517 
    518         writeNameValuePair(ts, "patternUnits", attributes.patternUnits());
    519         writeNameValuePair(ts, "patternContentUnits", attributes.patternContentUnits());
    520 
    521         AffineTransform transform = attributes.patternTransform();
    522         if (!transform.isIdentity())
    523             ts << " [patternTransform=" << transform << "]";
    524         ts << "\n";
    525     } else if (resource->resourceType() == LinearGradientResourceType) {
    526         RenderSVGResourceLinearGradient* gradient = static_cast<RenderSVGResourceLinearGradient*>(resource);
    527 
    528         // Dump final results that are used for rendering. No use in asking SVGGradientElement for its gradientUnits(), as it may
    529         // link to other gradients using xlink:href, we need to build the full inheritance chain, aka. collectGradientProperties()
    530         LinearGradientAttributes attributes;
    531         toSVGLinearGradientElement(gradient->element())->collectGradientAttributes(attributes);
    532         writeCommonGradientProperties(ts, attributes.spreadMethod(), attributes.gradientTransform(), attributes.gradientUnits());
    533 
    534         ts << " [start=" << gradient->startPoint(attributes) << "] [end=" << gradient->endPoint(attributes) << "]\n";
    535     }  else if (resource->resourceType() == RadialGradientResourceType) {
    536         RenderSVGResourceRadialGradient* gradient = toRenderSVGResourceRadialGradient(resource);
    537 
    538         // Dump final results that are used for rendering. No use in asking SVGGradientElement for its gradientUnits(), as it may
    539         // link to other gradients using xlink:href, we need to build the full inheritance chain, aka. collectGradientProperties()
    540         RadialGradientAttributes attributes;
    541         toSVGRadialGradientElement(gradient->element())->collectGradientAttributes(attributes);
    542         writeCommonGradientProperties(ts, attributes.spreadMethod(), attributes.gradientTransform(), attributes.gradientUnits());
    543 
    544         FloatPoint focalPoint = gradient->focalPoint(attributes);
    545         FloatPoint centerPoint = gradient->centerPoint(attributes);
    546         float radius = gradient->radius(attributes);
    547         float focalRadius = gradient->focalRadius(attributes);
    548 
    549         ts << " [center=" << centerPoint << "] [focal=" << focalPoint << "] [radius=" << radius << "] [focalRadius=" << focalRadius << "]\n";
    550     } else
    551         ts << "\n";
    552     writeChildren(ts, object, indent);
    553 }
    554 
    555 void writeSVGContainer(TextStream& ts, const RenderObject& container, int indent)
    556 {
    557     // Currently RenderSVGResourceFilterPrimitive has no meaningful output.
    558     if (container.isSVGResourceFilterPrimitive())
    559         return;
    560     writeStandardPrefix(ts, container, indent);
    561     writePositionAndStyle(ts, container);
    562     ts << "\n";
    563     writeResources(ts, container, indent);
    564     writeChildren(ts, container, indent);
    565 }
    566 
    567 void write(TextStream& ts, const RenderSVGRoot& root, int indent)
    568 {
    569     writeStandardPrefix(ts, root, indent);
    570     ts << root << "\n";
    571     writeChildren(ts, root, indent);
    572 }
    573 
    574 void writeSVGText(TextStream& ts, const RenderSVGText& text, int indent)
    575 {
    576     writeStandardPrefix(ts, text, indent);
    577     writeRenderSVGTextBox(ts, text);
    578     ts << "\n";
    579     writeResources(ts, text, indent);
    580     writeChildren(ts, text, indent);
    581 }
    582 
    583 void writeSVGInlineText(TextStream& ts, const RenderSVGInlineText& text, int indent)
    584 {
    585     writeStandardPrefix(ts, text, indent);
    586     ts << " " << enclosingIntRect(FloatRect(text.firstRunOrigin(), text.floatLinesBoundingBox().size())) << "\n";
    587     writeResources(ts, text, indent);
    588     writeSVGInlineTextBoxes(ts, text, indent);
    589 }
    590 
    591 void writeSVGImage(TextStream& ts, const RenderSVGImage& image, int indent)
    592 {
    593     writeStandardPrefix(ts, image, indent);
    594     writePositionAndStyle(ts, image);
    595     ts << "\n";
    596     writeResources(ts, image, indent);
    597 }
    598 
    599 void write(TextStream& ts, const RenderSVGShape& shape, int indent)
    600 {
    601     writeStandardPrefix(ts, shape, indent);
    602     ts << shape << "\n";
    603     writeResources(ts, shape, indent);
    604 }
    605 
    606 void writeSVGGradientStop(TextStream& ts, const RenderSVGGradientStop& stop, int indent)
    607 {
    608     writeStandardPrefix(ts, stop, indent);
    609 
    610     SVGStopElement* stopElement = toSVGStopElement(stop.node());
    611     ASSERT(stopElement);
    612 
    613     RenderStyle* style = stop.style();
    614     if (!style)
    615         return;
    616 
    617     ts << " [offset=" << stopElement->offsetCurrentValue() << "] [color=" << stopElement->stopColorIncludingOpacity() << "]\n";
    618 }
    619 
    620 void writeResources(TextStream& ts, const RenderObject& object, int indent)
    621 {
    622     const RenderStyle* style = object.style();
    623     const SVGRenderStyle* svgStyle = style->svgStyle();
    624 
    625     // FIXME: We want to use SVGResourcesCache to determine which resources are present, instead of quering the resource <-> id cache.
    626     // For now leave the DRT output as is, but later on we should change this so cycles are properly ignored in the DRT output.
    627     RenderObject& renderer = const_cast<RenderObject&>(object);
    628     if (!svgStyle->maskerResource().isEmpty()) {
    629         if (RenderSVGResourceMasker* masker = getRenderSVGResourceById<RenderSVGResourceMasker>(object.document(), svgStyle->maskerResource())) {
    630             writeIndent(ts, indent);
    631             ts << " ";
    632             writeNameAndQuotedValue(ts, "masker", svgStyle->maskerResource());
    633             ts << " ";
    634             writeStandardPrefix(ts, *masker, 0);
    635             ts << " " << masker->resourceBoundingBox(&renderer) << "\n";
    636         }
    637     }
    638     if (!svgStyle->clipperResource().isEmpty()) {
    639         if (RenderSVGResourceClipper* clipper = getRenderSVGResourceById<RenderSVGResourceClipper>(object.document(), svgStyle->clipperResource())) {
    640             writeIndent(ts, indent);
    641             ts << " ";
    642             writeNameAndQuotedValue(ts, "clipPath", svgStyle->clipperResource());
    643             ts << " ";
    644             writeStandardPrefix(ts, *clipper, 0);
    645             ts << " " << clipper->resourceBoundingBox(&renderer) << "\n";
    646         }
    647     }
    648     if (!svgStyle->filterResource().isEmpty()) {
    649         if (RenderSVGResourceFilter* filter = getRenderSVGResourceById<RenderSVGResourceFilter>(object.document(), svgStyle->filterResource())) {
    650             writeIndent(ts, indent);
    651             ts << " ";
    652             writeNameAndQuotedValue(ts, "filter", svgStyle->filterResource());
    653             ts << " ";
    654             writeStandardPrefix(ts, *filter, 0);
    655             ts << " " << filter->resourceBoundingBox(&renderer) << "\n";
    656         }
    657     }
    658 }
    659 
    660 } // namespace WebCore
    661