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