Reviewed by eseidel.
[WebKit-https.git] / WebCore / ksvg2 / svg / SVGLength.cpp
1 /*
2     Copyright (C) 2004, 2005 Nikolas Zimmermann <wildfox@kde.org>
3                   2004, 2005 Rob Buis <buis@kde.org>
4
5     This file is part of the KDE project
6
7     This library is free software; you can redistribute it and/or
8     modify it under the terms of the GNU Library General Public
9     License as published by the Free Software Foundation; either
10     version 2 of the License, or (at your option) any later version.
11
12     This library is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15     Library General Public License for more details.
16
17     You should have received a copy of the GNU Library General Public License
18     along with this library; see the file COPYING.LIB.  If not, write to
19     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20     Boston, MA 02111-1307, USA.
21 */
22
23 #include "config.h"
24 #ifdef SVG_SUPPORT
25 #include "IntRect.h"
26
27 #include <kcanvas/RenderPath.h>
28 #include "KCanvasRenderingStyle.h"
29
30 #include "ksvg.h"
31 #include "svgpathparser.h"
32 #include "SVGLength.h"
33 #include "SVGElement.h"
34 #include "SVGSVGElement.h"
35 #include "SVGLength.h"
36
37 #include <math.h>
38
39 using namespace std;
40
41 // keep track of textual description of the unit type
42 static const char* UnitText[] =
43 {
44     "", "",
45     "%", "em",
46     "ex", "px",
47     "cm", "mm",
48     "in", "pt",
49     "pc"
50 };
51
52 namespace WebCore {
53
54 SVGLength::SVGLength(const SVGStyledElement *context, LengthMode mode, const SVGElement *viewport)
55     : Shared<SVGLength>()
56     , m_value(0)
57     , m_valueInSpecifiedUnits(0)
58     , m_mode(mode)
59     , m_bboxRelative(false)
60     , m_unitType(SVG_LENGTHTYPE_UNKNOWN)
61     , m_requiresLayout(false)
62     , m_context(context)
63     , m_viewportElement(viewport)
64 {
65 }
66
67 SVGLength::~SVGLength()
68 {
69 }
70
71 SVGLength::SVGLengthType SVGLength::unitType() const
72 {
73     return m_unitType;
74 }
75
76 void SVGLength::setValue(float value)
77 {
78     m_value = value;
79     updateValueInSpecifiedUnits();
80 }
81
82 float SVGLength::value() const
83 {
84     if (m_requiresLayout)
85         const_cast<SVGLength*>(this)->updateValue(false);
86
87     if (m_unitType != SVG_LENGTHTYPE_PERCENTAGE)
88         return m_value;
89
90     float value = m_valueInSpecifiedUnits / 100.0;
91     if (m_bboxRelative)
92         return value;
93
94     // Use the manual override "m_viewportElement" when there is no context element off of which to establish the viewport.
95     return SVGHelper::PercentageOfViewport(value, m_context ? m_context->viewportElement() : m_viewportElement, static_cast<LengthMode>(m_mode));
96 }
97
98 void SVGLength::setValueInSpecifiedUnits(float valueInSpecifiedUnits)
99 {
100     m_valueInSpecifiedUnits = valueInSpecifiedUnits;
101     updateValue();
102 }
103
104 float SVGLength::valueInSpecifiedUnits() const
105 {
106     return m_valueInSpecifiedUnits;
107 }                                                
108
109 void SVGLength::setValueAsString(const String& s)
110 {
111     if(s.isEmpty())
112         return;
113
114     DeprecatedString valueAsQString = s.deprecatedString();
115
116     double convertedNumber = 0;
117     const char *start = valueAsQString.latin1();
118     const char *end = parseCoord(start, convertedNumber);
119     m_valueInSpecifiedUnits = convertedNumber;
120
121     unsigned int diff = end - start;
122     if (diff < valueAsQString.length()) {
123         if (s.endsWith(UnitText[SVG_LENGTHTYPE_PX]))
124             m_unitType = SVG_LENGTHTYPE_PX;
125         else if (s.endsWith(UnitText[SVG_LENGTHTYPE_CM]))
126             m_unitType = SVG_LENGTHTYPE_CM;
127         else if (s.endsWith(UnitText[SVG_LENGTHTYPE_PC]))
128             m_unitType = SVG_LENGTHTYPE_PC;
129         else if (s.endsWith(UnitText[SVG_LENGTHTYPE_MM]))
130             m_unitType = SVG_LENGTHTYPE_MM;
131         else if (s.endsWith(UnitText[SVG_LENGTHTYPE_IN]))
132             m_unitType = SVG_LENGTHTYPE_IN;
133         else if (s.endsWith(UnitText[SVG_LENGTHTYPE_PT]))
134             m_unitType = SVG_LENGTHTYPE_PT;
135          else if (s.endsWith(UnitText[SVG_LENGTHTYPE_PERCENTAGE]))
136             m_unitType = SVG_LENGTHTYPE_PERCENTAGE;
137         else if (s.endsWith(UnitText[SVG_LENGTHTYPE_EMS]))
138             m_unitType = SVG_LENGTHTYPE_EMS;
139         else if (s.endsWith(UnitText[SVG_LENGTHTYPE_EXS]))
140             m_unitType = SVG_LENGTHTYPE_EXS;
141         else if (s.isEmpty())
142             m_unitType = SVG_LENGTHTYPE_NUMBER;
143         else
144             m_unitType = SVG_LENGTHTYPE_UNKNOWN;
145     }
146     else
147         m_unitType = SVG_LENGTHTYPE_PX;
148
149     updateValue();
150 }
151
152 String SVGLength::valueAsString() const
153 {
154     return String::number(m_valueInSpecifiedUnits) + UnitText[m_unitType];
155 }
156
157 void SVGLength::newValueSpecifiedUnits(unsigned short unitType, float valueInSpecifiedUnits)
158 {
159     m_valueInSpecifiedUnits = valueInSpecifiedUnits;
160     m_unitType = (SVGLengthType)unitType;
161     updateValue();
162 }
163
164 void SVGLength::convertToSpecifiedUnits(unsigned short unitType)
165 {
166     m_unitType = (SVGLengthType)unitType;
167     updateValueInSpecifiedUnits();
168 }
169
170 double SVGLength::dpi() const
171 {
172     /* FIXME: DPI detection
173     if(context && context->ownerDoc())
174     {
175         if(mode == LM_WIDTH)
176             return 25.4 * context->ownerDoc()->screenPixelsPerMillimeterX();
177         else if(mode == LM_HEIGHT)
178             return 25.4 * context->ownerDoc()->screenPixelsPerMillimeterY();
179         else if(mode == LM_OTHER)
180             return 25.4 * context->ownerDoc()->screenPixelsPerMillimeterX();
181     }
182     */
183
184     return 90.0;
185 }
186
187 void SVGLength::updateValue(bool notify)
188 {
189     switch(m_unitType)
190     {
191         case SVG_LENGTHTYPE_PX:
192             m_value = m_valueInSpecifiedUnits;
193             break;
194         case SVG_LENGTHTYPE_CM:
195             m_value = (m_valueInSpecifiedUnits / 2.54) * dpi();
196             break;
197         case SVG_LENGTHTYPE_MM:
198             m_value = (m_valueInSpecifiedUnits / 25.4) * dpi();
199             break;
200         case SVG_LENGTHTYPE_IN:
201             m_value = m_valueInSpecifiedUnits * dpi();
202             break;
203         case SVG_LENGTHTYPE_PT:
204             m_value = (m_valueInSpecifiedUnits / 72.0) * dpi();
205             break;
206         case SVG_LENGTHTYPE_PC:
207             m_value = (m_valueInSpecifiedUnits / 6.0) * dpi();
208             break;
209         case SVG_LENGTHTYPE_EMS:
210         case SVG_LENGTHTYPE_EXS:
211         {
212             RenderStyle *style = 0;
213             if (m_context && m_context->renderer())
214                 style = m_context->renderer()->style();
215             else if (m_viewportElement && m_viewportElement->renderer())
216                 style = m_viewportElement->renderer()->style();
217             if (style) {
218                 float useSize = style->fontSize();
219                 ASSERT(useSize > 0);
220                 if (m_unitType == SVG_LENGTHTYPE_EMS)
221                     m_value = m_valueInSpecifiedUnits * useSize;
222                 else {
223                     float xHeight = style->font().xHeight();
224                     // Use of ceil allows a pixel match to the W3Cs expected output of coords-units-03-b.svg
225                     // if this causes problems in real world cases maybe it would be best to remove this
226                     m_value = m_valueInSpecifiedUnits * ceil(xHeight);
227                 }
228                 m_requiresLayout = false;
229             } else {
230                 m_requiresLayout = true;
231             }
232             break;
233         }
234         case SVG_LENGTHTYPE_UNKNOWN:
235         case SVG_LENGTHTYPE_NUMBER:
236         case SVG_LENGTHTYPE_PERCENTAGE:
237             break;
238     }
239     if (notify && m_context)
240         m_context->notifyAttributeChange();
241 }
242
243 bool SVGLength::updateValueInSpecifiedUnits(bool notify)
244 {
245     if(m_unitType == SVG_LENGTHTYPE_UNKNOWN)
246         return false;
247
248     switch(m_unitType)
249     {
250         case SVG_LENGTHTYPE_PERCENTAGE:
251             //kdError() << "updateValueInSpecifiedUnits() SVG_LENGTHTYPE_PERCENTAGE - UNSUPPORTED! Please report!" << endl;
252             return false;
253         case SVG_LENGTHTYPE_EMS:
254             //kdError() << "updateValueInSpecifiedUnits() SVG_LENGTHTYPE_EMS - UNSUPPORTED! Please report!" << endl;
255             return false;
256         case SVG_LENGTHTYPE_EXS:
257             //kdError() << "updateValueInSpecifiedUnits() SVG_LENGTHTYPE_EXS - UNSUPPORTED! Please report!" << endl;
258             return false;
259         case SVG_LENGTHTYPE_PX:
260             m_valueInSpecifiedUnits = m_value;
261             break;
262         case SVG_LENGTHTYPE_CM:
263             m_valueInSpecifiedUnits = m_value / dpi() * 2.54;
264             break;
265         case SVG_LENGTHTYPE_MM:
266             m_valueInSpecifiedUnits = m_value / dpi() * 25.4;
267             break;
268         case SVG_LENGTHTYPE_IN:
269             m_valueInSpecifiedUnits = m_value / dpi();
270             break;
271         case SVG_LENGTHTYPE_PT:
272             m_valueInSpecifiedUnits = m_value / dpi() * 72.0;
273             break;
274         case SVG_LENGTHTYPE_PC:
275             m_valueInSpecifiedUnits = m_value / dpi() * 6.0;
276             break;
277         case SVG_LENGTHTYPE_UNKNOWN:
278         case SVG_LENGTHTYPE_NUMBER:
279             break;
280     };
281     
282     if (notify && m_context)
283         m_context->notifyAttributeChange();
284
285     return true;
286 }
287
288 bool SVGLength::bboxRelative() const
289 {
290     return m_bboxRelative;
291 }
292
293 void SVGLength::setBboxRelative(bool relative)
294 {
295     m_bboxRelative = relative;
296 }
297
298 const SVGStyledElement *SVGLength::context() const
299 {
300     return m_context;
301 }
302
303 void SVGLength::setContext(const SVGStyledElement *context)
304 {
305     m_context = context;
306 }
307
308 }
309
310 // vim:ts=4:noet
311 #endif // SVG_SUPPORT
312