Home | History | Annotate | Download | only in svg
      1 /*
      2     Copyright (C) 2004, 2005, 2006 Nikolas Zimmermann <zimmermann (at) kde.org>
      3                   2004, 2005, 2006, 2007 Rob Buis <buis (at) kde.org>
      4                   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 "SVGParserUtilities.h"
     33 #include "SVGSVGElement.h"
     34 
     35 #include <math.h>
     36 #include <wtf/Assertions.h>
     37 
     38 namespace WebCore {
     39 
     40 // Helper functions
     41 static inline unsigned int storeUnit(SVGLengthMode mode, SVGLengthType type)
     42 {
     43     return (mode << 4) | type;
     44 }
     45 
     46 static inline SVGLengthMode extractMode(unsigned int unit)
     47 {
     48     unsigned int mode = unit >> 4;
     49     return static_cast<SVGLengthMode>(mode);
     50 }
     51 
     52 static inline SVGLengthType extractType(unsigned int unit)
     53 {
     54     unsigned int mode = unit >> 4;
     55     unsigned int type = unit ^ (mode << 4);
     56     return static_cast<SVGLengthType>(type);
     57 }
     58 
     59 static inline String lengthTypeToString(SVGLengthType type)
     60 {
     61     switch (type) {
     62     case LengthTypeUnknown:
     63     case LengthTypeNumber:
     64         return "";
     65     case LengthTypePercentage:
     66         return "%";
     67     case LengthTypeEMS:
     68         return "em";
     69     case LengthTypeEXS:
     70         return "ex";
     71     case LengthTypePX:
     72         return "px";
     73     case LengthTypeCM:
     74         return "cm";
     75     case LengthTypeMM:
     76         return "mm";
     77     case LengthTypeIN:
     78         return "in";
     79     case LengthTypePT:
     80         return "pt";
     81     case LengthTypePC:
     82         return "pc";
     83     }
     84 
     85     return String();
     86 }
     87 
     88 inline SVGLengthType stringToLengthType(const String& string)
     89 {
     90     if (string.endsWith("%"))
     91         return LengthTypePercentage;
     92     else if (string.endsWith("em"))
     93         return LengthTypeEMS;
     94     else if (string.endsWith("ex"))
     95         return LengthTypeEXS;
     96     else if (string.endsWith("px"))
     97         return LengthTypePX;
     98     else if (string.endsWith("cm"))
     99         return LengthTypeCM;
    100     else if (string.endsWith("mm"))
    101         return LengthTypeMM;
    102     else if (string.endsWith("in"))
    103         return LengthTypeIN;
    104     else if (string.endsWith("pt"))
    105         return LengthTypePT;
    106     else if (string.endsWith("pc"))
    107         return LengthTypePC;
    108     else if (!string.isEmpty())
    109         return LengthTypeNumber;
    110 
    111     return LengthTypeUnknown;
    112 }
    113 
    114 SVGLength::SVGLength(SVGLengthMode mode, const String& valueAsString)
    115     : m_valueInSpecifiedUnits(0.0f)
    116     , m_unit(storeUnit(mode, LengthTypeNumber))
    117 {
    118     setValueAsString(valueAsString);
    119 }
    120 
    121 SVGLengthType SVGLength::unitType() const
    122 {
    123     return extractType(m_unit);
    124 }
    125 
    126 float SVGLength::value(const SVGElement* context) const
    127 {
    128     SVGLengthType type = extractType(m_unit);
    129     if (type == LengthTypeUnknown)
    130         return 0.0f;
    131 
    132     switch (type) {
    133     case LengthTypeNumber:
    134         return m_valueInSpecifiedUnits;
    135     case LengthTypePercentage:
    136         return SVGLength::PercentageOfViewport(m_valueInSpecifiedUnits / 100.0f, context, extractMode(m_unit));
    137     case LengthTypeEMS:
    138     case LengthTypeEXS:
    139     {
    140         RenderStyle* style = 0;
    141         if (context && context->renderer())
    142             style = context->renderer()->style();
    143         if (style) {
    144             float useSize = style->fontSize();
    145             ASSERT(useSize > 0);
    146             if (type == LengthTypeEMS)
    147                 return m_valueInSpecifiedUnits * useSize;
    148             else {
    149                 float xHeight = style->font().xHeight();
    150                 // Use of ceil allows a pixel match to the W3Cs expected output of coords-units-03-b.svg
    151                 // if this causes problems in real world cases maybe it would be best to remove this
    152                 return m_valueInSpecifiedUnits * ceilf(xHeight);
    153             }
    154         }
    155         return 0.0f;
    156     }
    157     case LengthTypePX:
    158         return m_valueInSpecifiedUnits;
    159     case LengthTypeCM:
    160         return m_valueInSpecifiedUnits / 2.54f * cssPixelsPerInch;
    161     case LengthTypeMM:
    162         return m_valueInSpecifiedUnits / 25.4f * cssPixelsPerInch;
    163     case LengthTypeIN:
    164         return m_valueInSpecifiedUnits * cssPixelsPerInch;
    165     case LengthTypePT:
    166         return m_valueInSpecifiedUnits / 72.0f * cssPixelsPerInch;
    167     case LengthTypePC:
    168         return m_valueInSpecifiedUnits / 6.0f * cssPixelsPerInch;
    169     default:
    170         break;
    171     }
    172 
    173     ASSERT_NOT_REACHED();
    174     return 0.0f;
    175 }
    176 
    177 void SVGLength::setValue(float value)
    178 {
    179     SVGLengthType type = extractType(m_unit);
    180     ASSERT(type != LengthTypeUnknown);
    181 
    182     switch (type) {
    183     case LengthTypeNumber:
    184         m_valueInSpecifiedUnits = value;
    185         break;
    186     case LengthTypePercentage:
    187     case LengthTypeEMS:
    188     case LengthTypeEXS:
    189         ASSERT_NOT_REACHED();
    190         break;
    191     case LengthTypePX:
    192         m_valueInSpecifiedUnits = value;
    193         break;
    194     case LengthTypeCM:
    195         m_valueInSpecifiedUnits = value * 2.54f / cssPixelsPerInch;
    196         break;
    197     case LengthTypeMM:
    198         m_valueInSpecifiedUnits = value * 25.4f / cssPixelsPerInch;
    199         break;
    200     case LengthTypeIN:
    201         m_valueInSpecifiedUnits = value / cssPixelsPerInch;
    202         break;
    203     case LengthTypePT:
    204         m_valueInSpecifiedUnits = value * 72.0f / cssPixelsPerInch;
    205         break;
    206     case LengthTypePC:
    207         m_valueInSpecifiedUnits = value / 6.0f * cssPixelsPerInch;
    208         break;
    209     default:
    210         break;
    211     }
    212 }
    213 
    214 void SVGLength::setValueInSpecifiedUnits(float value)
    215 {
    216     m_valueInSpecifiedUnits = value;
    217 }
    218 
    219 float SVGLength::valueInSpecifiedUnits() const
    220 {
    221     return m_valueInSpecifiedUnits;
    222 }
    223 
    224 float SVGLength::valueAsPercentage() const
    225 {
    226     // 100% = 100.0 instead of 1.0 for historical reasons, this could eventually be changed
    227     if (extractType(m_unit) == LengthTypePercentage)
    228         return valueInSpecifiedUnits() / 100.0f;
    229 
    230     return valueInSpecifiedUnits();
    231 }
    232 
    233 bool SVGLength::setValueAsString(const String& s)
    234 {
    235     if (s.isEmpty())
    236         return false;
    237 
    238     float convertedNumber = 0.0f;
    239     const UChar* ptr = s.characters();
    240     const UChar* end = ptr + s.length();
    241 
    242     if (!parseNumber(ptr, end, convertedNumber, false))
    243         return false;
    244 
    245     SVGLengthType type = stringToLengthType(s);
    246     if (ptr != end && type == LengthTypeNumber)
    247         return false;
    248 
    249     m_unit = storeUnit(extractMode(m_unit), type);
    250     m_valueInSpecifiedUnits = convertedNumber;
    251     return true;
    252 }
    253 
    254 String SVGLength::valueAsString() const
    255 {
    256     return String::number(m_valueInSpecifiedUnits) + lengthTypeToString(extractType(m_unit));
    257 }
    258 
    259 void SVGLength::newValueSpecifiedUnits(unsigned short type, float value)
    260 {
    261     ASSERT(type <= LengthTypePC);
    262 
    263     m_unit = storeUnit(extractMode(m_unit), (SVGLengthType) type);
    264     m_valueInSpecifiedUnits = value;
    265 }
    266 
    267 void SVGLength::convertToSpecifiedUnits(unsigned short type, const SVGElement* context)
    268 {
    269     ASSERT(type <= LengthTypePC);
    270 
    271     float valueInUserUnits = value(context);
    272     m_unit = storeUnit(extractMode(m_unit), (SVGLengthType) type);
    273     setValue(valueInUserUnits);
    274 }
    275 
    276 float SVGLength::PercentageOfViewport(float value, const SVGElement* context, SVGLengthMode mode)
    277 {
    278     ASSERT(context);
    279 
    280     float width = 0.0f, height = 0.0f;
    281     SVGElement* viewportElement = context->viewportElement();
    282 
    283     Document* doc = context->document();
    284     if (doc->documentElement() == context) {
    285         // We have to ask the canvas for the full "canvas size"...
    286         RenderView* view = toRenderView(doc->renderer());
    287         if (view && view->frameView()) {
    288             width = view->frameView()->visibleWidth(); // TODO: recheck!
    289             height = view->frameView()->visibleHeight(); // TODO: recheck!
    290          }
    291     } else if (viewportElement && viewportElement->isSVG()) {
    292         const SVGSVGElement* svg = static_cast<const SVGSVGElement*>(viewportElement);
    293         if (svg->hasAttribute(SVGNames::viewBoxAttr)) {
    294             width = svg->viewBox().width();
    295             height = svg->viewBox().height();
    296         } else {
    297             width = svg->width().value(svg);
    298             height = svg->height().value(svg);
    299         }
    300     } else if (context->parent() && !context->parent()->isSVGElement()) {
    301         if (RenderObject* renderer = context->renderer()) {
    302             if (renderer->isBox()) {
    303                 RenderBox* box = toRenderBox(renderer);
    304                 width = box->width();
    305                 height = box->height();
    306             }
    307         }
    308     }
    309 
    310     if (mode == LengthModeWidth)
    311         return value * width;
    312     else if (mode == LengthModeHeight)
    313         return value * height;
    314     else if (mode == LengthModeOther)
    315         return value * sqrtf(powf(width, 2) + powf(height, 2)) / sqrtf(2.0f);
    316 
    317     return 0.0f;
    318 }
    319 
    320 }
    321 
    322 #endif // ENABLE(SVG)
    323 
    324 // vim:ts=4:noet
    325