• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2004, 2005, 2007 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 "InlineTextBox.h"
35 #include "HTMLNames.h"
36 #include "RenderSVGContainer.h"
37 #include "RenderSVGInlineText.h"
38 #include "RenderSVGText.h"
39 #include "RenderSVGRoot.h"
40 #include "RenderTreeAsText.h"
41 #include "SVGCharacterLayoutInfo.h"
42 #include "SVGInlineTextBox.h"
43 #include "SVGPaintServerGradient.h"
44 #include "SVGPaintServerPattern.h"
45 #include "SVGPaintServerSolid.h"
46 #include "SVGResourceClipper.h"
47 #include "SVGRootInlineBox.h"
48 #include "SVGStyledElement.h"
49 #include <math.h>
50 
51 namespace WebCore {
52 
53 /** class + iomanip to help streaming list separators, i.e. ", " in string "a, b, c, d"
54  * Can be used in cases where you don't know which item in the list is the first
55  * one to be printed, but still want to avoid strings like ", b, c".
56  */
57 class TextStreamSeparator {
58 public:
TextStreamSeparator(const String & s)59     TextStreamSeparator(const String& s)
60         : m_separator(s)
61         , m_needToSeparate(false)
62     {
63     }
64 
65 private:
66     friend TextStream& operator<<(TextStream&, TextStreamSeparator&);
67 
68     String m_separator;
69     bool m_needToSeparate;
70 };
71 
operator <<(TextStream & ts,TextStreamSeparator & sep)72 TextStream& operator<<(TextStream& ts, TextStreamSeparator& sep)
73 {
74     if (sep.m_needToSeparate)
75         ts << sep.m_separator;
76     else
77         sep.m_needToSeparate = true;
78     return ts;
79 }
80 
operator <<(TextStream & ts,const IntPoint & p)81 TextStream& operator<<(TextStream& ts, const IntPoint& p)
82 {
83     return ts << "(" << p.x() << "," << p.y() << ")";
84 }
85 
operator <<(TextStream & ts,const IntRect & r)86 TextStream& operator<<(TextStream& ts, const IntRect& r)
87 {
88     return ts << "at (" << r.x() << "," << r.y() << ") size " << r.width() << "x" << r.height();
89 }
90 
hasFractions(double val)91 static bool hasFractions(double val)
92 {
93     double epsilon = 0.0001;
94     int ival = static_cast<int>(val);
95     double dval = static_cast<double>(ival);
96     return fabs(val - dval) > epsilon;
97 }
98 
operator <<(TextStream & ts,const FloatRect & r)99 TextStream& operator<<(TextStream& ts, const FloatRect &r)
100 {
101     ts << "at (";
102     if (hasFractions(r.x()))
103         ts << r.x();
104     else
105         ts << int(r.x());
106     ts << ",";
107     if (hasFractions(r.y()))
108         ts << r.y();
109     else
110         ts << int(r.y());
111     ts << ") size ";
112     if (hasFractions(r.width()))
113         ts << r.width();
114     else
115         ts << int(r.width());
116     ts << "x";
117     if (hasFractions(r.height()))
118         ts << r.height();
119     else
120         ts << int(r.height());
121     return ts;
122 }
123 
operator <<(TextStream & ts,const FloatPoint & p)124 TextStream& operator<<(TextStream& ts, const FloatPoint& p)
125 {
126     ts << "(";
127     if (hasFractions(p.x()))
128         ts << p.x();
129     else
130         ts << int(p.x());
131     ts << ",";
132     if (hasFractions(p.y()))
133         ts << p.y();
134     else
135         ts << int(p.y());
136     return ts << ")";
137 }
138 
operator <<(TextStream & ts,const FloatSize & s)139 TextStream& operator<<(TextStream& ts, const FloatSize& s)
140 {
141     ts << "width=";
142     if (hasFractions(s.width()))
143         ts << s.width();
144     else
145         ts << int(s.width());
146     ts << " height=";
147     if (hasFractions(s.height()))
148         ts << s.height();
149     else
150         ts << int(s.height());
151     return ts;
152 }
153 
operator <<(TextStream & ts,const TransformationMatrix & transform)154 TextStream& operator<<(TextStream& ts, const TransformationMatrix& transform)
155 {
156     if (transform.isIdentity())
157         ts << "identity";
158     else
159         ts << "{m=(("
160            << transform.a() << "," << transform.b()
161            << ")("
162            << transform.c() << "," << transform.d()
163            << ")) t=("
164            << transform.e() << "," << transform.f()
165            << ")}";
166 
167     return ts;
168 }
169 
operator <<(TextStream & ts,const Color & c)170 TextStream& operator<<(TextStream& ts, const Color& c)
171 {
172     return ts << c.name();
173 }
174 
writeIndent(TextStream & ts,int indent)175 static void writeIndent(TextStream& ts, int indent)
176 {
177     for (int i = 0; i != indent; ++i)
178         ts << "  ";
179 }
180 
181 // FIXME: Maybe this should be in KCanvasRenderingStyle.cpp
operator <<(TextStream & ts,const DashArray & a)182 static TextStream& operator<<(TextStream& ts, const DashArray& a)
183 {
184     ts << "{";
185     DashArray::const_iterator end = a.end();
186     for (DashArray::const_iterator it = a.begin(); it != end; ++it) {
187         if (it != a.begin())
188             ts << ", ";
189         ts << *it;
190     }
191     ts << "}";
192     return ts;
193 }
194 
195 // FIXME: Maybe this should be in GraphicsTypes.cpp
operator <<(TextStream & ts,LineCap style)196 static TextStream& operator<<(TextStream& ts, LineCap style)
197 {
198     switch (style) {
199         case ButtCap:
200             ts << "BUTT";
201             break;
202         case RoundCap:
203             ts << "ROUND";
204             break;
205         case SquareCap:
206             ts << "SQUARE";
207             break;
208     }
209     return ts;
210 }
211 
212 // FIXME: Maybe this should be in GraphicsTypes.cpp
operator <<(TextStream & ts,LineJoin style)213 static TextStream& operator<<(TextStream& ts, LineJoin style)
214 {
215     switch (style) {
216         case MiterJoin:
217             ts << "MITER";
218             break;
219         case RoundJoin:
220             ts << "ROUND";
221             break;
222         case BevelJoin:
223             ts << "BEVEL";
224             break;
225     }
226     return ts;
227 }
228 
writeStyle(TextStream & ts,const RenderObject & object)229 static void writeStyle(TextStream& ts, const RenderObject& object)
230 {
231     const RenderStyle* style = object.style();
232     const SVGRenderStyle* svgStyle = style->svgStyle();
233 
234     if (!object.localTransform().isIdentity())
235         ts << " [transform=" << object.localTransform() << "]";
236     if (svgStyle->imageRendering() != SVGRenderStyle::initialImageRendering()) {
237         unsigned imageRenderingAsInteger = svgStyle->imageRendering();
238         ts << " [image rendering=" << imageRenderingAsInteger << "]";
239     }
240     if (style->opacity() != RenderStyle::initialOpacity())
241         ts << " [opacity=" << style->opacity() << "]";
242     if (object.isRenderPath()) {
243         const RenderPath& path = static_cast<const RenderPath&>(object);
244         SVGPaintServer* strokePaintServer = SVGPaintServer::strokePaintServer(style, &path);
245         if (strokePaintServer) {
246             TextStreamSeparator s(" ");
247             ts << " [stroke={";
248             if (strokePaintServer)
249                 ts << s << *strokePaintServer;
250 
251             double dashOffset = SVGRenderStyle::cssPrimitiveToLength(&path, svgStyle->strokeDashOffset(), 0.0f);
252             const DashArray& dashArray = dashArrayFromRenderingStyle(style);
253             double strokeWidth = SVGRenderStyle::cssPrimitiveToLength(&path, svgStyle->strokeWidth(), 1.0f);
254 
255             if (svgStyle->strokeOpacity() != 1.0f)
256                 ts << s << "[opacity=" << svgStyle->strokeOpacity() << "]";
257             if (strokeWidth != 1.0f)
258                 ts << s << "[stroke width=" << strokeWidth << "]";
259             if (svgStyle->strokeMiterLimit() != 4)
260                 ts << s << "[miter limit=" << svgStyle->strokeMiterLimit() << "]";
261             if (svgStyle->capStyle() != 0)
262                 ts << s << "[line cap=" << svgStyle->capStyle() << "]";
263             if (svgStyle->joinStyle() != 0)
264                 ts << s << "[line join=" << svgStyle->joinStyle() << "]";
265             if (dashOffset != 0.0f)
266                 ts << s << "[dash offset=" << dashOffset << "]";
267             if (!dashArray.isEmpty())
268                 ts << s << "[dash array=" << dashArray << "]";
269             ts << "}]";
270         }
271         SVGPaintServer* fillPaintServer = SVGPaintServer::fillPaintServer(style, &path);
272         if (fillPaintServer) {
273             TextStreamSeparator s(" ");
274             ts << " [fill={";
275             if (fillPaintServer)
276                 ts << s << *fillPaintServer;
277 
278             if (style->svgStyle()->fillOpacity() != 1.0f)
279                 ts << s << "[opacity=" << style->svgStyle()->fillOpacity() << "]";
280             if (style->svgStyle()->fillRule() != RULE_NONZERO)
281                 ts << s << "[fill rule=" << style->svgStyle()->fillRule() << "]";
282             ts << "}]";
283         }
284     }
285     if (!svgStyle->clipPath().isEmpty())
286         ts << " [clip path=\"" << svgStyle->clipPath() << "\"]";
287     if (!svgStyle->startMarker().isEmpty())
288         ts << " [start marker=" << svgStyle->startMarker() << "]";
289     if (!svgStyle->midMarker().isEmpty())
290         ts << " [middle marker=" << svgStyle->midMarker() << "]";
291     if (!svgStyle->endMarker().isEmpty())
292         ts << " [end marker=" << svgStyle->endMarker() << "]";
293     if (!svgStyle->filter().isEmpty())
294         ts << " [filter=" << svgStyle->filter() << "]";
295 }
296 
operator <<(TextStream & ts,const RenderPath & path)297 static TextStream& operator<<(TextStream& ts, const RenderPath& path)
298 {
299     ts << " " << path.absoluteTransform().mapRect(path.relativeBBox());
300 
301     writeStyle(ts, path);
302 
303     ts << " [data=\"" << path.path().debugString() << "\"]";
304 
305     return ts;
306 }
307 
operator <<(TextStream & ts,const RenderSVGContainer & container)308 static TextStream& operator<<(TextStream& ts, const RenderSVGContainer& container)
309 {
310     ts << " " << container.absoluteTransform().mapRect(container.relativeBBox());
311 
312     writeStyle(ts, container);
313 
314     return ts;
315 }
316 
operator <<(TextStream & ts,const RenderSVGRoot & root)317 static TextStream& operator<<(TextStream& ts, const RenderSVGRoot& root)
318 {
319     ts << " " << root.absoluteTransform().mapRect(root.relativeBBox());
320 
321     writeStyle(ts, root);
322 
323     return ts;
324 }
325 
operator <<(TextStream & ts,const RenderSVGText & text)326 static TextStream& operator<<(TextStream& ts, const RenderSVGText& text)
327 {
328     SVGRootInlineBox* box = static_cast<SVGRootInlineBox*>(text.firstRootBox());
329 
330     if (!box)
331         return ts;
332 
333     Vector<SVGTextChunk>& chunks = const_cast<Vector<SVGTextChunk>& >(box->svgTextChunks());
334     ts << " at (" << text.x() << "," << text.y() << ") size " << box->width() << "x" << box->height() << " contains " << chunks.size() << " chunk(s)";
335 
336     if (text.parent() && (text.parent()->style()->color() != text.style()->color()))
337         ts << " [color=" << text.style()->color().name() << "]";
338 
339     return ts;
340 }
341 
containsInlineTextBox(SVGTextChunk & chunk,SVGInlineTextBox * box)342 static inline bool containsInlineTextBox(SVGTextChunk& chunk, SVGInlineTextBox* box)
343 {
344     Vector<SVGInlineBoxCharacterRange>::iterator boxIt = chunk.boxes.begin();
345     Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = chunk.boxes.end();
346 
347     bool found = false;
348     for (; boxIt != boxEnd; ++boxIt) {
349         SVGInlineBoxCharacterRange& range = *boxIt;
350 
351         if (box == static_cast<SVGInlineTextBox*>(range.box)) {
352             found = true;
353             break;
354         }
355     }
356 
357     return found;
358 }
359 
writeSVGInlineTextBox(TextStream & ts,SVGInlineTextBox * textBox,int indent)360 static inline void writeSVGInlineTextBox(TextStream& ts, SVGInlineTextBox* textBox, int indent)
361 {
362     SVGRootInlineBox* rootBox = textBox->svgRootInlineBox();
363     if (!rootBox)
364         return;
365 
366     Vector<SVGTextChunk>& chunks = const_cast<Vector<SVGTextChunk>& >(rootBox->svgTextChunks());
367 
368     Vector<SVGTextChunk>::iterator it = chunks.begin();
369     Vector<SVGTextChunk>::iterator end = chunks.end();
370 
371     // Write text chunks
372     unsigned int i = 1;
373     for (; it != end; ++it) {
374         SVGTextChunk& cur = *it;
375 
376         // Write inline box character ranges
377         Vector<SVGInlineBoxCharacterRange>::iterator boxIt = cur.boxes.begin();
378         Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = cur.boxes.end();
379 
380         if (!containsInlineTextBox(cur, textBox)) {
381             i++;
382             continue;
383         }
384 
385         writeIndent(ts, indent + 1);
386 
387         unsigned int j = 1;
388         ts << "chunk " << i << " ";
389 
390         if (cur.anchor == TA_MIDDLE) {
391             ts << "(middle anchor";
392             if (cur.isVerticalText)
393                 ts << ", vertical";
394             ts << ") ";
395         } else if (cur.anchor == TA_END) {
396             ts << "(end anchor";
397             if (cur.isVerticalText)
398                 ts << ", vertical";
399             ts << ") ";
400         } else if (cur.isVerticalText)
401             ts << "(vertical) ";
402 
403         unsigned int totalOffset = 0;
404 
405         for (; boxIt != boxEnd; ++boxIt) {
406             SVGInlineBoxCharacterRange& range = *boxIt;
407 
408             unsigned int offset = range.endOffset - range.startOffset;
409             ASSERT(cur.start + totalOffset <= cur.end);
410 
411             totalOffset += offset;
412 
413             if (textBox != static_cast<SVGInlineTextBox*>(range.box)) {
414                 j++;
415                 continue;
416             }
417 
418             FloatPoint topLeft = topLeftPositionOfCharacterRange(cur.start + totalOffset - offset, cur.start + totalOffset);
419 
420             ts << "text run " << j << " at (" << topLeft.x() << "," << topLeft.y() << ") ";
421             ts << "startOffset " << range.startOffset << " endOffset " << range.endOffset;
422 
423             if (cur.isVerticalText)
424                 ts << " height " << cummulatedHeightOfInlineBoxCharacterRange(range);
425             else
426                 ts << " width " << cummulatedWidthOfInlineBoxCharacterRange(range);
427 
428             if (textBox->direction() == RTL || textBox->m_dirOverride) {
429                 ts << (textBox->direction() == RTL ? " RTL" : " LTR");
430 
431                 if (textBox->m_dirOverride)
432                     ts << " override";
433             }
434 
435             ts << ": " << quoteAndEscapeNonPrintables(String(textBox->textObject()->text()).substring(textBox->start() + range.startOffset, offset)) << "\n";
436 
437             j++;
438         }
439 
440         i++;
441     }
442 }
443 
writeSVGInlineText(TextStream & ts,const RenderSVGInlineText & text,int indent)444 static inline void writeSVGInlineText(TextStream& ts, const RenderSVGInlineText& text, int indent)
445 {
446     for (InlineTextBox* box = text.firstTextBox(); box; box = box->nextTextBox())
447         writeSVGInlineTextBox(ts, static_cast<SVGInlineTextBox*>(box), indent);
448 }
449 
getTagName(SVGStyledElement * elem)450 static String getTagName(SVGStyledElement* elem)
451 {
452     if (elem)
453         return elem->nodeName();
454     return "";
455 }
456 
write(TextStream & ts,const RenderSVGContainer & container,int indent)457 void write(TextStream& ts, const RenderSVGContainer& container, int indent)
458 {
459     writeIndent(ts, indent);
460     ts << container.renderName();
461 
462     if (container.element()) {
463         String tagName = getTagName(static_cast<SVGStyledElement*>(container.element()));
464         if (!tagName.isEmpty())
465             ts << " {" << tagName << "}";
466     }
467 
468     ts << container << "\n";
469 
470     for (RenderObject* child = container.firstChild(); child; child = child->nextSibling())
471         write(ts, *child, indent + 1);
472 }
473 
write(TextStream & ts,const RenderSVGRoot & root,int indent)474 void write(TextStream& ts, const RenderSVGRoot& root, int indent)
475 {
476     writeIndent(ts, indent);
477     ts << root.renderName();
478 
479     if (root.element()) {
480         String tagName = getTagName(static_cast<SVGStyledElement*>(root.element()));
481         if (!tagName.isEmpty())
482             ts << " {" << tagName << "}";
483     }
484 
485     ts << root << "\n";
486 
487     for (RenderObject* child = root.firstChild(); child; child = child->nextSibling())
488         write(ts, *child, indent + 1);
489 }
490 
write(TextStream & ts,const RenderSVGText & text,int indent)491 void write(TextStream& ts, const RenderSVGText& text, int indent)
492 {
493     writeIndent(ts, indent);
494     ts << text.renderName();
495 
496     if (text.element()) {
497         String tagName = getTagName(static_cast<SVGStyledElement*>(text.element()));
498         if (!tagName.isEmpty())
499             ts << " {" << tagName << "}";
500     }
501 
502     ts << text << "\n";
503 
504     for (RenderObject* child = text.firstChild(); child; child = child->nextSibling())
505         write(ts, *child, indent + 1);
506 }
507 
write(TextStream & ts,const RenderSVGInlineText & text,int indent)508 void write(TextStream& ts, const RenderSVGInlineText& text, int indent)
509 {
510     writeIndent(ts, indent);
511     ts << text.renderName();
512 
513     if (text.element()) {
514         String tagName = getTagName(static_cast<SVGStyledElement*>(text.element()));
515         if (!tagName.isEmpty())
516             ts << " {" << tagName << "}";
517     }
518 
519     IntRect linesBox = text.linesBoundingBox();
520 
521     ts << " at (" << text.firstRunX() << "," << text.firstRunY() << ") size " << linesBox.width() << "x" << linesBox.height() << "\n";
522     writeSVGInlineText(ts, text, indent);
523 }
524 
write(TextStream & ts,const RenderPath & path,int indent)525 void write(TextStream& ts, const RenderPath& path, int indent)
526 {
527     writeIndent(ts, indent);
528     ts << path.renderName();
529 
530     if (path.element()) {
531         String tagName = getTagName(static_cast<SVGStyledElement*>(path.element()));
532         if (!tagName.isEmpty())
533             ts << " {" << tagName << "}";
534     }
535 
536     ts << path << "\n";
537 }
538 
writeRenderResources(TextStream & ts,Node * parent)539 void writeRenderResources(TextStream& ts, Node* parent)
540 {
541     ASSERT(parent);
542     Node* node = parent;
543     do {
544         if (!node->isSVGElement())
545             continue;
546         SVGElement* svgElement = static_cast<SVGElement*>(node);
547         if (!svgElement->isStyled())
548             continue;
549 
550         SVGStyledElement* styled = static_cast<SVGStyledElement*>(svgElement);
551         RefPtr<SVGResource> resource(styled->canvasResource());
552         if (!resource)
553             continue;
554 
555         String elementId = svgElement->getAttribute(HTMLNames::idAttr);
556         if (resource->isPaintServer()) {
557             RefPtr<SVGPaintServer> paintServer = WTF::static_pointer_cast<SVGPaintServer>(resource);
558             ts << "KRenderingPaintServer {id=\"" << elementId << "\" " << *paintServer << "}" << "\n";
559         } else
560             ts << "KCanvasResource {id=\"" << elementId << "\" " << *resource << "}" << "\n";
561     } while ((node = node->traverseNextNode(parent)));
562 }
563 
564 } // namespace WebCore
565 
566 #endif // ENABLE(SVG)
567