1/*
2 * Copyright (C) 2004, 2005, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3 * Copyright (C) 2004, 2005, 2006 Rob Buis <buis@kde.org>
4 * Copyright (C) Research In Motion Limited 2010. 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 "SVGAngle.h"
26
27#include "ExceptionCode.h"
28#include "SVGParserUtilities.h"
29#include <wtf/MathExtras.h>
30#include <wtf/text/WTFString.h>
31
32namespace WebCore {
33
34SVGAngle::SVGAngle()
35    : m_unitType(SVG_ANGLETYPE_UNSPECIFIED)
36    , m_valueInSpecifiedUnits(0)
37{
38}
39
40float SVGAngle::value() const
41{
42    switch (m_unitType) {
43    case SVG_ANGLETYPE_GRAD:
44        return grad2deg(m_valueInSpecifiedUnits);
45    case SVG_ANGLETYPE_RAD:
46        return rad2deg(m_valueInSpecifiedUnits);
47    case SVG_ANGLETYPE_UNSPECIFIED:
48    case SVG_ANGLETYPE_UNKNOWN:
49    case SVG_ANGLETYPE_DEG:
50        return m_valueInSpecifiedUnits;
51    }
52
53    ASSERT_NOT_REACHED();
54    return 0;
55}
56
57void SVGAngle::setValue(float value)
58{
59    switch (m_unitType) {
60    case SVG_ANGLETYPE_GRAD:
61        m_valueInSpecifiedUnits = deg2grad(value);
62        break;
63    case SVG_ANGLETYPE_RAD:
64        m_valueInSpecifiedUnits = deg2rad(value);
65        break;
66    case SVG_ANGLETYPE_UNSPECIFIED:
67    case SVG_ANGLETYPE_UNKNOWN:
68    case SVG_ANGLETYPE_DEG:
69        m_valueInSpecifiedUnits = value;
70        break;
71    }
72}
73
74inline SVGAngle::SVGAngleType stringToAngleType(const UChar*& ptr, const UChar* end)
75{
76    // If there's no unit given, the angle type is unspecified.
77    if (ptr == end)
78        return SVGAngle::SVG_ANGLETYPE_UNSPECIFIED;
79
80    const UChar firstChar = *ptr;
81
82    // If the unit contains only one character, the angle type is unknown.
83    ++ptr;
84    if (ptr == end)
85        return SVGAngle::SVG_ANGLETYPE_UNKNOWN;
86
87    const UChar secondChar = *ptr;
88
89    // If the unit contains only two characters, the angle type is unknown.
90    ++ptr;
91    if (ptr == end)
92        return SVGAngle::SVG_ANGLETYPE_UNKNOWN;
93
94    const UChar thirdChar = *ptr;
95    if (firstChar == 'd' && secondChar == 'e' && thirdChar == 'g')
96        return SVGAngle::SVG_ANGLETYPE_DEG;
97    if (firstChar == 'r' && secondChar == 'a' && thirdChar == 'd')
98        return SVGAngle::SVG_ANGLETYPE_RAD;
99
100    // If the unit contains three characters, but is not deg or rad, then it's unknown.
101    ++ptr;
102    if (ptr == end)
103        return SVGAngle::SVG_ANGLETYPE_UNKNOWN;
104
105    const UChar fourthChar = *ptr;
106
107    if (firstChar == 'g' && secondChar == 'r' && thirdChar == 'a' && fourthChar == 'd')
108        return SVGAngle::SVG_ANGLETYPE_GRAD;
109
110    return SVGAngle::SVG_ANGLETYPE_UNKNOWN;
111}
112
113String SVGAngle::valueAsString() const
114{
115    switch (m_unitType) {
116    case SVG_ANGLETYPE_DEG: {
117        DEFINE_STATIC_LOCAL(String, degString, (ASCIILiteral("deg")));
118        return String::number(m_valueInSpecifiedUnits) + degString;
119    }
120    case SVG_ANGLETYPE_RAD: {
121        DEFINE_STATIC_LOCAL(String, radString, (ASCIILiteral("rad")));
122        return String::number(m_valueInSpecifiedUnits) + radString;
123    }
124    case SVG_ANGLETYPE_GRAD: {
125        DEFINE_STATIC_LOCAL(String, gradString, (ASCIILiteral("grad")));
126        return String::number(m_valueInSpecifiedUnits) + gradString;
127    }
128    case SVG_ANGLETYPE_UNSPECIFIED:
129    case SVG_ANGLETYPE_UNKNOWN:
130        return String::number(m_valueInSpecifiedUnits);
131    }
132
133    ASSERT_NOT_REACHED();
134    return String();
135}
136
137void SVGAngle::setValueAsString(const String& value, ExceptionCode& ec)
138{
139    if (value.isEmpty()) {
140        m_unitType = SVG_ANGLETYPE_UNSPECIFIED;
141        return;
142    }
143
144    float valueInSpecifiedUnits = 0;
145    const UChar* ptr = value.characters();
146    const UChar* end = ptr + value.length();
147
148    if (!parseNumber(ptr, end, valueInSpecifiedUnits, false)) {
149        ec = SYNTAX_ERR;
150        return;
151    }
152
153    SVGAngleType unitType = stringToAngleType(ptr, end);
154    if (unitType == SVG_ANGLETYPE_UNKNOWN) {
155        ec = SYNTAX_ERR;
156        return;
157    }
158
159    m_unitType = unitType;
160    m_valueInSpecifiedUnits = valueInSpecifiedUnits;
161}
162
163void SVGAngle::newValueSpecifiedUnits(unsigned short unitType, float valueInSpecifiedUnits, ExceptionCode& ec)
164{
165    if (unitType == SVG_ANGLETYPE_UNKNOWN || unitType > SVG_ANGLETYPE_GRAD) {
166        ec = NOT_SUPPORTED_ERR;
167        return;
168    }
169
170    if (unitType != m_unitType)
171        m_unitType = static_cast<SVGAngleType>(unitType);
172
173    m_valueInSpecifiedUnits = valueInSpecifiedUnits;
174}
175
176void SVGAngle::convertToSpecifiedUnits(unsigned short unitType, ExceptionCode& ec)
177{
178    if (unitType == SVG_ANGLETYPE_UNKNOWN || m_unitType == SVG_ANGLETYPE_UNKNOWN || unitType > SVG_ANGLETYPE_GRAD) {
179        ec = NOT_SUPPORTED_ERR;
180        return;
181    }
182
183    if (unitType == m_unitType)
184        return;
185
186    switch (m_unitType) {
187    case SVG_ANGLETYPE_RAD:
188        switch (unitType) {
189        case SVG_ANGLETYPE_GRAD:
190            m_valueInSpecifiedUnits = rad2grad(m_valueInSpecifiedUnits);
191            break;
192        case SVG_ANGLETYPE_UNSPECIFIED:
193        case SVG_ANGLETYPE_DEG:
194            m_valueInSpecifiedUnits = rad2deg(m_valueInSpecifiedUnits);
195            break;
196        case SVG_ANGLETYPE_RAD:
197        case SVG_ANGLETYPE_UNKNOWN:
198            ASSERT_NOT_REACHED();
199            break;
200        }
201        break;
202    case SVG_ANGLETYPE_GRAD:
203        switch (unitType) {
204        case SVG_ANGLETYPE_RAD:
205            m_valueInSpecifiedUnits = grad2rad(m_valueInSpecifiedUnits);
206            break;
207        case SVG_ANGLETYPE_UNSPECIFIED:
208        case SVG_ANGLETYPE_DEG:
209            m_valueInSpecifiedUnits = grad2deg(m_valueInSpecifiedUnits);
210            break;
211        case SVG_ANGLETYPE_GRAD:
212        case SVG_ANGLETYPE_UNKNOWN:
213            ASSERT_NOT_REACHED();
214            break;
215        }
216        break;
217    case SVG_ANGLETYPE_UNSPECIFIED:
218        // Spec: For angles, a unitless value is treated the same as if degrees were specified.
219    case SVG_ANGLETYPE_DEG:
220        switch (unitType) {
221        case SVG_ANGLETYPE_RAD:
222            m_valueInSpecifiedUnits = deg2rad(m_valueInSpecifiedUnits);
223            break;
224        case SVG_ANGLETYPE_GRAD:
225            m_valueInSpecifiedUnits = deg2grad(m_valueInSpecifiedUnits);
226            break;
227        case SVG_ANGLETYPE_UNSPECIFIED:
228            break;
229        case SVG_ANGLETYPE_DEG:
230        case SVG_ANGLETYPE_UNKNOWN:
231            ASSERT_NOT_REACHED();
232            break;
233        }
234        break;
235    case SVG_ANGLETYPE_UNKNOWN:
236        ASSERT_NOT_REACHED();
237        break;
238    }
239
240    m_unitType = static_cast<SVGAngleType>(unitType);
241}
242
243}
244
245#endif // ENABLE(SVG)
246