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