File indexing completed on 2024-04-28 11:39:15

0001 /*
0002     Copyright (C) 2004, 2005, 2006 Nikolas Zimmermann <zimmermann@kde.org>
0003                   2004, 2005, 2006, 2007 Rob Buis <buis@kde.org>
0004                   2007 Apple Inc.  All rights reserved.
0005 
0006     This file is part of the KDE project
0007 
0008     This library is free software; you can redistribute it and/or
0009     modify it under the terms of the GNU Library General Public
0010     License as published by the Free Software Foundation; either
0011     version 2 of the License, or (at your option) any later version.
0012 
0013     This library is distributed in the hope that it will be useful,
0014     but WITHOUT ANY WARRANTY; without even the implied warranty of
0015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0016     Library General Public License for more details.
0017 
0018     You should have received a copy of the GNU Library General Public License
0019     along with this library; see the file COPYING.LIB.  If not, write to
0020     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0021     Boston, MA 02110-1301, USA.
0022 */
0023 
0024 #include "wtf/Platform.h"
0025 
0026 #if ENABLE(SVG)
0027 #include "SVGLength.h"
0028 
0029 #include "css/csshelper.h"
0030 #include "FloatConversion.h"
0031 /*#include "FrameView.h"*/
0032 #include "RenderObject.h"
0033 #include "RenderView.h"
0034 #include "SVGParserUtilities.h"
0035 #include "SVGSVGElement.h"
0036 #include "SVGStyledElement.h"
0037 
0038 #include <math.h>
0039 #include <wtf/Assertions.h>
0040 
0041 namespace WebCore
0042 {
0043 
0044 // Helper functions
0045 static inline unsigned int storeUnit(SVGLengthMode mode, SVGLengthType type)
0046 {
0047     return (mode << 4) | type;
0048 }
0049 
0050 static inline SVGLengthMode extractMode(unsigned int unit)
0051 {
0052     unsigned int mode = unit >> 4;
0053     return static_cast<SVGLengthMode>(mode);
0054 }
0055 
0056 static inline SVGLengthType extractType(unsigned int unit)
0057 {
0058     unsigned int mode = unit >> 4;
0059     unsigned int type = unit ^ (mode << 4);
0060     return static_cast<SVGLengthType>(type);
0061 }
0062 
0063 static inline String lengthTypeToString(SVGLengthType type)
0064 {
0065     switch (type) {
0066     case LengthTypeUnknown:
0067     case LengthTypeNumber:
0068         return "";
0069     case LengthTypePercentage:
0070         return "%";
0071     case LengthTypeEMS:
0072         return "em";
0073     case LengthTypeEXS:
0074         return "ex";
0075     case LengthTypePX:
0076         return "px";
0077     case LengthTypeCM:
0078         return "cm";
0079     case LengthTypeMM:
0080         return "mm";
0081     case LengthTypeIN:
0082         return "in";
0083     case LengthTypePT:
0084         return "pt";
0085     case LengthTypePC:
0086         return "pc";
0087     }
0088 
0089     return String();
0090 }
0091 
0092 inline SVGLengthType stringToLengthType(const String &string)
0093 {
0094     if (string.endsWith("%")) {
0095         return LengthTypePercentage;
0096     } else if (string.endsWith("em")) {
0097         return LengthTypeEMS;
0098     } else if (string.endsWith("ex")) {
0099         return LengthTypeEXS;
0100     } else if (string.endsWith("px")) {
0101         return LengthTypePX;
0102     } else if (string.endsWith("cm")) {
0103         return LengthTypeCM;
0104     } else if (string.endsWith("mm")) {
0105         return LengthTypeMM;
0106     } else if (string.endsWith("in")) {
0107         return LengthTypeIN;
0108     } else if (string.endsWith("pt")) {
0109         return LengthTypePT;
0110     } else if (string.endsWith("pc")) {
0111         return LengthTypePC;
0112     } else if (!string.isEmpty()) {
0113         return LengthTypeNumber;
0114     }
0115 
0116     return LengthTypeUnknown;
0117 }
0118 
0119 SVGLength::SVGLength(const SVGStyledElement *context, SVGLengthMode mode, const String &valueAsString)
0120     : m_valueInSpecifiedUnits(0.0f)
0121     , m_unit(storeUnit(mode, LengthTypeNumber))
0122     , m_context(context)
0123 {
0124     setValueAsString(valueAsString);
0125 }
0126 
0127 SVGLengthType SVGLength::unitType() const
0128 {
0129     return extractType(m_unit);
0130 }
0131 
0132 float SVGLength::value() const
0133 {
0134     SVGLengthType type = extractType(m_unit);
0135     if (type == LengthTypeUnknown) {
0136         return 0.0f;
0137     }
0138 
0139     switch (type) {
0140     case LengthTypeNumber:
0141         return m_valueInSpecifiedUnits;
0142     case LengthTypePercentage:
0143         return SVGLength::PercentageOfViewport(m_valueInSpecifiedUnits / 100.0f, m_context, extractMode(m_unit));
0144     case LengthTypeEMS:
0145     case LengthTypeEXS: {
0146         /*RenderStyle* style = 0;
0147         if (m_context && m_context->renderer())
0148             style = m_context->renderer()->style();
0149         if (style) {
0150             float useSize = style->fontSize();
0151             ASSERT(useSize > 0);
0152             if (type == LengthTypeEMS)
0153                 return m_valueInSpecifiedUnits * useSize;
0154             else {
0155                 float xHeight = style->font().xHeight();
0156                 // Use of ceil allows a pixel match to the W3Cs expected output of coords-units-03-b.svg
0157                 // if this causes problems in real world cases maybe it would be best to remove this
0158                 return m_valueInSpecifiedUnits * ceilf(xHeight);
0159             }
0160         }*/
0161         return 0.0f;
0162     }
0163     case LengthTypePX:
0164         return m_valueInSpecifiedUnits;
0165     case LengthTypeCM:
0166         return m_valueInSpecifiedUnits / 2.54f * cssPixelsPerInch;
0167     case LengthTypeMM:
0168         return m_valueInSpecifiedUnits / 25.4f * cssPixelsPerInch;
0169     case LengthTypeIN:
0170         return m_valueInSpecifiedUnits * cssPixelsPerInch;
0171     case LengthTypePT:
0172         return m_valueInSpecifiedUnits / 72.0f * cssPixelsPerInch;
0173     case LengthTypePC:
0174         return m_valueInSpecifiedUnits / 6.0f * cssPixelsPerInch;
0175     default:
0176         break;
0177     }
0178 
0179     ASSERT_NOT_REACHED();
0180     return 0.0f;
0181 }
0182 
0183 void SVGLength::setValue(float value)
0184 {
0185     SVGLengthType type = extractType(m_unit);
0186     ASSERT(type != LengthTypeUnknown);
0187 
0188     switch (type) {
0189     case LengthTypeNumber:
0190         m_valueInSpecifiedUnits = value;
0191         break;
0192     case LengthTypePercentage:
0193     case LengthTypeEMS:
0194     case LengthTypeEXS:
0195         ASSERT_NOT_REACHED();
0196         break;
0197     case LengthTypePX:
0198         m_valueInSpecifiedUnits = value;
0199         break;
0200     case LengthTypeCM:
0201         m_valueInSpecifiedUnits = value * 2.54f / cssPixelsPerInch;
0202         break;
0203     case LengthTypeMM:
0204         m_valueInSpecifiedUnits = value * 25.4f / cssPixelsPerInch;
0205         break;
0206     case LengthTypeIN:
0207         m_valueInSpecifiedUnits = value / cssPixelsPerInch;
0208         break;
0209     case LengthTypePT:
0210         m_valueInSpecifiedUnits = value * 72.0f / cssPixelsPerInch;
0211         break;
0212     case LengthTypePC:
0213         m_valueInSpecifiedUnits = value / 6.0f * cssPixelsPerInch;
0214         break;
0215     default:
0216         break;
0217     }
0218 }
0219 
0220 void SVGLength::setValueInSpecifiedUnits(float value)
0221 {
0222     m_valueInSpecifiedUnits = value;
0223 }
0224 
0225 float SVGLength::valueInSpecifiedUnits() const
0226 {
0227     return m_valueInSpecifiedUnits;
0228 }
0229 
0230 float SVGLength::valueAsPercentage() const
0231 {
0232     // 100% = 100.0 instead of 1.0 for historical reasons, this could eventually be changed
0233     if (extractType(m_unit) == LengthTypePercentage) {
0234         return valueInSpecifiedUnits() / 100.0f;
0235     }
0236 
0237     return valueInSpecifiedUnits();
0238 }
0239 
0240 bool SVGLength::setValueAsString(const String &s)
0241 {
0242     if (s.isEmpty()) {
0243         return false;
0244     }
0245 
0246     float convertedNumber = 0.0f;
0247     const UChar *ptr = s.characters();
0248     const UChar *end = ptr + s.length();
0249 
0250     if (!parseNumber(ptr, end, convertedNumber, false)) {
0251         return false;
0252     }
0253 
0254     SVGLengthType type = stringToLengthType(s);
0255     if (ptr != end && type == LengthTypeNumber) {
0256         return false;
0257     }
0258 
0259     // qCDebug(KHTML_LOG) << convertedNumber << type;
0260 
0261     m_unit = storeUnit(extractMode(m_unit), type);
0262     m_valueInSpecifiedUnits = convertedNumber;
0263     return true;
0264 }
0265 
0266 String SVGLength::valueAsString() const
0267 {
0268     //return String::number(m_valueInSpecifiedUnits) + lengthTypeToString(extractType(m_unit));
0269     ASSERT(false);
0270     return "";
0271 }
0272 
0273 void SVGLength::newValueSpecifiedUnits(unsigned short type, float value)
0274 {
0275     ASSERT(type <= LengthTypePC);
0276 
0277     m_unit = storeUnit(extractMode(m_unit), (SVGLengthType) type);
0278     m_valueInSpecifiedUnits = value;
0279 }
0280 
0281 void SVGLength::convertToSpecifiedUnits(unsigned short type)
0282 {
0283     ASSERT(type <= LengthTypePC);
0284 
0285     float valueInUserUnits = value();
0286     m_unit = storeUnit(extractMode(m_unit), (SVGLengthType) type);
0287     setValue(valueInUserUnits);
0288 }
0289 
0290 float SVGLength::PercentageOfViewport(float value, const SVGStyledElement *context, SVGLengthMode mode)
0291 {
0292     ASSERT(context);
0293 
0294     float width = 0.0f, height = 0.0f;
0295     SVGElement *viewportElement = context->viewportElement();
0296 
0297     Document *doc = context->document();
0298     if (doc->documentElement() == context) {
0299         // We have to ask the canvas for the full "canvas size"...
0300         RenderView *view = static_cast<RenderView *>(doc->renderer());
0301         if (view && view->view()) {
0302             width = view->view()->visibleWidth(); // TODO: recheck!
0303             height = view->view()->visibleHeight(); // TODO: recheck!
0304         }
0305     } else if (viewportElement && viewportElement->isSVG()) {
0306         const SVGSVGElement *svg = static_cast<const SVGSVGElement *>(viewportElement);
0307         if (svg->hasAttribute(SVGNames::viewBoxAttr)) {
0308             width = svg->viewBox().width();
0309             height = svg->viewBox().height();
0310         } else {
0311             width = svg->width().value();
0312             height = svg->height().value();
0313         }
0314     } else if (context->parent() && !context->parent()->isSVGElement()) {
0315         if (RenderObject *renderer = context->renderer()) {
0316             width = renderer->width();
0317             height = renderer->height();
0318         }
0319     }
0320 
0321     if (mode == LengthModeWidth) {
0322         return value * width;
0323     } else if (mode == LengthModeHeight) {
0324         return value * height;
0325     } else if (mode == LengthModeOther) {
0326         return value * sqrtf(powf(width, 2) + powf(height, 2)) / sqrtf(2.0f);
0327     }
0328 
0329     return 0.0f;
0330 }
0331 
0332 }
0333 
0334 #endif // ENABLE(SVG)
0335