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