• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2     Copyright (C) 2004, 2005, 2006 Nikolas Zimmermann <zimmermann@kde.org>
3                   2004, 2005, 2006, 2007 Rob Buis <buis@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
storeUnit(SVGLengthMode mode,SVGLengthType type)41 static inline unsigned int storeUnit(SVGLengthMode mode, SVGLengthType type)
42 {
43     return (mode << 4) | type;
44 }
45 
extractMode(unsigned int unit)46 static inline SVGLengthMode extractMode(unsigned int unit)
47 {
48     unsigned int mode = unit >> 4;
49     return static_cast<SVGLengthMode>(mode);
50 }
51 
extractType(unsigned int unit)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 
lengthTypeToString(SVGLengthType type)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 
stringToLengthType(const String & string)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 
SVGLength(SVGLengthMode mode,const String & valueAsString)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 
unitType() const121 SVGLengthType SVGLength::unitType() const
122 {
123     return extractType(m_unit);
124 }
125 
value(const SVGElement * context) const126 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 
setValue(float value)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 
setValueInSpecifiedUnits(float value)214 void SVGLength::setValueInSpecifiedUnits(float value)
215 {
216     m_valueInSpecifiedUnits = value;
217 }
218 
valueInSpecifiedUnits() const219 float SVGLength::valueInSpecifiedUnits() const
220 {
221     return m_valueInSpecifiedUnits;
222 }
223 
valueAsPercentage() const224 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 
setValueAsString(const String & s)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 
valueAsString() const254 String SVGLength::valueAsString() const
255 {
256     return String::number(m_valueInSpecifiedUnits) + lengthTypeToString(extractType(m_unit));
257 }
258 
newValueSpecifiedUnits(unsigned short type,float value)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 
convertToSpecifiedUnits(unsigned short type,const SVGElement * context)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 
PercentageOfViewport(float value,const SVGElement * context,SVGLengthMode mode)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