• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2004, 2005, 2006 Nikolas Zimmermann <zimmermann@kde.org>
3  * Copyright (C) 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org>
4  * Copyright (C) 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 "SVGNames.h"
33 #include "SVGParserUtilities.h"
34 #include "SVGSVGElement.h"
35 
36 #include <wtf/MathExtras.h>
37 #include <wtf/text/StringConcatenate.h>
38 
39 namespace WebCore {
40 
41 // Helper functions
storeUnit(SVGLengthMode mode,SVGLengthType type)42 static inline unsigned int storeUnit(SVGLengthMode mode, SVGLengthType type)
43 {
44     return (mode << 4) | type;
45 }
46 
extractMode(unsigned int unit)47 static inline SVGLengthMode extractMode(unsigned int unit)
48 {
49     unsigned int mode = unit >> 4;
50     return static_cast<SVGLengthMode>(mode);
51 }
52 
extractType(unsigned int unit)53 static inline SVGLengthType extractType(unsigned int unit)
54 {
55     unsigned int mode = unit >> 4;
56     unsigned int type = unit ^ (mode << 4);
57     return static_cast<SVGLengthType>(type);
58 }
59 
lengthTypeToString(SVGLengthType type)60 static inline String lengthTypeToString(SVGLengthType type)
61 {
62     switch (type) {
63     case LengthTypeUnknown:
64     case LengthTypeNumber:
65         return "";
66     case LengthTypePercentage:
67         return "%";
68     case LengthTypeEMS:
69         return "em";
70     case LengthTypeEXS:
71         return "ex";
72     case LengthTypePX:
73         return "px";
74     case LengthTypeCM:
75         return "cm";
76     case LengthTypeMM:
77         return "mm";
78     case LengthTypeIN:
79         return "in";
80     case LengthTypePT:
81         return "pt";
82     case LengthTypePC:
83         return "pc";
84     }
85 
86     ASSERT_NOT_REACHED();
87     return String();
88 }
89 
stringToLengthType(const UChar * & ptr,const UChar * end)90 inline SVGLengthType stringToLengthType(const UChar*& ptr, const UChar* end)
91 {
92     if (ptr == end)
93         return LengthTypeNumber;
94 
95     const UChar firstChar = *ptr;
96 
97     if (++ptr == end)
98         return firstChar == '%' ? LengthTypePercentage : LengthTypeUnknown;
99 
100     const UChar secondChar = *ptr;
101 
102     if (++ptr != end)
103         return LengthTypeUnknown;
104 
105     if (firstChar == 'e' && secondChar == 'm')
106         return LengthTypeEMS;
107     if (firstChar == 'e' && secondChar == 'x')
108         return LengthTypeEXS;
109     if (firstChar == 'p' && secondChar == 'x')
110         return LengthTypePX;
111     if (firstChar == 'c' && secondChar == 'm')
112         return LengthTypeCM;
113     if (firstChar == 'm' && secondChar == 'm')
114         return LengthTypeMM;
115     if (firstChar == 'i' && secondChar == 'n')
116         return LengthTypeIN;
117     if (firstChar == 'p' && secondChar == 't')
118         return LengthTypePT;
119     if (firstChar == 'p' && secondChar == 'c')
120         return LengthTypePC;
121 
122     return LengthTypeUnknown;
123 }
124 
SVGLength(SVGLengthMode mode,const String & valueAsString)125 SVGLength::SVGLength(SVGLengthMode mode, const String& valueAsString)
126     : m_valueInSpecifiedUnits(0)
127     , m_unit(storeUnit(mode, LengthTypeNumber))
128 {
129     ExceptionCode ec = 0;
130     setValueAsString(valueAsString, ec);
131 }
132 
SVGLength(const SVGLength & other)133 SVGLength::SVGLength(const SVGLength& other)
134     : m_valueInSpecifiedUnits(other.m_valueInSpecifiedUnits)
135     , m_unit(other.m_unit)
136 {
137 }
138 
operator ==(const SVGLength & other) const139 bool SVGLength::operator==(const SVGLength& other) const
140 {
141     return m_unit == other.m_unit
142         && m_valueInSpecifiedUnits == other.m_valueInSpecifiedUnits;
143 }
144 
operator !=(const SVGLength & other) const145 bool SVGLength::operator!=(const SVGLength& other) const
146 {
147     return !operator==(other);
148 }
149 
unitType() const150 SVGLengthType SVGLength::unitType() const
151 {
152     return extractType(m_unit);
153 }
154 
value(const SVGElement * context) const155 float SVGLength::value(const SVGElement* context) const
156 {
157     ExceptionCode ec = 0;
158     return value(context, ec);
159 }
160 
value(const SVGElement * context,ExceptionCode & ec) const161 float SVGLength::value(const SVGElement* context, ExceptionCode& ec) const
162 {
163     switch (extractType(m_unit)) {
164     case LengthTypeUnknown:
165         ec = NOT_SUPPORTED_ERR;
166         return 0;
167     case LengthTypeNumber:
168         return m_valueInSpecifiedUnits;
169     case LengthTypePercentage:
170         return convertValueFromPercentageToUserUnits(m_valueInSpecifiedUnits / 100, context, ec);
171     case LengthTypeEMS:
172         return convertValueFromEMSToUserUnits(m_valueInSpecifiedUnits, context, ec);
173     case LengthTypeEXS:
174         return convertValueFromEXSToUserUnits(m_valueInSpecifiedUnits, context, ec);
175     case LengthTypePX:
176         return m_valueInSpecifiedUnits;
177     case LengthTypeCM:
178         return m_valueInSpecifiedUnits / 2.54f * cssPixelsPerInch;
179     case LengthTypeMM:
180         return m_valueInSpecifiedUnits / 25.4f * cssPixelsPerInch;
181     case LengthTypeIN:
182         return m_valueInSpecifiedUnits * cssPixelsPerInch;
183     case LengthTypePT:
184         return m_valueInSpecifiedUnits / 72 * cssPixelsPerInch;
185     case LengthTypePC:
186         return m_valueInSpecifiedUnits / 6 * cssPixelsPerInch;
187     }
188 
189     ASSERT_NOT_REACHED();
190     return 0;
191 }
192 
setValue(float value,const SVGElement * context,ExceptionCode & ec)193 void SVGLength::setValue(float value, const SVGElement* context, ExceptionCode& ec)
194 {
195     switch (extractType(m_unit)) {
196     case LengthTypeUnknown:
197         ec = NOT_SUPPORTED_ERR;
198         break;
199     case LengthTypeNumber:
200         m_valueInSpecifiedUnits = value;
201         break;
202     case LengthTypePercentage: {
203         float result = convertValueFromUserUnitsToPercentage(value, context, ec);
204         if (!ec)
205             m_valueInSpecifiedUnits = result;
206         break;
207     }
208     case LengthTypeEMS: {
209         float result = convertValueFromUserUnitsToEMS(value, context, ec);
210         if (!ec)
211             m_valueInSpecifiedUnits = result;
212         break;
213     }
214     case LengthTypeEXS: {
215         float result = convertValueFromUserUnitsToEXS(value, context, ec);
216         if (!ec)
217             m_valueInSpecifiedUnits = result;
218         break;
219     }
220     case LengthTypePX:
221         m_valueInSpecifiedUnits = value;
222         break;
223     case LengthTypeCM:
224         m_valueInSpecifiedUnits = value * 2.54f / cssPixelsPerInch;
225         break;
226     case LengthTypeMM:
227         m_valueInSpecifiedUnits = value * 25.4f / cssPixelsPerInch;
228         break;
229     case LengthTypeIN:
230         m_valueInSpecifiedUnits = value / cssPixelsPerInch;
231         break;
232     case LengthTypePT:
233         m_valueInSpecifiedUnits = value * 72 / cssPixelsPerInch;
234         break;
235     case LengthTypePC:
236         m_valueInSpecifiedUnits = value * 6 / cssPixelsPerInch;
237         break;
238     }
239 }
240 
valueAsPercentage() const241 float SVGLength::valueAsPercentage() const
242 {
243     // 100% = 100.0 instead of 1.0 for historical reasons, this could eventually be changed
244     if (extractType(m_unit) == LengthTypePercentage)
245         return m_valueInSpecifiedUnits / 100;
246 
247     return m_valueInSpecifiedUnits;
248 }
249 
setValueAsString(const String & string,ExceptionCode & ec)250 void SVGLength::setValueAsString(const String& string, ExceptionCode& ec)
251 {
252     if (string.isEmpty())
253         return;
254 
255     float convertedNumber = 0;
256     const UChar* ptr = string.characters();
257     const UChar* end = ptr + string.length();
258 
259     if (!parseNumber(ptr, end, convertedNumber, false)) {
260         ec = SYNTAX_ERR;
261         return;
262     }
263 
264     SVGLengthType type = stringToLengthType(ptr, end);
265     ASSERT(ptr <= end);
266     if (type == LengthTypeUnknown) {
267         ec = SYNTAX_ERR;
268         return;
269     }
270 
271     m_unit = storeUnit(extractMode(m_unit), type);
272     m_valueInSpecifiedUnits = convertedNumber;
273 }
274 
valueAsString() const275 String SVGLength::valueAsString() const
276 {
277     return makeString(String::number(m_valueInSpecifiedUnits), lengthTypeToString(extractType(m_unit)));
278 }
279 
newValueSpecifiedUnits(unsigned short type,float value,ExceptionCode & ec)280 void SVGLength::newValueSpecifiedUnits(unsigned short type, float value, ExceptionCode& ec)
281 {
282     if (type == LengthTypeUnknown || type > LengthTypePC) {
283         ec = NOT_SUPPORTED_ERR;
284         return;
285     }
286 
287     m_unit = storeUnit(extractMode(m_unit), static_cast<SVGLengthType>(type));
288     m_valueInSpecifiedUnits = value;
289 }
290 
convertToSpecifiedUnits(unsigned short type,const SVGElement * context,ExceptionCode & ec)291 void SVGLength::convertToSpecifiedUnits(unsigned short type, const SVGElement* context, ExceptionCode& ec)
292 {
293     if (type == LengthTypeUnknown || type > LengthTypePC) {
294         ec = NOT_SUPPORTED_ERR;
295         return;
296     }
297 
298     float valueInUserUnits = value(context, ec);
299     if (ec)
300         return;
301 
302     unsigned int originalUnitAndType = m_unit;
303     m_unit = storeUnit(extractMode(m_unit), static_cast<SVGLengthType>(type));
304     setValue(valueInUserUnits, context, ec);
305     if (!ec)
306         return;
307 
308     // Eventually restore old unit and type
309     m_unit = originalUnitAndType;
310 }
311 
determineViewport(const SVGElement * context,float & width,float & height) const312 bool SVGLength::determineViewport(const SVGElement* context, float& width, float& height) const
313 {
314     if (!context)
315         return false;
316 
317     // Take size from outermost <svg> element.
318     Document* document = context->document();
319     if (document->documentElement() == context) {
320         if (RenderView* view = toRenderView(document->renderer())) {
321             width = view->viewWidth();
322             height = view->viewHeight();
323             return true;
324         }
325 
326         return false;
327     }
328 
329     // Resolve value against nearest viewport element (common case: inner <svg> elements)
330     SVGElement* viewportElement = context->viewportElement();
331     if (viewportElement && viewportElement->isSVG()) {
332         const SVGSVGElement* svg = static_cast<const SVGSVGElement*>(viewportElement);
333         if (svg->hasAttribute(SVGNames::viewBoxAttr)) {
334             width = svg->viewBox().width();
335             height = svg->viewBox().height();
336         } else {
337             width = svg->width().value(svg);
338             height = svg->height().value(svg);
339         }
340 
341         return true;
342     }
343 
344     // Resolve value against enclosing non-SVG RenderBox
345     if (!context->parentNode() || context->parentNode()->isSVGElement())
346         return false;
347 
348     RenderObject* renderer = context->renderer();
349     if (!renderer || !renderer->isBox())
350         return false;
351 
352     RenderBox* box = toRenderBox(renderer);
353     width = box->width();
354     height = box->height();
355     return true;
356 }
357 
convertValueFromUserUnitsToPercentage(float value,const SVGElement * context,ExceptionCode & ec) const358 float SVGLength::convertValueFromUserUnitsToPercentage(float value, const SVGElement* context, ExceptionCode& ec) const
359 {
360     float width = 0;
361     float height = 0;
362     if (!determineViewport(context, width, height)) {
363         ec = NOT_SUPPORTED_ERR;
364         return 0;
365     }
366 
367     switch (extractMode(m_unit)) {
368     case LengthModeWidth:
369         return value / width * 100;
370     case LengthModeHeight:
371         return value / height * 100;
372     case LengthModeOther:
373         return value / (sqrtf((width * width + height * height) / 2)) * 100;
374     };
375 
376     ASSERT_NOT_REACHED();
377     return 0;
378 }
379 
convertValueFromPercentageToUserUnits(float value,const SVGElement * context,ExceptionCode & ec) const380 float SVGLength::convertValueFromPercentageToUserUnits(float value, const SVGElement* context, ExceptionCode& ec) const
381 {
382     float width = 0;
383     float height = 0;
384     if (!determineViewport(context, width, height)) {
385         ec = NOT_SUPPORTED_ERR;
386         return 0;
387     }
388 
389     switch (extractMode(m_unit)) {
390     case LengthModeWidth:
391         return value * width;
392     case LengthModeHeight:
393         return value * height;
394     case LengthModeOther:
395         return value * sqrtf((width * width + height * height) / 2);
396     };
397 
398     ASSERT_NOT_REACHED();
399     return 0;
400 }
401 
convertValueFromUserUnitsToEMS(float value,const SVGElement * context,ExceptionCode & ec) const402 float SVGLength::convertValueFromUserUnitsToEMS(float value, const SVGElement* context, ExceptionCode& ec) const
403 {
404     if (!context || !context->renderer() || !context->renderer()->style()) {
405         ec = NOT_SUPPORTED_ERR;
406         return 0;
407     }
408 
409     RenderStyle* style = context->renderer()->style();
410     float fontSize = style->fontSize();
411     if (!fontSize) {
412         ec = NOT_SUPPORTED_ERR;
413         return 0;
414     }
415 
416     return value / fontSize;
417 }
418 
convertValueFromEMSToUserUnits(float value,const SVGElement * context,ExceptionCode & ec) const419 float SVGLength::convertValueFromEMSToUserUnits(float value, const SVGElement* context, ExceptionCode& ec) const
420 {
421     if (!context || !context->renderer() || !context->renderer()->style()) {
422         ec = NOT_SUPPORTED_ERR;
423         return 0;
424     }
425 
426     RenderStyle* style = context->renderer()->style();
427     return value * style->fontSize();
428 }
429 
convertValueFromUserUnitsToEXS(float value,const SVGElement * context,ExceptionCode & ec) const430 float SVGLength::convertValueFromUserUnitsToEXS(float value, const SVGElement* context, ExceptionCode& ec) const
431 {
432     if (!context || !context->renderer() || !context->renderer()->style()) {
433         ec = NOT_SUPPORTED_ERR;
434         return 0;
435     }
436 
437     RenderStyle* style = context->renderer()->style();
438 
439     // Use of ceil allows a pixel match to the W3Cs expected output of coords-units-03-b.svg
440     // if this causes problems in real world cases maybe it would be best to remove this
441     float xHeight = ceilf(style->fontMetrics().xHeight());
442     if (!xHeight) {
443         ec = NOT_SUPPORTED_ERR;
444         return 0;
445     }
446 
447     return value / xHeight;
448 }
449 
convertValueFromEXSToUserUnits(float value,const SVGElement * context,ExceptionCode & ec) const450 float SVGLength::convertValueFromEXSToUserUnits(float value, const SVGElement* context, ExceptionCode& ec) const
451 {
452     if (!context || !context->renderer() || !context->renderer()->style()) {
453         ec = NOT_SUPPORTED_ERR;
454         return 0;
455     }
456 
457     RenderStyle* style = context->renderer()->style();
458     // Use of ceil allows a pixel match to the W3Cs expected output of coords-units-03-b.svg
459     // if this causes problems in real world cases maybe it would be best to remove this
460     return value * ceilf(style->fontMetrics().xHeight());
461 }
462 
fromCSSPrimitiveValue(CSSPrimitiveValue * value)463 SVGLength SVGLength::fromCSSPrimitiveValue(CSSPrimitiveValue* value)
464 {
465     ASSERT(value);
466 
467     SVGLengthType svgType;
468     switch (value->primitiveType()) {
469     case CSSPrimitiveValue::CSS_NUMBER:
470         svgType = LengthTypeNumber;
471         break;
472     case CSSPrimitiveValue::CSS_PERCENTAGE:
473         svgType = LengthTypePercentage;
474         break;
475     case CSSPrimitiveValue::CSS_EMS:
476         svgType = LengthTypeEMS;
477         break;
478     case CSSPrimitiveValue::CSS_EXS:
479         svgType = LengthTypeEXS;
480         break;
481     case CSSPrimitiveValue::CSS_PX:
482         svgType = LengthTypePX;
483         break;
484     case CSSPrimitiveValue::CSS_CM:
485         svgType = LengthTypeCM;
486         break;
487     case CSSPrimitiveValue::CSS_MM:
488         svgType = LengthTypeMM;
489         break;
490     case CSSPrimitiveValue::CSS_IN:
491         svgType = LengthTypeIN;
492         break;
493     case CSSPrimitiveValue::CSS_PT:
494         svgType = LengthTypePT;
495         break;
496     case CSSPrimitiveValue::CSS_PC:
497         svgType = LengthTypePC;
498         break;
499     case CSSPrimitiveValue::CSS_UNKNOWN:
500     default:
501         svgType = LengthTypeUnknown;
502         break;
503     };
504 
505     if (svgType == LengthTypeUnknown)
506         return SVGLength();
507 
508     ExceptionCode ec = 0;
509     SVGLength length;
510     length.newValueSpecifiedUnits(svgType, value->getFloatValue(), ec);
511     if (ec)
512         return SVGLength();
513 
514     return length;
515 }
516 
toCSSPrimitiveValue(const SVGLength & length)517 PassRefPtr<CSSPrimitiveValue> SVGLength::toCSSPrimitiveValue(const SVGLength& length)
518 {
519     CSSPrimitiveValue::UnitTypes cssType = CSSPrimitiveValue::CSS_UNKNOWN;
520     switch (length.unitType()) {
521     case LengthTypeUnknown:
522         break;
523     case LengthTypeNumber:
524         cssType = CSSPrimitiveValue::CSS_NUMBER;
525         break;
526     case LengthTypePercentage:
527         cssType = CSSPrimitiveValue::CSS_PERCENTAGE;
528         break;
529     case LengthTypeEMS:
530         cssType = CSSPrimitiveValue::CSS_EMS;
531         break;
532     case LengthTypeEXS:
533         cssType = CSSPrimitiveValue::CSS_EXS;
534         break;
535     case LengthTypePX:
536         cssType = CSSPrimitiveValue::CSS_PX;
537         break;
538     case LengthTypeCM:
539         cssType = CSSPrimitiveValue::CSS_CM;
540         break;
541     case LengthTypeMM:
542         cssType = CSSPrimitiveValue::CSS_MM;
543         break;
544     case LengthTypeIN:
545         cssType = CSSPrimitiveValue::CSS_IN;
546         break;
547     case LengthTypePT:
548         cssType = CSSPrimitiveValue::CSS_PT;
549         break;
550     case LengthTypePC:
551         cssType = CSSPrimitiveValue::CSS_PC;
552         break;
553     };
554 
555     return CSSPrimitiveValue::create(length.valueInSpecifiedUnits(), cssType);
556 }
557 
558 }
559 
560 #endif
561