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