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