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.
31 #include "core/rendering/svg/SVGRenderTreeAsText.h"
33 #include "core/rendering/InlineTextBox.h"
34 #include "core/rendering/RenderTreeAsText.h"
35 #include "core/rendering/svg/RenderSVGGradientStop.h"
36 #include "core/rendering/svg/RenderSVGImage.h"
37 #include "core/rendering/svg/RenderSVGInlineText.h"
38 #include "core/rendering/svg/RenderSVGResourceClipper.h"
39 #include "core/rendering/svg/RenderSVGResourceFilter.h"
40 #include "core/rendering/svg/RenderSVGResourceLinearGradient.h"
41 #include "core/rendering/svg/RenderSVGResourceMarker.h"
42 #include "core/rendering/svg/RenderSVGResourceMasker.h"
43 #include "core/rendering/svg/RenderSVGResourcePattern.h"
44 #include "core/rendering/svg/RenderSVGResourceRadialGradient.h"
45 #include "core/rendering/svg/RenderSVGRoot.h"
46 #include "core/rendering/svg/RenderSVGShape.h"
47 #include "core/rendering/svg/RenderSVGText.h"
48 #include "core/rendering/svg/SVGInlineTextBox.h"
49 #include "core/rendering/svg/SVGRootInlineBox.h"
50 #include "core/svg/LinearGradientAttributes.h"
51 #include "core/svg/PatternAttributes.h"
52 #include "core/svg/RadialGradientAttributes.h"
53 #include "core/svg/SVGCircleElement.h"
54 #include "core/svg/SVGEllipseElement.h"
55 #include "core/svg/SVGLineElement.h"
56 #include "core/svg/SVGLinearGradientElement.h"
57 #include "core/svg/SVGPathElement.h"
58 #include "core/svg/SVGPathUtilities.h"
59 #include "core/svg/SVGPatternElement.h"
60 #include "core/svg/SVGPointList.h"
61 #include "core/svg/SVGPolyElement.h"
62 #include "core/svg/SVGRadialGradientElement.h"
63 #include "core/svg/SVGRectElement.h"
64 #include "core/svg/SVGStopElement.h"
65 #include "platform/graphics/DashArray.h"
66 #include "platform/graphics/GraphicsTypes.h"
73 /** class + iomanip to help streaming list separators, i.e. ", " in string "a, b, c, d"
74 * Can be used in cases where you don't know which item in the list is the first
75 * one to be printed, but still want to avoid strings like ", b, c".
77 class TextStreamSeparator {
79 TextStreamSeparator(const String& s)
81 , m_needToSeparate(false)
86 friend TextStream& operator<<(TextStream&, TextStreamSeparator&);
89 bool m_needToSeparate;
92 TextStream& operator<<(TextStream& ts, TextStreamSeparator& sep)
94 if (sep.m_needToSeparate)
95 ts << sep.m_separator;
97 sep.m_needToSeparate = true;
101 template<typename ValueType>
102 static void writeNameValuePair(TextStream& ts, const char* name, ValueType value)
104 ts << " [" << name << "=" << value << "]";
107 template<typename ValueType>
108 static void writeNameAndQuotedValue(TextStream& ts, const char* name, ValueType value)
110 ts << " [" << name << "=\"" << value << "\"]";
113 static void writeIfNotEmpty(TextStream& ts, const char* name, const String& value)
115 if (!value.isEmpty())
116 writeNameValuePair(ts, name, value);
119 template<typename ValueType>
120 static void writeIfNotDefault(TextStream& ts, const char* name, ValueType value, ValueType defaultValue)
122 if (value != defaultValue)
123 writeNameValuePair(ts, name, value);
126 TextStream& operator<<(TextStream& ts, const AffineTransform& transform)
128 if (transform.isIdentity())
132 << transform.a() << "," << transform.b()
134 << transform.c() << "," << transform.d()
136 << transform.e() << "," << transform.f()
142 static TextStream& operator<<(TextStream& ts, const WindRule rule)
158 template<typename Enum>
159 String SVGEnumerationToString(Enum value)
161 const SVGEnumerationStringEntries& entries = getStaticStringEntries<Enum>();
163 SVGEnumerationStringEntries::const_iterator it = entries.begin();
164 SVGEnumerationStringEntries::const_iterator itEnd = entries.end();
165 for (; it != itEnd; ++it) {
166 if (value == it->first)
170 ASSERT_NOT_REACHED();
176 static TextStream& operator<<(TextStream& ts, const SVGUnitTypes::SVGUnitType& unitType)
178 ts << SVGEnumerationToString<SVGUnitTypes::SVGUnitType>(unitType);
182 static TextStream& operator<<(TextStream& ts, const SVGMarkerUnitsType& markerUnit)
184 ts << SVGEnumerationToString<SVGMarkerUnitsType>(markerUnit);
188 static TextStream& operator<<(TextStream& ts, const SVGMarkerOrientType& orientType)
190 ts << SVGEnumerationToString<SVGMarkerOrientType>(orientType);
194 // FIXME: Maybe this should be in DashArray.cpp
195 static TextStream& operator<<(TextStream& ts, const DashArray& a)
198 DashArray::const_iterator end = a.end();
199 for (DashArray::const_iterator it = a.begin(); it != end; ++it) {
208 // FIXME: Maybe this should be in GraphicsTypes.cpp
209 static TextStream& operator<<(TextStream& ts, LineCap style)
225 // FIXME: Maybe this should be in GraphicsTypes.cpp
226 static TextStream& operator<<(TextStream& ts, LineJoin style)
242 static TextStream& operator<<(TextStream& ts, const SVGSpreadMethodType& type)
244 ts << SVGEnumerationToString<SVGSpreadMethodType>(type).upper();
248 static void writeSVGPaintingResource(TextStream& ts, const SVGPaintDescription& paintDescription)
250 ASSERT(paintDescription.isValid);
251 if (!paintDescription.resource) {
252 ts << "[type=SOLID] [color=" << paintDescription.color << "]";
256 RenderSVGResourcePaintServer* paintServerContainer = paintDescription.resource;
257 SVGElement* element = paintServerContainer->element();
260 if (paintServerContainer->resourceType() == PatternResourceType)
261 ts << "[type=PATTERN]";
262 else if (paintServerContainer->resourceType() == LinearGradientResourceType)
263 ts << "[type=LINEAR-GRADIENT]";
264 else if (paintServerContainer->resourceType() == RadialGradientResourceType)
265 ts << "[type=RADIAL-GRADIENT]";
267 ts << " [id=\"" << element->getIdAttribute() << "\"]";
270 static void writeStyle(TextStream& ts, const RenderObject& object)
272 const RenderStyle* style = object.style();
273 const SVGRenderStyle& svgStyle = style->svgStyle();
275 if (!object.localTransform().isIdentity())
276 writeNameValuePair(ts, "transform", object.localTransform());
277 writeIfNotDefault(ts, "image rendering", style->imageRendering(), RenderStyle::initialImageRendering());
278 writeIfNotDefault(ts, "opacity", style->opacity(), RenderStyle::initialOpacity());
279 if (object.isSVGShape()) {
280 const RenderSVGShape& shape = static_cast<const RenderSVGShape&>(object);
281 ASSERT(shape.element());
283 SVGPaintDescription strokePaintDescription = RenderSVGResourcePaintServer::requestPaintDescription(shape, shape.style(), ApplyToStrokeMode);
284 if (strokePaintDescription.isValid) {
285 TextStreamSeparator s(" ");
286 ts << " [stroke={" << s;
287 writeSVGPaintingResource(ts, strokePaintDescription);
289 SVGLengthContext lengthContext(shape.element());
290 double dashOffset = svgStyle.strokeDashOffset()->value(lengthContext);
291 double strokeWidth = svgStyle.strokeWidth()->value(lengthContext);
292 RefPtr<SVGLengthList> dashes = svgStyle.strokeDashArray();
295 SVGLengthList::ConstIterator it = dashes->begin();
296 SVGLengthList::ConstIterator itEnd = dashes->end();
297 for (; it != itEnd; ++it)
298 dashArray.append(it->value(lengthContext));
300 writeIfNotDefault(ts, "opacity", svgStyle.strokeOpacity(), 1.0f);
301 writeIfNotDefault(ts, "stroke width", strokeWidth, 1.0);
302 writeIfNotDefault(ts, "miter limit", svgStyle.strokeMiterLimit(), 4.0f);
303 writeIfNotDefault(ts, "line cap", svgStyle.capStyle(), ButtCap);
304 writeIfNotDefault(ts, "line join", svgStyle.joinStyle(), MiterJoin);
305 writeIfNotDefault(ts, "dash offset", dashOffset, 0.0);
306 if (!dashArray.isEmpty())
307 writeNameValuePair(ts, "dash array", dashArray);
312 SVGPaintDescription fillPaintDescription = RenderSVGResourcePaintServer::requestPaintDescription(shape, shape.style(), ApplyToFillMode);
313 if (fillPaintDescription.isValid) {
314 TextStreamSeparator s(" ");
315 ts << " [fill={" << s;
316 writeSVGPaintingResource(ts, fillPaintDescription);
318 writeIfNotDefault(ts, "opacity", svgStyle.fillOpacity(), 1.0f);
319 writeIfNotDefault(ts, "fill rule", svgStyle.fillRule(), RULE_NONZERO);
322 writeIfNotDefault(ts, "clip rule", svgStyle.clipRule(), RULE_NONZERO);
325 writeIfNotEmpty(ts, "start marker", svgStyle.markerStartResource());
326 writeIfNotEmpty(ts, "middle marker", svgStyle.markerMidResource());
327 writeIfNotEmpty(ts, "end marker", svgStyle.markerEndResource());
330 static TextStream& writePositionAndStyle(TextStream& ts, const RenderObject& object)
332 ts << " " << enclosingIntRect(const_cast<RenderObject&>(object).absoluteClippedOverflowRect());
333 writeStyle(ts, object);
337 static TextStream& operator<<(TextStream& ts, const RenderSVGShape& shape)
339 writePositionAndStyle(ts, shape);
341 SVGElement* svgElement = shape.element();
343 SVGLengthContext lengthContext(svgElement);
345 if (isSVGRectElement(*svgElement)) {
346 SVGRectElement& element = toSVGRectElement(*svgElement);
347 writeNameValuePair(ts, "x", element.x()->currentValue()->value(lengthContext));
348 writeNameValuePair(ts, "y", element.y()->currentValue()->value(lengthContext));
349 writeNameValuePair(ts, "width", element.width()->currentValue()->value(lengthContext));
350 writeNameValuePair(ts, "height", element.height()->currentValue()->value(lengthContext));
351 } else if (isSVGLineElement(*svgElement)) {
352 SVGLineElement& element = toSVGLineElement(*svgElement);
353 writeNameValuePair(ts, "x1", element.x1()->currentValue()->value(lengthContext));
354 writeNameValuePair(ts, "y1", element.y1()->currentValue()->value(lengthContext));
355 writeNameValuePair(ts, "x2", element.x2()->currentValue()->value(lengthContext));
356 writeNameValuePair(ts, "y2", element.y2()->currentValue()->value(lengthContext));
357 } else if (isSVGEllipseElement(*svgElement)) {
358 SVGEllipseElement& element = toSVGEllipseElement(*svgElement);
359 writeNameValuePair(ts, "cx", element.cx()->currentValue()->value(lengthContext));
360 writeNameValuePair(ts, "cy", element.cy()->currentValue()->value(lengthContext));
361 writeNameValuePair(ts, "rx", element.rx()->currentValue()->value(lengthContext));
362 writeNameValuePair(ts, "ry", element.ry()->currentValue()->value(lengthContext));
363 } else if (isSVGCircleElement(*svgElement)) {
364 SVGCircleElement& element = toSVGCircleElement(*svgElement);
365 writeNameValuePair(ts, "cx", element.cx()->currentValue()->value(lengthContext));
366 writeNameValuePair(ts, "cy", element.cy()->currentValue()->value(lengthContext));
367 writeNameValuePair(ts, "r", element.r()->currentValue()->value(lengthContext));
368 } else if (isSVGPolyElement(*svgElement)) {
369 writeNameAndQuotedValue(ts, "points", toSVGPolyElement(*svgElement).points()->currentValue()->valueAsString());
370 } else if (isSVGPathElement(*svgElement)) {
372 // FIXME: We should switch to UnalteredParsing here - this will affect the path dumping output of dozens of tests.
373 buildStringFromByteStream(toSVGPathElement(*svgElement).pathByteStream(), pathString, NormalizedParsing);
374 writeNameAndQuotedValue(ts, "data", pathString);
376 ASSERT_NOT_REACHED();
380 static TextStream& operator<<(TextStream& ts, const RenderSVGRoot& root)
382 return writePositionAndStyle(ts, root);
385 static void writeRenderSVGTextBox(TextStream& ts, const RenderSVGText& text)
387 SVGRootInlineBox* box = toSVGRootInlineBox(text.firstRootBox());
391 ts << " " << enclosingIntRect(FloatRect(text.location(), FloatSize(box->logicalWidth(), box->logicalHeight())));
393 // FIXME: Remove this hack, once the new text layout engine is completly landed. We want to preserve the old layout test results for now.
394 ts << " contains 1 chunk(s)";
396 if (text.parent() && (text.parent()->style()->visitedDependentColor(CSSPropertyColor) != text.style()->visitedDependentColor(CSSPropertyColor)))
397 writeNameValuePair(ts, "color", text.resolveColor(CSSPropertyColor).nameForRenderTreeAsText());
400 static inline void writeSVGInlineTextBox(TextStream& ts, SVGInlineTextBox* textBox, int indent)
402 Vector<SVGTextFragment>& fragments = textBox->textFragments();
403 if (fragments.isEmpty())
406 RenderSVGInlineText& textRenderer = toRenderSVGInlineText(textBox->renderer());
408 const SVGRenderStyle& svgStyle = textRenderer.style()->svgStyle();
409 String text = textBox->renderer().text();
411 unsigned fragmentsSize = fragments.size();
412 for (unsigned i = 0; i < fragmentsSize; ++i) {
413 SVGTextFragment& fragment = fragments.at(i);
414 writeIndent(ts, indent + 1);
416 unsigned startOffset = fragment.characterOffset;
417 unsigned endOffset = fragment.characterOffset + fragment.length;
419 // FIXME: Remove this hack, once the new text layout engine is completly landed. We want to preserve the old layout test results for now.
421 ETextAnchor anchor = svgStyle.textAnchor();
422 bool isVerticalText = svgStyle.isVerticalWritingMode();
423 if (anchor == TA_MIDDLE) {
424 ts << "(middle anchor";
428 } else if (anchor == TA_END) {
433 } else if (isVerticalText)
435 startOffset -= textBox->start();
436 endOffset -= textBox->start();
439 ts << "text run " << i + 1 << " at (" << fragment.x << "," << fragment.y << ")";
440 ts << " startOffset " << startOffset << " endOffset " << endOffset;
442 ts << " height " << fragment.height;
444 ts << " width " << fragment.width;
446 if (!textBox->isLeftToRightDirection() || textBox->dirOverride()) {
447 ts << (textBox->isLeftToRightDirection() ? " LTR" : " RTL");
448 if (textBox->dirOverride())
452 ts << ": " << quoteAndEscapeNonPrintables(text.substring(fragment.characterOffset, fragment.length)) << "\n";
456 static inline void writeSVGInlineTextBoxes(TextStream& ts, const RenderText& text, int indent)
458 for (InlineTextBox* box = text.firstTextBox(); box; box = box->nextTextBox()) {
459 if (!box->isSVGInlineTextBox())
462 writeSVGInlineTextBox(ts, toSVGInlineTextBox(box), indent);
466 static void writeStandardPrefix(TextStream& ts, const RenderObject& object, int indent)
468 writeIndent(ts, indent);
469 ts << object.renderName();
472 ts << " {" << object.node()->nodeName() << "}";
475 static void writeChildren(TextStream& ts, const RenderObject& object, int indent)
477 for (RenderObject* child = object.slowFirstChild(); child; child = child->nextSibling())
478 write(ts, *child, indent + 1);
481 static inline void writeCommonGradientProperties(TextStream& ts, SVGSpreadMethodType spreadMethod, const AffineTransform& gradientTransform, SVGUnitTypes::SVGUnitType gradientUnits)
483 writeNameValuePair(ts, "gradientUnits", gradientUnits);
485 if (spreadMethod != SVGSpreadMethodPad)
486 ts << " [spreadMethod=" << spreadMethod << "]";
488 if (!gradientTransform.isIdentity())
489 ts << " [gradientTransform=" << gradientTransform << "]";
492 void writeSVGResourceContainer(TextStream& ts, const RenderObject& object, int indent)
494 writeStandardPrefix(ts, object, indent);
496 Element* element = toElement(object.node());
497 const AtomicString& id = element->getIdAttribute();
498 writeNameAndQuotedValue(ts, "id", id);
500 RenderSVGResourceContainer* resource = toRenderSVGResourceContainer(const_cast<RenderObject*>(&object));
503 if (resource->resourceType() == MaskerResourceType) {
504 RenderSVGResourceMasker* masker = toRenderSVGResourceMasker(resource);
505 writeNameValuePair(ts, "maskUnits", masker->maskUnits());
506 writeNameValuePair(ts, "maskContentUnits", masker->maskContentUnits());
508 } else if (resource->resourceType() == FilterResourceType) {
509 RenderSVGResourceFilter* filter = toRenderSVGResourceFilter(resource);
510 writeNameValuePair(ts, "filterUnits", filter->filterUnits());
511 writeNameValuePair(ts, "primitiveUnits", filter->primitiveUnits());
513 // Creating a placeholder filter which is passed to the builder.
515 IntRect dummyIntRect;
516 RefPtr<SVGFilter> dummyFilter = SVGFilter::create(dummyIntRect, dummyRect, dummyRect, true);
517 if (RefPtr<SVGFilterBuilder> builder = filter->buildPrimitives(dummyFilter.get())) {
518 if (FilterEffect* lastEffect = builder->lastEffect())
519 lastEffect->externalRepresentation(ts, indent + 1);
521 } else if (resource->resourceType() == ClipperResourceType) {
522 writeNameValuePair(ts, "clipPathUnits", toRenderSVGResourceClipper(resource)->clipPathUnits());
524 } else if (resource->resourceType() == MarkerResourceType) {
525 RenderSVGResourceMarker* marker = toRenderSVGResourceMarker(resource);
526 writeNameValuePair(ts, "markerUnits", marker->markerUnits());
527 ts << " [ref at " << marker->referencePoint() << "]";
529 if (marker->angle() == -1)
530 ts << marker->orientType() << "]\n";
532 ts << marker->angle() << "]\n";
533 } else if (resource->resourceType() == PatternResourceType) {
534 RenderSVGResourcePattern* pattern = static_cast<RenderSVGResourcePattern*>(resource);
536 // Dump final results that are used for rendering. No use in asking SVGPatternElement for its patternUnits(), as it may
537 // link to other patterns using xlink:href, we need to build the full inheritance chain, aka. collectPatternProperties()
538 PatternAttributes attributes;
539 toSVGPatternElement(pattern->element())->collectPatternAttributes(attributes);
541 writeNameValuePair(ts, "patternUnits", attributes.patternUnits());
542 writeNameValuePair(ts, "patternContentUnits", attributes.patternContentUnits());
544 AffineTransform transform = attributes.patternTransform();
545 if (!transform.isIdentity())
546 ts << " [patternTransform=" << transform << "]";
548 } else if (resource->resourceType() == LinearGradientResourceType) {
549 RenderSVGResourceLinearGradient* gradient = static_cast<RenderSVGResourceLinearGradient*>(resource);
551 // Dump final results that are used for rendering. No use in asking SVGGradientElement for its gradientUnits(), as it may
552 // link to other gradients using xlink:href, we need to build the full inheritance chain, aka. collectGradientProperties()
553 LinearGradientAttributes attributes;
554 toSVGLinearGradientElement(gradient->element())->collectGradientAttributes(attributes);
555 writeCommonGradientProperties(ts, attributes.spreadMethod(), attributes.gradientTransform(), attributes.gradientUnits());
557 ts << " [start=" << gradient->startPoint(attributes) << "] [end=" << gradient->endPoint(attributes) << "]\n";
558 } else if (resource->resourceType() == RadialGradientResourceType) {
559 RenderSVGResourceRadialGradient* gradient = toRenderSVGResourceRadialGradient(resource);
561 // Dump final results that are used for rendering. No use in asking SVGGradientElement for its gradientUnits(), as it may
562 // link to other gradients using xlink:href, we need to build the full inheritance chain, aka. collectGradientProperties()
563 RadialGradientAttributes attributes;
564 toSVGRadialGradientElement(gradient->element())->collectGradientAttributes(attributes);
565 writeCommonGradientProperties(ts, attributes.spreadMethod(), attributes.gradientTransform(), attributes.gradientUnits());
567 FloatPoint focalPoint = gradient->focalPoint(attributes);
568 FloatPoint centerPoint = gradient->centerPoint(attributes);
569 float radius = gradient->radius(attributes);
570 float focalRadius = gradient->focalRadius(attributes);
572 ts << " [center=" << centerPoint << "] [focal=" << focalPoint << "] [radius=" << radius << "] [focalRadius=" << focalRadius << "]\n";
575 writeChildren(ts, object, indent);
578 void writeSVGContainer(TextStream& ts, const RenderObject& container, int indent)
580 // Currently RenderSVGResourceFilterPrimitive has no meaningful output.
581 if (container.isSVGResourceFilterPrimitive())
583 writeStandardPrefix(ts, container, indent);
584 writePositionAndStyle(ts, container);
586 writeResources(ts, container, indent);
587 writeChildren(ts, container, indent);
590 void write(TextStream& ts, const RenderSVGRoot& root, int indent)
592 writeStandardPrefix(ts, root, indent);
594 writeChildren(ts, root, indent);
597 void writeSVGText(TextStream& ts, const RenderSVGText& text, int indent)
599 writeStandardPrefix(ts, text, indent);
600 writeRenderSVGTextBox(ts, text);
602 writeResources(ts, text, indent);
603 writeChildren(ts, text, indent);
606 void writeSVGInlineText(TextStream& ts, const RenderSVGInlineText& text, int indent)
608 writeStandardPrefix(ts, text, indent);
609 ts << " " << enclosingIntRect(FloatRect(text.firstRunOrigin(), text.floatLinesBoundingBox().size())) << "\n";
610 writeResources(ts, text, indent);
611 writeSVGInlineTextBoxes(ts, text, indent);
614 void writeSVGImage(TextStream& ts, const RenderSVGImage& image, int indent)
616 writeStandardPrefix(ts, image, indent);
617 writePositionAndStyle(ts, image);
619 writeResources(ts, image, indent);
622 void write(TextStream& ts, const RenderSVGShape& shape, int indent)
624 writeStandardPrefix(ts, shape, indent);
626 writeResources(ts, shape, indent);
629 void writeSVGGradientStop(TextStream& ts, const RenderSVGGradientStop& stop, int indent)
631 writeStandardPrefix(ts, stop, indent);
633 SVGStopElement* stopElement = toSVGStopElement(stop.node());
636 RenderStyle* style = stop.style();
640 ts << " [offset=" << stopElement->offset()->currentValue()->value() << "] [color=" << stopElement->stopColorIncludingOpacity() << "]\n";
643 void writeResources(TextStream& ts, const RenderObject& object, int indent)
645 const RenderStyle* style = object.style();
646 const SVGRenderStyle& svgStyle = style->svgStyle();
648 // FIXME: We want to use SVGResourcesCache to determine which resources are present, instead of quering the resource <-> id cache.
649 // For now leave the DRT output as is, but later on we should change this so cycles are properly ignored in the DRT output.
650 RenderObject& renderer = const_cast<RenderObject&>(object);
651 if (!svgStyle.maskerResource().isEmpty()) {
652 if (RenderSVGResourceMasker* masker = getRenderSVGResourceById<RenderSVGResourceMasker>(object.document(), svgStyle.maskerResource())) {
653 writeIndent(ts, indent);
655 writeNameAndQuotedValue(ts, "masker", svgStyle.maskerResource());
657 writeStandardPrefix(ts, *masker, 0);
658 ts << " " << masker->resourceBoundingBox(&renderer) << "\n";
661 if (!svgStyle.clipperResource().isEmpty()) {
662 if (RenderSVGResourceClipper* clipper = getRenderSVGResourceById<RenderSVGResourceClipper>(object.document(), svgStyle.clipperResource())) {
663 writeIndent(ts, indent);
665 writeNameAndQuotedValue(ts, "clipPath", svgStyle.clipperResource());
667 writeStandardPrefix(ts, *clipper, 0);
668 ts << " " << clipper->resourceBoundingBox(&renderer) << "\n";
671 if (!svgStyle.filterResource().isEmpty()) {
672 if (RenderSVGResourceFilter* filter = getRenderSVGResourceById<RenderSVGResourceFilter>(object.document(), svgStyle.filterResource())) {
673 writeIndent(ts, indent);
675 writeNameAndQuotedValue(ts, "filter", svgStyle.filterResource());
677 writeStandardPrefix(ts, *filter, 0);
678 ts << " " << filter->resourceBoundingBox(&renderer) << "\n";