Home | History | Annotate | Download | only in svg
      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 "SVGNames.h"
     27 #include "bindings/v8/ExceptionState.h"
     28 #include "bindings/v8/ExceptionStatePlaceholder.h"
     29 #include "core/css/CSSPrimitiveValue.h"
     30 #include "core/dom/ExceptionCode.h"
     31 #include "core/svg/SVGParserUtilities.h"
     32 #include "wtf/MathExtras.h"
     33 #include "wtf/text/WTFString.h"
     34 
     35 namespace WebCore {
     36 
     37 static inline unsigned int storeUnit(SVGLengthMode mode, SVGLengthType type)
     38 {
     39     return (mode << 4) | type;
     40 }
     41 
     42 static inline SVGLengthMode extractMode(unsigned int unit)
     43 {
     44     unsigned int mode = unit >> 4;
     45     return static_cast<SVGLengthMode>(mode);
     46 }
     47 
     48 static inline SVGLengthType extractType(unsigned int unit)
     49 {
     50     unsigned int mode = unit >> 4;
     51     unsigned int type = unit ^ (mode << 4);
     52     return static_cast<SVGLengthType>(type);
     53 }
     54 
     55 static inline String lengthTypeToString(SVGLengthType type)
     56 {
     57     switch (type) {
     58     case LengthTypeUnknown:
     59     case LengthTypeNumber:
     60         return "";
     61     case LengthTypePercentage:
     62         return "%";
     63     case LengthTypeEMS:
     64         return "em";
     65     case LengthTypeEXS:
     66         return "ex";
     67     case LengthTypePX:
     68         return "px";
     69     case LengthTypeCM:
     70         return "cm";
     71     case LengthTypeMM:
     72         return "mm";
     73     case LengthTypeIN:
     74         return "in";
     75     case LengthTypePT:
     76         return "pt";
     77     case LengthTypePC:
     78         return "pc";
     79     }
     80 
     81     ASSERT_NOT_REACHED();
     82     return String();
     83 }
     84 
     85 template<typename CharType>
     86 static SVGLengthType stringToLengthType(const CharType*& ptr, const CharType* end)
     87 {
     88     if (ptr == end)
     89         return LengthTypeNumber;
     90 
     91     const UChar firstChar = *ptr;
     92 
     93     if (++ptr == end)
     94         return firstChar == '%' ? LengthTypePercentage : LengthTypeUnknown;
     95 
     96     const UChar secondChar = *ptr;
     97 
     98     if (++ptr != end)
     99         return LengthTypeUnknown;
    100 
    101     if (firstChar == 'e' && secondChar == 'm')
    102         return LengthTypeEMS;
    103     if (firstChar == 'e' && secondChar == 'x')
    104         return LengthTypeEXS;
    105     if (firstChar == 'p' && secondChar == 'x')
    106         return LengthTypePX;
    107     if (firstChar == 'c' && secondChar == 'm')
    108         return LengthTypeCM;
    109     if (firstChar == 'm' && secondChar == 'm')
    110         return LengthTypeMM;
    111     if (firstChar == 'i' && secondChar == 'n')
    112         return LengthTypeIN;
    113     if (firstChar == 'p' && secondChar == 't')
    114         return LengthTypePT;
    115     if (firstChar == 'p' && secondChar == 'c')
    116         return LengthTypePC;
    117 
    118     return LengthTypeUnknown;
    119 }
    120 
    121 SVGLength::SVGLength(SVGLengthMode mode, const String& valueAsString)
    122     : m_valueInSpecifiedUnits(0)
    123     , m_unit(storeUnit(mode, LengthTypeNumber))
    124 {
    125     setValueAsString(valueAsString, IGNORE_EXCEPTION);
    126 }
    127 
    128 SVGLength::SVGLength(const SVGLengthContext& context, float value, SVGLengthMode mode, SVGLengthType unitType)
    129     : m_valueInSpecifiedUnits(0)
    130     , m_unit(storeUnit(mode, unitType))
    131 {
    132     setValue(value, context, ASSERT_NO_EXCEPTION);
    133 }
    134 
    135 SVGLength::SVGLength(const SVGLength& other)
    136     : m_valueInSpecifiedUnits(other.m_valueInSpecifiedUnits)
    137     , m_unit(other.m_unit)
    138 {
    139 }
    140 
    141 void SVGLength::setValueAsString(const String& valueAsString, SVGLengthMode mode, ExceptionState& es)
    142 {
    143     m_valueInSpecifiedUnits = 0;
    144     m_unit = storeUnit(mode, LengthTypeNumber);
    145     setValueAsString(valueAsString, es);
    146 }
    147 
    148 bool SVGLength::operator==(const SVGLength& other) const
    149 {
    150     return m_unit == other.m_unit
    151         && m_valueInSpecifiedUnits == other.m_valueInSpecifiedUnits;
    152 }
    153 
    154 bool SVGLength::operator!=(const SVGLength& other) const
    155 {
    156     return !operator==(other);
    157 }
    158 
    159 SVGLength SVGLength::construct(SVGLengthMode mode, const String& valueAsString, SVGParsingError& parseError, SVGLengthNegativeValuesMode negativeValuesMode)
    160 {
    161     TrackExceptionState es;
    162     SVGLength length(mode);
    163 
    164     length.setValueAsString(valueAsString, es);
    165 
    166     if (es.hadException())
    167         parseError = ParsingAttributeFailedError;
    168     else if (negativeValuesMode == ForbidNegativeLengths && length.valueInSpecifiedUnits() < 0)
    169         parseError = NegativeValueForbiddenError;
    170 
    171     return length;
    172 }
    173 
    174 SVGLengthType SVGLength::unitType() const
    175 {
    176     return extractType(m_unit);
    177 }
    178 
    179 SVGLengthMode SVGLength::unitMode() const
    180 {
    181     return extractMode(m_unit);
    182 }
    183 
    184 float SVGLength::value(const SVGLengthContext& context) const
    185 {
    186     return value(context, IGNORE_EXCEPTION);
    187 }
    188 
    189 float SVGLength::value(const SVGLengthContext& context, ExceptionState& es) const
    190 {
    191     return context.convertValueToUserUnits(m_valueInSpecifiedUnits, extractMode(m_unit), extractType(m_unit), es);
    192 }
    193 
    194 void SVGLength::setValue(const SVGLengthContext& context, float value, SVGLengthMode mode, SVGLengthType unitType, ExceptionState& es)
    195 {
    196     m_unit = storeUnit(mode, unitType);
    197     setValue(value, context, es);
    198 }
    199 
    200 void SVGLength::setValue(float value, const SVGLengthContext& context, ExceptionState& es)
    201 {
    202     // 100% = 100.0 instead of 1.0 for historical reasons, this could eventually be changed
    203     if (extractType(m_unit) == LengthTypePercentage)
    204         value = value / 100;
    205 
    206     float convertedValue = context.convertValueFromUserUnits(value, extractMode(m_unit), extractType(m_unit), es);
    207     if (!es.hadException())
    208         m_valueInSpecifiedUnits = convertedValue;
    209 }
    210 float SVGLength::valueAsPercentage() const
    211 {
    212     // 100% = 100.0 instead of 1.0 for historical reasons, this could eventually be changed
    213     if (extractType(m_unit) == LengthTypePercentage)
    214         return m_valueInSpecifiedUnits / 100;
    215 
    216     return m_valueInSpecifiedUnits;
    217 }
    218 
    219 template<typename CharType>
    220 static bool parseValueInternal(const String& string, float& convertedNumber, SVGLengthType& type)
    221 {
    222     const CharType* ptr = string.getCharacters<CharType>();
    223     const CharType* end = ptr + string.length();
    224 
    225     if (!parseNumber(ptr, end, convertedNumber, false))
    226         return false;
    227 
    228     type = stringToLengthType(ptr, end);
    229     ASSERT(ptr <= end);
    230     if (type == LengthTypeUnknown)
    231         return false;
    232 
    233     return true;
    234 }
    235 
    236 void SVGLength::setValueAsString(const String& string, ExceptionState& es)
    237 {
    238     if (string.isEmpty())
    239         return;
    240 
    241     float convertedNumber = 0;
    242     SVGLengthType type = LengthTypeUnknown;
    243 
    244     bool success = string.is8Bit() ?
    245         parseValueInternal<LChar>(string, convertedNumber, type) :
    246         parseValueInternal<UChar>(string, convertedNumber, type);
    247 
    248     if (!success) {
    249         es.throwDOMException(SyntaxError);
    250         return;
    251     }
    252 
    253     m_unit = storeUnit(extractMode(m_unit), type);
    254     m_valueInSpecifiedUnits = convertedNumber;
    255 }
    256 
    257 String SVGLength::valueAsString() const
    258 {
    259     return String::number(m_valueInSpecifiedUnits) + lengthTypeToString(extractType(m_unit));
    260 }
    261 
    262 void SVGLength::newValueSpecifiedUnits(unsigned short type, float value, ExceptionState& es)
    263 {
    264     if (type == LengthTypeUnknown || type > LengthTypePC) {
    265         es.throwDOMException(NotSupportedError);
    266         return;
    267     }
    268 
    269     m_unit = storeUnit(extractMode(m_unit), static_cast<SVGLengthType>(type));
    270     m_valueInSpecifiedUnits = value;
    271 }
    272 
    273 void SVGLength::convertToSpecifiedUnits(unsigned short type, const SVGLengthContext& context, ExceptionState& es)
    274 {
    275     if (type == LengthTypeUnknown || type > LengthTypePC) {
    276         es.throwDOMException(NotSupportedError);
    277         return;
    278     }
    279 
    280     float valueInUserUnits = value(context, es);
    281     if (es.hadException())
    282         return;
    283 
    284     unsigned int originalUnitAndType = m_unit;
    285     m_unit = storeUnit(extractMode(m_unit), static_cast<SVGLengthType>(type));
    286     setValue(valueInUserUnits, context, es);
    287     if (!es.hadException())
    288         return;
    289 
    290     // Eventually restore old unit and type
    291     m_unit = originalUnitAndType;
    292 }
    293 
    294 SVGLength SVGLength::fromCSSPrimitiveValue(CSSPrimitiveValue* value)
    295 {
    296     ASSERT(value);
    297 
    298     SVGLengthType svgType;
    299     switch (value->primitiveType()) {
    300     case CSSPrimitiveValue::CSS_NUMBER:
    301         svgType = LengthTypeNumber;
    302         break;
    303     case CSSPrimitiveValue::CSS_PERCENTAGE:
    304         svgType = LengthTypePercentage;
    305         break;
    306     case CSSPrimitiveValue::CSS_EMS:
    307         svgType = LengthTypeEMS;
    308         break;
    309     case CSSPrimitiveValue::CSS_EXS:
    310         svgType = LengthTypeEXS;
    311         break;
    312     case CSSPrimitiveValue::CSS_PX:
    313         svgType = LengthTypePX;
    314         break;
    315     case CSSPrimitiveValue::CSS_CM:
    316         svgType = LengthTypeCM;
    317         break;
    318     case CSSPrimitiveValue::CSS_MM:
    319         svgType = LengthTypeMM;
    320         break;
    321     case CSSPrimitiveValue::CSS_IN:
    322         svgType = LengthTypeIN;
    323         break;
    324     case CSSPrimitiveValue::CSS_PT:
    325         svgType = LengthTypePT;
    326         break;
    327     case CSSPrimitiveValue::CSS_PC:
    328         svgType = LengthTypePC;
    329         break;
    330     case CSSPrimitiveValue::CSS_UNKNOWN:
    331     default:
    332         svgType = LengthTypeUnknown;
    333         break;
    334     };
    335 
    336     if (svgType == LengthTypeUnknown)
    337         return SVGLength();
    338 
    339     TrackExceptionState es;
    340     SVGLength length;
    341     length.newValueSpecifiedUnits(svgType, value->getFloatValue(), es);
    342     if (es.hadException())
    343         return SVGLength();
    344 
    345     return length;
    346 }
    347 
    348 PassRefPtr<CSSPrimitiveValue> SVGLength::toCSSPrimitiveValue(const SVGLength& length)
    349 {
    350     CSSPrimitiveValue::UnitTypes cssType = CSSPrimitiveValue::CSS_UNKNOWN;
    351     switch (length.unitType()) {
    352     case LengthTypeUnknown:
    353         break;
    354     case LengthTypeNumber:
    355         cssType = CSSPrimitiveValue::CSS_NUMBER;
    356         break;
    357     case LengthTypePercentage:
    358         cssType = CSSPrimitiveValue::CSS_PERCENTAGE;
    359         break;
    360     case LengthTypeEMS:
    361         cssType = CSSPrimitiveValue::CSS_EMS;
    362         break;
    363     case LengthTypeEXS:
    364         cssType = CSSPrimitiveValue::CSS_EXS;
    365         break;
    366     case LengthTypePX:
    367         cssType = CSSPrimitiveValue::CSS_PX;
    368         break;
    369     case LengthTypeCM:
    370         cssType = CSSPrimitiveValue::CSS_CM;
    371         break;
    372     case LengthTypeMM:
    373         cssType = CSSPrimitiveValue::CSS_MM;
    374         break;
    375     case LengthTypeIN:
    376         cssType = CSSPrimitiveValue::CSS_IN;
    377         break;
    378     case LengthTypePT:
    379         cssType = CSSPrimitiveValue::CSS_PT;
    380         break;
    381     case LengthTypePC:
    382         cssType = CSSPrimitiveValue::CSS_PC;
    383         break;
    384     };
    385 
    386     return CSSPrimitiveValue::create(length.valueInSpecifiedUnits(), cssType);
    387 }
    388 
    389 SVGLengthMode SVGLength::lengthModeForAnimatedLengthAttribute(const QualifiedName& attrName)
    390 {
    391     typedef HashMap<QualifiedName, SVGLengthMode> LengthModeForLengthAttributeMap;
    392     DEFINE_STATIC_LOCAL(LengthModeForLengthAttributeMap, s_lengthModeMap, ());
    393 
    394     if (s_lengthModeMap.isEmpty()) {
    395         s_lengthModeMap.set(SVGNames::xAttr, LengthModeWidth);
    396         s_lengthModeMap.set(SVGNames::yAttr, LengthModeHeight);
    397         s_lengthModeMap.set(SVGNames::cxAttr, LengthModeWidth);
    398         s_lengthModeMap.set(SVGNames::cyAttr, LengthModeHeight);
    399         s_lengthModeMap.set(SVGNames::dxAttr, LengthModeWidth);
    400         s_lengthModeMap.set(SVGNames::dyAttr, LengthModeHeight);
    401         s_lengthModeMap.set(SVGNames::fxAttr, LengthModeWidth);
    402         s_lengthModeMap.set(SVGNames::fyAttr, LengthModeHeight);
    403         s_lengthModeMap.set(SVGNames::rAttr, LengthModeOther);
    404         s_lengthModeMap.set(SVGNames::widthAttr, LengthModeWidth);
    405         s_lengthModeMap.set(SVGNames::heightAttr, LengthModeHeight);
    406         s_lengthModeMap.set(SVGNames::x1Attr, LengthModeWidth);
    407         s_lengthModeMap.set(SVGNames::x2Attr, LengthModeWidth);
    408         s_lengthModeMap.set(SVGNames::y1Attr, LengthModeHeight);
    409         s_lengthModeMap.set(SVGNames::y2Attr, LengthModeHeight);
    410         s_lengthModeMap.set(SVGNames::refXAttr, LengthModeWidth);
    411         s_lengthModeMap.set(SVGNames::refYAttr, LengthModeHeight);
    412         s_lengthModeMap.set(SVGNames::markerWidthAttr, LengthModeWidth);
    413         s_lengthModeMap.set(SVGNames::markerHeightAttr, LengthModeHeight);
    414         s_lengthModeMap.set(SVGNames::textLengthAttr, LengthModeWidth);
    415         s_lengthModeMap.set(SVGNames::startOffsetAttr, LengthModeWidth);
    416     }
    417 
    418     if (s_lengthModeMap.contains(attrName))
    419         return s_lengthModeMap.get(attrName);
    420 
    421     return LengthModeOther;
    422 }
    423 
    424 }
    425