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