SVGLength.cpp   [plain text]


/*
    Copyright (C) 2004, 2005 Nikolas Zimmermann <wildfox@kde.org>
                  2004, 2005 Rob Buis <buis@kde.org>

    This file is part of the KDE project

    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., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.
*/

#include "config.h"
#if SVG_SUPPORT
#include "IntRect.h"

#include <kcanvas/RenderPath.h>
#include "KCanvasRenderingStyle.h"

#include "ksvg.h"
#include "svgpathparser.h"
#include "SVGLength.h"
#include "SVGElement.h"
#include "SVGSVGElement.h"
#include "SVGAnimatedRect.h"
#include "SVGAnimatedLength.h"

#include <math.h>

using namespace WebCore;
using namespace std;

// keep track of textual description of the unit type
static const char* UnitText[] =
{
    "", "",
    "%", "em",
    "ex", "px",
    "cm", "mm",
    "in", "pt",
    "pc"
};

SVGLength::SVGLength(const SVGStyledElement *context, LengthMode mode, const SVGElement *viewport)
    : Shared<SVGLength>()
    , m_value(0)
    , m_valueInSpecifiedUnits(0)
    , m_mode(mode)
    , m_bboxRelative(false)
    , m_unitType(SVG_LENGTHTYPE_UNKNOWN)
    , m_requiresLayout(false)
    , m_context(context)
    , m_viewportElement(viewport)
{
}

SVGLength::~SVGLength()
{
}

unsigned short SVGLength::unitType() const
{
    return m_unitType;
}

void SVGLength::setValue(float value)
{
    m_value = value;
    updateValueInSpecifiedUnits();
}

float SVGLength::value() const
{
    if (m_requiresLayout)
        const_cast<SVGLength*>(this)->updateValue(false);

    if (m_unitType != SVG_LENGTHTYPE_PERCENTAGE)
        return m_value;

    float value = m_valueInSpecifiedUnits / 100.0;
    if (m_bboxRelative)
        return value;

    // Use the manual override "m_viewportElement" when there is no context element off of which to establish the viewport.
    return SVGHelper::PercentageOfViewport(value, m_context ? m_context->viewportElement() : m_viewportElement, static_cast<LengthMode>(m_mode));
}

void SVGLength::setValueInSpecifiedUnits(float valueInSpecifiedUnits)
{
    m_valueInSpecifiedUnits = valueInSpecifiedUnits;
    updateValue();
}

float SVGLength::valueInSpecifiedUnits() const
{
    return m_valueInSpecifiedUnits;
}                                                

void SVGLength::setValueAsString(const String& s)
{
    if(s.isEmpty())
        return;

    DeprecatedString valueAsQString = s.deprecatedString();

    double convertedNumber = 0;
    const char *start = valueAsQString.latin1();
    const char *end = parseCoord(start, convertedNumber);
    m_valueInSpecifiedUnits = convertedNumber;

    unsigned int diff = end - start;
    if (diff < valueAsQString.length()) {
        if (s.endsWith(UnitText[SVG_LENGTHTYPE_PX]))
            m_unitType = SVG_LENGTHTYPE_PX;
        else if (s.endsWith(UnitText[SVG_LENGTHTYPE_CM]))
            m_unitType = SVG_LENGTHTYPE_CM;
        else if (s.endsWith(UnitText[SVG_LENGTHTYPE_PC]))
            m_unitType = SVG_LENGTHTYPE_PC;
        else if (s.endsWith(UnitText[SVG_LENGTHTYPE_MM]))
            m_unitType = SVG_LENGTHTYPE_MM;
        else if (s.endsWith(UnitText[SVG_LENGTHTYPE_IN]))
            m_unitType = SVG_LENGTHTYPE_IN;
        else if (s.endsWith(UnitText[SVG_LENGTHTYPE_PT]))
            m_unitType = SVG_LENGTHTYPE_PT;
         else if (s.endsWith(UnitText[SVG_LENGTHTYPE_PERCENTAGE]))
            m_unitType = SVG_LENGTHTYPE_PERCENTAGE;
        else if (s.endsWith(UnitText[SVG_LENGTHTYPE_EMS]))
            m_unitType = SVG_LENGTHTYPE_EMS;
        else if (s.endsWith(UnitText[SVG_LENGTHTYPE_EXS]))
            m_unitType = SVG_LENGTHTYPE_EXS;
        else if (s.isEmpty())
            m_unitType = SVG_LENGTHTYPE_NUMBER;
        else
            m_unitType = SVG_LENGTHTYPE_UNKNOWN;
    }
    else
        m_unitType = SVG_LENGTHTYPE_PX;

    updateValue();
}

