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