1 /*
2  * Copyright (C) 2006 Apple Computer, Inc.
3  * Copyright (C) 2006 Alexander Kellett <lypanov@kde.org>
4  * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz>
5  * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
6  * Copyright (C) 2008 Rob Buis <buis@kde.org>
7  * Copyright (C) 2009 Dirk Schulze <krit@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 
RenderSVGText(SVGTextElement * node)52 RenderSVGText::RenderSVGText(SVGTextElement* node)
53     : RenderSVGBlock(node)
54     , m_needsReordering(false)
55     , m_needsPositioningValuesUpdate(true)
56     , m_needsTransformUpdate(true)
57 {
58 }
59 
isChildAllowed(RenderObject * child,RenderStyle *) const60 bool RenderSVGText::isChildAllowed(RenderObject* child, RenderStyle*) const
61 {
62     return child->isInline();
63 }
64 
locateRenderSVGTextAncestor(RenderObject * start)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 
locateRenderSVGTextAncestor(const RenderObject * start)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 
clippedOverflowRectForRepaint(RenderBoxModelObject * repaintContainer)85 IntRect RenderSVGText::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer)
86 {
87     return SVGRenderSupport::clippedOverflowRectForRepaint(this, repaintContainer);
88 }
89 
computeRectForRepaint(RenderBoxModelObject * repaintContainer,IntRect & repaintRect,bool fixed)90 void RenderSVGText::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& repaintRect, bool fixed)
91 {
92     SVGRenderSupport::computeRectForRepaint(this, repaintContainer, repaintRect, fixed);
93 }
94 
mapLocalToContainer(RenderBoxModelObject * repaintContainer,bool fixed,bool useTransforms,TransformState & transformState) const95 void RenderSVGText::mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed, bool useTransforms, TransformState& transformState) const
96 {
97     SVGRenderSupport::mapLocalToContainer(this, repaintContainer, fixed, useTransforms, transformState);
98 }
99 
recursiveUpdateScaledFont(RenderObject * start)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 
layout()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 
createRootInlineBox()178 RootInlineBox* RenderSVGText::createRootInlineBox()
179 {
180     RootInlineBox* box = new (renderArena()) SVGRootInlineBox(this);
181     box->setHasVirtualLogicalHeight();
182     return box;
183 }
184 
nodeAtFloatPoint(const HitTestRequest & request,HitTestResult & result,const FloatPoint & pointInParent,HitTestAction hitTestAction)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 
nodeAtPoint(const HitTestRequest &,HitTestResult &,int,int,int,int,HitTestAction)204 bool RenderSVGText::nodeAtPoint(const HitTestRequest&, HitTestResult&, int, int, int, int, HitTestAction)
205 {
206     ASSERT_NOT_REACHED();
207     return false;
208 }
209 
positionForPoint(const IntPoint & pointInContents)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 
absoluteQuads(Vector<FloatQuad> & quads)227 void RenderSVGText::absoluteQuads(Vector<FloatQuad>& quads)
228 {
229     quads.append(localToAbsoluteQuad(strokeBoundingBox()));
230 }
231 
paint(PaintInfo & paintInfo,int,int)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     GraphicsContextStateSaver stateSaver(*blockInfo.context);
244     blockInfo.applyTransform(localToParentTransform());
245     RenderBlock::paint(blockInfo, 0, 0);
246 }
247 
strokeBoundingBox() const248 FloatRect RenderSVGText::strokeBoundingBox() const
249 {
250     FloatRect strokeBoundaries = objectBoundingBox();
251     const SVGRenderStyle* svgStyle = style()->svgStyle();
252     if (!svgStyle->hasStroke())
253         return strokeBoundaries;
254 
255     ASSERT(node());
256     ASSERT(node()->isSVGElement());
257     strokeBoundaries.inflate(svgStyle->strokeWidth().value(static_cast<SVGElement*>(node())));
258     return strokeBoundaries;
259 }
260 
repaintRectInLocalCoordinates() const261 FloatRect RenderSVGText::repaintRectInLocalCoordinates() const
262 {
263     FloatRect repaintRect = strokeBoundingBox();
264     SVGRenderSupport::intersectRepaintRectWithResources(this, repaintRect);
265 
266     if (const ShadowData* textShadow = style()->textShadow())
267         textShadow->adjustRectForShadow(repaintRect);
268 
269     return repaintRect;
270 }
271 
272 // Fix for <rdar://problem/8048875>. We should not render :first-line CSS Style
273 // in a SVG text element context.
firstLineBlock() const274 RenderBlock* RenderSVGText::firstLineBlock() const
275 {
276     return 0;
277 }
278 
279 // Fix for <rdar://problem/8048875>. We should not render :first-letter CSS Style
280 // in a SVG text element context.
updateFirstLetter()281 void RenderSVGText::updateFirstLetter()
282 {
283 }
284 
285 }
286 
287 #endif // ENABLE(SVG)
288