1 /* 2 * Copyright (C) 2004, 2005, 2006 Nikolas Zimmermann <zimmermann (at) kde.org> 3 * Copyright (C) 2004, 2005, 2006, 2007 Rob Buis <buis (at) 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 24 #include "core/svg/SVGLength.h" 25 26 #include "bindings/core/v8/ExceptionState.h" 27 #include "core/SVGNames.h" 28 #include "core/css/CSSPrimitiveValue.h" 29 #include "core/dom/ExceptionCode.h" 30 #include "core/svg/SVGAnimationElement.h" 31 #include "core/svg/SVGParserUtilities.h" 32 #include "platform/animation/AnimationUtilities.h" 33 #include "wtf/MathExtras.h" 34 #include "wtf/text/WTFString.h" 35 36 namespace blink { 37 38 namespace { 39 40 inline const char* lengthTypeToString(SVGLengthType type) 41 { 42 switch (type) { 43 case LengthTypeUnknown: 44 case LengthTypeNumber: 45 return ""; 46 case LengthTypePercentage: 47 return "%"; 48 case LengthTypeEMS: 49 return "em"; 50 case LengthTypeEXS: 51 return "ex"; 52 case LengthTypePX: 53 return "px"; 54 case LengthTypeCM: 55 return "cm"; 56 case LengthTypeMM: 57 return "mm"; 58 case LengthTypeIN: 59 return "in"; 60 case LengthTypePT: 61 return "pt"; 62 case LengthTypePC: 63 return "pc"; 64 } 65 66 ASSERT_NOT_REACHED(); 67 return ""; 68 } 69 70 template<typename CharType> 71 SVGLengthType stringToLengthType(const CharType*& ptr, const CharType* end) 72 { 73 if (ptr == end) 74 return LengthTypeNumber; 75 76 SVGLengthType type = LengthTypeUnknown; 77 const CharType firstChar = *ptr++; 78 79 if (firstChar == '%') { 80 type = LengthTypePercentage; 81 } else if (isHTMLSpace<CharType>(firstChar)) { 82 type = LengthTypeNumber; 83 } else if (ptr < end) { 84 const CharType secondChar = *ptr++; 85 86 if (firstChar == 'p') { 87 if (secondChar == 'x') 88 type = LengthTypePX; 89 if (secondChar == 't') 90 type = LengthTypePT; 91 if (secondChar == 'c') 92 type = LengthTypePC; 93 } else if (firstChar == 'e') { 94 if (secondChar == 'm') 95 type = LengthTypeEMS; 96 if (secondChar == 'x') 97 type = LengthTypeEXS; 98 } else if (firstChar == 'c' && secondChar == 'm') { 99 type = LengthTypeCM; 100 } else if (firstChar == 'm' && secondChar == 'm') { 101 type = LengthTypeMM; 102 } else if (firstChar == 'i' && secondChar == 'n') { 103 type = LengthTypeIN; 104 } else if (isHTMLSpace<CharType>(firstChar) && isHTMLSpace<CharType>(secondChar)) { 105 type = LengthTypeNumber; 106 } 107 } 108 109 if (!skipOptionalSVGSpaces(ptr, end)) 110 return type; 111 112 return LengthTypeUnknown; 113 } 114 115 } // namespace 116 117 SVGLength::SVGLength(SVGLengthMode mode) 118 : SVGPropertyBase(classType()) 119 , m_valueInSpecifiedUnits(0) 120 , m_unitMode(mode) 121 , m_unitType(LengthTypeNumber) 122 { 123 } 124 125 SVGLength::SVGLength(const SVGLength& o) 126 : SVGPropertyBase(classType()) 127 , m_valueInSpecifiedUnits(o.m_valueInSpecifiedUnits) 128 , m_unitMode(o.m_unitMode) 129 , m_unitType(o.m_unitType) 130 { 131 } 132 133 PassRefPtr<SVGLength> SVGLength::clone() const 134 { 135 return adoptRef(new SVGLength(*this)); 136 } 137 138 PassRefPtr<SVGPropertyBase> SVGLength::cloneForAnimation(const String& value) const 139 { 140 RefPtr<SVGLength> length = create(); 141 142 length->m_unitMode = m_unitMode; 143 length->m_unitType = m_unitType; 144 145 TrackExceptionState exceptionState; 146 length->setValueAsString(value, exceptionState); 147 if (exceptionState.hadException()) { 148 length->m_unitType = LengthTypeNumber; 149 length->m_valueInSpecifiedUnits = 0; 150 } 151 152 return length.release(); 153 } 154 155 bool SVGLength::operator==(const SVGLength& other) const 156 { 157 return m_unitMode == other.m_unitMode 158 && m_unitType == other.m_unitType 159 && m_valueInSpecifiedUnits == other.m_valueInSpecifiedUnits; 160 } 161 162 float SVGLength::value(const SVGLengthContext& context, ExceptionState& es) const 163 { 164 return context.convertValueToUserUnits(m_valueInSpecifiedUnits, unitMode(), unitType(), es); 165 } 166 167 void SVGLength::setValue(float value, const SVGLengthContext& context, ExceptionState& es) 168 { 169 // 100% = 100.0 instead of 1.0 for historical reasons, this could eventually be changed 170 if (m_unitType == LengthTypePercentage) 171 value = value / 100; 172 173 float convertedValue = context.convertValueFromUserUnits(value, unitMode(), unitType(), es); 174 if (es.hadException()) 175 return; 176 177 m_valueInSpecifiedUnits = convertedValue; 178 } 179 180 void SVGLength::setUnitType(SVGLengthType type) 181 { 182 ASSERT(type != LengthTypeUnknown && type <= LengthTypePC); 183 m_unitType = type; 184 } 185 186 float SVGLength::valueAsPercentage() const 187 { 188 // 100% = 100.0 instead of 1.0 for historical reasons, this could eventually be changed 189 if (m_unitType == LengthTypePercentage) 190 return m_valueInSpecifiedUnits / 100; 191 192 return m_valueInSpecifiedUnits; 193 } 194 195 template<typename CharType> 196 static bool parseValueInternal(const String& string, float& convertedNumber, SVGLengthType& type) 197 { 198 const CharType* ptr = string.getCharacters<CharType>(); 199 const CharType* end = ptr + string.length(); 200 201 if (!parseNumber(ptr, end, convertedNumber, AllowLeadingWhitespace)) 202 return false; 203 204 type = stringToLengthType(ptr, end); 205 ASSERT(ptr <= end); 206 if (type == LengthTypeUnknown) 207 return false; 208 209 return true; 210 } 211 212 void SVGLength::setValueAsString(const String& string, ExceptionState& exceptionState) 213 { 214 if (string.isEmpty()) { 215 m_unitType = LengthTypeNumber; 216 m_valueInSpecifiedUnits = 0; 217 return; 218 } 219 220 float convertedNumber = 0; 221 SVGLengthType type = LengthTypeUnknown; 222 223 bool success = string.is8Bit() ? 224 parseValueInternal<LChar>(string, convertedNumber, type) : 225 parseValueInternal<UChar>(string, convertedNumber, type); 226 227 if (!success) { 228 exceptionState.throwDOMException(SyntaxError, "The value provided ('" + string + "') is invalid."); 229 return; 230 } 231 232 m_unitType = type; 233 m_valueInSpecifiedUnits = convertedNumber; 234 } 235 236 String SVGLength::valueAsString() const 237 { 238 return String::number(m_valueInSpecifiedUnits) + lengthTypeToString(unitType()); 239 } 240 241 void SVGLength::newValueSpecifiedUnits(SVGLengthType type, float value) 242 { 243 setUnitType(type); 244 m_valueInSpecifiedUnits = value; 245 } 246 247 void SVGLength::convertToSpecifiedUnits(SVGLengthType type, const SVGLengthContext& context, ExceptionState& exceptionState) 248 { 249 ASSERT(type != LengthTypeUnknown && type <= LengthTypePC); 250 251 float valueInUserUnits = value(context, exceptionState); 252 if (exceptionState.hadException()) 253 return; 254 255 SVGLengthType originalType = unitType(); 256 m_unitType = type; 257 setValue(valueInUserUnits, context, exceptionState); 258 if (!exceptionState.hadException()) 259 return; 260 261 // Eventually restore old unit and type 262 m_unitType = originalType; 263 } 264 265 PassRefPtr<SVGLength> SVGLength::fromCSSPrimitiveValue(CSSPrimitiveValue* value) 266 { 267 ASSERT(value); 268 269 SVGLengthType svgType; 270 switch (value->primitiveType()) { 271 case CSSPrimitiveValue::CSS_NUMBER: 272 svgType = LengthTypeNumber; 273 break; 274 case CSSPrimitiveValue::CSS_PERCENTAGE: 275 svgType = LengthTypePercentage; 276 break; 277 case CSSPrimitiveValue::CSS_EMS: 278 svgType = LengthTypeEMS; 279 break; 280 case CSSPrimitiveValue::CSS_EXS: 281 svgType = LengthTypeEXS; 282 break; 283 case CSSPrimitiveValue::CSS_PX: 284 svgType = LengthTypePX; 285 break; 286 case CSSPrimitiveValue::CSS_CM: 287 svgType = LengthTypeCM; 288 break; 289 case CSSPrimitiveValue::CSS_MM: 290 svgType = LengthTypeMM; 291 break; 292 case CSSPrimitiveValue::CSS_IN: 293 svgType = LengthTypeIN; 294 break; 295 case CSSPrimitiveValue::CSS_PT: 296 svgType = LengthTypePT; 297 break; 298 default: 299 ASSERT(value->primitiveType() == CSSPrimitiveValue::CSS_PC); 300 svgType = LengthTypePC; 301 break; 302 }; 303 304 RefPtr<SVGLength> length = SVGLength::create(); 305 length->newValueSpecifiedUnits(svgType, value->getFloatValue()); 306 return length.release(); 307 } 308 309 PassRefPtrWillBeRawPtr<CSSPrimitiveValue> SVGLength::toCSSPrimitiveValue(PassRefPtr<SVGLength> passLength) 310 { 311 RefPtr<SVGLength> length = passLength; 312 313 CSSPrimitiveValue::UnitType cssType = CSSPrimitiveValue::CSS_UNKNOWN; 314 switch (length->unitType()) { 315 case LengthTypeUnknown: 316 break; 317 case LengthTypeNumber: 318 cssType = CSSPrimitiveValue::CSS_NUMBER; 319 break; 320 case LengthTypePercentage: 321 cssType = CSSPrimitiveValue::CSS_PERCENTAGE; 322 break; 323 case LengthTypeEMS: 324 cssType = CSSPrimitiveValue::CSS_EMS; 325 break; 326 case LengthTypeEXS: 327 cssType = CSSPrimitiveValue::CSS_EXS; 328 break; 329 case LengthTypePX: 330 cssType = CSSPrimitiveValue::CSS_PX; 331 break; 332 case LengthTypeCM: 333 cssType = CSSPrimitiveValue::CSS_CM; 334 break; 335 case LengthTypeMM: 336 cssType = CSSPrimitiveValue::CSS_MM; 337 break; 338 case LengthTypeIN: 339 cssType = CSSPrimitiveValue::CSS_IN; 340 break; 341 case LengthTypePT: 342 cssType = CSSPrimitiveValue::CSS_PT; 343 break; 344 case LengthTypePC: 345 cssType = CSSPrimitiveValue::CSS_PC; 346 break; 347 }; 348 349 return CSSPrimitiveValue::create(length->valueInSpecifiedUnits(), cssType); 350 } 351 352 SVGLengthMode SVGLength::lengthModeForAnimatedLengthAttribute(const QualifiedName& attrName) 353 { 354 typedef HashMap<QualifiedName, SVGLengthMode> LengthModeForLengthAttributeMap; 355 DEFINE_STATIC_LOCAL(LengthModeForLengthAttributeMap, s_lengthModeMap, ()); 356 357 if (s_lengthModeMap.isEmpty()) { 358 s_lengthModeMap.set(SVGNames::xAttr, LengthModeWidth); 359 s_lengthModeMap.set(SVGNames::yAttr, LengthModeHeight); 360 s_lengthModeMap.set(SVGNames::cxAttr, LengthModeWidth); 361 s_lengthModeMap.set(SVGNames::cyAttr, LengthModeHeight); 362 s_lengthModeMap.set(SVGNames::dxAttr, LengthModeWidth); 363 s_lengthModeMap.set(SVGNames::dyAttr, LengthModeHeight); 364 s_lengthModeMap.set(SVGNames::fxAttr, LengthModeWidth); 365 s_lengthModeMap.set(SVGNames::fyAttr, LengthModeHeight); 366 s_lengthModeMap.set(SVGNames::rAttr, LengthModeOther); 367 s_lengthModeMap.set(SVGNames::rxAttr, LengthModeWidth); 368 s_lengthModeMap.set(SVGNames::ryAttr, LengthModeHeight); 369 s_lengthModeMap.set(SVGNames::widthAttr, LengthModeWidth); 370 s_lengthModeMap.set(SVGNames::heightAttr, LengthModeHeight); 371 s_lengthModeMap.set(SVGNames::x1Attr, LengthModeWidth); 372 s_lengthModeMap.set(SVGNames::x2Attr, LengthModeWidth); 373 s_lengthModeMap.set(SVGNames::y1Attr, LengthModeHeight); 374 s_lengthModeMap.set(SVGNames::y2Attr, LengthModeHeight); 375 s_lengthModeMap.set(SVGNames::refXAttr, LengthModeWidth); 376 s_lengthModeMap.set(SVGNames::refYAttr, LengthModeHeight); 377 s_lengthModeMap.set(SVGNames::markerWidthAttr, LengthModeWidth); 378 s_lengthModeMap.set(SVGNames::markerHeightAttr, LengthModeHeight); 379 s_lengthModeMap.set(SVGNames::textLengthAttr, LengthModeWidth); 380 s_lengthModeMap.set(SVGNames::startOffsetAttr, LengthModeWidth); 381 } 382 383 if (s_lengthModeMap.contains(attrName)) 384 return s_lengthModeMap.get(attrName); 385 386 return LengthModeOther; 387 } 388 389 PassRefPtr<SVGLength> SVGLength::blend(PassRefPtr<SVGLength> passFrom, float progress) const 390 { 391 RefPtr<SVGLength> from = passFrom; 392 393 SVGLengthType toType = unitType(); 394 SVGLengthType fromType = from->unitType(); 395 if ((from->isZero() && isZero()) 396 || fromType == LengthTypeUnknown 397 || toType == LengthTypeUnknown 398 || (!from->isZero() && fromType != LengthTypePercentage && toType == LengthTypePercentage) 399 || (!isZero() && fromType == LengthTypePercentage && toType != LengthTypePercentage) 400 || (!from->isZero() && !isZero() && (fromType == LengthTypeEMS || fromType == LengthTypeEXS) && fromType != toType)) 401 return clone(); 402 403 RefPtr<SVGLength> length = create(); 404 405 if (fromType == LengthTypePercentage || toType == LengthTypePercentage) { 406 float fromPercent = from->valueAsPercentage() * 100; 407 float toPercent = valueAsPercentage() * 100; 408 length->newValueSpecifiedUnits(LengthTypePercentage, blink::blend(fromPercent, toPercent, progress)); 409 return length; 410 } 411 412 if (fromType == toType || from->isZero() || isZero() || fromType == LengthTypeEMS || fromType == LengthTypeEXS) { 413 float fromValue = from->valueInSpecifiedUnits(); 414 float toValue = valueInSpecifiedUnits(); 415 if (isZero()) 416 length->newValueSpecifiedUnits(fromType, blink::blend(fromValue, toValue, progress)); 417 else 418 length->newValueSpecifiedUnits(toType, blink::blend(fromValue, toValue, progress)); 419 return length; 420 } 421 422 ASSERT(!isRelative()); 423 ASSERT(!from->isRelative()); 424 425 TrackExceptionState es; 426 SVGLengthContext nonRelativeLengthContext(0); 427 float fromValueInUserUnits = nonRelativeLengthContext.convertValueToUserUnits(from->valueInSpecifiedUnits(), from->unitMode(), fromType, es); 428 if (es.hadException()) 429 return create(); 430 431 float fromValue = nonRelativeLengthContext.convertValueFromUserUnits(fromValueInUserUnits, unitMode(), toType, es); 432 if (es.hadException()) 433 return create(); 434 435 float toValue = valueInSpecifiedUnits(); 436 length->newValueSpecifiedUnits(toType, blink::blend(fromValue, toValue, progress)); 437 return length; 438 } 439 440 void SVGLength::add(PassRefPtrWillBeRawPtr<SVGPropertyBase> other, SVGElement* contextElement) 441 { 442 SVGLengthContext lengthContext(contextElement); 443 444 setValue(value(lengthContext) + toSVGLength(other)->value(lengthContext), lengthContext, ASSERT_NO_EXCEPTION); 445 } 446 447 void SVGLength::calculateAnimatedValue(SVGAnimationElement* animationElement, float percentage, unsigned repeatCount, PassRefPtr<SVGPropertyBase> fromValue, PassRefPtr<SVGPropertyBase> toValue, PassRefPtr<SVGPropertyBase> toAtEndOfDurationValue, SVGElement* contextElement) 448 { 449 RefPtr<SVGLength> fromLength = toSVGLength(fromValue); 450 RefPtr<SVGLength> toLength = toSVGLength(toValue); 451 RefPtr<SVGLength> toAtEndOfDurationLength = toSVGLength(toAtEndOfDurationValue); 452 453 SVGLengthContext lengthContext(contextElement); 454 float animatedNumber = value(lengthContext, IGNORE_EXCEPTION); 455 animationElement->animateAdditiveNumber(percentage, repeatCount, fromLength->value(lengthContext, IGNORE_EXCEPTION), toLength->value(lengthContext, IGNORE_EXCEPTION), toAtEndOfDurationLength->value(lengthContext, IGNORE_EXCEPTION), animatedNumber); 456 457 ASSERT(unitMode() == lengthModeForAnimatedLengthAttribute(animationElement->attributeName())); 458 m_unitType = percentage < 0.5 ? fromLength->unitType() : toLength->unitType(); 459 setValue(animatedNumber, lengthContext, ASSERT_NO_EXCEPTION); 460 } 461 462 float SVGLength::calculateDistance(PassRefPtr<SVGPropertyBase> toValue, SVGElement* contextElement) 463 { 464 SVGLengthContext lengthContext(contextElement); 465 RefPtr<SVGLength> toLength = toSVGLength(toValue); 466 467 return fabsf(toLength->value(lengthContext, IGNORE_EXCEPTION) - value(lengthContext, IGNORE_EXCEPTION)); 468 } 469 470 } 471