String SVGLength::valueAsString() const
{
    return String::number(m_valueInSpecifiedUnits) + UnitText[m_unitType];
}

void SVGLength::newValueSpecifiedUnits(unsigned short unitType, float valueInSpecifiedUnits)
{
    m_valueInSpecifiedUnits = valueInSpecifiedUnits;
    m_unitType = unitType;
    updateValue();
}

void SVGLength::convertToSpecifiedUnits(unsigned short unitType)
{
    m_unitType = unitType;
    updateValueInSpecifiedUnits();
}

double SVGLength::dpi() const
{
    /* FIXME: DPI detection
    if(context && context->ownerDoc())
    {
        if(mode == LM_WIDTH)
            return 25.4 * context->ownerDoc()->screenPixelsPerMillimeterX();
        else if(mode == LM_HEIGHT)
            return 25.4 * context->ownerDoc()->screenPixelsPerMillimeterY();
        else if(mode == LM_OTHER)
            return 25.4 * context->ownerDoc()->screenPixelsPerMillimeterX();
    }
    */

    return 90.0;
}

void SVGLength::updateValue(bool notify)
{
    switch(m_unitType)
    {
        case SVG_LENGTHTYPE_PX:
            m_value = m_valueInSpecifiedUnits;
            break;
        case SVG_LENGTHTYPE_CM:
            m_value = (m_valueInSpecifiedUnits / 2.54) * dpi();
            break;
        case SVG_LENGTHTYPE_MM:
            m_value = (m_valueInSpecifiedUnits / 25.4) * dpi();
            break;
        case SVG_LENGTHTYPE_IN:
            m_value = m_valueInSpecifiedUnits * dpi();
            break;
        case SVG_LENGTHTYPE_PT:
            m_value = (m_valueInSpecifiedUnits / 72.0) * dpi();
            break;
        case SVG_LENGTHTYPE_PC:
            m_value = (m_valueInSpecifiedUnits / 6.0) * dpi();
            break;
        case SVG_LENGTHTYPE_EMS:
        case SVG_LENGTHTYPE_EXS:
            if (m_context && m_context->renderer()) {
                RenderStyle *style = m_context->renderer()->style();
                float useSize = style->fontSize();
                ASSERT(useSize > 0);
                if (m_unitType == SVG_LENGTHTYPE_EMS)
                    m_value = 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
                    m_value = m_valueInSpecifiedUnits * ceil(xHeight);
                }
                m_requiresLayout = false;
            } else {
                m_requiresLayout = true;
            }
            break;
    }
    if (notify && m_context)
        m_context->notifyAttributeChange();
}

bool SVGLength::updateValueInSpecifiedUnits(bool notify)
{
    if(m_unitType == SVG_LENGTHTYPE_UNKNOWN)
        return false;

    switch(m_unitType)
    {
        case SVG_LENGTHTYPE_PERCENTAGE:
            //kdError() << "updateValueInSpecifiedUnits() SVG_LENGTHTYPE_PERCENTAGE - UNSUPPORTED! Please report!" << endl;
            return false;
        case SVG_LENGTHTYPE_EMS:
            //kdError() << "updateValueInSpecifiedUnits() SVG_LENGTHTYPE_EMS - UNSUPPORTED! Please report!" << endl;
            return false;
        case SVG_LENGTHTYPE_EXS:
            //kdError() << "updateValueInSpecifiedUnits() SVG_LENGTHTYPE_EXS - UNSUPPORTED! Please report!" << endl;
            return false;
        case SVG_LENGTHTYPE_PX:
            m_valueInSpecifiedUnits = m_value;
            break;
        case SVG_LENGTHTYPE_CM:
            m_valueInSpecifiedUnits = m_value / dpi() * 2.54;
            break;
        case SVG_LENGTHTYPE_MM:
            m_valueInSpecifiedUnits = m_value / dpi() * 25.4;
            break;
        case SVG_LENGTHTYPE_IN:
            m_valueInSpecifiedUnits = m_value / dpi();
            break;
        case SVG_LENGTHTYPE_PT:
            m_valueInSpecifiedUnits = m_value / dpi() * 72.0;
            break;
        case SVG_LENGTHTYPE_PC:
            m_valueInSpecifiedUnits = m_value / dpi() * 6.0;
            break;
    };
    
    if (notify && m_context)
        m_context->notifyAttributeChange();

    return true;
}

bool SVGLength::bboxRelative() const
{
    return m_bboxRelative;
}

void SVGLength::setBboxRelative(bool relative)
{
    m_bboxRelative = relative;
}

const SVGStyledElement *SVGLength::context() const
{
    return m_context;
}

void SVGLength::setContext(const SVGStyledElement *context)
{
    m_context = context;
}

// vim:ts=4:noet
#endif // SVG_SUPPORT