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 | * Copyright (C) 2014 Adobe Systems Incorporated. All rights reserved. |
7 | * |
8 | * This library is free software; you can redistribute it and/or |
9 | * modify it under the terms of the GNU Library General Public |
10 | * License as published by the Free Software Foundation; either |
11 | * version 2 of the License, or (at your option) any later version. |
12 | * |
13 | * This library is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
16 | * Library General Public License for more details. |
17 | * |
18 | * You should have received a copy of the GNU Library General Public License |
19 | * along with this library; see the file COPYING.LIB. If not, write to |
20 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
21 | * Boston, MA 02110-1301, USA. |
22 | */ |
23 | |
24 | #include "config.h" |
25 | #include "SVGLengthContext.h" |
26 | |
27 | #include "CSSHelper.h" |
28 | #include "FontMetrics.h" |
29 | #include "Frame.h" |
30 | #include "LengthFunctions.h" |
31 | #include "RenderSVGRoot.h" |
32 | #include "RenderSVGViewportContainer.h" |
33 | #include "RenderView.h" |
34 | #include "SVGSVGElement.h" |
35 | |
36 | namespace WebCore { |
37 | |
38 | SVGLengthContext::SVGLengthContext(const SVGElement* context) |
39 | : m_context(context) |
40 | { |
41 | } |
42 | |
43 | SVGLengthContext::SVGLengthContext(const SVGElement* context, const FloatRect& viewport) |
44 | : m_context(context) |
45 | , m_overriddenViewport(viewport) |
46 | { |
47 | } |
48 | |
49 | FloatRect SVGLengthContext::resolveRectangle(const SVGElement* context, SVGUnitTypes::SVGUnitType type, const FloatRect& viewport, const SVGLengthValue& x, const SVGLengthValue& y, const SVGLengthValue& width, const SVGLengthValue& height) |
50 | { |
51 | ASSERT(type != SVGUnitTypes::SVG_UNIT_TYPE_UNKNOWN); |
52 | if (type == SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE) { |
53 | SVGLengthContext lengthContext(context); |
54 | return FloatRect(x.value(lengthContext), y.value(lengthContext), width.value(lengthContext), height.value(lengthContext)); |
55 | } |
56 | |
57 | SVGLengthContext lengthContext(context, viewport); |
58 | return FloatRect(x.value(lengthContext) + viewport.x(), |
59 | y.value(lengthContext) + viewport.y(), |
60 | width.value(lengthContext), |
61 | height.value(lengthContext)); |
62 | } |
63 | |
64 | FloatPoint SVGLengthContext::resolvePoint(const SVGElement* context, SVGUnitTypes::SVGUnitType type, const SVGLengthValue& x, const SVGLengthValue& y) |
65 | { |
66 | ASSERT(type != SVGUnitTypes::SVG_UNIT_TYPE_UNKNOWN); |
67 | if (type == SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE) { |
68 | SVGLengthContext lengthContext(context); |
69 | return FloatPoint(x.value(lengthContext), y.value(lengthContext)); |
70 | } |
71 | |
72 | // 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. |
73 | return FloatPoint(x.valueAsPercentage(), y.valueAsPercentage()); |
74 | } |
75 | |
76 | float SVGLengthContext::resolveLength(const SVGElement* context, SVGUnitTypes::SVGUnitType type, const SVGLengthValue& x) |
77 | { |
78 | ASSERT(type != SVGUnitTypes::SVG_UNIT_TYPE_UNKNOWN); |
79 | if (type == SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE) { |
80 | SVGLengthContext lengthContext(context); |
81 | return x.value(lengthContext); |
82 | } |
83 | |
84 | // 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. |
85 | return x.valueAsPercentage(); |
86 | } |
87 | |
88 | float SVGLengthContext::valueForLength(const Length& length, SVGLengthMode mode) |
89 | { |
90 | if (length.isPercent()) { |
91 | auto result = convertValueFromPercentageToUserUnits(length.value() / 100, mode); |
92 | if (result.hasException()) |
93 | return 0; |
94 | return result.releaseReturnValue(); |
95 | } |
96 | if (length.isAuto() || !length.isSpecified()) |
97 | return 0; |
98 | |
99 | FloatSize viewportSize; |
100 | determineViewport(viewportSize); |
101 | |
102 | switch (mode) { |
103 | case LengthModeWidth: |
104 | return floatValueForLength(length, viewportSize.width()); |
105 | case LengthModeHeight: |
106 | return floatValueForLength(length, viewportSize.height()); |
107 | case LengthModeOther: |
108 | return floatValueForLength(length, std::sqrt(viewportSize.diagonalLengthSquared() / 2)); |
109 | }; |
110 | return 0; |
111 | } |
112 | |
113 | ExceptionOr<float> SVGLengthContext::convertValueToUserUnits(float value, SVGLengthMode mode, SVGLengthType fromUnit) const |
114 | { |
115 | // If the SVGLengthContext carries a custom viewport, force resolving against it. |
116 | if (!m_overriddenViewport.isEmpty()) { |
117 | // 100% = 100.0 instead of 1.0 for historical reasons, this could eventually be changed |
118 | if (fromUnit == LengthTypePercentage) |
119 | value /= 100; |
120 | return convertValueFromPercentageToUserUnits(value, mode); |
121 | } |
122 | |
123 | switch (fromUnit) { |
124 | case LengthTypeUnknown: |
125 | return Exception { NotSupportedError }; |
126 | case LengthTypeNumber: |
127 | return value; |
128 | case LengthTypePX: |
129 | return value; |
130 | case LengthTypePercentage: |
131 | return convertValueFromPercentageToUserUnits(value / 100, mode); |
132 | case LengthTypeEMS: |
133 | return convertValueFromEMSToUserUnits(value); |
134 | case LengthTypeEXS: |
135 | return convertValueFromEXSToUserUnits(value); |
136 | case LengthTypeCM: |
137 | return value * cssPixelsPerInch / 2.54f; |
138 | case LengthTypeMM: |
139 | return value * cssPixelsPerInch / 25.4f; |
140 | case LengthTypeIN: |
141 | return value * cssPixelsPerInch; |
142 | case LengthTypePT: |
143 | return value * cssPixelsPerInch / 72; |
144 | case LengthTypePC: |
145 | return value * cssPixelsPerInch / 6; |
146 | } |
147 | |
148 | ASSERT_NOT_REACHED(); |
149 | return 0; |
150 | } |
151 | |
152 | ExceptionOr<float> SVGLengthContext::convertValueFromUserUnits(float value, SVGLengthMode mode, SVGLengthType toUnit) const |
153 | { |
154 | switch (toUnit) { |
155 | case LengthTypeUnknown: |
156 | return Exception { NotSupportedError }; |
157 | case LengthTypeNumber: |
158 | return value; |
159 | case LengthTypePercentage: |
160 | return convertValueFromUserUnitsToPercentage(value * 100, mode); |
161 | case LengthTypeEMS: |
162 | return convertValueFromUserUnitsToEMS(value); |
163 | case LengthTypeEXS: |
164 | return convertValueFromUserUnitsToEXS(value); |
165 | case LengthTypePX: |
166 | return value; |
167 | case LengthTypeCM: |
168 | return value * 2.54f / cssPixelsPerInch; |
169 | case LengthTypeMM: |
170 | return value * 25.4f / cssPixelsPerInch; |
171 | case LengthTypeIN: |
172 | return value / cssPixelsPerInch; |
173 | case LengthTypePT: |
174 | return value * 72 / cssPixelsPerInch; |
175 | case LengthTypePC: |
176 | return value * 6 / cssPixelsPerInch; |
177 | } |
178 | |
179 | ASSERT_NOT_REACHED(); |
180 | return 0; |
181 | } |
182 | |
183 | ExceptionOr<float> SVGLengthContext::convertValueFromUserUnitsToPercentage(float value, SVGLengthMode mode) const |
184 | { |
185 | FloatSize viewportSize; |
186 | if (!determineViewport(viewportSize)) |
187 | return Exception { NotSupportedError }; |
188 | |
189 | switch (mode) { |
190 | case LengthModeWidth: |
191 | return value / viewportSize.width() * 100; |
192 | case LengthModeHeight: |
193 | return value / viewportSize.height() * 100; |
194 | case LengthModeOther: |
195 | return value / (std::sqrt(viewportSize.diagonalLengthSquared() / 2)) * 100; |
196 | }; |
197 | |
198 | ASSERT_NOT_REACHED(); |
199 | return 0; |
200 | } |
201 | |
202 | ExceptionOr<float> SVGLengthContext::convertValueFromPercentageToUserUnits(float value, SVGLengthMode mode) const |
203 | { |
204 | FloatSize viewportSize; |
205 | if (!determineViewport(viewportSize)) |
206 | return Exception { NotSupportedError }; |
207 | |
208 | switch (mode) { |
209 | case LengthModeWidth: |
210 | return value * viewportSize.width(); |
211 | case LengthModeHeight: |
212 | return value * viewportSize.height(); |
213 | case LengthModeOther: |
214 | return value * std::sqrt(viewportSize.diagonalLengthSquared() / 2); |
215 | }; |
216 | |
217 | ASSERT_NOT_REACHED(); |
218 | return 0; |
219 | } |
220 | |
221 | static inline const RenderStyle* renderStyleForLengthResolving(const SVGElement* context) |
222 | { |
223 | if (!context) |
224 | return nullptr; |
225 | |
226 | const ContainerNode* currentContext = context; |
227 | do { |
228 | if (currentContext->renderer()) |
229 | return ¤tContext->renderer()->style(); |
230 | currentContext = currentContext->parentNode(); |
231 | } while (currentContext); |
232 | |
233 | // There must be at least a RenderSVGRoot renderer, carrying a style. |
234 | ASSERT_NOT_REACHED(); |
235 | return nullptr; |
236 | } |
237 | |
238 | ExceptionOr<float> SVGLengthContext::convertValueFromUserUnitsToEMS(float value) const |
239 | { |
240 | auto* style = renderStyleForLengthResolving(m_context); |
241 | if (!style) |
242 | return Exception { NotSupportedError }; |
243 | |
244 | float fontSize = style->computedFontPixelSize(); |
245 | if (!fontSize) |
246 | return Exception { NotSupportedError }; |
247 | |
248 | return value / fontSize; |
249 | } |
250 | |
251 | ExceptionOr<float> SVGLengthContext::convertValueFromEMSToUserUnits(float value) const |
252 | { |
253 | auto* style = renderStyleForLengthResolving(m_context); |
254 | if (!style) |
255 | return Exception { NotSupportedError }; |
256 | |
257 | return value * style->computedFontPixelSize(); |
258 | } |
259 | |
260 | ExceptionOr<float> SVGLengthContext::convertValueFromUserUnitsToEXS(float value) const |
261 | { |
262 | auto* style = renderStyleForLengthResolving(m_context); |
263 | if (!style) |
264 | return Exception { NotSupportedError }; |
265 | |
266 | // Use of ceil allows a pixel match to the W3Cs expected output of coords-units-03-b.svg |
267 | // if this causes problems in real world cases maybe it would be best to remove this |
268 | float xHeight = std::ceil(style->fontMetrics().xHeight()); |
269 | if (!xHeight) |
270 | return Exception { NotSupportedError }; |
271 | |
272 | return value / xHeight; |
273 | } |
274 | |
275 | ExceptionOr<float> SVGLengthContext::convertValueFromEXSToUserUnits(float value) const |
276 | { |
277 | auto* style = renderStyleForLengthResolving(m_context); |
278 | if (!style) |
279 | return Exception { NotSupportedError }; |
280 | |
281 | // Use of ceil allows a pixel match to the W3Cs expected output of coords-units-03-b.svg |
282 | // if this causes problems in real world cases maybe it would be best to remove this |
283 | return value * std::ceil(style->fontMetrics().xHeight()); |
284 | } |
285 | |
286 | bool SVGLengthContext::determineViewport(FloatSize& viewportSize) const |
287 | { |
288 | if (!m_context) |
289 | return false; |
290 | |
291 | // If an overridden viewport is given, it has precedence. |
292 | if (!m_overriddenViewport.isEmpty()) { |
293 | viewportSize = m_overriddenViewport.size(); |
294 | return true; |
295 | } |
296 | |
297 | // Root <svg> element lengths are resolved against the top level viewport. |
298 | if (m_context->isOutermostSVGSVGElement()) { |
299 | viewportSize = downcast<SVGSVGElement>(*m_context).currentViewportSize(); |
300 | return true; |
301 | } |
302 | |
303 | // Take size from nearest viewport element. |
304 | auto viewportElement = makeRefPtr(m_context->viewportElement()); |
305 | if (!is<SVGSVGElement>(viewportElement)) |
306 | return false; |
307 | |
308 | const SVGSVGElement& svg = downcast<SVGSVGElement>(*viewportElement); |
309 | viewportSize = svg.currentViewBoxRect().size(); |
310 | if (viewportSize.isEmpty()) |
311 | viewportSize = svg.currentViewportSize(); |
312 | |
313 | return true; |
314 | } |
315 | |
316 | } |
317 | |