• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2004, 2005, 2006 Nikolas Zimmermann <zimmermann@kde.org>
3  * Copyright (C) 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org>
4  * Copyright (C) 2007 Apple Inc. All rights reserved.
5  * Copyright (C) Research In Motion Limited 2011. All rights reserved.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public License
18  * along with this library; see the file COPYING.LIB.  If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22 
23 #include "config.h"
24 #include "core/svg/SVGLengthContext.h"
25 
26 #include "bindings/v8/ExceptionMessages.h"
27 #include "bindings/v8/ExceptionState.h"
28 #include "core/SVGNames.h"
29 #include "core/css/CSSHelper.h"
30 #include "core/dom/ExceptionCode.h"
31 #include "core/rendering/RenderPart.h"
32 #include "core/rendering/RenderView.h"
33 #include "core/rendering/svg/RenderSVGRoot.h"
34 #include "core/rendering/svg/RenderSVGViewportContainer.h"
35 #include "core/svg/SVGSVGElement.h"
36 #include "platform/fonts/FontMetrics.h"
37 
38 namespace WebCore {
39 
SVGLengthContext(const SVGElement * context)40 SVGLengthContext::SVGLengthContext(const SVGElement* context)
41     : m_context(context)
42 {
43 }
44 
SVGLengthContext(const SVGElement * context,const FloatRect & viewport)45 SVGLengthContext::SVGLengthContext(const SVGElement* context, const FloatRect& viewport)
46     : m_context(context)
47     , m_overridenViewport(viewport)
48 {
49 }
50 
resolveRectangle(const SVGElement * context,SVGUnitTypes::SVGUnitType type,const FloatRect & viewport,PassRefPtr<SVGLength> passX,PassRefPtr<SVGLength> passY,PassRefPtr<SVGLength> passWidth,PassRefPtr<SVGLength> passHeight)51 FloatRect SVGLengthContext::resolveRectangle(const SVGElement* context, SVGUnitTypes::SVGUnitType type, const FloatRect& viewport, PassRefPtr<SVGLength> passX, PassRefPtr<SVGLength> passY, PassRefPtr<SVGLength> passWidth, PassRefPtr<SVGLength> passHeight)
52 {
53     RefPtr<SVGLength> x = passX;
54     RefPtr<SVGLength> y = passY;
55     RefPtr<SVGLength> width = passWidth;
56     RefPtr<SVGLength> height = passHeight;
57 
58     ASSERT(type != SVGUnitTypes::SVG_UNIT_TYPE_UNKNOWN);
59     if (type == SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE) {
60         SVGLengthContext lengthContext(context);
61         return FloatRect(x->value(lengthContext), y->value(lengthContext), width->value(lengthContext), height->value(lengthContext));
62     }
63 
64     SVGLengthContext lengthContext(context, viewport);
65     return FloatRect(
66         x->value(lengthContext) + viewport.x(),
67         y->value(lengthContext) + viewport.y(),
68         width->value(lengthContext),
69         height->value(lengthContext));
70 }
71 
resolvePoint(const SVGElement * context,SVGUnitTypes::SVGUnitType type,PassRefPtr<SVGLength> passX,PassRefPtr<SVGLength> passY)72 FloatPoint SVGLengthContext::resolvePoint(const SVGElement* context, SVGUnitTypes::SVGUnitType type, PassRefPtr<SVGLength> passX, PassRefPtr<SVGLength> passY)
73 {
74     RefPtr<SVGLength> x = passX;
75     RefPtr<SVGLength> y = passY;
76 
77     ASSERT(type != SVGUnitTypes::SVG_UNIT_TYPE_UNKNOWN);
78     if (type == SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE) {
79         SVGLengthContext lengthContext(context);
80         return FloatPoint(x->value(lengthContext), y->value(lengthContext));
81     }
82 
83     // FIXME: valueAsPercentage() won't be correct for eg. cm units. They need to be resolved in user space and then be considered in objectBoundingBox space.
84     return FloatPoint(x->valueAsPercentage(), y->valueAsPercentage());
85 }
86 
resolveLength(const SVGElement * context,SVGUnitTypes::SVGUnitType type,PassRefPtr<SVGLength> passX)87 float SVGLengthContext::resolveLength(const SVGElement* context, SVGUnitTypes::SVGUnitType type, PassRefPtr<SVGLength> passX)
88 {
89     RefPtr<SVGLength> x = passX;
90 
91     ASSERT(type != SVGUnitTypes::SVG_UNIT_TYPE_UNKNOWN);
92     if (type == SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE) {
93         SVGLengthContext lengthContext(context);
94         return x->value(lengthContext);
95     }
96 
97     // FIXME: valueAsPercentage() won't be correct for eg. cm units. They need to be resolved in user space and then be considered in objectBoundingBox space.
98     return x->valueAsPercentage();
99 }
100 
convertValueToUserUnits(float value,SVGLengthMode mode,SVGLengthType fromUnit,ExceptionState & exceptionState) const101 float SVGLengthContext::convertValueToUserUnits(float value, SVGLengthMode mode, SVGLengthType fromUnit, ExceptionState& exceptionState) const
102 {
103     // If the SVGLengthContext carries a custom viewport, force resolving against it.
104     if (!m_overridenViewport.isEmpty()) {
105         // 100% = 100.0 instead of 1.0 for historical reasons, this could eventually be changed
106         if (fromUnit == LengthTypePercentage)
107             value /= 100;
108         return convertValueFromPercentageToUserUnits(value, mode, exceptionState);
109     }
110 
111     switch (fromUnit) {
112     case LengthTypeUnknown:
113         exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::argumentNullOrIncorrectType(3, "SVGLengthType"));
114         return 0;
115     case LengthTypeNumber:
116         return value;
117     case LengthTypePX:
118         return value;
119     case LengthTypePercentage:
120         return convertValueFromPercentageToUserUnits(value / 100, mode, exceptionState);
121     case LengthTypeEMS:
122         return convertValueFromEMSToUserUnits(value, exceptionState);
123     case LengthTypeEXS:
124         return convertValueFromEXSToUserUnits(value, exceptionState);
125     case LengthTypeCM:
126         return value * cssPixelsPerCentimeter;
127     case LengthTypeMM:
128         return value * cssPixelsPerMillimeter;
129     case LengthTypeIN:
130         return value * cssPixelsPerInch;
131     case LengthTypePT:
132         return value * cssPixelsPerPoint;
133     case LengthTypePC:
134         return value * cssPixelsPerPica;
135     }
136 
137     ASSERT_NOT_REACHED();
138     return 0;
139 }
140 
convertValueFromUserUnits(float value,SVGLengthMode mode,SVGLengthType toUnit,ExceptionState & exceptionState) const141 float SVGLengthContext::convertValueFromUserUnits(float value, SVGLengthMode mode, SVGLengthType toUnit, ExceptionState& exceptionState) const
142 {
143     switch (toUnit) {
144     case LengthTypeUnknown:
145         exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::argumentNullOrIncorrectType(3, "SVGLengthType"));
146         return 0;
147     case LengthTypeNumber:
148         return value;
149     case LengthTypePercentage:
150         return convertValueFromUserUnitsToPercentage(value * 100, mode, exceptionState);
151     case LengthTypeEMS:
152         return convertValueFromUserUnitsToEMS(value, exceptionState);
153     case LengthTypeEXS:
154         return convertValueFromUserUnitsToEXS(value, exceptionState);
155     case LengthTypePX:
156         return value;
157     case LengthTypeCM:
158         return value / cssPixelsPerCentimeter;
159     case LengthTypeMM:
160         return value / cssPixelsPerMillimeter;
161     case LengthTypeIN:
162         return value / cssPixelsPerInch;
163     case LengthTypePT:
164         return value / cssPixelsPerPoint;
165     case LengthTypePC:
166         return value / cssPixelsPerPica;
167     }
168 
169     ASSERT_NOT_REACHED();
170     return 0;
171 }
172 
convertValueFromUserUnitsToPercentage(float value,SVGLengthMode mode,ExceptionState & exceptionState) const173 float SVGLengthContext::convertValueFromUserUnitsToPercentage(float value, SVGLengthMode mode, ExceptionState& exceptionState) const
174 {
175     FloatSize viewportSize;
176     if (!determineViewport(viewportSize)) {
177         exceptionState.throwDOMException(NotSupportedError, "The viewport could not be determined.");
178         return 0;
179     }
180 
181     switch (mode) {
182     case LengthModeWidth:
183         return value / viewportSize.width() * 100;
184     case LengthModeHeight:
185         return value / viewportSize.height() * 100;
186     case LengthModeOther:
187         return value / sqrtf(viewportSize.diagonalLengthSquared() / 2) * 100;
188     };
189 
190     ASSERT_NOT_REACHED();
191     return 0;
192 }
193 
convertValueFromPercentageToUserUnits(float value,SVGLengthMode mode,ExceptionState & exceptionState) const194 float SVGLengthContext::convertValueFromPercentageToUserUnits(float value, SVGLengthMode mode, ExceptionState& exceptionState) const
195 {
196     FloatSize viewportSize;
197     if (!determineViewport(viewportSize)) {
198         exceptionState.throwDOMException(NotSupportedError, "The viewport could not be determined.");
199         return 0;
200     }
201 
202     switch (mode) {
203     case LengthModeWidth:
204         return value * viewportSize.width();
205     case LengthModeHeight:
206         return value * viewportSize.height();
207     case LengthModeOther:
208         return value * sqrtf(viewportSize.diagonalLengthSquared() / 2);
209     };
210 
211     ASSERT_NOT_REACHED();
212     return 0;
213 }
214 
renderStyleForLengthResolving(const SVGElement * context)215 static inline RenderStyle* renderStyleForLengthResolving(const SVGElement* context)
216 {
217     if (!context)
218         return 0;
219 
220     const ContainerNode* currentContext = context;
221     do {
222         if (currentContext->renderer())
223             return currentContext->renderer()->style();
224         currentContext = currentContext->parentNode();
225     } while (currentContext);
226 
227     // There must be at least a RenderSVGRoot renderer, carrying a style.
228     ASSERT_NOT_REACHED();
229     return 0;
230 }
231 
convertValueFromUserUnitsToEMS(float value,ExceptionState & exceptionState) const232 float SVGLengthContext::convertValueFromUserUnitsToEMS(float value, ExceptionState& exceptionState) const
233 {
234     RenderStyle* style = renderStyleForLengthResolving(m_context);
235     if (!style) {
236         exceptionState.throwDOMException(NotSupportedError, "No context could be found.");
237         return 0;
238     }
239 
240     float fontSize = style->specifiedFontSize();
241     if (!fontSize) {
242         exceptionState.throwDOMException(NotSupportedError, "No font-size could be determined.");
243         return 0;
244     }
245 
246     return value / fontSize;
247 }
248 
convertValueFromEMSToUserUnits(float value,ExceptionState & exceptionState) const249 float SVGLengthContext::convertValueFromEMSToUserUnits(float value, ExceptionState& exceptionState) const
250 {
251     RenderStyle* style = renderStyleForLengthResolving(m_context);
252     if (!style) {
253         exceptionState.throwDOMException(NotSupportedError, "No context could be found.");
254         return 0;
255     }
256 
257     return value * style->specifiedFontSize();
258 }
259 
convertValueFromUserUnitsToEXS(float value,ExceptionState & exceptionState) const260 float SVGLengthContext::convertValueFromUserUnitsToEXS(float value, ExceptionState& exceptionState) const
261 {
262     RenderStyle* style = renderStyleForLengthResolving(m_context);
263     if (!style) {
264         exceptionState.throwDOMException(NotSupportedError, "No context could be found.");
265         return 0;
266     }
267 
268     // Use of ceil allows a pixel match to the W3Cs expected output of coords-units-03-b.svg
269     // if this causes problems in real world cases maybe it would be best to remove this
270     float xHeight = ceilf(style->fontMetrics().xHeight());
271     if (!xHeight) {
272         exceptionState.throwDOMException(NotSupportedError, "No x-height could be determined.");
273         return 0;
274     }
275 
276     return value / xHeight;
277 }
278 
convertValueFromEXSToUserUnits(float value,ExceptionState & exceptionState) const279 float SVGLengthContext::convertValueFromEXSToUserUnits(float value, ExceptionState& exceptionState) const
280 {
281     RenderStyle* style = renderStyleForLengthResolving(m_context);
282     if (!style) {
283         exceptionState.throwDOMException(NotSupportedError, "No context could be found.");
284         return 0;
285     }
286 
287     // Use of ceil allows a pixel match to the W3Cs expected output of coords-units-03-b.svg
288     // if this causes problems in real world cases maybe it would be best to remove this
289     return value * ceilf(style->fontMetrics().xHeight());
290 }
291 
determineViewport(FloatSize & viewportSize) const292 bool SVGLengthContext::determineViewport(FloatSize& viewportSize) const
293 {
294     if (!m_context)
295         return false;
296 
297     // If an overriden viewport is given, it has precedence.
298     if (!m_overridenViewport.isEmpty()) {
299         viewportSize = m_overridenViewport.size();
300         return true;
301     }
302 
303     // Root <svg> element lengths are resolved against the top level viewport.
304     if (m_context->isOutermostSVGSVGElement()) {
305         viewportSize = toSVGSVGElement(m_context)->currentViewportSize();
306         return true;
307     }
308 
309     // Take size from nearest viewport element.
310     SVGElement* viewportElement = m_context->viewportElement();
311     if (!isSVGSVGElement(viewportElement))
312         return false;
313 
314     const SVGSVGElement& svg = toSVGSVGElement(*viewportElement);
315     viewportSize = svg.currentViewBoxRect().size();
316     if (viewportSize.isEmpty())
317         viewportSize = svg.currentViewportSize();
318 
319     return true;
320 }
321 
322 }
323