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