1/* 2 * Copyright (C) 2004, 2005, 2007, 2009 Apple Inc. All rights reserved. 3 * (C) 2005 Rob Buis <buis@kde.org> 4 * (C) 2006 Alexander Kellett <lypanov@kde.org> 5 * Copyright (C) Research In Motion Limited 2010. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 23 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 24 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include "config.h" 30 31#if ENABLE(SVG) 32#include "SVGRenderTreeAsText.h" 33 34#include "GraphicsTypes.h" 35#include "HTMLNames.h" 36#include "InlineTextBox.h" 37#include "LinearGradientAttributes.h" 38#include "NodeRenderStyle.h" 39#include "Path.h" 40#include "PatternAttributes.h" 41#include "RadialGradientAttributes.h" 42#include "RenderImage.h" 43#include "RenderSVGContainer.h" 44#include "RenderSVGGradientStop.h" 45#include "RenderSVGImage.h" 46#include "RenderSVGInlineText.h" 47#include "RenderSVGPath.h" 48#include "RenderSVGResourceClipper.h" 49#include "RenderSVGResourceFilter.h" 50#include "RenderSVGResourceGradient.h" 51#include "RenderSVGResourceLinearGradient.h" 52#include "RenderSVGResourceMarker.h" 53#include "RenderSVGResourceMasker.h" 54#include "RenderSVGResourcePattern.h" 55#include "RenderSVGResourceRadialGradient.h" 56#include "RenderSVGResourceSolidColor.h" 57#include "RenderSVGRoot.h" 58#include "RenderSVGShape.h" 59#include "RenderSVGText.h" 60#include "RenderTreeAsText.h" 61#include "SVGCircleElement.h" 62#include "SVGEllipseElement.h" 63#include "SVGInlineTextBox.h" 64#include "SVGLineElement.h" 65#include "SVGLinearGradientElement.h" 66#include "SVGNames.h" 67#include "SVGPathElement.h" 68#include "SVGPathUtilities.h" 69#include "SVGPatternElement.h" 70#include "SVGPointList.h" 71#include "SVGPolyElement.h" 72#include "SVGRadialGradientElement.h" 73#include "SVGRectElement.h" 74#include "SVGRootInlineBox.h" 75#include "SVGStopElement.h" 76#include "SVGStyledElement.h" 77 78#include <math.h> 79 80namespace WebCore { 81 82/** class + iomanip to help streaming list separators, i.e. ", " in string "a, b, c, d" 83 * Can be used in cases where you don't know which item in the list is the first 84 * one to be printed, but still want to avoid strings like ", b, c". 85 */ 86class TextStreamSeparator { 87public: 88 TextStreamSeparator(const String& s) 89 : m_separator(s) 90 , m_needToSeparate(false) 91 { 92 } 93 94private: 95 friend TextStream& operator<<(TextStream&, TextStreamSeparator&); 96 97 String m_separator; 98 bool m_needToSeparate; 99}; 100 101TextStream& operator<<(TextStream& ts, TextStreamSeparator& sep) 102{ 103 if (sep.m_needToSeparate) 104 ts << sep.m_separator; 105 else 106 sep.m_needToSeparate = true; 107 return ts; 108} 109 110template<typename ValueType> 111static void writeNameValuePair(TextStream& ts, const char* name, ValueType value) 112{ 113 ts << " [" << name << "=" << value << "]"; 114} 115 116template<typename ValueType> 117static void writeNameAndQuotedValue(TextStream& ts, const char* name, ValueType value) 118{ 119 ts << " [" << name << "=\"" << value << "\"]"; 120} 121 122static void writeIfNotEmpty(TextStream& ts, const char* name, const String& value) 123{ 124 if (!value.isEmpty()) 125 writeNameValuePair(ts, name, value); 126} 127 128template<typename ValueType> 129static void writeIfNotDefault(TextStream& ts, const char* name, ValueType value, ValueType defaultValue) 130{ 131 if (value != defaultValue) 132 writeNameValuePair(ts, name, value); 133} 134 135TextStream& operator<<(TextStream& ts, const FloatRect& r) 136{ 137 ts << "at (" << TextStream::FormatNumberRespectingIntegers(r.x()); 138 ts << "," << TextStream::FormatNumberRespectingIntegers(r.y()); 139 ts << ") size " << TextStream::FormatNumberRespectingIntegers(r.width()); 140 ts << "x" << TextStream::FormatNumberRespectingIntegers(r.height()); 141 return ts; 142} 143 144TextStream& operator<<(TextStream& ts, const AffineTransform& transform) 145{ 146 if (transform.isIdentity()) 147 ts << "identity"; 148 else 149 ts << "{m=((" 150 << transform.a() << "," << transform.b() 151 << ")(" 152 << transform.c() << "," << transform.d() 153 << ")) t=(" 154 << transform.e() << "," << transform.f() 155 << ")}"; 156 157 return ts; 158} 159 160static TextStream& operator<<(TextStream& ts, const WindRule rule) 161{ 162 switch (rule) { 163 case RULE_NONZERO: 164 ts << "NON-ZERO"; 165 break; 166 case RULE_EVENODD: 167 ts << "EVEN-ODD"; 168 break; 169 } 170 171 return ts; 172} 173 174static TextStream& operator<<(TextStream& ts, const SVGUnitTypes::SVGUnitType& unitType) 175{ 176 ts << SVGPropertyTraits<SVGUnitTypes::SVGUnitType>::toString(unitType); 177 return ts; 178} 179 180static TextStream& operator<<(TextStream& ts, const SVGMarkerUnitsType& markerUnit) 181{ 182 ts << SVGPropertyTraits<SVGMarkerUnitsType>::toString(markerUnit); 183 return ts; 184} 185 186TextStream& operator<<(TextStream& ts, const Color& c) 187{ 188 return ts << c.nameForRenderTreeAsText(); 189} 190 191// FIXME: Maybe this should be in KCanvasRenderingStyle.cpp 192static TextStream& operator<<(TextStream& ts, const DashArray& a) 193{ 194 ts << "{"; 195 DashArray::const_iterator end = a.end(); 196 for (DashArray::const_iterator it = a.begin(); it != end; ++it) { 197 if (it != a.begin()) 198 ts << ", "; 199 ts << *it; 200 } 201 ts << "}"; 202 return ts; 203} 204 205// FIXME: Maybe this should be in GraphicsTypes.cpp 206static TextStream& operator<<(TextStream& ts, LineCap style) 207{ 208 switch (style) { 209 case ButtCap: 210 ts << "BUTT"; 211 break; 212 case RoundCap: 213 ts << "ROUND"; 214 break; 215 case SquareCap: 216 ts << "SQUARE"; 217 break; 218 } 219 return ts; 220} 221 222// FIXME: Maybe this should be in GraphicsTypes.cpp 223static TextStream& operator<<(TextStream& ts, LineJoin style) 224{ 225 switch (style) { 226 case MiterJoin: 227 ts << "MITER"; 228 break; 229 case RoundJoin: 230 ts << "ROUND"; 231 break; 232 case BevelJoin: 233 ts << "BEVEL"; 234 break; 235 } 236 return ts; 237} 238 239static TextStream& operator<<(TextStream& ts, const SVGSpreadMethodType& type) 240{ 241 ts << SVGPropertyTraits<SVGSpreadMethodType>::toString(type).upper(); 242 return ts; 243} 244 245static void writeSVGPaintingResource(TextStream& ts, RenderSVGResource* resource) 246{ 247 if (resource->resourceType() == SolidColorResourceType) { 248 ts << "[type=SOLID] [color=" << static_cast<RenderSVGResourceSolidColor*>(resource)->color() << "]"; 249 return; 250 } 251 252 // All other resources derive from RenderSVGResourceContainer 253 RenderSVGResourceContainer* container = static_cast<RenderSVGResourceContainer*>(resource); 254 Node* node = container->node(); 255 ASSERT(node); 256 ASSERT(node->isSVGElement()); 257 258 if (resource->resourceType() == PatternResourceType) 259 ts << "[type=PATTERN]"; 260 else if (resource->resourceType() == LinearGradientResourceType) 261 ts << "[type=LINEAR-GRADIENT]"; 262 else if (resource->resourceType() == RadialGradientResourceType) 263 ts << "[type=RADIAL-GRADIENT]"; 264 265 ts << " [id=\"" << toSVGElement(node)->getIdAttribute() << "\"]"; 266} 267 268static void writeStyle(TextStream& ts, const RenderObject& object) 269{ 270 const RenderStyle* style = object.style(); 271 const SVGRenderStyle* svgStyle = style->svgStyle(); 272 273 if (!object.localTransform().isIdentity()) 274 writeNameValuePair(ts, "transform", object.localTransform()); 275 writeIfNotDefault(ts, "image rendering", style->imageRendering(), RenderStyle::initialImageRendering()); 276 writeIfNotDefault(ts, "opacity", style->opacity(), RenderStyle::initialOpacity()); 277 if (object.isSVGShape()) { 278 const RenderSVGShape& shape = static_cast<const RenderSVGShape&>(object); 279 ASSERT(shape.node()); 280 ASSERT(shape.node()->isSVGElement()); 281 282 Color fallbackColor; 283 if (RenderSVGResource* strokePaintingResource = RenderSVGResource::strokePaintingResource(const_cast<RenderSVGShape*>(&shape), shape.style(), fallbackColor)) { 284 TextStreamSeparator s(" "); 285 ts << " [stroke={" << s; 286 writeSVGPaintingResource(ts, strokePaintingResource); 287 288 SVGLengthContext lengthContext(toSVGElement(shape.node())); 289 double dashOffset = svgStyle->strokeDashOffset().value(lengthContext); 290 double strokeWidth = svgStyle->strokeWidth().value(lengthContext); 291 const Vector<SVGLength>& dashes = svgStyle->strokeDashArray(); 292 293 DashArray dashArray; 294 const Vector<SVGLength>::const_iterator end = dashes.end(); 295 for (Vector<SVGLength>::const_iterator it = dashes.begin(); it != end; ++it) 296 dashArray.append((*it).value(lengthContext)); 297 298 writeIfNotDefault(ts, "opacity", svgStyle->strokeOpacity(), 1.0f); 299 writeIfNotDefault(ts, "stroke width", strokeWidth, 1.0); 300 writeIfNotDefault(ts, "miter limit", svgStyle->strokeMiterLimit(), 4.0f); 301 writeIfNotDefault(ts, "line cap", svgStyle->capStyle(), ButtCap); 302 writeIfNotDefault(ts, "line join", svgStyle->joinStyle(), MiterJoin); 303 writeIfNotDefault(ts, "dash offset", dashOffset, 0.0); 304 if (!dashArray.isEmpty()) 305 writeNameValuePair(ts, "dash array", dashArray); 306 307 ts << "}]"; 308 } 309 310 if (RenderSVGResource* fillPaintingResource = RenderSVGResource::fillPaintingResource(const_cast<RenderSVGShape*>(&shape), shape.style(), fallbackColor)) { 311 TextStreamSeparator s(" "); 312 ts << " [fill={" << s; 313 writeSVGPaintingResource(ts, fillPaintingResource); 314 315 writeIfNotDefault(ts, "opacity", svgStyle->fillOpacity(), 1.0f); 316 writeIfNotDefault(ts, "fill rule", svgStyle->fillRule(), RULE_NONZERO); 317 ts << "}]"; 318 } 319 writeIfNotDefault(ts, "clip rule", svgStyle->clipRule(), RULE_NONZERO); 320 } 321 322 writeIfNotEmpty(ts, "start marker", svgStyle->markerStartResource()); 323 writeIfNotEmpty(ts, "middle marker", svgStyle->markerMidResource()); 324 writeIfNotEmpty(ts, "end marker", svgStyle->markerEndResource()); 325} 326 327static TextStream& writePositionAndStyle(TextStream& ts, const RenderObject& object) 328{ 329 ts << " " << enclosingIntRect(const_cast<RenderObject&>(object).absoluteClippedOverflowRect()); 330 writeStyle(ts, object); 331 return ts; 332} 333 334static TextStream& operator<<(TextStream& ts, const RenderSVGShape& shape) 335{ 336 writePositionAndStyle(ts, shape); 337 338 ASSERT(shape.node()->isSVGElement()); 339 SVGElement* svgElement = toSVGElement(shape.node()); 340 SVGLengthContext lengthContext(svgElement); 341 342 if (svgElement->hasTagName(SVGNames::rectTag)) { 343 SVGRectElement* element = static_cast<SVGRectElement*>(svgElement); 344 writeNameValuePair(ts, "x", element->x().value(lengthContext)); 345 writeNameValuePair(ts, "y", element->y().value(lengthContext)); 346 writeNameValuePair(ts, "width", element->width().value(lengthContext)); 347 writeNameValuePair(ts, "height", element->height().value(lengthContext)); 348 } else if (svgElement->hasTagName(SVGNames::lineTag)) { 349 SVGLineElement* element = static_cast<SVGLineElement*>(svgElement); 350 writeNameValuePair(ts, "x1", element->x1().value(lengthContext)); 351 writeNameValuePair(ts, "y1", element->y1().value(lengthContext)); 352 writeNameValuePair(ts, "x2", element->x2().value(lengthContext)); 353 writeNameValuePair(ts, "y2", element->y2().value(lengthContext)); 354 } else if (svgElement->hasTagName(SVGNames::ellipseTag)) { 355 SVGEllipseElement* element = static_cast<SVGEllipseElement*>(svgElement); 356 writeNameValuePair(ts, "cx", element->cx().value(lengthContext)); 357 writeNameValuePair(ts, "cy", element->cy().value(lengthContext)); 358 writeNameValuePair(ts, "rx", element->rx().value(lengthContext)); 359 writeNameValuePair(ts, "ry", element->ry().value(lengthContext)); 360 } else if (svgElement->hasTagName(SVGNames::circleTag)) { 361 SVGCircleElement* element = static_cast<SVGCircleElement*>(svgElement); 362 writeNameValuePair(ts, "cx", element->cx().value(lengthContext)); 363 writeNameValuePair(ts, "cy", element->cy().value(lengthContext)); 364 writeNameValuePair(ts, "r", element->r().value(lengthContext)); 365 } else if (svgElement->hasTagName(SVGNames::polygonTag) || svgElement->hasTagName(SVGNames::polylineTag)) { 366 SVGPolyElement* element = static_cast<SVGPolyElement*>(svgElement); 367 writeNameAndQuotedValue(ts, "points", element->pointList().valueAsString()); 368 } else if (svgElement->hasTagName(SVGNames::pathTag)) { 369 SVGPathElement* element = static_cast<SVGPathElement*>(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(element->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 = static_cast<SVGRootInlineBox*>(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.style()->visitedDependentColor(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 ASSERT(textRenderer); 407 408 const SVGRenderStyle* svgStyle = textRenderer->style()->svgStyle(); 409 String text = textBox->textRenderer()->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 456static 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, static_cast<SVGInlineTextBox*>(box), indent); 463 } 464} 465 466static 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 475static void writeChildren(TextStream& ts, const RenderObject& object, int indent) 476{ 477 for (RenderObject* child = object.firstChild(); child; child = child->nextSibling()) 478 write(ts, *child, indent + 1); 479} 480 481static 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 492void 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 = const_cast<RenderObject&>(object).toRenderSVGResourceContainer(); 501 ASSERT(resource); 502 503 if (resource->resourceType() == MaskerResourceType) { 504 RenderSVGResourceMasker* masker = static_cast<RenderSVGResourceMasker*>(resource); 505 writeNameValuePair(ts, "maskUnits", masker->maskUnits()); 506 writeNameValuePair(ts, "maskContentUnits", masker->maskContentUnits()); 507 ts << "\n"; 508#if ENABLE(FILTERS) 509 } else if (resource->resourceType() == FilterResourceType) { 510 RenderSVGResourceFilter* filter = static_cast<RenderSVGResourceFilter*>(resource); 511 writeNameValuePair(ts, "filterUnits", filter->filterUnits()); 512 writeNameValuePair(ts, "primitiveUnits", filter->primitiveUnits()); 513 ts << "\n"; 514 // Creating a placeholder filter which is passed to the builder. 515 FloatRect dummyRect; 516 RefPtr<SVGFilter> dummyFilter = SVGFilter::create(AffineTransform(), dummyRect, 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#endif 522 } else if (resource->resourceType() == ClipperResourceType) { 523 RenderSVGResourceClipper* clipper = static_cast<RenderSVGResourceClipper*>(resource); 524 writeNameValuePair(ts, "clipPathUnits", clipper->clipPathUnits()); 525 ts << "\n"; 526 } else if (resource->resourceType() == MarkerResourceType) { 527 RenderSVGResourceMarker* marker = static_cast<RenderSVGResourceMarker*>(resource); 528 writeNameValuePair(ts, "markerUnits", marker->markerUnits()); 529 ts << " [ref at " << marker->referencePoint() << "]"; 530 ts << " [angle="; 531 if (marker->angle() == -1) 532 ts << "auto" << "]\n"; 533 else 534 ts << marker->angle() << "]\n"; 535 } else if (resource->resourceType() == PatternResourceType) { 536 RenderSVGResourcePattern* pattern = static_cast<RenderSVGResourcePattern*>(resource); 537 538 // Dump final results that are used for rendering. No use in asking SVGPatternElement for its patternUnits(), as it may 539 // link to other patterns using xlink:href, we need to build the full inheritance chain, aka. collectPatternProperties() 540 PatternAttributes attributes; 541 static_cast<SVGPatternElement*>(pattern->node())->collectPatternAttributes(attributes); 542 543 writeNameValuePair(ts, "patternUnits", attributes.patternUnits()); 544 writeNameValuePair(ts, "patternContentUnits", attributes.patternContentUnits()); 545 546 AffineTransform transform = attributes.patternTransform(); 547 if (!transform.isIdentity()) 548 ts << " [patternTransform=" << transform << "]"; 549 ts << "\n"; 550 } else if (resource->resourceType() == LinearGradientResourceType) { 551 RenderSVGResourceLinearGradient* gradient = static_cast<RenderSVGResourceLinearGradient*>(resource); 552 553 // Dump final results that are used for rendering. No use in asking SVGGradientElement for its gradientUnits(), as it may 554 // link to other gradients using xlink:href, we need to build the full inheritance chain, aka. collectGradientProperties() 555 SVGLinearGradientElement* linearGradientElement = static_cast<SVGLinearGradientElement*>(gradient->node()); 556 557 LinearGradientAttributes attributes; 558 linearGradientElement->collectGradientAttributes(attributes); 559 writeCommonGradientProperties(ts, attributes.spreadMethod(), attributes.gradientTransform(), attributes.gradientUnits()); 560 561 ts << " [start=" << gradient->startPoint(attributes) << "] [end=" << gradient->endPoint(attributes) << "]\n"; 562 } else if (resource->resourceType() == RadialGradientResourceType) { 563 RenderSVGResourceRadialGradient* gradient = static_cast<RenderSVGResourceRadialGradient*>(resource); 564 565 // Dump final results that are used for rendering. No use in asking SVGGradientElement for its gradientUnits(), as it may 566 // link to other gradients using xlink:href, we need to build the full inheritance chain, aka. collectGradientProperties() 567 SVGRadialGradientElement* radialGradientElement = static_cast<SVGRadialGradientElement*>(gradient->node()); 568 569 RadialGradientAttributes attributes; 570 radialGradientElement->collectGradientAttributes(attributes); 571 writeCommonGradientProperties(ts, attributes.spreadMethod(), attributes.gradientTransform(), attributes.gradientUnits()); 572 573 FloatPoint focalPoint = gradient->focalPoint(attributes); 574 FloatPoint centerPoint = gradient->centerPoint(attributes); 575 float radius = gradient->radius(attributes); 576 float focalRadius = gradient->focalRadius(attributes); 577 578 ts << " [center=" << centerPoint << "] [focal=" << focalPoint << "] [radius=" << radius << "] [focalRadius=" << focalRadius << "]\n"; 579 } else 580 ts << "\n"; 581 writeChildren(ts, object, indent); 582} 583 584void writeSVGContainer(TextStream& ts, const RenderObject& container, int indent) 585{ 586 // Currently RenderSVGResourceFilterPrimitive has no meaningful output. 587 if (container.isSVGResourceFilterPrimitive()) 588 return; 589 writeStandardPrefix(ts, container, indent); 590 writePositionAndStyle(ts, container); 591 ts << "\n"; 592 writeResources(ts, container, indent); 593 writeChildren(ts, container, indent); 594} 595 596void write(TextStream& ts, const RenderSVGRoot& root, int indent) 597{ 598 writeStandardPrefix(ts, root, indent); 599 ts << root << "\n"; 600 writeChildren(ts, root, indent); 601} 602 603void writeSVGText(TextStream& ts, const RenderSVGText& text, int indent) 604{ 605 writeStandardPrefix(ts, text, indent); 606 writeRenderSVGTextBox(ts, text); 607 ts << "\n"; 608 writeResources(ts, text, indent); 609 writeChildren(ts, text, indent); 610} 611 612void writeSVGInlineText(TextStream& ts, const RenderSVGInlineText& text, int indent) 613{ 614 writeStandardPrefix(ts, text, indent); 615 ts << " " << enclosingIntRect(FloatRect(text.firstRunOrigin(), text.floatLinesBoundingBox().size())) << "\n"; 616 writeResources(ts, text, indent); 617 writeSVGInlineTextBoxes(ts, text, indent); 618} 619 620void writeSVGImage(TextStream& ts, const RenderSVGImage& image, int indent) 621{ 622 writeStandardPrefix(ts, image, indent); 623 writePositionAndStyle(ts, image); 624 ts << "\n"; 625 writeResources(ts, image, indent); 626} 627 628void write(TextStream& ts, const RenderSVGShape& shape, int indent) 629{ 630 writeStandardPrefix(ts, shape, indent); 631 ts << shape << "\n"; 632 writeResources(ts, shape, indent); 633} 634 635void writeSVGGradientStop(TextStream& ts, const RenderSVGGradientStop& stop, int indent) 636{ 637 writeStandardPrefix(ts, stop, indent); 638 639 SVGStopElement* stopElement = static_cast<SVGStopElement*>(stop.node()); 640 ASSERT(stopElement); 641 642 RenderStyle* style = stop.style(); 643 if (!style) 644 return; 645 646 ts << " [offset=" << stopElement->offset() << "] [color=" << stopElement->stopColorIncludingOpacity() << "]\n"; 647} 648 649void writeResources(TextStream& ts, const RenderObject& object, int indent) 650{ 651 const RenderStyle* style = object.style(); 652 const SVGRenderStyle* svgStyle = style->svgStyle(); 653 654 // FIXME: We want to use SVGResourcesCache to determine which resources are present, instead of quering the resource <-> id cache. 655 // For now leave the DRT output as is, but later on we should change this so cycles are properly ignored in the DRT output. 656 RenderObject& renderer = const_cast<RenderObject&>(object); 657 if (!svgStyle->maskerResource().isEmpty()) { 658 if (RenderSVGResourceMasker* masker = getRenderSVGResourceById<RenderSVGResourceMasker>(object.document(), svgStyle->maskerResource())) { 659 writeIndent(ts, indent); 660 ts << " "; 661 writeNameAndQuotedValue(ts, "masker", svgStyle->maskerResource()); 662 ts << " "; 663 writeStandardPrefix(ts, *masker, 0); 664 ts << " " << masker->resourceBoundingBox(&renderer) << "\n"; 665 } 666 } 667 if (!svgStyle->clipperResource().isEmpty()) { 668 if (RenderSVGResourceClipper* clipper = getRenderSVGResourceById<RenderSVGResourceClipper>(object.document(), svgStyle->clipperResource())) { 669 writeIndent(ts, indent); 670 ts << " "; 671 writeNameAndQuotedValue(ts, "clipPath", svgStyle->clipperResource()); 672 ts << " "; 673 writeStandardPrefix(ts, *clipper, 0); 674 ts << " " << clipper->resourceBoundingBox(&renderer) << "\n"; 675 } 676 } 677#if ENABLE(FILTERS) 678 if (!svgStyle->filterResource().isEmpty()) { 679 if (RenderSVGResourceFilter* filter = getRenderSVGResourceById<RenderSVGResourceFilter>(object.document(), svgStyle->filterResource())) { 680 writeIndent(ts, indent); 681 ts << " "; 682 writeNameAndQuotedValue(ts, "filter", svgStyle->filterResource()); 683 ts << " "; 684 writeStandardPrefix(ts, *filter, 0); 685 ts << " " << filter->resourceBoundingBox(&renderer) << "\n"; 686 } 687 } 688#endif 689} 690 691} // namespace WebCore 692 693#endif // ENABLE(SVG) 694