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