• 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 "SVGNames.h"
27 #include "bindings/v8/ExceptionState.h"
28 #include "core/css/CSSHelper.h"
29 #include "core/dom/ExceptionCode.h"
30 #include "core/rendering/RenderPart.h"
31 #include "core/rendering/RenderView.h"
32 #include "core/rendering/svg/RenderSVGRoot.h"
33 #include "core/rendering/svg/RenderSVGViewportContainer.h"
34 #include "core/svg/SVGSVGElement.h"
35 #include "platform/fonts/FontMetrics.h"
36 
37 namespace WebCore {
38 
SVGLengthContext(const SVGElement * context)39 SVGLengthContext::SVGLengthContext(const SVGElement* context)
40     : m_context(context)
41 {
42 }
43 
SVGLengthContext(const SVGElement * context,const FloatRect & viewport)44 SVGLengthContext::SVGLengthContext(const SVGElement* context, const FloatRect& viewport)
45     : m_context(context)
46     , m_overridenViewport(viewport)
47 {
48 }
49 
resolveRectangle(const SVGElement * context,SVGUnitTypes::SVGUnitType type,const FloatRect & viewport,const SVGLength & x,const SVGLength & y,const SVGLength & width,const SVGLength & height)50 FloatRect SVGLengthContext::resolveRectangle(const SVGElement* context, SVGUnitTypes::SVGUnitType type, const FloatRect& viewport, const SVGLength& x, const SVGLength& y, const SVGLength& width, const SVGLength& height)
51 {
52     ASSERT(type != SVGUnitTypes::SVG_UNIT_TYPE_UNKNOWN);
53     if (type == SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE) {
54         SVGLengthContext lengthContext(context);
55         return FloatRect(x.value(lengthContext), y.value(lengthContext), width.value(lengthContext), height.value(lengthContext));
56     }
57 
58     SVGLengthContext lengthContext(context, viewport);
59     return FloatRect(x.value(lengthContext) + viewport.x(),
60                      y.value(lengthContext) + viewport.y(),
61                      width.value(lengthContext),
62                      height.value(lengthContext));
63 }
64 
resolvePoint(const SVGElement * context,SVGUnitTypes::SVGUnitType type,const SVGLength & x,const SVGLength & y)65 FloatPoint SVGLengthContext::resolvePoint(const SVGElement* context, SVGUnitTypes::SVGUnitType type, const SVGLength& x, const SVGLength& y)
66 {
67     ASSERT(type != SVGUnitTypes::SVG_UNIT_TYPE_UNKNOWN);
68     if (type == SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE) {
69         SVGLengthContext lengthContext(context);
70         return FloatPoint(x.value(lengthContext), y.value(lengthContext));
71     }
72 
73     // 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.
74     return FloatPoint(x.valueAsPercentage(), y.valueAsPercentage());
75 }
76 
resolveLength(const SVGElement * context,SVGUnitTypes::SVGUnitType type,const SVGLength & x)77 float SVGLengthContext::resolveLength(const SVGElement* context, SVGUnitTypes::SVGUnitType type, const SVGLength& x)
78 {
79     ASSERT(type != SVGUnitTypes::SVG_UNIT_TYPE_UNKNOWN);
80     if (type == SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE) {
81         SVGLengthContext lengthContext(context);
82         return x.value(lengthContext);
83     }
84 
85     // 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.
86     return x.valueAsPercentage();
87 }
88 
convertValueToUserUnits(float value,SVGLengthMode mode,SVGLengthType fromUnit,ExceptionState & exceptionState) const89 float SVGLengthContext::convertValueToUserUnits(float value, SVGLengthMode mode, SVGLengthType fromUnit, ExceptionState& exceptionState) const
90 {
91     // If the SVGLengthContext carries a custom viewport, force resolving against it.
92     if (!m_overridenViewport.isEmpty()) {
93         // 100% = 100.0 instead of 1.0 for historical reasons, this could eventually be changed
94         if (fromUnit == LengthTypePercentage)
95             value /= 100;
96         return convertValueFromPercentageToUserUnits(value, mode, exceptionState);
97     }
98 
99     switch (fromUnit) {
100     case LengthTypeUnknown:
101         exceptionState.throwUninformativeAndGenericDOMException(NotSupportedError);
102         return 0;
103     case LengthTypeNumber:
104         return value;
105     case LengthTypePX:
106         return value;
107     case LengthTypePercentage:
108         return convertValueFromPercentageToUserUnits(value / 100, mode, exceptionState);
109     case LengthTypeEMS:
110         return convertValueFromEMSToUserUnits(value, exceptionState);
111     case LengthTypeEXS:
112         return convertValueFromEXSToUserUnits(value, exceptionState);
113     case LengthTypeCM:
114         return value * cssPixelsPerCentimeter;
115     case LengthTypeMM:
116         return value * cssPixelsPerMillimeter;
117     case LengthTypeIN:
118         return value * cssPixelsPerInch;
119     case LengthTypePT:
120         return value * cssPixelsPerPoint;
121     case LengthTypePC:
122         return value * cssPixelsPerPica;
123     }
124 
125     ASSERT_NOT_REACHED();
126     return 0;
127 }
128 
convertValueFromUserUnits(float value,SVGLengthMode mode,SVGLengthType toUnit,ExceptionState & exceptionState) const129 float SVGLengthContext::convertValueFromUserUnits(float value, SVGLengthMode mode, SVGLengthType toUnit, ExceptionState& exceptionState) const
130 {
131     switch (toUnit) {
132     case LengthTypeUnknown:
133         exceptionState.throwUninformativeAndGenericDOMException(NotSupportedError);
134         return 0;
135     case LengthTypeNumber:
136         return value;
137     case LengthTypePercentage:
138         return convertValueFromUserUnitsToPercentage(value * 100, mode, exceptionState);
139     case LengthTypeEMS:
140         return convertValueFromUserUnitsToEMS(value, exceptionState);
141     case LengthTypeEXS:
142         return convertValueFromUserUnitsToEXS(value, exceptionState);
143     case LengthTypePX:
144         return value;
145     case LengthTypeCM:
146         return value / cssPixelsPerCentimeter;
147     case LengthTypeMM:
148         return value / cssPixelsPerMillimeter;
149     case LengthTypeIN:
150         return value / cssPixelsPerInch;
151     case LengthTypePT:
152         return value / cssPixelsPerPoint;
153     case LengthTypePC:
154         return value / cssPixelsPerPica;
155     }
156 
157     ASSERT_NOT_REACHED();
158     return 0;
159 }
160 
convertValueFromUserUnitsToPercentage(float value,SVGLengthMode mode,ExceptionState & exceptionState) const161 float SVGLengthContext::convertValueFromUserUnitsToPercentage(float value, SVGLengthMode mode, ExceptionState& exceptionState) const
162 {
163     FloatSize viewportSize;
164     if (!determineViewport(viewportSize)) {
165         exceptionState.throwUninformativeAndGenericDOMException(NotSupportedError);
166         return 0;
167     }
168 
169     switch (mode) {
170     case LengthModeWidth:
171         return value / viewportSize.width() * 100;
172     case LengthModeHeight:
173         return value / viewportSize.height() * 100;
174     case LengthModeOther:
175         return value / sqrtf(viewportSize.diagonalLengthSquared() / 2) * 100;
176     };
177 
178     ASSERT_NOT_REACHED();
179     return 0;
180 }
181 
convertValueFromPercentageToUserUnits(float value,SVGLengthMode mode,ExceptionState & exceptionState) const182 float SVGLengthContext::convertValueFromPercentageToUserUnits(float value, SVGLengthMode mode, ExceptionState& exceptionState) const
183 {
184     FloatSize viewportSize;
185     if (!determineViewport(viewportSize)) {
186         exceptionState.throwUninformativeAndGenericDOMException(NotSupportedError);
187         return 0;
188     }
189 
190     switch (mode) {
191     case LengthModeWidth:
192         return value * viewportSize.width();
193     case LengthModeHeight:
194         return value * viewportSize.height();
195     case LengthModeOther:
196         return value * sqrtf(viewportSize.diagonalLengthSquared() / 2);
197     };
198 
199     ASSERT_NOT_REACHED();
200     return 0;
201 }
202 
renderStyleForLengthResolving(const SVGElement * context)203 static inline RenderStyle* renderStyleForLengthResolving(const SVGElement* context)
204 {
205     if (!context)
206         return 0;
207 
208     const ContainerNode* currentContext = context;
209     while (currentContext) {
210         if (currentContext->renderer())
211             return currentContext->renderer()->style();
212         currentContext = currentContext->parentNode();
213     }
214 
215     // There must be at least a RenderSVGRoot renderer, carrying a style.
216     ASSERT_NOT_REACHED();
217     return 0;
218 }
219 
convertValueFromUserUnitsToEMS(float value,ExceptionState & exceptionState) const220 float SVGLengthContext::convertValueFromUserUnitsToEMS(float value, ExceptionState& exceptionState) const
221 {
222     RenderStyle* style = renderStyleForLengthResolving(m_context);
223     if (!style) {
224         exceptionState.throwUninformativeAndGenericDOMException(NotSupportedError);
225         return 0;
226     }
227 
228     float fontSize = style->specifiedFontSize();
229     if (!fontSize) {
230         exceptionState.throwUninformativeAndGenericDOMException(NotSupportedError);
231         return 0;
232     }
233 
234     return value / fontSize;
235 }
236 
convertValueFromEMSToUserUnits(float value,ExceptionState & exceptionState) const237 float SVGLengthContext::convertValueFromEMSToUserUnits(float value, ExceptionState& exceptionState) const
238 {
239     RenderStyle* style = renderStyleForLengthResolving(m_context);
240     if (!style) {
241         exceptionState.throwUninformativeAndGenericDOMException(NotSupportedError);
242         return 0;
243     }
244 
245     return value * style->specifiedFontSize();
246 }
247 
convertValueFromUserUnitsToEXS(float value,ExceptionState & exceptionState) const248 float SVGLengthContext::convertValueFromUserUnitsToEXS(float value, ExceptionState& exceptionState) const
249 {
250     RenderStyle* style = renderStyleForLengthResolving(m_context);
251     if (!style) {
252         exceptionState.throwUninformativeAndGenericDOMException(NotSupportedError);
253         return 0;
254     }
255 
256     // Use of ceil allows a pixel match to the W3Cs expected output of coords-units-03-b.svg
257     // if this causes problems in real world cases maybe it would be best to remove this
258     float xHeight = ceilf(style->fontMetrics().xHeight());
259     if (!xHeight) {
260         exceptionState.throwUninformativeAndGenericDOMException(NotSupportedError);
261         return 0;
262     }
263 
264     return value / xHeight;
265 }
266 
convertValueFromEXSToUserUnits(float value,ExceptionState & exceptionState) const267 float SVGLengthContext::convertValueFromEXSToUserUnits(float value, ExceptionState& exceptionState) const
268 {
269     RenderStyle* style = renderStyleForLengthResolving(m_context);
270     if (!style) {
271         exceptionState.throwUninformativeAndGenericDOMException(NotSupportedError);
272         return 0;
273     }
274 
275     // Use of ceil allows a pixel match to the W3Cs expected output of coords-units-03-b.svg
276     // if this causes problems in real world cases maybe it would be best to remove this
277     return value * ceilf(style->fontMetrics().xHeight());
278 }
279 
determineViewport(FloatSize & viewportSize) const280 bool SVGLengthContext::determineViewport(FloatSize& viewportSize) const
281 {
282     if (!m_context)
283         return false;
284 
285     // If an overriden viewport is given, it has precedence.
286     if (!m_overridenViewport.isEmpty()) {
287         viewportSize = m_overridenViewport.size();
288         return true;
289     }
290 
291     // Root <svg> element lengths are resolved against the top level viewport.
292     if (m_context->isOutermostSVGSVGElement()) {
293         viewportSize = toSVGSVGElement(m_context)->currentViewportSize();
294         return true;
295     }
296 
297     // Take size from nearest viewport element.
298     SVGElement* viewportElement = m_context->viewportElement();
299     if (!viewportElement || !viewportElement->isSVGSVGElement())
300         return false;
301 
302     const SVGSVGElement* svg = toSVGSVGElement(viewportElement);
303     viewportSize = svg->currentViewBoxRect().size();
304     if (viewportSize.isEmpty())
305         viewportSize = svg->currentViewportSize();
306 
307     return true;
308 }
309 
310 }
311