2010-11-10 Nikolas Zimmermann <nzimmermann@rim.com>
[WebKit-https.git] / WebCore / svg / SVGTransformable.cpp
1 /*
2  * Copyright (C) 2004, 2005, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3  * Copyright (C) 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org>
4  * Copyright (C) 2007 Eric Seidel <eric@webkit.org>
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 "SVGTransformable.h"
26
27 #include "AffineTransform.h"
28 #include "FloatConversion.h"
29 #include "SVGNames.h"
30 #include "SVGParserUtilities.h"
31 #include "SVGStyledElement.h"
32 #include "SVGTransformList.h"
33
34 namespace WebCore {
35
36 static int parseTransformParamList(const UChar*& ptr, const UChar* end, float* values, int required, int optional)
37 {
38     int optionalParams = 0, requiredParams = 0;
39     
40     if (!skipOptionalSpaces(ptr, end) || *ptr != '(')
41         return -1;
42     
43     ptr++;
44    
45     skipOptionalSpaces(ptr, end);
46
47     while (requiredParams < required) {
48         if (ptr >= end || !parseNumber(ptr, end, values[requiredParams], false))
49             return -1;
50         requiredParams++;
51         if (requiredParams < required)
52             skipOptionalSpacesOrDelimiter(ptr, end);
53     }
54     if (!skipOptionalSpaces(ptr, end))
55         return -1;
56     
57     bool delimParsed = skipOptionalSpacesOrDelimiter(ptr, end);
58
59     if (ptr >= end)
60         return -1;
61     
62     if (*ptr == ')') { // skip optionals
63         ptr++;
64         if (delimParsed)
65             return -1;
66     } else {
67         while (optionalParams < optional) {
68             if (ptr >= end || !parseNumber(ptr, end, values[requiredParams + optionalParams], false))
69                 return -1;
70             optionalParams++;
71             if (optionalParams < optional)
72                 skipOptionalSpacesOrDelimiter(ptr, end);
73         }
74         
75         if (!skipOptionalSpaces(ptr, end))
76             return -1;
77         
78         delimParsed = skipOptionalSpacesOrDelimiter(ptr, end);
79         
80         if (ptr >= end || *ptr != ')' || delimParsed)
81             return -1;
82         ptr++;
83     }
84
85     return requiredParams + optionalParams;
86 }
87
88 // These should be kept in sync with enum SVGTransformType
89 static const int requiredValuesForType[] =  {0, 6, 1, 1, 1, 1, 1};
90 static const int optionalValuesForType[] =  {0, 0, 1, 1, 2, 0, 0};
91
92 bool SVGTransformable::parseTransformValue(unsigned type, const UChar*& ptr, const UChar* end, SVGTransform& transform)
93 {
94     if (type == SVGTransform::SVG_TRANSFORM_UNKNOWN)
95         return false;
96
97     int valueCount = 0;
98     float values[] = {0, 0, 0, 0, 0, 0};
99     if ((valueCount = parseTransformParamList(ptr, end, values, requiredValuesForType[type], optionalValuesForType[type])) < 0)
100         return false;
101
102     switch (type) {
103     case SVGTransform::SVG_TRANSFORM_SKEWX:
104         transform.setSkewX(values[0]);
105         break;
106     case SVGTransform::SVG_TRANSFORM_SKEWY:
107         transform.setSkewY(values[0]);
108         break;
109     case SVGTransform::SVG_TRANSFORM_SCALE:
110         if (valueCount == 1) // Spec: if only one param given, assume uniform scaling
111             transform.setScale(values[0], values[0]);
112         else
113             transform.setScale(values[0], values[1]);
114         break;
115     case SVGTransform::SVG_TRANSFORM_TRANSLATE:
116         if (valueCount == 1) // Spec: if only one param given, assume 2nd param to be 0
117             transform.setTranslate(values[0], 0);
118         else
119             transform.setTranslate(values[0], values[1]);
120         break;
121     case SVGTransform::SVG_TRANSFORM_ROTATE:
122         if (valueCount == 1)
123             transform.setRotate(values[0], 0, 0);
124         else
125             transform.setRotate(values[0], values[1], values[2]);
126         break;
127     case SVGTransform::SVG_TRANSFORM_MATRIX:
128         transform.setMatrix(AffineTransform(values[0], values[1], values[2], values[3], values[4], values[5]));
129         break;
130     }
131
132     return true;
133 }
134
135 static const UChar skewXDesc[] =  {'s', 'k', 'e', 'w', 'X'};
136 static const UChar skewYDesc[] =  {'s', 'k', 'e', 'w', 'Y'};
137 static const UChar scaleDesc[] =  {'s', 'c', 'a', 'l', 'e'};
138 static const UChar translateDesc[] =  {'t', 'r', 'a', 'n', 's', 'l', 'a', 't', 'e'};
139 static const UChar rotateDesc[] =  {'r', 'o', 't', 'a', 't', 'e'};
140 static const UChar matrixDesc[] =  {'m', 'a', 't', 'r', 'i', 'x'};
141
142 static inline bool parseAndSkipType(const UChar*& currTransform, const UChar* end, unsigned short& type)
143 {
144     if (currTransform >= end)
145         return false;
146     
147     if (*currTransform == 's') {
148         if (skipString(currTransform, end, skewXDesc, sizeof(skewXDesc) / sizeof(UChar)))
149             type = SVGTransform::SVG_TRANSFORM_SKEWX;
150         else if (skipString(currTransform, end, skewYDesc, sizeof(skewYDesc) / sizeof(UChar)))
151             type = SVGTransform::SVG_TRANSFORM_SKEWY;
152         else if (skipString(currTransform, end, scaleDesc, sizeof(scaleDesc) / sizeof(UChar)))
153             type = SVGTransform::SVG_TRANSFORM_SCALE;
154         else
155             return false;
156     } else if (skipString(currTransform, end, translateDesc, sizeof(translateDesc) / sizeof(UChar)))
157         type = SVGTransform::SVG_TRANSFORM_TRANSLATE;
158     else if (skipString(currTransform, end, rotateDesc, sizeof(rotateDesc) / sizeof(UChar)))
159         type = SVGTransform::SVG_TRANSFORM_ROTATE;
160     else if (skipString(currTransform, end, matrixDesc, sizeof(matrixDesc) / sizeof(UChar)))
161         type = SVGTransform::SVG_TRANSFORM_MATRIX;
162     else 
163         return false;
164     
165     return true;
166 }
167
168 bool SVGTransformable::parseTransformAttribute(SVGTransformList& list, const AtomicString& transform)
169 {
170     const UChar* start = transform.characters();
171     return parseTransformAttribute(list, start, start + transform.length());
172 }
173
174 bool SVGTransformable::parseTransformAttribute(SVGTransformList& list, const UChar*& currTransform, const UChar* end, TransformParsingMode mode)
175 {
176     if (mode == ClearList)
177         list.clear();
178
179     bool delimParsed = false;
180     while (currTransform < end) {
181         delimParsed = false;
182         unsigned short type = SVGTransform::SVG_TRANSFORM_UNKNOWN;
183         skipOptionalSpaces(currTransform, end);
184
185         if (!parseAndSkipType(currTransform, end, type))
186             return false;
187
188         SVGTransform transform;
189         if (!parseTransformValue(type, currTransform, end, transform))
190             return false;
191
192         list.append(transform);
193         skipOptionalSpaces(currTransform, end);
194         if (currTransform < end && *currTransform == ',') {
195             delimParsed = true;
196             ++currTransform;
197         }
198         skipOptionalSpaces(currTransform, end);
199     }
200
201     return !delimParsed;
202 }
203
204 bool SVGTransformable::isKnownAttribute(const QualifiedName& attrName)
205 {
206     return attrName == SVGNames::transformAttr;
207 }
208
209 }
210
211 #endif // ENABLE(SVG)