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 blockInfo.context->save();
244 blockInfo.applyTransform(localToParentTransform());
245 RenderBlock::paint(blockInfo, 0, 0);
246 blockInfo.context->restore();
247 }
248
strokeBoundingBox() const249 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
repaintRectInLocalCoordinates() const262 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.
firstLineBlock() const275 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.
updateFirstLetter()282 void RenderSVGText::updateFirstLetter()
283 {
284 }
285
286 }
287
288 #endif // ENABLE(SVG)
289