diff -r 000000000000 -r 4f2f89ce4247 WebCore/svg/SVGLength.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/WebCore/svg/SVGLength.cpp Fri Sep 17 09:02:29 2010 +0300 @@ -0,0 +1,324 @@ +/* + Copyright (C) 2004, 2005, 2006 Nikolas Zimmermann + 2004, 2005, 2006, 2007 Rob Buis + 2007 Apple Inc. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config.h" + +#if ENABLE(SVG) +#include "SVGLength.h" + +#include "CSSHelper.h" +#include "FloatConversion.h" +#include "FrameView.h" +#include "RenderObject.h" +#include "RenderView.h" +#include "SVGParserUtilities.h" +#include "SVGSVGElement.h" + +#include +#include + +namespace WebCore { + +// Helper functions +static inline unsigned int storeUnit(SVGLengthMode mode, SVGLengthType type) +{ + return (mode << 4) | type; +} + +static inline SVGLengthMode extractMode(unsigned int unit) +{ + unsigned int mode = unit >> 4; + return static_cast(mode); +} + +static inline SVGLengthType extractType(unsigned int unit) +{ + unsigned int mode = unit >> 4; + unsigned int type = unit ^ (mode << 4); + return static_cast(type); +} + +static inline String lengthTypeToString(SVGLengthType type) +{ + switch (type) { + case LengthTypeUnknown: + case LengthTypeNumber: + return ""; + case LengthTypePercentage: + return "%"; + case LengthTypeEMS: + return "em"; + case LengthTypeEXS: + return "ex"; + case LengthTypePX: + return "px"; + case LengthTypeCM: + return "cm"; + case LengthTypeMM: + return "mm"; + case LengthTypeIN: + return "in"; + case LengthTypePT: + return "pt"; + case LengthTypePC: + return "pc"; + } + + return String(); +} + +inline SVGLengthType stringToLengthType(const String& string) +{ + if (string.endsWith("%")) + return LengthTypePercentage; + else if (string.endsWith("em")) + return LengthTypeEMS; + else if (string.endsWith("ex")) + return LengthTypeEXS; + else if (string.endsWith("px")) + return LengthTypePX; + else if (string.endsWith("cm")) + return LengthTypeCM; + else if (string.endsWith("mm")) + return LengthTypeMM; + else if (string.endsWith("in")) + return LengthTypeIN; + else if (string.endsWith("pt")) + return LengthTypePT; + else if (string.endsWith("pc")) + return LengthTypePC; + else if (!string.isEmpty()) + return LengthTypeNumber; + + return LengthTypeUnknown; +} + +SVGLength::SVGLength(SVGLengthMode mode, const String& valueAsString) + : m_valueInSpecifiedUnits(0.0f) + , m_unit(storeUnit(mode, LengthTypeNumber)) +{ + setValueAsString(valueAsString); +} + +SVGLengthType SVGLength::unitType() const +{ + return extractType(m_unit); +} + +float SVGLength::value(const SVGElement* context) const +{ + SVGLengthType type = extractType(m_unit); + if (type == LengthTypeUnknown) + return 0.0f; + + switch (type) { + case LengthTypeNumber: + return m_valueInSpecifiedUnits; + case LengthTypePercentage: + return SVGLength::PercentageOfViewport(m_valueInSpecifiedUnits / 100.0f, context, extractMode(m_unit)); + case LengthTypeEMS: + case LengthTypeEXS: + { + RenderStyle* style = 0; + if (context && context->renderer()) + style = context->renderer()->style(); + if (style) { + float useSize = style->fontSize(); + ASSERT(useSize > 0); + if (type == LengthTypeEMS) + return m_valueInSpecifiedUnits * useSize; + else { + float xHeight = style->font().xHeight(); + // Use of ceil allows a pixel match to the W3Cs expected output of coords-units-03-b.svg + // if this causes problems in real world cases maybe it would be best to remove this + return m_valueInSpecifiedUnits * ceilf(xHeight); + } + } + return 0.0f; + } + case LengthTypePX: + return m_valueInSpecifiedUnits; + case LengthTypeCM: + return m_valueInSpecifiedUnits / 2.54f * cssPixelsPerInch; + case LengthTypeMM: + return m_valueInSpecifiedUnits / 25.4f * cssPixelsPerInch; + case LengthTypeIN: + return m_valueInSpecifiedUnits * cssPixelsPerInch; + case LengthTypePT: + return m_valueInSpecifiedUnits / 72.0f * cssPixelsPerInch; + case LengthTypePC: + return m_valueInSpecifiedUnits / 6.0f * cssPixelsPerInch; + default: + break; + } + + ASSERT_NOT_REACHED(); + return 0.0f; +} + +void SVGLength::setValue(float value) +{ + SVGLengthType type = extractType(m_unit); + ASSERT(type != LengthTypeUnknown); + + switch (type) { + case LengthTypeNumber: + m_valueInSpecifiedUnits = value; + break; + case LengthTypePercentage: + case LengthTypeEMS: + case LengthTypeEXS: + ASSERT_NOT_REACHED(); + break; + case LengthTypePX: + m_valueInSpecifiedUnits = value; + break; + case LengthTypeCM: + m_valueInSpecifiedUnits = value * 2.54f / cssPixelsPerInch; + break; + case LengthTypeMM: + m_valueInSpecifiedUnits = value * 25.4f / cssPixelsPerInch; + break; + case LengthTypeIN: + m_valueInSpecifiedUnits = value / cssPixelsPerInch; + break; + case LengthTypePT: + m_valueInSpecifiedUnits = value * 72.0f / cssPixelsPerInch; + break; + case LengthTypePC: + m_valueInSpecifiedUnits = value / 6.0f * cssPixelsPerInch; + break; + default: + break; + } +} + +void SVGLength::setValueInSpecifiedUnits(float value) +{ + m_valueInSpecifiedUnits = value; +} + +float SVGLength::valueInSpecifiedUnits() const +{ + return m_valueInSpecifiedUnits; +} + +float SVGLength::valueAsPercentage() const +{ + // 100% = 100.0 instead of 1.0 for historical reasons, this could eventually be changed + if (extractType(m_unit) == LengthTypePercentage) + return valueInSpecifiedUnits() / 100.0f; + + return valueInSpecifiedUnits(); +} + +bool SVGLength::setValueAsString(const String& s) +{ + if (s.isEmpty()) + return false; + + float convertedNumber = 0.0f; + const UChar* ptr = s.characters(); + const UChar* end = ptr + s.length(); + + if (!parseNumber(ptr, end, convertedNumber, false)) + return false; + + SVGLengthType type = stringToLengthType(s); + if (ptr != end && type == LengthTypeNumber) + return false; + + m_unit = storeUnit(extractMode(m_unit), type); + m_valueInSpecifiedUnits = convertedNumber; + return true; +} + +String SVGLength::valueAsString() const +{ + return String::number(m_valueInSpecifiedUnits) + lengthTypeToString(extractType(m_unit)); +} + +void SVGLength::newValueSpecifiedUnits(unsigned short type, float value) +{ + ASSERT(type <= LengthTypePC); + + m_unit = storeUnit(extractMode(m_unit), (SVGLengthType) type); + m_valueInSpecifiedUnits = value; +} + +void SVGLength::convertToSpecifiedUnits(unsigned short type, const SVGElement* context) +{ + ASSERT(type <= LengthTypePC); + + float valueInUserUnits = value(context); + m_unit = storeUnit(extractMode(m_unit), (SVGLengthType) type); + setValue(valueInUserUnits); +} + +float SVGLength::PercentageOfViewport(float value, const SVGElement* context, SVGLengthMode mode) +{ + ASSERT(context); + + float width = 0.0f, height = 0.0f; + SVGElement* viewportElement = context->viewportElement(); + + // PercentageOfViewport() is used to resolve all relative-positioned values within a SVG document (fragment) + Document* doc = context->document(); + if (doc->documentElement() == context) { + // Resolve value against outermost element + if (RenderView* view = toRenderView(doc->renderer())) { + width = view->viewWidth(); + height = view->viewHeight(); + } + } else if (viewportElement && viewportElement->isSVG()) { + // Resolve value against nearest viewport element (common case: inner elements) + const SVGSVGElement* svg = static_cast(viewportElement); + if (svg->hasAttribute(SVGNames::viewBoxAttr)) { + width = svg->viewBox().width(); + height = svg->viewBox().height(); + } else { + width = svg->width().value(svg); + height = svg->height().value(svg); + } + } else if (context->parent() && !context->parent()->isSVGElement()) { + // Resolve value against enclosing non-SVG RenderBox + if (RenderObject* renderer = context->renderer()) { + if (renderer->isBox()) { + RenderBox* box = toRenderBox(renderer); + width = box->width(); + height = box->height(); + } + } + } + + if (mode == LengthModeWidth) + return value * width; + else if (mode == LengthModeHeight) + return value * height; + else if (mode == LengthModeOther) + return value * sqrtf(powf(width, 2) + powf(height, 2)) / sqrtf(2.0f); + + return 0.0f; +} + +} + +#endif