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