blob: e708b76d0c533939a4a07fb607f9775ade3652d8 [file] [log] [blame]
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001/*
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 *
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
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010024#include "core/svg/SVGLength.h"
25
26#include "SVGNames.h"
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010027#include "core/css/CSSPrimitiveValue.h"
28#include "core/dom/ExceptionCode.h"
29#include "core/dom/ExceptionCodePlaceholder.h"
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010030#include "core/svg/SVGParserUtilities.h"
31
32#include <wtf/MathExtras.h>
33#include <wtf/text/WTFString.h>
34
35namespace WebCore {
36
37// Helper functions
38static inline unsigned int storeUnit(SVGLengthMode mode, SVGLengthType type)
39{
40 return (mode << 4) | type;
41}
42
43static inline SVGLengthMode extractMode(unsigned int unit)
44{
45 unsigned int mode = unit >> 4;
46 return static_cast<SVGLengthMode>(mode);
47}
48
49static inline SVGLengthType extractType(unsigned int unit)
50{
51 unsigned int mode = unit >> 4;
52 unsigned int type = unit ^ (mode << 4);
53 return static_cast<SVGLengthType>(type);
54}
55
56static inline String lengthTypeToString(SVGLengthType type)
57{
58 switch (type) {
59 case LengthTypeUnknown:
60 case LengthTypeNumber:
61 return "";
62 case LengthTypePercentage:
63 return "%";
64 case LengthTypeEMS:
65 return "em";
66 case LengthTypeEXS:
67 return "ex";
68 case LengthTypePX:
69 return "px";
70 case LengthTypeCM:
71 return "cm";
72 case LengthTypeMM:
73 return "mm";
74 case LengthTypeIN:
75 return "in";
76 case LengthTypePT:
77 return "pt";
78 case LengthTypePC:
79 return "pc";
80 }
81
82 ASSERT_NOT_REACHED();
83 return String();
84}
85
86inline SVGLengthType stringToLengthType(const UChar*& ptr, const UChar* end)
87{
88 if (ptr == end)
89 return LengthTypeNumber;
90
91 const UChar firstChar = *ptr;
92
93 if (++ptr == end)
94 return firstChar == '%' ? LengthTypePercentage : LengthTypeUnknown;
95
96 const UChar secondChar = *ptr;
97
98 if (++ptr != end)
99 return LengthTypeUnknown;
100
101 if (firstChar == 'e' && secondChar == 'm')
102 return LengthTypeEMS;
103 if (firstChar == 'e' && secondChar == 'x')
104 return LengthTypeEXS;
105 if (firstChar == 'p' && secondChar == 'x')
106 return LengthTypePX;
107 if (firstChar == 'c' && secondChar == 'm')
108 return LengthTypeCM;
109 if (firstChar == 'm' && secondChar == 'm')
110 return LengthTypeMM;
111 if (firstChar == 'i' && secondChar == 'n')
112 return LengthTypeIN;
113 if (firstChar == 'p' && secondChar == 't')
114 return LengthTypePT;
115 if (firstChar == 'p' && secondChar == 'c')
116 return LengthTypePC;
117
118 return LengthTypeUnknown;
119}
120
121SVGLength::SVGLength(SVGLengthMode mode, const String& valueAsString)
122 : m_valueInSpecifiedUnits(0)
123 , m_unit(storeUnit(mode, LengthTypeNumber))
124{
125 setValueAsString(valueAsString, IGNORE_EXCEPTION);
126}
127
128SVGLength::SVGLength(const SVGLengthContext& context, float value, SVGLengthMode mode, SVGLengthType unitType)
129 : m_valueInSpecifiedUnits(0)
130 , m_unit(storeUnit(mode, unitType))
131{
132 setValue(value, context, ASSERT_NO_EXCEPTION);
133}
134
135SVGLength::SVGLength(const SVGLength& other)
136 : m_valueInSpecifiedUnits(other.m_valueInSpecifiedUnits)
137 , m_unit(other.m_unit)
138{
139}
140
141void SVGLength::setValueAsString(const String& valueAsString, SVGLengthMode mode, ExceptionCode& ec)
142{
143 m_valueInSpecifiedUnits = 0;
144 m_unit = storeUnit(mode, LengthTypeNumber);
145 setValueAsString(valueAsString, ec);
146}
147
148bool SVGLength::operator==(const SVGLength& other) const
149{
150 return m_unit == other.m_unit
151 && m_valueInSpecifiedUnits == other.m_valueInSpecifiedUnits;
152}
153
154bool SVGLength::operator!=(const SVGLength& other) const
155{
156 return !operator==(other);
157}
158
159SVGLength SVGLength::construct(SVGLengthMode mode, const String& valueAsString, SVGParsingError& parseError, SVGLengthNegativeValuesMode negativeValuesMode)
160{
161 ExceptionCode ec = 0;
162 SVGLength length(mode);
163
164 length.setValueAsString(valueAsString, ec);
165
166 if (ec)
167 parseError = ParsingAttributeFailedError;
168 else if (negativeValuesMode == ForbidNegativeLengths && length.valueInSpecifiedUnits() < 0)
169 parseError = NegativeValueForbiddenError;
170
171 return length;
172}
173
174SVGLengthType SVGLength::unitType() const
175{
176 return extractType(m_unit);
177}
178
179SVGLengthMode SVGLength::unitMode() const
180{
181 return extractMode(m_unit);
182}
183
184float SVGLength::value(const SVGLengthContext& context) const
185{
186 return value(context, IGNORE_EXCEPTION);
187}
188
189float SVGLength::value(const SVGLengthContext& context, ExceptionCode& ec) const
190{
191 return context.convertValueToUserUnits(m_valueInSpecifiedUnits, extractMode(m_unit), extractType(m_unit), ec);
192}
193
194void SVGLength::setValue(const SVGLengthContext& context, float value, SVGLengthMode mode, SVGLengthType unitType, ExceptionCode& ec)
195{
196 m_unit = storeUnit(mode, unitType);
197 setValue(value, context, ec);
198}
199
200void SVGLength::setValue(float value, const SVGLengthContext& context, ExceptionCode& ec)
201{
202 // 100% = 100.0 instead of 1.0 for historical reasons, this could eventually be changed
203 if (extractType(m_unit) == LengthTypePercentage)
204 value = value / 100;
205
206 ec = 0;
207 float convertedValue = context.convertValueFromUserUnits(value, extractMode(m_unit), extractType(m_unit), ec);
208 if (!ec)
209 m_valueInSpecifiedUnits = convertedValue;
210}
211float SVGLength::valueAsPercentage() const
212{
213 // 100% = 100.0 instead of 1.0 for historical reasons, this could eventually be changed
214 if (extractType(m_unit) == LengthTypePercentage)
215 return m_valueInSpecifiedUnits / 100;
216
217 return m_valueInSpecifiedUnits;
218}
219
220void SVGLength::setValueAsString(const String& string, ExceptionCode& ec)
221{
222 if (string.isEmpty())
223 return;
224
225 float convertedNumber = 0;
226 const UChar* ptr = string.characters();
227 const UChar* end = ptr + string.length();
228
229 if (!parseNumber(ptr, end, convertedNumber, false)) {
230 ec = SYNTAX_ERR;
231 return;
232 }
233
234 SVGLengthType type = stringToLengthType(ptr, end);
235 ASSERT(ptr <= end);
236 if (type == LengthTypeUnknown) {
237 ec = SYNTAX_ERR;
238 return;
239 }
240
241 m_unit = storeUnit(extractMode(m_unit), type);
242 m_valueInSpecifiedUnits = convertedNumber;
243}
244
245String SVGLength::valueAsString() const
246{
247 return String::number(m_valueInSpecifiedUnits) + lengthTypeToString(extractType(m_unit));
248}
249
250void SVGLength::newValueSpecifiedUnits(unsigned short type, float value, ExceptionCode& ec)
251{
252 if (type == LengthTypeUnknown || type > LengthTypePC) {
253 ec = NOT_SUPPORTED_ERR;
254 return;
255 }
256
257 m_unit = storeUnit(extractMode(m_unit), static_cast<SVGLengthType>(type));
258 m_valueInSpecifiedUnits = value;
259}
260
261void SVGLength::convertToSpecifiedUnits(unsigned short type, const SVGLengthContext& context, ExceptionCode& ec)
262{
263 if (type == LengthTypeUnknown || type > LengthTypePC) {
264 ec = NOT_SUPPORTED_ERR;
265 return;
266 }
267
268 float valueInUserUnits = value(context, ec);
269 if (ec)
270 return;
271
272 unsigned int originalUnitAndType = m_unit;
273 m_unit = storeUnit(extractMode(m_unit), static_cast<SVGLengthType>(type));
274 setValue(valueInUserUnits, context, ec);
275 if (!ec)
276 return;
277
278 // Eventually restore old unit and type
279 m_unit = originalUnitAndType;
280}
281
282SVGLength SVGLength::fromCSSPrimitiveValue(CSSPrimitiveValue* value)
283{
284 ASSERT(value);
285
286 SVGLengthType svgType;
287 switch (value->primitiveType()) {
288 case CSSPrimitiveValue::CSS_NUMBER:
289 svgType = LengthTypeNumber;
290 break;
291 case CSSPrimitiveValue::CSS_PERCENTAGE:
292 svgType = LengthTypePercentage;
293 break;
294 case CSSPrimitiveValue::CSS_EMS:
295 svgType = LengthTypeEMS;
296 break;
297 case CSSPrimitiveValue::CSS_EXS:
298 svgType = LengthTypeEXS;
299 break;
300 case CSSPrimitiveValue::CSS_PX:
301 svgType = LengthTypePX;
302 break;
303 case CSSPrimitiveValue::CSS_CM:
304 svgType = LengthTypeCM;
305 break;
306 case CSSPrimitiveValue::CSS_MM:
307 svgType = LengthTypeMM;
308 break;
309 case CSSPrimitiveValue::CSS_IN:
310 svgType = LengthTypeIN;
311 break;
312 case CSSPrimitiveValue::CSS_PT:
313 svgType = LengthTypePT;
314 break;
315 case CSSPrimitiveValue::CSS_PC:
316 svgType = LengthTypePC;
317 break;
318 case CSSPrimitiveValue::CSS_UNKNOWN:
319 default:
320 svgType = LengthTypeUnknown;
321 break;
322 };
323
324 if (svgType == LengthTypeUnknown)
325 return SVGLength();
326
327 ExceptionCode ec = 0;
328 SVGLength length;
329 length.newValueSpecifiedUnits(svgType, value->getFloatValue(), ec);
330 if (ec)
331 return SVGLength();
332
333 return length;
334}
335
336PassRefPtr<CSSPrimitiveValue> SVGLength::toCSSPrimitiveValue(const SVGLength& length)
337{
338 CSSPrimitiveValue::UnitTypes cssType = CSSPrimitiveValue::CSS_UNKNOWN;
339 switch (length.unitType()) {
340 case LengthTypeUnknown:
341 break;
342 case LengthTypeNumber:
343 cssType = CSSPrimitiveValue::CSS_NUMBER;
344 break;
345 case LengthTypePercentage:
346 cssType = CSSPrimitiveValue::CSS_PERCENTAGE;
347 break;
348 case LengthTypeEMS:
349 cssType = CSSPrimitiveValue::CSS_EMS;
350 break;
351 case LengthTypeEXS:
352 cssType = CSSPrimitiveValue::CSS_EXS;
353 break;
354 case LengthTypePX:
355 cssType = CSSPrimitiveValue::CSS_PX;
356 break;
357 case LengthTypeCM:
358 cssType = CSSPrimitiveValue::CSS_CM;
359 break;
360 case LengthTypeMM:
361 cssType = CSSPrimitiveValue::CSS_MM;
362 break;
363 case LengthTypeIN:
364 cssType = CSSPrimitiveValue::CSS_IN;
365 break;
366 case LengthTypePT:
367 cssType = CSSPrimitiveValue::CSS_PT;
368 break;
369 case LengthTypePC:
370 cssType = CSSPrimitiveValue::CSS_PC;
371 break;
372 };
373
374 return CSSPrimitiveValue::create(length.valueInSpecifiedUnits(), cssType);
375}
376
377SVGLengthMode SVGLength::lengthModeForAnimatedLengthAttribute(const QualifiedName& attrName)
378{
379 typedef HashMap<QualifiedName, SVGLengthMode> LengthModeForLengthAttributeMap;
380 DEFINE_STATIC_LOCAL(LengthModeForLengthAttributeMap, s_lengthModeMap, ());
381
382 if (s_lengthModeMap.isEmpty()) {
383 s_lengthModeMap.set(SVGNames::xAttr, LengthModeWidth);
384 s_lengthModeMap.set(SVGNames::yAttr, LengthModeHeight);
385 s_lengthModeMap.set(SVGNames::cxAttr, LengthModeWidth);
386 s_lengthModeMap.set(SVGNames::cyAttr, LengthModeHeight);
387 s_lengthModeMap.set(SVGNames::dxAttr, LengthModeWidth);
388 s_lengthModeMap.set(SVGNames::dyAttr, LengthModeHeight);
389 s_lengthModeMap.set(SVGNames::fxAttr, LengthModeWidth);
390 s_lengthModeMap.set(SVGNames::fyAttr, LengthModeHeight);
391 s_lengthModeMap.set(SVGNames::rAttr, LengthModeOther);
392 s_lengthModeMap.set(SVGNames::widthAttr, LengthModeWidth);
393 s_lengthModeMap.set(SVGNames::heightAttr, LengthModeHeight);
394 s_lengthModeMap.set(SVGNames::x1Attr, LengthModeWidth);
395 s_lengthModeMap.set(SVGNames::x2Attr, LengthModeWidth);
396 s_lengthModeMap.set(SVGNames::y1Attr, LengthModeHeight);
397 s_lengthModeMap.set(SVGNames::y2Attr, LengthModeHeight);
398 s_lengthModeMap.set(SVGNames::refXAttr, LengthModeWidth);
399 s_lengthModeMap.set(SVGNames::refYAttr, LengthModeHeight);
400 s_lengthModeMap.set(SVGNames::markerWidthAttr, LengthModeWidth);
401 s_lengthModeMap.set(SVGNames::markerHeightAttr, LengthModeHeight);
402 s_lengthModeMap.set(SVGNames::textLengthAttr, LengthModeWidth);
403 s_lengthModeMap.set(SVGNames::startOffsetAttr, LengthModeWidth);
404 }
405
406 if (s_lengthModeMap.contains(attrName))
407 return s_lengthModeMap.get(attrName);
408
409 return LengthModeOther;
410}
411
412}