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 #if ENABLE(SVG) 25 #include "SVGLength.h" 26 27 #include "CSSHelper.h" 28 #include "FloatConversion.h" 29 #include "FrameView.h" 30 #include "RenderObject.h" 31 #include "RenderView.h" 32 #include "SVGNames.h" 33 #include "SVGParserUtilities.h" 34 #include "SVGSVGElement.h" 35 36 #include <wtf/MathExtras.h> 37 #include <wtf/text/StringConcatenate.h> 38 39 namespace WebCore { 40 41 // Helper functions 42 static inline unsigned int storeUnit(SVGLengthMode mode, SVGLengthType type) 43 { 44 return (mode << 4) | type; 45 } 46 47 static inline SVGLengthMode extractMode(unsigned int unit) 48 { 49 unsigned int mode = unit >> 4; 50 return static_cast<SVGLengthMode>(mode); 51 } 52 53 static inline SVGLengthType extractType(unsigned int unit) 54 { 55 unsigned int mode = unit >> 4; 56 unsigned int type = unit ^ (mode << 4); 57 return static_cast<SVGLengthType>(type); 58 } 59 60 static inline String lengthTypeToString(SVGLengthType type) 61 { 62 switch (type) { 63 case LengthTypeUnknown: 64 case LengthTypeNumber: 65 return ""; 66 case LengthTypePercentage: 67 return "%"; 68 case LengthTypeEMS: 69 return "em"; 70 case LengthTypeEXS: 71 return "ex"; 72 case LengthTypePX: 73 return "px"; 74 case LengthTypeCM: 75 return "cm"; 76 case LengthTypeMM: 77 return "mm"; 78 case LengthTypeIN: 79 return "in"; 80 case LengthTypePT: 81 return "pt"; 82 case LengthTypePC: 83 return "pc"; 84 } 85 86 ASSERT_NOT_REACHED(); 87 return String(); 88 } 89 90 inline SVGLengthType stringToLengthType(const UChar*& ptr, const UChar* end) 91 { 92 if (ptr == end) 93 return LengthTypeNumber; 94 95 const UChar firstChar = *ptr; 96 97 if (++ptr == end) 98 return firstChar == '%' ? LengthTypePercentage : LengthTypeUnknown; 99 100 const UChar secondChar = *ptr; 101 102 if (++ptr != end) 103 return LengthTypeUnknown; 104 105 if (firstChar == 'e' && secondChar == 'm') 106 return LengthTypeEMS; 107 if (firstChar == 'e' && secondChar == 'x') 108 return LengthTypeEXS; 109 if (firstChar == 'p' && secondChar == 'x') 110 return LengthTypePX; 111 if (firstChar == 'c' && secondChar == 'm') 112 return LengthTypeCM; 113 if (firstChar == 'm' && secondChar == 'm') 114 return LengthTypeMM; 115 if (firstChar == 'i' && secondChar == 'n') 116 return LengthTypeIN; 117 if (firstChar == 'p' && secondChar == 't') 118 return LengthTypePT; 119 if (firstChar == 'p' && secondChar == 'c') 120 return LengthTypePC; 121 122 return LengthTypeUnknown; 123 } 124 125 SVGLength::SVGLength(SVGLengthMode mode, const String& valueAsString) 126 : m_valueInSpecifiedUnits(0) 127 , m_unit(storeUnit(mode, LengthTypeNumber)) 128 { 129 ExceptionCode ec = 0; 130 setValueAsString(valueAsString, ec); 131 } 132 133 SVGLength::SVGLength(const SVGLength& other) 134 : m_valueInSpecifiedUnits(other.m_valueInSpecifiedUnits) 135 , m_unit(other.m_unit) 136 { 137 } 138 139 bool SVGLength::operator==(const SVGLength& other) const 140 { 141 return m_unit == other.m_unit 142 && m_valueInSpecifiedUnits == other.m_valueInSpecifiedUnits; 143 } 144 145 bool SVGLength::operator!=(const SVGLength& other) const 146 { 147 return !operator==(other); 148 } 149 150 SVGLengthType SVGLength::unitType() const 151 { 152 return extractType(m_unit); 153 } 154 155 float SVGLength::value(const SVGElement* context) const 156 { 157 ExceptionCode ec = 0; 158 return value(context, ec); 159 } 160 161 float SVGLength::value(const SVGElement* context, ExceptionCode& ec) const 162 { 163 switch (extractType(m_unit)) { 164 case LengthTypeUnknown: 165 ec = NOT_SUPPORTED_ERR; 166 return 0; 167 case LengthTypeNumber: 168 return m_valueInSpecifiedUnits; 169 case LengthTypePercentage: 170 return convertValueFromPercentageToUserUnits(m_valueInSpecifiedUnits / 100, context, ec); 171 case LengthTypeEMS: 172 return convertValueFromEMSToUserUnits(m_valueInSpecifiedUnits, context, ec); 173 case LengthTypeEXS: 174 return convertValueFromEXSToUserUnits(m_valueInSpecifiedUnits, context, ec); 175 case LengthTypePX: 176 return m_valueInSpecifiedUnits; 177 case LengthTypeCM: 178 return m_valueInSpecifiedUnits / 2.54f * cssPixelsPerInch; 179 case LengthTypeMM: 180 return m_valueInSpecifiedUnits / 25.4f * cssPixelsPerInch; 181 case LengthTypeIN: 182 return m_valueInSpecifiedUnits * cssPixelsPerInch; 183 case LengthTypePT: 184 return m_valueInSpecifiedUnits / 72 * cssPixelsPerInch; 185 case LengthTypePC: 186 return m_valueInSpecifiedUnits / 6 * cssPixelsPerInch; 187 } 188 189 ASSERT_NOT_REACHED(); 190 return 0; 191 } 192 193 void SVGLength::setValue(float value, const SVGElement* context, ExceptionCode& ec) 194 { 195 switch (extractType(m_unit)) { 196 case LengthTypeUnknown: 197 ec = NOT_SUPPORTED_ERR; 198 break; 199 case LengthTypeNumber: 200 m_valueInSpecifiedUnits = value; 201 break; 202 case LengthTypePercentage: { 203 float result = convertValueFromUserUnitsToPercentage(value, context, ec); 204 if (!ec) 205 m_valueInSpecifiedUnits = result; 206 break; 207 } 208 case LengthTypeEMS: { 209 float result = convertValueFromUserUnitsToEMS(value, context, ec); 210 if (!ec) 211 m_valueInSpecifiedUnits = result; 212 break; 213 } 214 case LengthTypeEXS: { 215 float result = convertValueFromUserUnitsToEXS(value, context, ec); 216 if (!ec) 217 m_valueInSpecifiedUnits = result; 218 break; 219 } 220 case LengthTypePX: 221 m_valueInSpecifiedUnits = value; 222 break; 223 case LengthTypeCM: 224 m_valueInSpecifiedUnits = value * 2.54f / cssPixelsPerInch; 225 break; 226 case LengthTypeMM: 227 m_valueInSpecifiedUnits = value * 25.4f / cssPixelsPerInch; 228 break; 229 case LengthTypeIN: 230 m_valueInSpecifiedUnits = value / cssPixelsPerInch; 231 break; 232 case LengthTypePT: 233 m_valueInSpecifiedUnits = value * 72 / cssPixelsPerInch; 234 break; 235 case LengthTypePC: 236 m_valueInSpecifiedUnits = value * 6 / cssPixelsPerInch; 237 break; 238 } 239 } 240 241 float SVGLength::valueAsPercentage() const 242 { 243 // 100% = 100.0 instead of 1.0 for historical reasons, this could eventually be changed 244 if (extractType(m_unit) == LengthTypePercentage) 245 return m_valueInSpecifiedUnits / 100; 246 247 return m_valueInSpecifiedUnits; 248 } 249 250 void SVGLength::setValueAsString(const String& string, ExceptionCode& ec) 251 { 252 if (string.isEmpty()) 253 return; 254 255 float convertedNumber = 0; 256 const UChar* ptr = string.characters(); 257 const UChar* end = ptr + string.length(); 258 259 if (!parseNumber(ptr, end, convertedNumber, false)) { 260 ec = SYNTAX_ERR; 261 return; 262 } 263 264 SVGLengthType type = stringToLengthType(ptr, end); 265 ASSERT(ptr <= end); 266 if (type == LengthTypeUnknown) { 267 ec = SYNTAX_ERR; 268 return; 269 } 270 271 m_unit = storeUnit(extractMode(m_unit), type); 272 m_valueInSpecifiedUnits = convertedNumber; 273 } 274 275 String SVGLength::valueAsString() const 276 { 277 return makeString(String::number(m_valueInSpecifiedUnits), lengthTypeToString(extractType(m_unit))); 278 } 279 280 void SVGLength::newValueSpecifiedUnits(unsigned short type, float value, ExceptionCode& ec) 281 { 282 if (type == LengthTypeUnknown || type > LengthTypePC) { 283 ec = NOT_SUPPORTED_ERR; 284 return; 285 } 286 287 m_unit = storeUnit(extractMode(m_unit), static_cast<SVGLengthType>(type)); 288 m_valueInSpecifiedUnits = value; 289 } 290 291 void SVGLength::convertToSpecifiedUnits(unsigned short type, const SVGElement* context, ExceptionCode& ec) 292 { 293 if (type == LengthTypeUnknown || type > LengthTypePC) { 294 ec = NOT_SUPPORTED_ERR; 295 return; 296 } 297 298 float valueInUserUnits = value(context, ec); 299 if (ec) 300 return; 301 302 unsigned int originalUnitAndType = m_unit; 303 m_unit = storeUnit(extractMode(m_unit), static_cast<SVGLengthType>(type)); 304 setValue(valueInUserUnits, context, ec); 305 if (!ec) 306 return; 307 308 // Eventually restore old unit and type 309 m_unit = originalUnitAndType; 310 } 311 312 bool SVGLength::determineViewport(const SVGElement* context, float& width, float& height) const 313 { 314 if (!context) 315 return false; 316 317 // Take size from outermost <svg> element. 318 Document* document = context->document(); 319 if (document->documentElement() == context) { 320 if (RenderView* view = toRenderView(document->renderer())) { 321 width = view->viewWidth(); 322 height = view->viewHeight(); 323 return true; 324 } 325 326 return false; 327 } 328 329 // Resolve value against nearest viewport element (common case: inner <svg> elements) 330 SVGElement* viewportElement = context->viewportElement(); 331 if (viewportElement && viewportElement->isSVG()) { 332 const SVGSVGElement* svg = static_cast<const SVGSVGElement*>(viewportElement); 333 if (svg->hasAttribute(SVGNames::viewBoxAttr)) { 334 width = svg->viewBox().width(); 335 height = svg->viewBox().height(); 336 } else { 337 width = svg->width().value(svg); 338 height = svg->height().value(svg); 339 } 340 341 return true; 342 } 343 344 // Resolve value against enclosing non-SVG RenderBox 345 if (!context->parentNode() || context->parentNode()->isSVGElement()) 346 return false; 347 348 RenderObject* renderer = context->renderer(); 349 if (!renderer || !renderer->isBox()) 350 return false; 351 352 RenderBox* box = toRenderBox(renderer); 353 width = box->width(); 354 height = box->height(); 355 return true; 356 } 357 358 float SVGLength::convertValueFromUserUnitsToPercentage(float value, const SVGElement* context, ExceptionCode& ec) const 359 { 360 float width = 0; 361 float height = 0; 362 if (!determineViewport(context, width, height)) { 363 ec = NOT_SUPPORTED_ERR; 364 return 0; 365 } 366 367 switch (extractMode(m_unit)) { 368 case LengthModeWidth: 369 return value / width * 100; 370 case LengthModeHeight: 371 return value / height * 100; 372 case LengthModeOther: 373 return value / (sqrtf((width * width + height * height) / 2)) * 100; 374 }; 375 376 ASSERT_NOT_REACHED(); 377 return 0; 378 } 379 380 float SVGLength::convertValueFromPercentageToUserUnits(float value, const SVGElement* context, ExceptionCode& ec) const 381 { 382 float width = 0; 383 float height = 0; 384 if (!determineViewport(context, width, height)) { 385 ec = NOT_SUPPORTED_ERR; 386 return 0; 387 } 388 389 switch (extractMode(m_unit)) { 390 case LengthModeWidth: 391 return value * width; 392 case LengthModeHeight: 393 return value * height; 394 case LengthModeOther: 395 return value * sqrtf((width * width + height * height) / 2); 396 }; 397 398 ASSERT_NOT_REACHED(); 399 return 0; 400 } 401 402 float SVGLength::convertValueFromUserUnitsToEMS(float value, const SVGElement* context, ExceptionCode& ec) const 403 { 404 if (!context || !context->renderer() || !context->renderer()->style()) { 405 ec = NOT_SUPPORTED_ERR; 406 return 0; 407 } 408 409 RenderStyle* style = context->renderer()->style(); 410 float fontSize = style->fontSize(); 411 if (!fontSize) { 412 ec = NOT_SUPPORTED_ERR; 413 return 0; 414 } 415 416 return value / fontSize; 417 } 418 419 float SVGLength::convertValueFromEMSToUserUnits(float value, const SVGElement* context, ExceptionCode& ec) const 420 { 421 if (!context || !context->renderer() || !context->renderer()->style()) { 422 ec = NOT_SUPPORTED_ERR; 423 return 0; 424 } 425 426 RenderStyle* style = context->renderer()->style(); 427 return value * style->fontSize(); 428 } 429 430 float SVGLength::convertValueFromUserUnitsToEXS(float value, const SVGElement* context, ExceptionCode& ec) const 431 { 432 if (!context || !context->renderer() || !context->renderer()->style()) { 433 ec = NOT_SUPPORTED_ERR; 434 return 0; 435 } 436 437 RenderStyle* style = context->renderer()->style(); 438 439 // Use of ceil allows a pixel match to the W3Cs expected output of coords-units-03-b.svg 440 // if this causes problems in real world cases maybe it would be best to remove this 441 float xHeight = ceilf(style->fontMetrics().xHeight()); 442 if (!xHeight) { 443 ec = NOT_SUPPORTED_ERR; 444 return 0; 445 } 446 447 return value / xHeight; 448 } 449 450 float SVGLength::convertValueFromEXSToUserUnits(float value, const SVGElement* context, ExceptionCode& ec) const 451 { 452 if (!context || !context->renderer() || !context->renderer()->style()) { 453 ec = NOT_SUPPORTED_ERR; 454 return 0; 455 } 456 457 RenderStyle* style = context->renderer()->style(); 458 // Use of ceil allows a pixel match to the W3Cs expected output of coords-units-03-b.svg 459 // if this causes problems in real world cases maybe it would be best to remove this 460 return value * ceilf(style->fontMetrics().xHeight()); 461 } 462 463 SVGLength SVGLength::fromCSSPrimitiveValue(CSSPrimitiveValue* value) 464 { 465 ASSERT(value); 466 467 SVGLengthType svgType; 468 switch (value->primitiveType()) { 469 case CSSPrimitiveValue::CSS_NUMBER: 470 svgType = LengthTypeNumber; 471 break; 472 case CSSPrimitiveValue::CSS_PERCENTAGE: 473 svgType = LengthTypePercentage; 474 break; 475 case CSSPrimitiveValue::CSS_EMS: 476 svgType = LengthTypeEMS; 477 break; 478 case CSSPrimitiveValue::CSS_EXS: 479 svgType = LengthTypeEXS; 480 break; 481 case CSSPrimitiveValue::CSS_PX: 482 svgType = LengthTypePX; 483 break; 484 case CSSPrimitiveValue::CSS_CM: 485 svgType = LengthTypeCM; 486 break; 487 case CSSPrimitiveValue::CSS_MM: 488 svgType = LengthTypeMM; 489 break; 490 case CSSPrimitiveValue::CSS_IN: 491 svgType = LengthTypeIN; 492 break; 493 case CSSPrimitiveValue::CSS_PT: 494 svgType = LengthTypePT; 495 break; 496 case CSSPrimitiveValue::CSS_PC: 497 svgType = LengthTypePC; 498 break; 499 case CSSPrimitiveValue::CSS_UNKNOWN: 500 default: 501 svgType = LengthTypeUnknown; 502 break; 503 }; 504 505 if (svgType == LengthTypeUnknown) 506 return SVGLength(); 507 508 ExceptionCode ec = 0; 509 SVGLength length; 510 length.newValueSpecifiedUnits(svgType, value->getFloatValue(), ec); 511 if (ec) 512 return SVGLength(); 513 514 return length; 515 } 516 517 PassRefPtr<CSSPrimitiveValue> SVGLength::toCSSPrimitiveValue(const SVGLength& length) 518 { 519 CSSPrimitiveValue::UnitTypes cssType = CSSPrimitiveValue::CSS_UNKNOWN; 520 switch (length.unitType()) { 521 case LengthTypeUnknown: 522 break; 523 case LengthTypeNumber: 524 cssType = CSSPrimitiveValue::CSS_NUMBER; 525 break; 526 case LengthTypePercentage: 527 cssType = CSSPrimitiveValue::CSS_PERCENTAGE; 528 break; 529 case LengthTypeEMS: 530 cssType = CSSPrimitiveValue::CSS_EMS; 531 break; 532 case LengthTypeEXS: 533 cssType = CSSPrimitiveValue::CSS_EXS; 534 break; 535 case LengthTypePX: 536 cssType = CSSPrimitiveValue::CSS_PX; 537 break; 538 case LengthTypeCM: 539 cssType = CSSPrimitiveValue::CSS_CM; 540 break; 541 case LengthTypeMM: 542 cssType = CSSPrimitiveValue::CSS_MM; 543 break; 544 case LengthTypeIN: 545 cssType = CSSPrimitiveValue::CSS_IN; 546 break; 547 case LengthTypePT: 548 cssType = CSSPrimitiveValue::CSS_PT; 549 break; 550 case LengthTypePC: 551 cssType = CSSPrimitiveValue::CSS_PC; 552 break; 553 }; 554 555 return CSSPrimitiveValue::create(length.valueInSpecifiedUnits(), cssType); 556 } 557 558 } 559 560 #endif 561