3543863f13ed0fd0ec33a09dc22defa081f92ece
[WebKit-https.git] / WebCore / ksvg2 / svg / SVGLength.cpp
1 /*
2     Copyright (C) 2004, 2005, 2006 Nikolas Zimmermann <zimmermann@kde.org>
3                   2004, 2005, 2006 Rob Buis <buis@kde.org>
4                   2007 Apple Inc.  All rights reserved.
5
6     This file is part of the KDE project
7
8     This library is free software; you can redistribute it and/or
9     modify it under the terms of the GNU Library General Public
10     License as published by the Free Software Foundation; either
11     version 2 of the License, or (at your option) any later version.
12
13     This library is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16     Library General Public License for more details.
17
18     You should have received a copy of the GNU Library General Public License
19     along with this library; see the file COPYING.LIB.  If not, write to
20     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21     Boston, MA 02111-1307, USA.
22 */
23
24 #include "config.h"
25
26 #if ENABLE(SVG)
27 #include "SVGLength.h"
28
29 #include "DeprecatedString.h"
30 #include "FrameView.h"
31 #include "RenderObject.h"
32 #include "RenderView.h"
33 #include "SVGSVGElement.h"
34 #include "SVGStyledElement.h"
35 #include "SVGParserUtilities.h"
36 #include "csshelper.h"
37
38 #include <math.h>
39 #include <wtf/Assertions.h>
40
41 namespace WebCore {
42
43 // Helper functions
44 inline unsigned int storeUnit(SVGLengthMode mode, SVGLengthType type)
45 {
46     return (mode << 4) | type;
47 }
48
49 inline SVGLengthMode extractMode(unsigned int unit)
50 {
51     unsigned int mode = unit >> 4;    
52     return static_cast<SVGLengthMode>(mode);
53 }
54
55 inline SVGLengthType extractType(unsigned int unit)
56 {
57     unsigned int mode = unit >> 4;
58     unsigned int type = unit ^ (mode << 4);
59     return static_cast<SVGLengthType>(type);
60 }
61
62 inline String lengthTypeToString(SVGLengthType type)
63 {
64     switch (type) {
65     case LengthTypeUnknown:
66     case LengthTypeNumber:
67         return "";    
68     case LengthTypePercentage:
69         return "%";
70     case LengthTypeEMS:
71         return "em";
72     case LengthTypeEXS:
73         return "ex";
74     case LengthTypePX:
75         return "px";
76     case LengthTypeCM:
77         return "cm";
78     case LengthTypeMM:
79         return "mm";
80     case LengthTypeIN:
81         return "in";
82     case LengthTypePT:
83         return "pt";
84     case LengthTypePC:
85         return "pc";
86     }
87
88     return String();
89 }
90
91 inline SVGLengthType stringToLengthType(const String& string)
92 {
93     if (string.endsWith("%"))
94         return LengthTypePercentage;
95     else if (string.endsWith("em"))
96         return LengthTypeEMS;
97     else if (string.endsWith("ex"))
98         return LengthTypeEXS;
99     else if (string.endsWith("px"))
100         return LengthTypePX;
101     else if (string.endsWith("cm"))
102         return LengthTypeCM;
103     else if (string.endsWith("mm"))
104         return LengthTypeMM;
105     else if (string.endsWith("in"))
106         return LengthTypeIN;
107     else if (string.endsWith("pt"))
108         return LengthTypePT;
109     else if (string.endsWith("pc"))
110         return LengthTypePC;
111     else if (!string.isEmpty())
112         return LengthTypeNumber;
113
114     return LengthTypeUnknown;
115 }
116
117 SVGLength::SVGLength(const SVGStyledElement* context, SVGLengthMode mode, const String& valueAsString)
118     : m_valueInSpecifiedUnits(0.0)
119     , m_unit(storeUnit(mode, LengthTypeNumber))
120     , m_context(context)
121 {
122     setValueAsString(valueAsString);
123 }
124
125 SVGLengthType SVGLength::unitType() const
126 {
127     return extractType(m_unit);
128 }
129
130 float SVGLength::value() const
131 {
132     SVGLengthType type = extractType(m_unit);
133     if (type == LengthTypeUnknown)
134         return 0.0;
135
136     switch (type) {
137     case LengthTypeNumber:
138         return m_valueInSpecifiedUnits;
139     case LengthTypePercentage:
140         return SVGLength::PercentageOfViewport(m_valueInSpecifiedUnits / 100.0, m_context, extractMode(m_unit));
141     case LengthTypeEMS:
142     case LengthTypeEXS:
143     {
144         RenderStyle* style = 0;
145         if (m_context && m_context->renderer())
146             style = m_context->renderer()->style();
147         if (style) {
148             float useSize = style->fontSize();
149             ASSERT(useSize > 0);
150             if (type == LengthTypeEMS)
151                 return m_valueInSpecifiedUnits * useSize;
152             else {
153                 float xHeight = style->font().xHeight();
154                 // Use of ceil allows a pixel match to the W3Cs expected output of coords-units-03-b.svg
155                 // if this causes problems in real world cases maybe it would be best to remove this
156                 return m_valueInSpecifiedUnits * ceil(xHeight);
157             }
158         }
159         return 0.0;
160     }
161     case LengthTypePX:
162         return m_valueInSpecifiedUnits;
163     case LengthTypeCM:
164         return m_valueInSpecifiedUnits / 2.54 * cssPixelsPerInch;
165     case LengthTypeMM:
166         return m_valueInSpecifiedUnits / 25.4 * cssPixelsPerInch;
167     case LengthTypeIN:
168         return m_valueInSpecifiedUnits * cssPixelsPerInch;
169     case LengthTypePT:
170         return m_valueInSpecifiedUnits / 72.0 * cssPixelsPerInch;
171     case LengthTypePC:
172         return m_valueInSpecifiedUnits / 6.0 * cssPixelsPerInch;
173     default:
174         break;
175     }
176
177     ASSERT_NOT_REACHED();
178     return 0.0;
179 }
180
181 void SVGLength::setValue(float value)
182 {
183     SVGLengthType type = extractType(m_unit);
184     ASSERT(type != LengthTypeUnknown);
185
186     switch (type) {
187     case LengthTypeNumber:
188         m_valueInSpecifiedUnits = value;
189         break;
190     case LengthTypePercentage:
191     case LengthTypeEMS:
192     case LengthTypeEXS:
193         ASSERT_NOT_REACHED();
194         break;
195     case LengthTypePX:
196         m_valueInSpecifiedUnits = value;
197         break;
198     case LengthTypeCM:
199         m_valueInSpecifiedUnits = value * 2.54 / cssPixelsPerInch;
200         break;
201     case LengthTypeMM:
202         m_valueInSpecifiedUnits = value * 25.4 / cssPixelsPerInch;
203         break;
204     case LengthTypeIN:
205         m_valueInSpecifiedUnits = value / cssPixelsPerInch;
206         break;
207     case LengthTypePT:
208         m_valueInSpecifiedUnits = value * 72.0 / cssPixelsPerInch;
209         break;
210     case LengthTypePC:
211         m_valueInSpecifiedUnits = value / 6.0 * cssPixelsPerInch;
212         break;
213     default:
214         break;
215     }
216 }
217
218 void SVGLength::setValueInSpecifiedUnits(float value)
219 {
220     m_valueInSpecifiedUnits = value;
221 }
222
223 float SVGLength::valueInSpecifiedUnits() const
224 {
225     return m_valueInSpecifiedUnits;
226 }
227
228 float SVGLength::valueAsPercentage() const
229 {
230     // 100% = 100.0 instead of 1.0 for historical reasons, this could eventually be changed
231     if (extractType(m_unit) == LengthTypePercentage)
232         return valueInSpecifiedUnits() / 100.0;
233
234     return valueInSpecifiedUnits();
235 }
236
237 void SVGLength::setValueAsString(const String& s)
238 {
239     if (s.isEmpty())
240         return;
241
242     double convertedNumber = 0;
243     const UChar* ptr = s.characters();
244     const UChar* end = ptr + s.length();
245     parseNumber(ptr, end, convertedNumber, false);
246
247     m_unit = storeUnit(extractMode(m_unit), stringToLengthType(s));
248     m_valueInSpecifiedUnits = convertedNumber;
249 }
250
251 String SVGLength::valueAsString() const
252 {
253     return String::number(m_valueInSpecifiedUnits) + lengthTypeToString(extractType(m_unit));
254 }
255
256 void SVGLength::newValueSpecifiedUnits(unsigned short type, float value)
257 {
258     ASSERT(type <= LengthTypePC);
259
260     m_unit = storeUnit(extractMode(m_unit), (SVGLengthType) type);
261     m_valueInSpecifiedUnits = value;
262 }
263
264 void SVGLength::convertToSpecifiedUnits(unsigned short type)
265 {
266     ASSERT(type <= LengthTypePC);
267
268     float valueInUserUnits = value();
269     m_unit = storeUnit(extractMode(m_unit), (SVGLengthType) type);
270     setValue(valueInUserUnits);
271 }
272
273 float SVGLength::PercentageOfViewport(float value, const SVGStyledElement* context, SVGLengthMode mode)
274 {
275     ASSERT(context);
276
277     float width = 0, height = 0;
278     SVGElement* viewportElement = context->viewportElement();
279
280     Document* doc = context->document();
281     if (doc->documentElement() == context) {
282         // We have to ask the canvas for the full "canvas size"...
283         RenderView* view = static_cast<RenderView*>(doc->renderer());
284         if (view && view->frameView()) {
285             width = view->frameView()->visibleWidth(); // TODO: recheck!
286             height = view->frameView()->visibleHeight(); // TODO: recheck!
287          }
288     } else if (viewportElement && viewportElement->isSVG()) {
289         const SVGSVGElement* svg = static_cast<const SVGSVGElement*>(viewportElement);
290         if (svg->hasAttribute(SVGNames::viewBoxAttr)) {
291             width = svg->viewBox().width();
292             height = svg->viewBox().height();
293         } else {
294             width = svg->width().value();
295             height = svg->height().value();
296         }
297     } else if (context->parent() && !context->parent()->isSVGElement()) {
298         if (RenderObject* renderer = context->renderer()) {
299             width = renderer->width();
300             height = renderer->height();
301         }
302     }
303
304     if (mode == LengthModeWidth)
305         return value * width;
306     else if (mode == LengthModeHeight)
307         return value * height;
308     else if (mode == LengthModeOther)
309         return value * sqrt(pow(double(width), 2) + pow(double(height), 2)) / sqrt(2.0);
310
311     return 0.0;
312 }
313
314 }
315
316 #endif // ENABLE(SVG)
317
318 // vim:ts=4:noet