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