• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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