2 * Copyright (C) 2004, 2005, 2007, 2009 Apple Inc. All rights reserved.
3 * (C) 2005 Rob Buis <buis@kde.org>
4 * (C) 2006 Alexander Kellett <lypanov@kde.org>
5 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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.
32 #include "SVGRenderTreeAsText.h"
34 #include "GraphicsTypes.h"
35 #include "HTMLNames.h"
36 #include "InlineTextBox.h"
37 #include "LinearGradientAttributes.h"
38 #include "NodeRenderStyle.h"
40 #include "PatternAttributes.h"
41 #include "RadialGradientAttributes.h"
42 #include "RenderImage.h"
43 #include "RenderSVGContainer.h"
44 #include "RenderSVGGradientStop.h"
45 #include "RenderSVGImage.h"
46 #include "RenderSVGInlineText.h"
47 #include "RenderSVGPath.h"
48 #include "RenderSVGResourceClipper.h"
49 #include "RenderSVGResourceFilter.h"
50 #include "RenderSVGResourceGradient.h"
51 #include "RenderSVGResourceLinearGradient.h"
52 #include "RenderSVGResourceMarker.h"
53 #include "RenderSVGResourceMasker.h"
54 #include "RenderSVGResourcePattern.h"
55 #include "RenderSVGResourceRadialGradient.h"
56 #include "RenderSVGResourceSolidColor.h"
57 #include "RenderSVGRoot.h"
58 #include "RenderSVGText.h"
59 #include "RenderTreeAsText.h"
60 #include "SVGCircleElement.h"
61 #include "SVGEllipseElement.h"
62 #include "SVGInlineTextBox.h"
63 #include "SVGLineElement.h"
64 #include "SVGLinearGradientElement.h"
66 #include "SVGPathElement.h"
67 #include "SVGPathParserFactory.h"
68 #include "SVGPatternElement.h"
69 #include "SVGPointList.h"
70 #include "SVGPolyElement.h"
71 #include "SVGRadialGradientElement.h"
72 #include "SVGRectElement.h"
73 #include "SVGRootInlineBox.h"
74 #include "SVGStopElement.h"
75 #include "SVGStyledElement.h"
81 /** class + iomanip to help streaming list separators, i.e. ", " in string "a, b, c, d"
82 * Can be used in cases where you don't know which item in the list is the first
83 * one to be printed, but still want to avoid strings like ", b, c".
85 class TextStreamSeparator {
87 TextStreamSeparator(const String& s)
89 , m_needToSeparate(false)
94 friend TextStream& operator<<(TextStream&, TextStreamSeparator&);
97 bool m_needToSeparate;
100 TextStream& operator<<(TextStream& ts, TextStreamSeparator& sep)
102 if (sep.m_needToSeparate)
103 ts << sep.m_separator;
105 sep.m_needToSeparate = true;
109 template<typename ValueType>
110 static void writeNameValuePair(TextStream& ts, const char* name, ValueType value)
112 ts << " [" << name << "=" << value << "]";
115 template<typename ValueType>
116 static void writeNameAndQuotedValue(TextStream& ts, const char* name, ValueType value)
118 ts << " [" << name << "=\"" << value << "\"]";
121 static void writeIfNotEmpty(TextStream& ts, const char* name, const String& value)
123 if (!value.isEmpty())
124 writeNameValuePair(ts, name, value);
127 template<typename ValueType>
128 static void writeIfNotDefault(TextStream& ts, const char* name, ValueType value, ValueType defaultValue)
130 if (value != defaultValue)
131 writeNameValuePair(ts, name, value);
134 TextStream& operator<<(TextStream& ts, const FloatRect &r)
137 if (hasFractions(r.x()))
142 if (hasFractions(r.y()))
147 if (hasFractions(r.width()))
150 ts << int(r.width());
152 if (hasFractions(r.height()))
155 ts << int(r.height());
159 TextStream& operator<<(TextStream& ts, const AffineTransform& transform)
161 if (transform.isIdentity())
165 << transform.a() << "," << transform.b()
167 << transform.c() << "," << transform.d()
169 << transform.e() << "," << transform.f()
175 static TextStream& operator<<(TextStream& ts, const WindRule rule)
189 static TextStream& operator<<(TextStream& ts, const SVGUnitTypes::SVGUnitType& unitType)
191 ts << SVGPropertyTraits<SVGUnitTypes::SVGUnitType>::toString(unitType);
195 static TextStream& operator<<(TextStream& ts, const SVGMarkerUnitsType& markerUnit)
197 ts << SVGPropertyTraits<SVGMarkerUnitsType>::toString(markerUnit);
201 TextStream& operator<<(TextStream& ts, const Color& c)
203 return ts << c.nameForRenderTreeAsText();
206 // FIXME: Maybe this should be in KCanvasRenderingStyle.cpp
207 static TextStream& operator<<(TextStream& ts, const DashArray& a)
210 DashArray::const_iterator end = a.end();
211 for (DashArray::const_iterator it = a.begin(); it != end; ++it) {
220 // FIXME: Maybe this should be in GraphicsTypes.cpp
221 static TextStream& operator<<(TextStream& ts, LineCap style)
237 // FIXME: Maybe this should be in GraphicsTypes.cpp
238 static TextStream& operator<<(TextStream& ts, LineJoin style)
254 static TextStream& operator<<(TextStream& ts, const SVGSpreadMethodType& type)
256 ts << SVGPropertyTraits<SVGSpreadMethodType>::toString(type).upper();
260 static void writeSVGPaintingResource(TextStream& ts, RenderSVGResource* resource)
262 if (resource->resourceType() == SolidColorResourceType) {
263 ts << "[type=SOLID] [color=" << static_cast<RenderSVGResourceSolidColor*>(resource)->color() << "]";
267 // All other resources derive from RenderSVGResourceContainer
268 RenderSVGResourceContainer* container = static_cast<RenderSVGResourceContainer*>(resource);
269 Node* node = container->node();
271 ASSERT(node->isSVGElement());
273 if (resource->resourceType() == PatternResourceType)
274 ts << "[type=PATTERN]";
275 else if (resource->resourceType() == LinearGradientResourceType)
276 ts << "[type=LINEAR-GRADIENT]";
277 else if (resource->resourceType() == RadialGradientResourceType)
278 ts << "[type=RADIAL-GRADIENT]";
280 ts << " [id=\"" << static_cast<SVGElement*>(node)->getIdAttribute() << "\"]";
283 static void writeStyle(TextStream& ts, const RenderObject& object)
285 const RenderStyle* style = object.style();
286 const SVGRenderStyle* svgStyle = style->svgStyle();
288 if (!object.localTransform().isIdentity())
289 writeNameValuePair(ts, "transform", object.localTransform());
290 writeIfNotDefault(ts, "image rendering", style->imageRendering(), RenderStyle::initialImageRendering());
291 writeIfNotDefault(ts, "opacity", style->opacity(), RenderStyle::initialOpacity());
292 if (object.isSVGPath()) {
293 const RenderSVGPath& path = static_cast<const RenderSVGPath&>(object);
295 ASSERT(path.node()->isSVGElement());
298 if (RenderSVGResource* strokePaintingResource = RenderSVGResource::strokePaintingResource(const_cast<RenderSVGPath*>(&path), path.style(), fallbackColor)) {
299 TextStreamSeparator s(" ");
300 ts << " [stroke={" << s;
301 writeSVGPaintingResource(ts, strokePaintingResource);
303 SVGElement* element = static_cast<SVGElement*>(path.node());
304 double dashOffset = svgStyle->strokeDashOffset().value(element);
305 double strokeWidth = svgStyle->strokeWidth().value(element);
306 const Vector<SVGLength>& dashes = svgStyle->strokeDashArray();
309 const Vector<SVGLength>::const_iterator end = dashes.end();
310 for (Vector<SVGLength>::const_iterator it = dashes.begin(); it != end; ++it)
311 dashArray.append((*it).value(element));
313 writeIfNotDefault(ts, "opacity", svgStyle->strokeOpacity(), 1.0f);
314 writeIfNotDefault(ts, "stroke width", strokeWidth, 1.0);
315 writeIfNotDefault(ts, "miter limit", svgStyle->strokeMiterLimit(), 4.0f);
316 writeIfNotDefault(ts, "line cap", svgStyle->capStyle(), ButtCap);
317 writeIfNotDefault(ts, "line join", svgStyle->joinStyle(), MiterJoin);
318 writeIfNotDefault(ts, "dash offset", dashOffset, 0.0);
319 if (!dashArray.isEmpty())
320 writeNameValuePair(ts, "dash array", dashArray);
325 if (RenderSVGResource* fillPaintingResource = RenderSVGResource::fillPaintingResource(const_cast<RenderSVGPath*>(&path), path.style(), fallbackColor)) {
326 TextStreamSeparator s(" ");
327 ts << " [fill={" << s;
328 writeSVGPaintingResource(ts, fillPaintingResource);
330 writeIfNotDefault(ts, "opacity", svgStyle->fillOpacity(), 1.0f);
331 writeIfNotDefault(ts, "fill rule", svgStyle->fillRule(), RULE_NONZERO);
334 writeIfNotDefault(ts, "clip rule", svgStyle->clipRule(), RULE_NONZERO);
337 writeIfNotEmpty(ts, "start marker", svgStyle->markerStartResource());
338 writeIfNotEmpty(ts, "middle marker", svgStyle->markerMidResource());
339 writeIfNotEmpty(ts, "end marker", svgStyle->markerEndResource());
342 static TextStream& writePositionAndStyle(TextStream& ts, const RenderObject& object)
344 ts << " " << const_cast<RenderObject&>(object).absoluteClippedOverflowRect();
345 writeStyle(ts, object);
349 static TextStream& operator<<(TextStream& ts, const RenderSVGPath& path)
351 writePositionAndStyle(ts, path);
353 ASSERT(path.node()->isSVGElement());
354 SVGElement* svgElement = static_cast<SVGElement*>(path.node());
356 if (svgElement->hasTagName(SVGNames::rectTag)) {
357 SVGRectElement* element = static_cast<SVGRectElement*>(svgElement);
358 writeNameValuePair(ts, "x", element->x().value(element));
359 writeNameValuePair(ts, "y", element->y().value(element));
360 writeNameValuePair(ts, "width", element->width().value(element));
361 writeNameValuePair(ts, "height", element->height().value(element));
362 } else if (svgElement->hasTagName(SVGNames::lineTag)) {
363 SVGLineElement* element = static_cast<SVGLineElement*>(svgElement);
364 writeNameValuePair(ts, "x1", element->x1().value(element));
365 writeNameValuePair(ts, "y1", element->y1().value(element));
366 writeNameValuePair(ts, "x2", element->x2().value(element));
367 writeNameValuePair(ts, "y2", element->y2().value(element));
368 } else if (svgElement->hasTagName(SVGNames::ellipseTag)) {
369 SVGEllipseElement* element = static_cast<SVGEllipseElement*>(svgElement);
370 writeNameValuePair(ts, "cx", element->cx().value(element));
371 writeNameValuePair(ts, "cy", element->cy().value(element));
372 writeNameValuePair(ts, "rx", element->rx().value(element));
373 writeNameValuePair(ts, "ry", element->ry().value(element));
374 } else if (svgElement->hasTagName(SVGNames::circleTag)) {
375 SVGCircleElement* element = static_cast<SVGCircleElement*>(svgElement);
376 writeNameValuePair(ts, "cx", element->cx().value(element));
377 writeNameValuePair(ts, "cy", element->cy().value(element));
378 writeNameValuePair(ts, "r", element->r().value(element));
379 } else if (svgElement->hasTagName(SVGNames::polygonTag) || svgElement->hasTagName(SVGNames::polylineTag)) {
380 SVGPolyElement* element = static_cast<SVGPolyElement*>(svgElement);
381 writeNameAndQuotedValue(ts, "points", element->pointList().valueAsString());
382 } else if (svgElement->hasTagName(SVGNames::pathTag)) {
383 SVGPathElement* element = static_cast<SVGPathElement*>(svgElement);
385 // FIXME: We should switch to UnalteredParsing here - this will affect the path dumping output of dozens of tests.
386 SVGPathParserFactory::self()->buildStringFromByteStream(element->pathByteStream(), pathString, NormalizedParsing);
387 writeNameAndQuotedValue(ts, "data", pathString);
389 ASSERT_NOT_REACHED();
393 static TextStream& operator<<(TextStream& ts, const RenderSVGRoot& root)
395 return writePositionAndStyle(ts, root);
398 static void writeRenderSVGTextBox(TextStream& ts, const RenderBlock& text)
400 SVGRootInlineBox* box = static_cast<SVGRootInlineBox*>(text.firstRootBox());
404 // FIXME: For now use an int for logicalWidth, although this makes it harder
405 // to detect any changes caused by the conversion to floating point. :(
406 int logicalWidth = ceilf(box->x() + box->logicalWidth()) - box->x();
407 ts << " at (" << text.x() << "," << text.y() << ") size " << logicalWidth << "x" << box->logicalHeight();
409 // FIXME: Remove this hack, once the new text layout engine is completly landed. We want to preserve the old layout test results for now.
410 ts << " contains 1 chunk(s)";
412 if (text.parent() && (text.parent()->style()->visitedDependentColor(CSSPropertyColor) != text.style()->visitedDependentColor(CSSPropertyColor)))
413 writeNameValuePair(ts, "color", text.style()->visitedDependentColor(CSSPropertyColor).nameForRenderTreeAsText());
416 static inline void writeSVGInlineTextBox(TextStream& ts, SVGInlineTextBox* textBox, int indent)
418 Vector<SVGTextFragment>& fragments = textBox->textFragments();
419 if (fragments.isEmpty())
422 RenderSVGInlineText* textRenderer = toRenderSVGInlineText(textBox->textRenderer());
423 ASSERT(textRenderer);
425 const SVGRenderStyle* svgStyle = textRenderer->style()->svgStyle();
426 String text = textBox->textRenderer()->text();
428 unsigned fragmentsSize = fragments.size();
429 for (unsigned i = 0; i < fragmentsSize; ++i) {
430 SVGTextFragment& fragment = fragments.at(i);
431 writeIndent(ts, indent + 1);
433 unsigned startOffset = fragment.characterOffset;
434 unsigned endOffset = fragment.characterOffset + fragment.length;
436 // FIXME: Remove this hack, once the new text layout engine is completly landed. We want to preserve the old layout test results for now.
438 ETextAnchor anchor = svgStyle->textAnchor();
439 bool isVerticalText = svgStyle->isVerticalWritingMode();
440 if (anchor == TA_MIDDLE) {
441 ts << "(middle anchor";
445 } else if (anchor == TA_END) {
450 } else if (isVerticalText)
452 startOffset -= textBox->start();
453 endOffset -= textBox->start();
456 ts << "text run " << i + 1 << " at (" << fragment.x << "," << fragment.y << ")";
457 ts << " startOffset " << startOffset << " endOffset " << endOffset;
459 ts << " height " << fragment.height;
461 ts << " width " << fragment.width;
463 if (!textBox->isLeftToRightDirection() || textBox->m_dirOverride) {
464 ts << (textBox->isLeftToRightDirection() ? " LTR" : " RTL");
465 if (textBox->m_dirOverride)
469 ts << ": " << quoteAndEscapeNonPrintables(text.substring(fragment.characterOffset, fragment.length)) << "\n";
473 static inline void writeSVGInlineTextBoxes(TextStream& ts, const RenderText& text, int indent)
475 for (InlineTextBox* box = text.firstTextBox(); box; box = box->nextTextBox()) {
476 if (!box->isSVGInlineTextBox())
479 writeSVGInlineTextBox(ts, static_cast<SVGInlineTextBox*>(box), indent);
483 static void writeStandardPrefix(TextStream& ts, const RenderObject& object, int indent)
485 writeIndent(ts, indent);
486 ts << object.renderName();
489 ts << " {" << object.node()->nodeName() << "}";
492 static void writeChildren(TextStream& ts, const RenderObject& object, int indent)
494 for (RenderObject* child = object.firstChild(); child; child = child->nextSibling())
495 write(ts, *child, indent + 1);
498 static inline String boundingBoxModeString(bool boundingBoxMode)
500 return boundingBoxMode ? "objectBoundingBox" : "userSpaceOnUse";
503 static inline void writeCommonGradientProperties(TextStream& ts, SVGSpreadMethodType spreadMethod, const AffineTransform& gradientTransform, bool boundingBoxMode)
505 writeNameValuePair(ts, "gradientUnits", boundingBoxModeString(boundingBoxMode));
507 if (spreadMethod != SVGSpreadMethodPad)
508 ts << " [spreadMethod=" << spreadMethod << "]";
510 if (!gradientTransform.isIdentity())
511 ts << " [gradientTransform=" << gradientTransform << "]";
514 void writeSVGResourceContainer(TextStream& ts, const RenderObject& object, int indent)
516 writeStandardPrefix(ts, object, indent);
518 Element* element = static_cast<Element*>(object.node());
519 const AtomicString& id = element->getIdAttribute();
520 writeNameAndQuotedValue(ts, "id", id);
522 RenderSVGResourceContainer* resource = const_cast<RenderObject&>(object).toRenderSVGResourceContainer();
525 if (resource->resourceType() == MaskerResourceType) {
526 RenderSVGResourceMasker* masker = static_cast<RenderSVGResourceMasker*>(resource);
527 writeNameValuePair(ts, "maskUnits", masker->maskUnits());
528 writeNameValuePair(ts, "maskContentUnits", masker->maskContentUnits());
531 } else if (resource->resourceType() == FilterResourceType) {
532 RenderSVGResourceFilter* filter = static_cast<RenderSVGResourceFilter*>(resource);
533 writeNameValuePair(ts, "filterUnits", filter->filterUnits());
534 writeNameValuePair(ts, "primitiveUnits", filter->primitiveUnits());
536 // Creating a placeholder filter which is passed to the builder.
538 RefPtr<SVGFilter> dummyFilter = SVGFilter::create(AffineTransform(), dummyRect, dummyRect, dummyRect, true);
539 if (RefPtr<SVGFilterBuilder> builder = filter->buildPrimitives(dummyFilter.get())) {
540 if (FilterEffect* lastEffect = builder->lastEffect())
541 lastEffect->externalRepresentation(ts, indent + 1);
544 } else if (resource->resourceType() == ClipperResourceType) {
545 RenderSVGResourceClipper* clipper = static_cast<RenderSVGResourceClipper*>(resource);
546 writeNameValuePair(ts, "clipPathUnits", clipper->clipPathUnits());
548 } else if (resource->resourceType() == MarkerResourceType) {
549 RenderSVGResourceMarker* marker = static_cast<RenderSVGResourceMarker*>(resource);
550 writeNameValuePair(ts, "markerUnits", marker->markerUnits());
551 ts << " [ref at " << marker->referencePoint() << "]";
553 if (marker->angle() == -1)
554 ts << "auto" << "]\n";
556 ts << marker->angle() << "]\n";
557 } else if (resource->resourceType() == PatternResourceType) {
558 RenderSVGResourcePattern* pattern = static_cast<RenderSVGResourcePattern*>(resource);
560 // Dump final results that are used for rendering. No use in asking SVGPatternElement for its patternUnits(), as it may
561 // link to other patterns using xlink:href, we need to build the full inheritance chain, aka. collectPatternProperties()
562 PatternAttributes attributes;
563 static_cast<SVGPatternElement*>(pattern->node())->collectPatternAttributes(attributes);
565 writeNameValuePair(ts, "patternUnits", boundingBoxModeString(attributes.boundingBoxMode()));
566 writeNameValuePair(ts, "patternContentUnits", boundingBoxModeString(attributes.boundingBoxModeContent()));
568 AffineTransform transform = attributes.patternTransform();
569 if (!transform.isIdentity())
570 ts << " [patternTransform=" << transform << "]";
572 } else if (resource->resourceType() == LinearGradientResourceType) {
573 RenderSVGResourceLinearGradient* gradient = static_cast<RenderSVGResourceLinearGradient*>(resource);
575 // Dump final results that are used for rendering. No use in asking SVGGradientElement for its gradientUnits(), as it may
576 // link to other gradients using xlink:href, we need to build the full inheritance chain, aka. collectGradientProperties()
577 SVGLinearGradientElement* linearGradientElement = static_cast<SVGLinearGradientElement*>(gradient->node());
579 LinearGradientAttributes attributes;
580 linearGradientElement->collectGradientAttributes(attributes);
581 writeCommonGradientProperties(ts, attributes.spreadMethod(), attributes.gradientTransform(), attributes.boundingBoxMode());
583 FloatPoint startPoint;
585 linearGradientElement->calculateStartEndPoints(attributes, startPoint, endPoint);
587 ts << " [start=" << startPoint << "] [end=" << endPoint << "]\n";
588 } else if (resource->resourceType() == RadialGradientResourceType) {
589 RenderSVGResourceRadialGradient* gradient = static_cast<RenderSVGResourceRadialGradient*>(resource);
591 // Dump final results that are used for rendering. No use in asking SVGGradientElement for its gradientUnits(), as it may
592 // link to other gradients using xlink:href, we need to build the full inheritance chain, aka. collectGradientProperties()
593 SVGRadialGradientElement* radialGradientElement = static_cast<SVGRadialGradientElement*>(gradient->node());
595 RadialGradientAttributes attributes;
596 radialGradientElement->collectGradientAttributes(attributes);
597 writeCommonGradientProperties(ts, attributes.spreadMethod(), attributes.gradientTransform(), attributes.boundingBoxMode());
599 FloatPoint focalPoint;
600 FloatPoint centerPoint;
602 radialGradientElement->calculateFocalCenterPointsAndRadius(attributes, focalPoint, centerPoint, radius);
604 ts << " [center=" << centerPoint << "] [focal=" << focalPoint << "] [radius=" << radius << "]\n";
607 writeChildren(ts, object, indent);
610 void writeSVGContainer(TextStream& ts, const RenderObject& container, int indent)
612 // Currently RenderSVGResourceFilterPrimitive has no meaningful output.
613 if (container.isSVGResourceFilterPrimitive())
615 writeStandardPrefix(ts, container, indent);
616 writePositionAndStyle(ts, container);
618 writeResources(ts, container, indent);
619 writeChildren(ts, container, indent);
622 void write(TextStream& ts, const RenderSVGRoot& root, int indent)
624 writeStandardPrefix(ts, root, indent);
626 writeChildren(ts, root, indent);
629 void writeSVGText(TextStream& ts, const RenderBlock& text, int indent)
631 writeStandardPrefix(ts, text, indent);
632 writeRenderSVGTextBox(ts, text);
634 writeResources(ts, text, indent);
635 writeChildren(ts, text, indent);
638 void writeSVGInlineText(TextStream& ts, const RenderText& text, int indent)
640 writeStandardPrefix(ts, text, indent);
642 // Why not just linesBoundingBox()?
643 ts << " " << FloatRect(text.firstRunOrigin(), text.linesBoundingBox().size()) << "\n";
644 writeResources(ts, text, indent);
645 writeSVGInlineTextBoxes(ts, text, indent);
648 void writeSVGImage(TextStream& ts, const RenderSVGImage& image, int indent)
650 writeStandardPrefix(ts, image, indent);
651 writePositionAndStyle(ts, image);
653 writeResources(ts, image, indent);
656 void write(TextStream& ts, const RenderSVGPath& path, int indent)
658 writeStandardPrefix(ts, path, indent);
660 writeResources(ts, path, indent);
663 void writeSVGGradientStop(TextStream& ts, const RenderSVGGradientStop& stop, int indent)
665 writeStandardPrefix(ts, stop, indent);
667 SVGStopElement* stopElement = static_cast<SVGStopElement*>(stop.node());
670 RenderStyle* style = stop.style();
674 ts << " [offset=" << stopElement->offset() << "] [color=" << stopElement->stopColorIncludingOpacity() << "]\n";
677 void writeResources(TextStream& ts, const RenderObject& object, int indent)
679 const RenderStyle* style = object.style();
680 const SVGRenderStyle* svgStyle = style->svgStyle();
682 // FIXME: We want to use SVGResourcesCache to determine which resources are present, instead of quering the resource <-> id cache.
683 // For now leave the DRT output as is, but later on we should change this so cycles are properly ignored in the DRT output.
684 RenderObject& renderer = const_cast<RenderObject&>(object);
685 if (!svgStyle->maskerResource().isEmpty()) {
686 if (RenderSVGResourceMasker* masker = getRenderSVGResourceById<RenderSVGResourceMasker>(object.document(), svgStyle->maskerResource())) {
687 writeIndent(ts, indent);
689 writeNameAndQuotedValue(ts, "masker", svgStyle->maskerResource());
691 writeStandardPrefix(ts, *masker, 0);
692 ts << " " << masker->resourceBoundingBox(&renderer) << "\n";
695 if (!svgStyle->clipperResource().isEmpty()) {
696 if (RenderSVGResourceClipper* clipper = getRenderSVGResourceById<RenderSVGResourceClipper>(object.document(), svgStyle->clipperResource())) {
697 writeIndent(ts, indent);
699 writeNameAndQuotedValue(ts, "clipPath", svgStyle->clipperResource());
701 writeStandardPrefix(ts, *clipper, 0);
702 ts << " " << clipper->resourceBoundingBox(&renderer) << "\n";
706 if (!svgStyle->filterResource().isEmpty()) {
707 if (RenderSVGResourceFilter* filter = getRenderSVGResourceById<RenderSVGResourceFilter>(object.document(), svgStyle->filterResource())) {
708 writeIndent(ts, indent);
710 writeNameAndQuotedValue(ts, "filter", svgStyle->filterResource());
712 writeStandardPrefix(ts, *filter, 0);
713 ts << " " << filter->resourceBoundingBox(&renderer) << "\n";
719 } // namespace WebCore
721 #endif // ENABLE(SVG)