|
1 /* |
|
2 Copyright (C) 2004, 2005, 2006 Nikolas Zimmermann <zimmermann@kde.org> |
|
3 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org> |
|
4 2007 Apple Inc. All rights reserved. |
|
5 |
|
6 This library is free software; you can redistribute it and/or |
|
7 modify it under the terms of the GNU Library General Public |
|
8 License as published by the Free Software Foundation; either |
|
9 version 2 of the License, or (at your option) any later version. |
|
10 |
|
11 This library is distributed in the hope that it will be useful, |
|
12 but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
14 Library General Public License for more details. |
|
15 |
|
16 You should have received a copy of the GNU Library General Public License |
|
17 along with this library; see the file COPYING.LIB. If not, write to |
|
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
|
19 Boston, MA 02110-1301, USA. |
|
20 */ |
|
21 |
|
22 #include "config.h" |
|
23 |
|
24 #if ENABLE(SVG) |
|
25 #include "SVGLength.h" |
|
26 |
|
27 #include "CSSHelper.h" |
|
28 #include "FloatConversion.h" |
|
29 #include "FrameView.h" |
|
30 #include "RenderObject.h" |
|
31 #include "RenderView.h" |
|
32 #include "SVGParserUtilities.h" |
|
33 #include "SVGSVGElement.h" |
|
34 |
|
35 #include <math.h> |
|
36 #include <wtf/Assertions.h> |
|
37 |
|
38 namespace WebCore { |
|
39 |
|
40 // Helper functions |
|
41 static inline unsigned int storeUnit(SVGLengthMode mode, SVGLengthType type) |
|
42 { |
|
43 return (mode << 4) | type; |
|
44 } |
|
45 |
|
46 static inline SVGLengthMode extractMode(unsigned int unit) |
|
47 { |
|
48 unsigned int mode = unit >> 4; |
|
49 return static_cast<SVGLengthMode>(mode); |
|
50 } |
|
51 |
|
52 static inline SVGLengthType extractType(unsigned int unit) |
|
53 { |
|
54 unsigned int mode = unit >> 4; |
|
55 unsigned int type = unit ^ (mode << 4); |
|
56 return static_cast<SVGLengthType>(type); |
|
57 } |
|
58 |
|
59 static inline String lengthTypeToString(SVGLengthType type) |
|
60 { |
|
61 switch (type) { |
|
62 case LengthTypeUnknown: |
|
63 case LengthTypeNumber: |
|
64 return ""; |
|
65 case LengthTypePercentage: |
|
66 return "%"; |
|
67 case LengthTypeEMS: |
|
68 return "em"; |
|
69 case LengthTypeEXS: |
|
70 return "ex"; |
|
71 case LengthTypePX: |
|
72 return "px"; |
|
73 case LengthTypeCM: |
|
74 return "cm"; |
|
75 case LengthTypeMM: |
|
76 return "mm"; |
|
77 case LengthTypeIN: |
|
78 return "in"; |
|
79 case LengthTypePT: |
|
80 return "pt"; |
|
81 case LengthTypePC: |
|
82 return "pc"; |
|
83 } |
|
84 |
|
85 return String(); |
|
86 } |
|
87 |
|
88 inline SVGLengthType stringToLengthType(const String& string) |
|
89 { |
|
90 if (string.endsWith("%")) |
|
91 return LengthTypePercentage; |
|
92 else if (string.endsWith("em")) |
|
93 return LengthTypeEMS; |
|
94 else if (string.endsWith("ex")) |
|
95 return LengthTypeEXS; |
|
96 else if (string.endsWith("px")) |
|
97 return LengthTypePX; |
|
98 else if (string.endsWith("cm")) |
|
99 return LengthTypeCM; |
|
100 else if (string.endsWith("mm")) |
|
101 return LengthTypeMM; |
|
102 else if (string.endsWith("in")) |
|
103 return LengthTypeIN; |
|
104 else if (string.endsWith("pt")) |
|
105 return LengthTypePT; |
|
106 else if (string.endsWith("pc")) |
|
107 return LengthTypePC; |
|
108 else if (!string.isEmpty()) |
|
109 return LengthTypeNumber; |
|
110 |
|
111 return LengthTypeUnknown; |
|
112 } |
|
113 |
|
114 SVGLength::SVGLength(SVGLengthMode mode, const String& valueAsString) |
|
115 : m_valueInSpecifiedUnits(0.0f) |
|
116 , m_unit(storeUnit(mode, LengthTypeNumber)) |
|
117 { |
|
118 setValueAsString(valueAsString); |
|
119 } |
|
120 |
|
121 SVGLengthType SVGLength::unitType() const |
|
122 { |
|
123 return extractType(m_unit); |
|
124 } |
|
125 |
|
126 float SVGLength::value(const SVGElement* context) const |
|
127 { |
|
128 SVGLengthType type = extractType(m_unit); |
|
129 if (type == LengthTypeUnknown) |
|
130 return 0.0f; |
|
131 |
|
132 switch (type) { |
|
133 case LengthTypeNumber: |
|
134 return m_valueInSpecifiedUnits; |
|
135 case LengthTypePercentage: |
|
136 return SVGLength::PercentageOfViewport(m_valueInSpecifiedUnits / 100.0f, context, extractMode(m_unit)); |
|
137 case LengthTypeEMS: |
|
138 case LengthTypeEXS: |
|
139 { |
|
140 RenderStyle* style = 0; |
|
141 if (context && context->renderer()) |
|
142 style = context->renderer()->style(); |
|
143 if (style) { |
|
144 float useSize = style->fontSize(); |
|
145 ASSERT(useSize > 0); |
|
146 if (type == LengthTypeEMS) |
|
147 return m_valueInSpecifiedUnits * useSize; |
|
148 else { |
|
149 float xHeight = style->font().xHeight(); |
|
150 // Use of ceil allows a pixel match to the W3Cs expected output of coords-units-03-b.svg |
|
151 // if this causes problems in real world cases maybe it would be best to remove this |
|
152 return m_valueInSpecifiedUnits * ceilf(xHeight); |
|
153 } |
|
154 } |
|
155 return 0.0f; |
|
156 } |
|
157 case LengthTypePX: |
|
158 return m_valueInSpecifiedUnits; |
|
159 case LengthTypeCM: |
|
160 return m_valueInSpecifiedUnits / 2.54f * cssPixelsPerInch; |
|
161 case LengthTypeMM: |
|
162 return m_valueInSpecifiedUnits / 25.4f * cssPixelsPerInch; |
|
163 case LengthTypeIN: |
|
164 return m_valueInSpecifiedUnits * cssPixelsPerInch; |
|
165 case LengthTypePT: |
|
166 return m_valueInSpecifiedUnits / 72.0f * cssPixelsPerInch; |
|
167 case LengthTypePC: |
|
168 return m_valueInSpecifiedUnits / 6.0f * cssPixelsPerInch; |
|
169 default: |
|
170 break; |
|
171 } |
|
172 |
|
173 ASSERT_NOT_REACHED(); |
|
174 return 0.0f; |
|
175 } |
|
176 |
|
177 void SVGLength::setValue(float value) |
|
178 { |
|
179 SVGLengthType type = extractType(m_unit); |
|
180 ASSERT(type != LengthTypeUnknown); |
|
181 |
|
182 switch (type) { |
|
183 case LengthTypeNumber: |
|
184 m_valueInSpecifiedUnits = value; |
|
185 break; |
|
186 case LengthTypePercentage: |
|
187 case LengthTypeEMS: |
|
188 case LengthTypeEXS: |
|
189 ASSERT_NOT_REACHED(); |
|
190 break; |
|
191 case LengthTypePX: |
|
192 m_valueInSpecifiedUnits = value; |
|
193 break; |
|
194 case LengthTypeCM: |
|
195 m_valueInSpecifiedUnits = value * 2.54f / cssPixelsPerInch; |
|
196 break; |
|
197 case LengthTypeMM: |
|
198 m_valueInSpecifiedUnits = value * 25.4f / cssPixelsPerInch; |
|
199 break; |
|
200 case LengthTypeIN: |
|
201 m_valueInSpecifiedUnits = value / cssPixelsPerInch; |
|
202 break; |
|
203 case LengthTypePT: |
|
204 m_valueInSpecifiedUnits = value * 72.0f / cssPixelsPerInch; |
|
205 break; |
|
206 case LengthTypePC: |
|
207 m_valueInSpecifiedUnits = value / 6.0f * cssPixelsPerInch; |
|
208 break; |
|
209 default: |
|
210 break; |
|
211 } |
|
212 } |
|
213 |
|
214 void SVGLength::setValueInSpecifiedUnits(float value) |
|
215 { |
|
216 m_valueInSpecifiedUnits = value; |
|
217 } |
|
218 |
|
219 float SVGLength::valueInSpecifiedUnits() const |
|
220 { |
|
221 return m_valueInSpecifiedUnits; |
|
222 } |
|
223 |
|
224 float SVGLength::valueAsPercentage() const |
|
225 { |
|
226 // 100% = 100.0 instead of 1.0 for historical reasons, this could eventually be changed |
|
227 if (extractType(m_unit) == LengthTypePercentage) |
|
228 return valueInSpecifiedUnits() / 100.0f; |
|
229 |
|
230 return valueInSpecifiedUnits(); |
|
231 } |
|
232 |
|
233 bool SVGLength::setValueAsString(const String& s) |
|
234 { |
|
235 if (s.isEmpty()) |
|
236 return false; |
|
237 |
|
238 float convertedNumber = 0.0f; |
|
239 const UChar* ptr = s.characters(); |
|
240 const UChar* end = ptr + s.length(); |
|
241 |
|
242 if (!parseNumber(ptr, end, convertedNumber, false)) |
|
243 return false; |
|
244 |
|
245 SVGLengthType type = stringToLengthType(s); |
|
246 if (ptr != end && type == LengthTypeNumber) |
|
247 return false; |
|
248 |
|
249 m_unit = storeUnit(extractMode(m_unit), type); |
|
250 m_valueInSpecifiedUnits = convertedNumber; |
|
251 return true; |
|
252 } |
|
253 |
|
254 String SVGLength::valueAsString() const |
|
255 { |
|
256 return String::number(m_valueInSpecifiedUnits) + lengthTypeToString(extractType(m_unit)); |
|
257 } |
|
258 |
|
259 void SVGLength::newValueSpecifiedUnits(unsigned short type, float value) |
|
260 { |
|
261 ASSERT(type <= LengthTypePC); |
|
262 |
|
263 m_unit = storeUnit(extractMode(m_unit), (SVGLengthType) type); |
|
264 m_valueInSpecifiedUnits = value; |
|
265 } |
|
266 |
|
267 void SVGLength::convertToSpecifiedUnits(unsigned short type, const SVGElement* context) |
|
268 { |
|
269 ASSERT(type <= LengthTypePC); |
|
270 |
|
271 float valueInUserUnits = value(context); |
|
272 m_unit = storeUnit(extractMode(m_unit), (SVGLengthType) type); |
|
273 setValue(valueInUserUnits); |
|
274 } |
|
275 |
|
276 float SVGLength::PercentageOfViewport(float value, const SVGElement* context, SVGLengthMode mode) |
|
277 { |
|
278 ASSERT(context); |
|
279 |
|
280 float width = 0.0f, height = 0.0f; |
|
281 SVGElement* viewportElement = context->viewportElement(); |
|
282 |
|
283 // PercentageOfViewport() is used to resolve all relative-positioned values within a SVG document (fragment) |
|
284 Document* doc = context->document(); |
|
285 if (doc->documentElement() == context) { |
|
286 // Resolve value against outermost <svg> element |
|
287 if (RenderView* view = toRenderView(doc->renderer())) { |
|
288 width = view->viewWidth(); |
|
289 height = view->viewHeight(); |
|
290 } |
|
291 } else if (viewportElement && viewportElement->isSVG()) { |
|
292 // Resolve value against nearest viewport element (common case: inner <svg> elements) |
|
293 const SVGSVGElement* svg = static_cast<const SVGSVGElement*>(viewportElement); |
|
294 if (svg->hasAttribute(SVGNames::viewBoxAttr)) { |
|
295 width = svg->viewBox().width(); |
|
296 height = svg->viewBox().height(); |
|
297 } else { |
|
298 width = svg->width().value(svg); |
|
299 height = svg->height().value(svg); |
|
300 } |
|
301 } else if (context->parent() && !context->parent()->isSVGElement()) { |
|
302 // Resolve value against enclosing non-SVG RenderBox |
|
303 if (RenderObject* renderer = context->renderer()) { |
|
304 if (renderer->isBox()) { |
|
305 RenderBox* box = toRenderBox(renderer); |
|
306 width = box->width(); |
|
307 height = box->height(); |
|
308 } |
|
309 } |
|
310 } |
|
311 |
|
312 if (mode == LengthModeWidth) |
|
313 return value * width; |
|
314 else if (mode == LengthModeHeight) |
|
315 return value * height; |
|
316 else if (mode == LengthModeOther) |
|
317 return value * sqrtf(powf(width, 2) + powf(height, 2)) / sqrtf(2.0f); |
|
318 |
|
319 return 0.0f; |
|
320 } |
|
321 |
|
322 } |
|
323 |
|
324 #endif |