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.
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.
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.
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.
24 #include "core/svg/SVGLengthContext.h"
26 #include "bindings/core/v8/ExceptionMessages.h"
27 #include "bindings/core/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"
40 SVGLengthContext::SVGLengthContext(const SVGElement* context)
45 SVGLengthContext::SVGLengthContext(const SVGElement* context, const FloatRect& viewport)
47 , m_overridenViewport(viewport)
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)
53 RefPtr<SVGLength> x = passX;
54 RefPtr<SVGLength> y = passY;
55 RefPtr<SVGLength> width = passWidth;
56 RefPtr<SVGLength> height = passHeight;
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));
64 SVGLengthContext lengthContext(context, viewport);
66 x->value(lengthContext) + viewport.x(),
67 y->value(lengthContext) + viewport.y(),
68 width->value(lengthContext),
69 height->value(lengthContext));
72 FloatPoint SVGLengthContext::resolvePoint(const SVGElement* context, SVGUnitTypes::SVGUnitType type, PassRefPtr<SVGLength> passX, PassRefPtr<SVGLength> passY)
74 RefPtr<SVGLength> x = passX;
75 RefPtr<SVGLength> y = passY;
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));
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());
87 float SVGLengthContext::resolveLength(const SVGElement* context, SVGUnitTypes::SVGUnitType type, PassRefPtr<SVGLength> passX)
89 RefPtr<SVGLength> x = passX;
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);
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();
101 float SVGLengthContext::convertValueToUserUnits(float value, SVGLengthMode mode, SVGLengthType fromUnit, ExceptionState& exceptionState) const
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)
108 return convertValueFromPercentageToUserUnits(value, mode, exceptionState);
112 case LengthTypeUnknown:
113 exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::argumentNullOrIncorrectType(3, "SVGLengthType"));
115 case LengthTypeNumber:
119 case LengthTypePercentage:
120 return convertValueFromPercentageToUserUnits(value / 100, mode, exceptionState);
122 return convertValueFromEMSToUserUnits(value, exceptionState);
124 return convertValueFromEXSToUserUnits(value, exceptionState);
126 return value * cssPixelsPerCentimeter;
128 return value * cssPixelsPerMillimeter;
130 return value * cssPixelsPerInch;
132 return value * cssPixelsPerPoint;
134 return value * cssPixelsPerPica;
137 ASSERT_NOT_REACHED();
141 float SVGLengthContext::convertValueFromUserUnits(float value, SVGLengthMode mode, SVGLengthType toUnit, ExceptionState& exceptionState) const
144 case LengthTypeUnknown:
145 exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::argumentNullOrIncorrectType(3, "SVGLengthType"));
147 case LengthTypeNumber:
149 case LengthTypePercentage:
150 return convertValueFromUserUnitsToPercentage(value * 100, mode, exceptionState);
152 return convertValueFromUserUnitsToEMS(value, exceptionState);
154 return convertValueFromUserUnitsToEXS(value, exceptionState);
158 return value / cssPixelsPerCentimeter;
160 return value / cssPixelsPerMillimeter;
162 return value / cssPixelsPerInch;
164 return value / cssPixelsPerPoint;
166 return value / cssPixelsPerPica;
169 ASSERT_NOT_REACHED();
173 float SVGLengthContext::convertValueFromUserUnitsToPercentage(float value, SVGLengthMode mode, ExceptionState& exceptionState) const
175 FloatSize viewportSize;
176 if (!determineViewport(viewportSize)) {
177 exceptionState.throwDOMException(NotSupportedError, "The viewport could not be determined.");
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;
190 ASSERT_NOT_REACHED();
194 float SVGLengthContext::convertValueFromPercentageToUserUnits(float value, SVGLengthMode mode, ExceptionState& exceptionState) const
196 FloatSize viewportSize;
197 if (!determineViewport(viewportSize)) {
198 exceptionState.throwDOMException(NotSupportedError, "The viewport could not be determined.");
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);
211 ASSERT_NOT_REACHED();
215 static inline RenderStyle* renderStyleForLengthResolving(const SVGElement* context)
220 const ContainerNode* currentContext = context;
222 if (currentContext->renderer())
223 return currentContext->renderer()->style();
224 currentContext = currentContext->parentNode();
225 } while (currentContext);
227 // There must be at least a RenderSVGRoot renderer, carrying a style.
228 ASSERT_NOT_REACHED();
232 float SVGLengthContext::convertValueFromUserUnitsToEMS(float value, ExceptionState& exceptionState) const
234 RenderStyle* style = renderStyleForLengthResolving(m_context);
236 exceptionState.throwDOMException(NotSupportedError, "No context could be found.");
240 float fontSize = style->specifiedFontSize();
242 exceptionState.throwDOMException(NotSupportedError, "No font-size could be determined.");
246 return value / fontSize;
249 float SVGLengthContext::convertValueFromEMSToUserUnits(float value, ExceptionState& exceptionState) const
251 RenderStyle* style = renderStyleForLengthResolving(m_context);
253 exceptionState.throwDOMException(NotSupportedError, "No context could be found.");
257 return value * style->specifiedFontSize();
260 float SVGLengthContext::convertValueFromUserUnitsToEXS(float value, ExceptionState& exceptionState) const
262 RenderStyle* style = renderStyleForLengthResolving(m_context);
264 exceptionState.throwDOMException(NotSupportedError, "No context could be found.");
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());
272 exceptionState.throwDOMException(NotSupportedError, "No x-height could be determined.");
276 return value / xHeight;
279 float SVGLengthContext::convertValueFromEXSToUserUnits(float value, ExceptionState& exceptionState) const
281 RenderStyle* style = renderStyleForLengthResolving(m_context);
283 exceptionState.throwDOMException(NotSupportedError, "No context could be found.");
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());
292 bool SVGLengthContext::determineViewport(FloatSize& viewportSize) const
297 // If an overriden viewport is given, it has precedence.
298 if (!m_overridenViewport.isEmpty()) {
299 viewportSize = m_overridenViewport.size();
303 // Root <svg> element lengths are resolved against the top level viewport.
304 if (m_context->isOutermostSVGSVGElement()) {
305 viewportSize = toSVGSVGElement(m_context)->currentViewportSize();
309 // Take size from nearest viewport element.
310 SVGElement* viewportElement = m_context->viewportElement();
311 if (!isSVGSVGElement(viewportElement))
314 const SVGSVGElement& svg = toSVGSVGElement(*viewportElement);
315 viewportSize = svg.currentViewBoxRect().size();
316 if (viewportSize.isEmpty())
317 viewportSize = svg.currentViewportSize();