Home | History | Annotate | Download | only in rendering
      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