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 *
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:
TextStreamSeparator(const String & s)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
operator <<(TextStream & ts,TextStreamSeparator & sep)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>
writeNameValuePair(TextStream & ts,const char * name,ValueType value)85 static void writeNameValuePair(TextStream& ts, const char* name, ValueType value)
86 {
87 ts << " [" << name << "=" << value << "]";
88 }
89
90 template<typename ValueType>
writeNameAndQuotedValue(TextStream & ts,const char * name,ValueType value)91 static void writeNameAndQuotedValue(TextStream& ts, const char* name, ValueType value)
92 {
93 ts << " [" << name << "=\"" << value << "\"]";
94 }
95
writeIfNotEmpty(TextStream & ts,const char * name,const String & value)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>
writeIfNotDefault(TextStream & ts,const char * name,ValueType value,ValueType defaultValue)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
operator <<(TextStream & ts,const IntPoint & p)109 TextStream& operator<<(TextStream& ts, const IntPoint& p)
110 {
111 return ts << "(" << p.x() << "," << p.y() << ")";
112 }
113
operator <<(TextStream & ts,const IntRect & r)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
hasFractions(double val)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
operator <<(TextStream & ts,const FloatRect & r)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
operator <<(TextStream & ts,const FloatPoint & p)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
operator <<(TextStream & ts,const FloatSize & s)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
operator <<(TextStream & ts,const TransformationMatrix & transform)182 TextStream& operator<<(TextStream& ts, const TransformationMatrix& 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
operator <<(TextStream & ts,const Color & c)198 TextStream& operator<<(TextStream& ts, const Color& c)
199 {
200 return ts << c.name();
201 }
202
writeIndent(TextStream & ts,int indent)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
operator <<(TextStream & ts,const DashArray & a)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
operator <<(TextStream & ts,LineCap style)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
operator <<(TextStream & ts,LineJoin style)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
writeStyle(TextStream & ts,const RenderObject & object)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
writePositionAndStyle(TextStream & ts,const RenderObject & object)311 static TextStream& writePositionAndStyle(TextStream& ts, const RenderObject& object)
312 {
313 ts << " " << object.absoluteTransform().mapRect(object.repaintRectInLocalCoordinates());
314 writeStyle(ts, object);
315 return ts;
316 }
317
operator <<(TextStream & ts,const RenderPath & path)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
operator <<(TextStream & ts,const RenderSVGRoot & root)325 static TextStream& operator<<(TextStream& ts, const RenderSVGRoot& root)
326 {
327 return writePositionAndStyle(ts, root);
328 }
329
writeRenderSVGTextBox(TextStream & ts,const RenderBlock & text)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
containsInlineTextBox(SVGTextChunk & chunk,SVGInlineTextBox * box)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
writeSVGInlineTextBox(TextStream & ts,SVGInlineTextBox * textBox,int indent)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
writeSVGInlineTextBoxes(TextStream & ts,const RenderText & text,int indent)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
writeStandardPrefix(TextStream & ts,const RenderObject & object,int indent)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
writeChildren(TextStream & ts,const RenderObject & object,int indent)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
writeSVGContainer(TextStream & ts,const RenderObject & container,int indent)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
write(TextStream & ts,const RenderSVGRoot & root,int indent)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
writeSVGText(TextStream & ts,const RenderBlock & text,int indent)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
writeSVGInlineText(TextStream & ts,const RenderText & text,int indent)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
write(TextStream & ts,const RenderPath & path,int indent)499 void write(TextStream& ts, const RenderPath& path, int indent)
500 {
501 writeStandardPrefix(ts, path, indent);
502 ts << path << "\n";
503 }
504
writeSVGImage(TextStream & ts,const RenderImage & image,int indent)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
writeRenderResources(TextStream & ts,Node * parent)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());
525 if (!resource)
526 continue;
527
528 String elementId = svgElement->getAttribute(HTMLNames::idAttr);
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