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