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