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 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include "config.h" 29 30 #if ENABLE(SVG) 31 #include "SVGRenderTreeAsText.h" 32 33 #include "GraphicsTypes.h" 34 #include "HTMLNames.h" 35 #include "InlineTextBox.h" 36 #include "NodeRenderStyle.h" 37 #include "RenderImage.h" 38 #include "RenderPath.h" 39 #include "RenderSVGContainer.h" 40 #include "RenderSVGInlineText.h" 41 #include "RenderSVGRoot.h" 42 #include "RenderSVGText.h" 43 #include "RenderTreeAsText.h" 44 #include "SVGCharacterLayoutInfo.h" 45 #include "SVGInlineTextBox.h" 46 #include "SVGPaintServerGradient.h" 47 #include "SVGPaintServerPattern.h" 48 #include "SVGPaintServerSolid.h" 49 #include "SVGResourceClipper.h" 50 #include "SVGRootInlineBox.h" 51 #include "SVGStyledElement.h" 52 #include <math.h> 53 54 namespace WebCore { 55 56 /** class + iomanip to help streaming list separators, i.e. ", " in string "a, b, c, d" 57 * Can be used in cases where you don't know which item in the list is the first 58 * one to be printed, but still want to avoid strings like ", b, c". 59 */ 60 class TextStreamSeparator { 61 public: 62 TextStreamSeparator(const String& s) 63 : m_separator(s) 64 , m_needToSeparate(false) 65 { 66 } 67 68 private: 69 friend TextStream& operator<<(TextStream&, TextStreamSeparator&); 70 71 String m_separator; 72 bool m_needToSeparate; 73 }; 74 75 TextStream& operator<<(TextStream& ts, TextStreamSeparator& sep) 76 { 77 if (sep.m_needToSeparate) 78 ts << sep.m_separator; 79 else 80 sep.m_needToSeparate = true; 81 return ts; 82 } 83 84 template<typename ValueType> 85 static void writeNameValuePair(TextStream& ts, const char* name, ValueType value) 86 { 87 ts << " [" << name << "=" << value << "]"; 88 } 89 90 template<typename ValueType> 91 static void writeNameAndQuotedValue(TextStream& ts, const char* name, ValueType value) 92 { 93 ts << " [" << name << "=\"" << value << "\"]"; 94 } 95 96 static void writeIfNotEmpty(TextStream& ts, const char* name, const String& value) 97 { 98 if (!value.isEmpty()) 99 writeNameValuePair(ts, name, value); 100 } 101 102 template<typename ValueType> 103 static void writeIfNotDefault(TextStream& ts, const char* name, ValueType value, ValueType defaultValue) 104 { 105 if (value != defaultValue) 106 writeNameValuePair(ts, name, value); 107 } 108 109 TextStream& operator<<(TextStream& ts, const IntPoint& p) 110 { 111 return ts << "(" << p.x() << "," << p.y() << ")"; 112 } 113 114 TextStream& operator<<(TextStream& ts, const IntRect& r) 115 { 116 return ts << "at (" << r.x() << "," << r.y() << ") size " << r.width() << "x" << r.height(); 117 } 118 119 static bool hasFractions(double val) 120 { 121 double epsilon = 0.0001; 122 int ival = static_cast<int>(val); 123 double dval = static_cast<double>(ival); 124 return fabs(val - dval) > epsilon; 125 } 126 127 TextStream& operator<<(TextStream& ts, const FloatRect &r) 128 { 129 ts << "at ("; 130 if (hasFractions(r.x())) 131 ts << r.x(); 132 else 133 ts << int(r.x()); 134 ts << ","; 135 if (hasFractions(r.y())) 136 ts << r.y(); 137 else 138 ts << int(r.y()); 139 ts << ") size "; 140 if (hasFractions(r.width())) 141 ts << r.width(); 142 else 143 ts << int(r.width()); 144 ts << "x"; 145 if (hasFractions(r.height())) 146 ts << r.height(); 147 else 148 ts << int(r.height()); 149 return ts; 150 } 151 152 TextStream& operator<<(TextStream& ts, const FloatPoint& p) 153 { 154 ts << "("; 155 if (hasFractions(p.x())) 156 ts << p.x(); 157 else 158 ts << int(p.x()); 159 ts << ","; 160 if (hasFractions(p.y())) 161 ts << p.y(); 162 else 163 ts << int(p.y()); 164 return ts << ")"; 165 } 166 167 TextStream& operator<<(TextStream& ts, const FloatSize& s) 168 { 169 ts << "width="; 170 if (hasFractions(s.width())) 171 ts << s.width(); 172 else 173 ts << int(s.width()); 174 ts << " height="; 175 if (hasFractions(s.height())) 176 ts << s.height(); 177 else 178 ts << int(s.height()); 179 return ts; 180 } 181 182 TextStream& operator<<(TextStream& ts, const AffineTransform& transform) 183 { 184 if (transform.isIdentity()) 185 ts << "identity"; 186 else 187 ts << "{m=((" 188 << transform.a() << "," << transform.b() 189 << ")(" 190 << transform.c() << "," << transform.d() 191 << ")) t=(" 192 << transform.e() << "," << transform.f() 193 << ")}"; 194 195 return ts; 196 } 197 198 TextStream& operator<<(TextStream& ts, const Color& c) 199 { 200 return ts << c.name(); 201 } 202 203 static void writeIndent(TextStream& ts, int indent) 204 { 205 for (int i = 0; i != indent; ++i) 206 ts << " "; 207 } 208 209 // FIXME: Maybe this should be in KCanvasRenderingStyle.cpp 210 static TextStream& operator<<(TextStream& ts, const DashArray& a) 211 { 212 ts << "{"; 213 DashArray::const_iterator end = a.end(); 214 for (DashArray::const_iterator it = a.begin(); it != end; ++it) { 215 if (it != a.begin()) 216 ts << ", "; 217 ts << *it; 218 } 219 ts << "}"; 220 return ts; 221 } 222 223 // FIXME: Maybe this should be in GraphicsTypes.cpp 224 static TextStream& operator<<(TextStream& ts, LineCap style) 225 { 226 switch (style) { 227 case ButtCap: 228 ts << "BUTT"; 229 break; 230 case RoundCap: 231 ts << "ROUND"; 232 break; 233 case SquareCap: 234 ts << "SQUARE"; 235 break; 236 } 237 return ts; 238 } 239 240 // FIXME: Maybe this should be in GraphicsTypes.cpp 241 static TextStream& operator<<(TextStream& ts, LineJoin style) 242 { 243 switch (style) { 244 case MiterJoin: 245 ts << "MITER"; 246 break; 247 case RoundJoin: 248 ts << "ROUND"; 249 break; 250 case BevelJoin: 251 ts << "BEVEL"; 252 break; 253 } 254 return ts; 255 } 256 257 static void writeStyle(TextStream& ts, const RenderObject& object) 258 { 259 const RenderStyle* style = object.style(); 260 const SVGRenderStyle* svgStyle = style->svgStyle(); 261 262 if (!object.localTransform().isIdentity()) 263 writeNameValuePair(ts, "transform", object.localTransform()); 264 writeIfNotDefault(ts, "image rendering", svgStyle->imageRendering(), SVGRenderStyle::initialImageRendering()); 265 writeIfNotDefault(ts, "opacity", style->opacity(), RenderStyle::initialOpacity()); 266 if (object.isRenderPath()) { 267 const RenderPath& path = static_cast<const RenderPath&>(object); 268 SVGPaintServer* strokePaintServer = SVGPaintServer::strokePaintServer(style, &path); 269 if (strokePaintServer) { 270 TextStreamSeparator s(" "); 271 ts << " [stroke={"; 272 if (strokePaintServer) 273 ts << s << *strokePaintServer; 274 275 double dashOffset = SVGRenderStyle::cssPrimitiveToLength(&path, svgStyle->strokeDashOffset(), 0.0f); 276 const DashArray& dashArray = dashArrayFromRenderingStyle(style, object.document()->documentElement()->renderStyle()); 277 double strokeWidth = SVGRenderStyle::cssPrimitiveToLength(&path, svgStyle->strokeWidth(), 1.0f); 278 279 writeIfNotDefault(ts, "opacity", svgStyle->strokeOpacity(), 1.0f); 280 writeIfNotDefault(ts, "stroke width", strokeWidth, 1.0); 281 writeIfNotDefault(ts, "miter limit", svgStyle->strokeMiterLimit(), 4.0f); 282 writeIfNotDefault(ts, "line cap", svgStyle->capStyle(), ButtCap); 283 writeIfNotDefault(ts, "line join", svgStyle->joinStyle(), MiterJoin); 284 writeIfNotDefault(ts, "dash offset", dashOffset, 0.0); 285 if (!dashArray.isEmpty()) 286 writeNameValuePair(ts, "dash array", dashArray); 287 288 ts << "}]"; 289 } 290 SVGPaintServer* fillPaintServer = SVGPaintServer::fillPaintServer(style, &path); 291 if (fillPaintServer) { 292 TextStreamSeparator s(" "); 293 ts << " [fill={"; 294 if (fillPaintServer) 295 ts << s << *fillPaintServer; 296 297 writeIfNotDefault(ts, "opacity", svgStyle->fillOpacity(), 1.0f); 298 writeIfNotDefault(ts, "fill rule", svgStyle->fillRule(), RULE_NONZERO); 299 ts << "}]"; 300 } 301 } 302 303 if (!svgStyle->clipPath().isEmpty()) 304 writeNameAndQuotedValue(ts, "clip path", svgStyle->clipPath()); 305 writeIfNotEmpty(ts, "start marker", svgStyle->startMarker()); 306 writeIfNotEmpty(ts, "middle marker", svgStyle->midMarker()); 307 writeIfNotEmpty(ts, "end marker", svgStyle->endMarker()); 308 writeIfNotEmpty(ts, "filter", svgStyle->filter()); 309 } 310 311 static TextStream& writePositionAndStyle(TextStream& ts, const RenderObject& object) 312 { 313 ts << " " << const_cast<RenderObject&>(object).absoluteClippedOverflowRect(); 314 writeStyle(ts, object); 315 return ts; 316 } 317 318 static TextStream& operator<<(TextStream& ts, const RenderPath& path) 319 { 320 writePositionAndStyle(ts, path); 321 writeNameAndQuotedValue(ts, "data", path.path().debugString()); 322 return ts; 323 } 324 325 static TextStream& operator<<(TextStream& ts, const RenderSVGRoot& root) 326 { 327 return writePositionAndStyle(ts, root); 328 } 329 330 static void writeRenderSVGTextBox(TextStream& ts, const RenderBlock& text) 331 { 332 SVGRootInlineBox* box = static_cast<SVGRootInlineBox*>(text.firstRootBox()); 333 334 if (!box) 335 return; 336 337 Vector<SVGTextChunk>& chunks = const_cast<Vector<SVGTextChunk>& >(box->svgTextChunks()); 338 ts << " at (" << text.x() << "," << text.y() << ") size " << box->width() << "x" << box->height() << " contains " << chunks.size() << " chunk(s)"; 339 340 if (text.parent() && (text.parent()->style()->color() != text.style()->color())) 341 writeNameValuePair(ts, "color", text.style()->color().name()); 342 } 343 344 static inline bool containsInlineTextBox(SVGTextChunk& chunk, SVGInlineTextBox* box) 345 { 346 Vector<SVGInlineBoxCharacterRange>::iterator boxIt = chunk.boxes.begin(); 347 Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = chunk.boxes.end(); 348 349 bool found = false; 350 for (; boxIt != boxEnd; ++boxIt) { 351 SVGInlineBoxCharacterRange& range = *boxIt; 352 353 if (box == static_cast<SVGInlineTextBox*>(range.box)) { 354 found = true; 355 break; 356 } 357 } 358 359 return found; 360 } 361 362 static inline void writeSVGInlineTextBox(TextStream& ts, SVGInlineTextBox* textBox, int indent) 363 { 364 SVGRootInlineBox* rootBox = textBox->svgRootInlineBox(); 365 if (!rootBox) 366 return; 367 368 Vector<SVGTextChunk>& chunks = const_cast<Vector<SVGTextChunk>& >(rootBox->svgTextChunks()); 369 370 Vector<SVGTextChunk>::iterator it = chunks.begin(); 371 Vector<SVGTextChunk>::iterator end = chunks.end(); 372 373 // Write text chunks 374 unsigned int i = 1; 375 for (; it != end; ++it) { 376 SVGTextChunk& cur = *it; 377 378 // Write inline box character ranges 379 Vector<SVGInlineBoxCharacterRange>::iterator boxIt = cur.boxes.begin(); 380 Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = cur.boxes.end(); 381 382 if (!containsInlineTextBox(cur, textBox)) { 383 i++; 384 continue; 385 } 386 387 writeIndent(ts, indent + 1); 388 389 unsigned int j = 1; 390 ts << "chunk " << i << " "; 391 392 if (cur.anchor == TA_MIDDLE) { 393 ts << "(middle anchor"; 394 if (cur.isVerticalText) 395 ts << ", vertical"; 396 ts << ") "; 397 } else if (cur.anchor == TA_END) { 398 ts << "(end anchor"; 399 if (cur.isVerticalText) 400 ts << ", vertical"; 401 ts << ") "; 402 } else if (cur.isVerticalText) 403 ts << "(vertical) "; 404 405 unsigned int totalOffset = 0; 406 407 for (; boxIt != boxEnd; ++boxIt) { 408 SVGInlineBoxCharacterRange& range = *boxIt; 409 410 unsigned int offset = range.endOffset - range.startOffset; 411 ASSERT(cur.start + totalOffset <= cur.end); 412 413 totalOffset += offset; 414 415 if (textBox != static_cast<SVGInlineTextBox*>(range.box)) { 416 j++; 417 continue; 418 } 419 420 FloatPoint topLeft = topLeftPositionOfCharacterRange(cur.start + totalOffset - offset, cur.start + totalOffset); 421 422 ts << "text run " << j << " at (" << topLeft.x() << "," << topLeft.y() << ") "; 423 ts << "startOffset " << range.startOffset << " endOffset " << range.endOffset; 424 425 if (cur.isVerticalText) 426 ts << " height " << cummulatedHeightOfInlineBoxCharacterRange(range); 427 else 428 ts << " width " << cummulatedWidthOfInlineBoxCharacterRange(range); 429 430 if (textBox->direction() == RTL || textBox->m_dirOverride) { 431 ts << (textBox->direction() == RTL ? " RTL" : " LTR"); 432 433 if (textBox->m_dirOverride) 434 ts << " override"; 435 } 436 437 ts << ": " << quoteAndEscapeNonPrintables(String(textBox->textRenderer()->text()).substring(textBox->start() + range.startOffset, offset)) << "\n"; 438 439 j++; 440 } 441 442 i++; 443 } 444 } 445 446 static inline void writeSVGInlineTextBoxes(TextStream& ts, const RenderText& text, int indent) 447 { 448 for (InlineTextBox* box = text.firstTextBox(); box; box = box->nextTextBox()) 449 writeSVGInlineTextBox(ts, static_cast<SVGInlineTextBox*>(box), indent); 450 } 451 452 static void writeStandardPrefix(TextStream& ts, const RenderObject& object, int indent) 453 { 454 writeIndent(ts, indent); 455 ts << object.renderName(); 456 457 if (object.node()) 458 ts << " {" << object.node()->nodeName() << "}"; 459 } 460 461 static void writeChildren(TextStream& ts, const RenderObject& object, int indent) 462 { 463 for (RenderObject* child = object.firstChild(); child; child = child->nextSibling()) 464 write(ts, *child, indent + 1); 465 } 466 467 void writeSVGContainer(TextStream& ts, const RenderObject& container, int indent) 468 { 469 writeStandardPrefix(ts, container, indent); 470 writePositionAndStyle(ts, container); 471 ts << "\n"; 472 writeChildren(ts, container, indent); 473 } 474 475 void write(TextStream& ts, const RenderSVGRoot& root, int indent) 476 { 477 writeStandardPrefix(ts, root, indent); 478 ts << root << "\n"; 479 writeChildren(ts, root, indent); 480 } 481 482 void writeSVGText(TextStream& ts, const RenderBlock& text, int indent) 483 { 484 writeStandardPrefix(ts, text, indent); 485 writeRenderSVGTextBox(ts, text); 486 ts << "\n"; 487 writeChildren(ts, text, indent); 488 } 489 490 void writeSVGInlineText(TextStream& ts, const RenderText& text, int indent) 491 { 492 writeStandardPrefix(ts, text, indent); 493 494 // Why not just linesBoundingBox()? 495 ts << " " << FloatRect(text.firstRunOrigin(), text.linesBoundingBox().size()) << "\n"; 496 writeSVGInlineTextBoxes(ts, text, indent); 497 } 498 499 void write(TextStream& ts, const RenderPath& path, int indent) 500 { 501 writeStandardPrefix(ts, path, indent); 502 ts << path << "\n"; 503 } 504 505 void writeSVGImage(TextStream& ts, const RenderImage& image, int indent) 506 { 507 writeStandardPrefix(ts, image, indent); 508 writePositionAndStyle(ts, image); 509 ts << "\n"; 510 } 511 512 void writeRenderResources(TextStream& ts, Node* parent) 513 { 514 ASSERT(parent); 515 Node* node = parent; 516 do { 517 if (!node->isSVGElement()) 518 continue; 519 SVGElement* svgElement = static_cast<SVGElement*>(node); 520 if (!svgElement->isStyled()) 521 continue; 522 523 SVGStyledElement* styled = static_cast<SVGStyledElement*>(svgElement); 524 RefPtr<SVGResource> resource(styled->canvasResource(node->renderer())); 525 if (!resource) 526 continue; 527 528 String elementId = svgElement->getAttribute(svgElement->idAttributeName()); 529 // FIXME: These names are lies! 530 if (resource->isPaintServer()) { 531 RefPtr<SVGPaintServer> paintServer = WTF::static_pointer_cast<SVGPaintServer>(resource); 532 ts << "KRenderingPaintServer {id=\"" << elementId << "\" " << *paintServer << "}" << "\n"; 533 } else 534 ts << "KCanvasResource {id=\"" << elementId << "\" " << *resource << "}" << "\n"; 535 } while ((node = node->traverseNextNode(parent))); 536 } 537 538 } // namespace WebCore 539 540 #endif // ENABLE(SVG) 541