WebCore/svg/SVGLength.cpp
changeset 0 4f2f89ce4247
--- /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 <zimmermann@kde.org>
+                  2004, 2005, 2006, 2007 Rob Buis <buis@kde.org>
+                  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 <math.h>
+#include <wtf/Assertions.h>
+
+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<SVGLengthMode>(mode);
+}
+
+static inline SVGLengthType extractType(unsigned int unit)
+{
+    unsigned int mode = unit >> 4;
+    unsigned int type = unit ^ (mode << 4);
+    return static_cast<SVGLengthType>(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 <svg> 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 <svg> elements)
+        const SVGSVGElement* svg = static_cast<const SVGSVGElement*>(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