Home | History | Annotate | Download | only in svg
      1 /*
      2  * Copyright (C) 2006 Apple Computer, Inc.
      3  * Copyright (C) 2006 Alexander Kellett <lypanov (at) kde.org>
      4  * Copyright (C) 2006 Oliver Hunt <ojh16 (at) student.canterbury.ac.nz>
      5  * Copyright (C) 2007 Nikolas Zimmermann <zimmermann (at) kde.org>
      6  * Copyright (C) 2008 Rob Buis <buis (at) kde.org>
      7  * Copyright (C) 2009 Dirk Schulze <krit (at) webkit.org>
      8  * Copyright (C) Research In Motion Limited 2010. All rights reserved.
      9  *
     10  * This library is free software; you can redistribute it and/or
     11  * modify it under the terms of the GNU Library General Public
     12  * License as published by the Free Software Foundation; either
     13  * version 2 of the License, or (at your option) any later version.
     14  *
     15  * This library is distributed in the hope that it will be useful,
     16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     18  * Library General Public License for more details.
     19  *
     20  * You should have received a copy of the GNU Library General Public License
     21  * along with this library; see the file COPYING.LIB.  If not, write to
     22  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     23  * Boston, MA 02110-1301, USA.
     24  */
     25 
     26 #include "config.h"
     27 
     28 #if ENABLE(SVG)
     29 #include "RenderSVGText.h"
     30 
     31 #include "FloatConversion.h"
     32 #include "FloatQuad.h"
     33 #include "GraphicsContext.h"
     34 #include "HitTestRequest.h"
     35 #include "PointerEventsHitRules.h"
     36 #include "RenderSVGInlineText.h"
     37 #include "RenderSVGResource.h"
     38 #include "RenderSVGRoot.h"
     39 #include "SVGLengthList.h"
     40 #include "SVGRenderSupport.h"
     41 #include "SVGRootInlineBox.h"
     42 #include "SVGTextElement.h"
     43 #include "SVGTextLayoutAttributesBuilder.h"
     44 #include "SVGTransformList.h"
     45 #include "SVGURIReference.h"
     46 #include "SimpleFontData.h"
     47 #include "TransformState.h"
     48 #include "VisiblePosition.h"
     49 
     50 namespace WebCore {
     51 
     52 RenderSVGText::RenderSVGText(SVGTextElement* node)
     53     : RenderSVGBlock(node)
     54     , m_needsReordering(false)
     55     , m_needsPositioningValuesUpdate(true)
     56     , m_needsTransformUpdate(true)
     57 {
     58 }
     59 
     60 bool RenderSVGText::isChildAllowed(RenderObject* child, RenderStyle*) const
     61 {
     62     return child->isInline();
     63 }
     64 
     65 RenderSVGText* RenderSVGText::locateRenderSVGTextAncestor(RenderObject* start)
     66 {
     67     ASSERT(start);
     68     while (start && !start->isSVGText())
     69         start = start->parent();
     70     if (!start || !start->isSVGText())
     71         return 0;
     72     return toRenderSVGText(start);
     73 }
     74 
     75 const RenderSVGText* RenderSVGText::locateRenderSVGTextAncestor(const RenderObject* start)
     76 {
     77     ASSERT(start);
     78     while (start && !start->isSVGText())
     79         start = start->parent();
     80     if (!start || !start->isSVGText())
     81         return 0;
     82     return toRenderSVGText(start);
     83 }
     84 
     85 IntRect RenderSVGText::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer)
     86 {
     87     return SVGRenderSupport::clippedOverflowRectForRepaint(this, repaintContainer);
     88 }
     89 
     90 void RenderSVGText::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& repaintRect, bool fixed)
     91 {
     92     SVGRenderSupport::computeRectForRepaint(this, repaintContainer, repaintRect, fixed);
     93 }
     94 
     95 void RenderSVGText::mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed, bool useTransforms, TransformState& transformState) const
     96 {
     97     SVGRenderSupport::mapLocalToContainer(this, repaintContainer, fixed, useTransforms, transformState);
     98 }
     99 
    100 static inline void recursiveUpdateScaledFont(RenderObject* start)
    101 {
    102     for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) {
    103         if (child->isSVGInlineText()) {
    104             toRenderSVGInlineText(child)->updateScaledFont();
    105             continue;
    106         }
    107 
    108         recursiveUpdateScaledFont(child);
    109     }
    110 }
    111 
    112 void RenderSVGText::layout()
    113 {
    114     ASSERT(needsLayout());
    115     LayoutRepainter repainter(*this, checkForRepaintDuringLayout());
    116 
    117     bool updateCachedBoundariesInParents = false;
    118     if (m_needsTransformUpdate) {
    119         SVGTextElement* text = static_cast<SVGTextElement*>(node());
    120         m_localTransform = text->animatedLocalTransform();
    121         m_needsTransformUpdate = false;
    122         updateCachedBoundariesInParents = true;
    123     }
    124 
    125     // If the root layout size changed (eg. window size changes) or the positioning values change, recompute the on-screen font size.
    126     if (m_needsPositioningValuesUpdate || SVGRenderSupport::findTreeRootObject(this)->isLayoutSizeChanged()) {
    127         recursiveUpdateScaledFont(this);
    128         m_needsPositioningValuesUpdate = true;
    129         updateCachedBoundariesInParents = true;
    130     }
    131 
    132     if (m_needsPositioningValuesUpdate) {
    133         // Perform SVG text layout phase one (see SVGTextLayoutAttributesBuilder for details).
    134         SVGTextLayoutAttributesBuilder layoutAttributesBuilder;
    135         layoutAttributesBuilder.buildLayoutAttributesForTextSubtree(this);
    136         m_needsReordering = true;
    137         m_needsPositioningValuesUpdate = false;
    138         updateCachedBoundariesInParents = true;
    139     }
    140 
    141     // Reduced version of RenderBlock::layoutBlock(), which only takes care of SVG text.
    142     // All if branches that could cause early exit in RenderBlocks layoutBlock() method are turned into assertions.
    143     ASSERT(!isInline());
    144     ASSERT(!simplifiedLayout());
    145     ASSERT(!scrollsOverflow());
    146     ASSERT(!hasControlClip());
    147     ASSERT(!hasColumns());
    148     ASSERT(!positionedObjects());
    149     ASSERT(!m_overflow);
    150     ASSERT(!isAnonymousBlock());
    151 
    152     if (!firstChild())
    153         setChildrenInline(true);
    154 
    155     // FIXME: We need to find a way to only layout the child boxes, if needed.
    156     FloatRect oldBoundaries = objectBoundingBox();
    157     ASSERT(childrenInline());
    158     forceLayoutInlineChildren();
    159 
    160     if (m_needsReordering)
    161         m_needsReordering = false;
    162 
    163     if (!updateCachedBoundariesInParents)
    164         updateCachedBoundariesInParents = oldBoundaries != objectBoundingBox();
    165 
    166     // Invalidate all resources of this client if our layout changed.
    167     if (m_everHadLayout && selfNeedsLayout())
    168         SVGResourcesCache::clientLayoutChanged(this);
    169 
    170     // If our bounds changed, notify the parents.
    171     if (updateCachedBoundariesInParents)
    172         RenderSVGBlock::setNeedsBoundariesUpdate();
    173 
    174     repainter.repaintAfterLayout();
    175     setNeedsLayout(false);
    176 }
    177 
    178 RootInlineBox* RenderSVGText::createRootInlineBox()
    179 {
    180     RootInlineBox* box = new (renderArena()) SVGRootInlineBox(this);
    181     box->setHasVirtualLogicalHeight();
    182     return box;
    183 }
    184 
    185 bool RenderSVGText::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
    186 {
    187     PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_TEXT_HITTESTING, request, style()->pointerEvents());
    188     bool isVisible = (style()->visibility() == VISIBLE);
    189     if (isVisible || !hitRules.requireVisible) {
    190         if ((hitRules.canHitStroke && (style()->svgStyle()->hasStroke() || !hitRules.requireStroke))
    191             || (hitRules.canHitFill && (style()->svgStyle()->hasFill() || !hitRules.requireFill))) {
    192             FloatPoint localPoint = localToParentTransform().inverse().mapPoint(pointInParent);
    193 
    194             if (!SVGRenderSupport::pointInClippingArea(this, localPoint))
    195                 return false;
    196 
    197             return RenderBlock::nodeAtPoint(request, result, (int)localPoint.x(), (int)localPoint.y(), 0, 0, hitTestAction);
    198         }
    199     }
    200 
    201     return false;
    202 }
    203 
    204 bool RenderSVGText::nodeAtPoint(const HitTestRequest&, HitTestResult&, int, int, int, int, HitTestAction)
    205 {
    206     ASSERT_NOT_REACHED();
    207     return false;
    208 }
    209 
    210 VisiblePosition RenderSVGText::positionForPoint(const IntPoint& pointInContents)
    211 {
    212     RootInlineBox* rootBox = firstRootBox();
    213     if (!rootBox)
    214         return createVisiblePosition(0, DOWNSTREAM);
    215 
    216     ASSERT(rootBox->isSVGRootInlineBox());
    217     ASSERT(!rootBox->nextRootBox());
    218     ASSERT(childrenInline());
    219 
    220     InlineBox* closestBox = static_cast<SVGRootInlineBox*>(rootBox)->closestLeafChildForPosition(pointInContents);
    221     if (!closestBox)
    222         return createVisiblePosition(0, DOWNSTREAM);
    223 
    224     return closestBox->renderer()->positionForPoint(IntPoint(pointInContents.x(), closestBox->m_y));
    225 }
    226 
    227 void RenderSVGText::absoluteQuads(Vector<FloatQuad>& quads)
    228 {
    229     quads.append(localToAbsoluteQuad(strokeBoundingBox()));
    230 }
    231 
    232 void RenderSVGText::paint(PaintInfo& paintInfo, int, int)
    233 {
    234     if (paintInfo.context->paintingDisabled())
    235         return;
    236 
    237     if (paintInfo.phase != PaintPhaseForeground
    238      && paintInfo.phase != PaintPhaseSelfOutline
    239      && paintInfo.phase != PaintPhaseSelection)
    240          return;
    241 
    242     PaintInfo blockInfo(paintInfo);
    243     blockInfo.context->save();
    244     blockInfo.applyTransform(localToParentTransform());
    245     RenderBlock::paint(blockInfo, 0, 0);
    246     blockInfo.context->restore();
    247 }
    248 
    249 FloatRect RenderSVGText::strokeBoundingBox() const
    250 {
    251     FloatRect strokeBoundaries = objectBoundingBox();
    252     const SVGRenderStyle* svgStyle = style()->svgStyle();
    253     if (!svgStyle->hasStroke())
    254         return strokeBoundaries;
    255 
    256     ASSERT(node());
    257     ASSERT(node()->isSVGElement());
    258     strokeBoundaries.inflate(svgStyle->strokeWidth().value(static_cast<SVGElement*>(node())));
    259     return strokeBoundaries;
    260 }
    261 
    262 FloatRect RenderSVGText::repaintRectInLocalCoordinates() const
    263 {
    264     FloatRect repaintRect = strokeBoundingBox();
    265     SVGRenderSupport::intersectRepaintRectWithResources(this, repaintRect);
    266 
    267     if (const ShadowData* textShadow = style()->textShadow())
    268         textShadow->adjustRectForShadow(repaintRect);
    269 
    270     return repaintRect;
    271 }
    272 
    273 // Fix for <rdar://problem/8048875>. We should not render :first-line CSS Style
    274 // in a SVG text element context.
    275 RenderBlock* RenderSVGText::firstLineBlock() const
    276 {
    277     return 0;
    278 }
    279 
    280 // Fix for <rdar://problem/8048875>. We should not render :first-letter CSS Style
    281 // in a SVG text element context.
    282 void RenderSVGText::updateFirstLetter()
    283 {
    284 }
    285 
    286 }
    287 
    288 #endif // ENABLE(SVG)
    289