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 #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