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