2006-09-25 Anders Carlsson <acarlsson@apple.com>
[WebKit-https.git] / WebCore / ksvg2 / css / SVGCSSParser.cpp
1 /*
2     Copyright (C) 2004, 2005 Nikolas Zimmermann <wildfox@kde.org>
3                   2004, 2005 Rob Buis <buis@kde.org>
4     Copyright (C) 2005, 2006 Apple Computer, Inc.
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 #ifdef SVG_SUPPORT
26
27 #include "ksvg.h"
28
29 #include "SVGPaint.h"
30 #include "CSSInheritedValue.h"
31 #include "CSSInitialValue.h"
32 #include "cssparser.h"
33 #include "CSSPropertyNames.h"
34 #include "CSSQuirkPrimitiveValue.h"
35 #include "CSSValueKeywords.h"
36 #include "CSSValueList.h"
37 #include "ksvgcssproperties.c"
38 #include "ksvgcssvalues.c"
39
40 using namespace std;
41
42 namespace WebCore {
43
44 bool CSSParser::parseSVGValue(int propId, bool important)
45 {
46     if (!valueList)
47         return false;
48
49     Value* value = valueList->current();
50     if (!value)
51         return false;
52
53     int id = value->id;
54
55     int num = inShorthand() ? 1 : valueList->size();
56
57     if (id == CSS_VAL_INHERIT) {
58         if (num != 1)
59             return false;
60         addProperty(propId, new CSSInheritedValue(), important);
61         return true;
62     } else if (id == CSS_VAL_INITIAL) {
63         if (num != 1)
64             return false;
65         addProperty(propId, new CSSInitialValue(), important);
66         return true;
67     }
68     
69     bool valid_primitive = false;
70     CSSValue* parsedValue = 0;
71
72     switch(propId)
73     {
74     /* The comment to the right defines all valid value of these
75      * properties as defined in SVG 1.1, Appendix N. Property index */
76     case SVGCSS_PROP_ALIGNMENT_BASELINE:
77     // auto | baseline | before-edge | text-before-edge | middle |
78     // central | after-edge | text-after-edge | ideographic | alphabetic |
79     // hanging | mathematical | inherit
80         if (id == CSS_VAL_AUTO || id == CSS_VAL_BASELINE || id == CSS_VAL_MIDDLE ||
81           (id >= SVGCSS_VAL_BEFORE_EDGE && id <= SVGCSS_VAL_MATHEMATICAL))
82             valid_primitive = true;
83         break;
84
85     case SVGCSS_PROP_BASELINE_SHIFT:
86     // baseline | super | sub | <percentage> | <length> | inherit
87         if (id == CSS_VAL_BASELINE || id == CSS_VAL_SUB ||
88            id >= CSS_VAL_SUPER)
89             valid_primitive = true;
90         else
91             valid_primitive = validUnit(value, FLength|FPercent, false);
92         break;
93
94     case SVGCSS_PROP_DOMINANT_BASELINE:
95     // auto | use-script | no-change | reset-size | ideographic |
96     // alphabetic | hanging | mathematical | central | middle |
97     // text-after-edge | text-before-edge | inherit
98         if (id == CSS_VAL_AUTO || id == CSS_VAL_MIDDLE ||
99           (id >= SVGCSS_VAL_USE_SCRIPT && id <= SVGCSS_VAL_RESET_SIZE) ||
100           (id >= SVGCSS_VAL_CENTRAL && id <= SVGCSS_VAL_MATHEMATICAL))
101             valid_primitive = true;
102         break;
103
104     case SVGCSS_PROP_ENABLE_BACKGROUND:
105     // accumulate | new [x] [y] [width] [height] | inherit
106         if (id == SVGCSS_VAL_ACCUMULATE) // TODO : new
107             valid_primitive = true;
108         break;
109
110     case SVGCSS_PROP_MARKER_START:
111     case SVGCSS_PROP_MARKER_MID:
112     case SVGCSS_PROP_MARKER_END:
113     case SVGCSS_PROP_MASK:
114         if (id == CSS_VAL_NONE)
115             valid_primitive = true;
116         else if (value->unit == CSSPrimitiveValue::CSS_URI) {
117             parsedValue = new CSSPrimitiveValue(domString(value->string), CSSPrimitiveValue::CSS_URI);
118             if (parsedValue)
119                 valueList->next();
120         }
121         break;
122
123     case SVGCSS_PROP_CLIP_RULE:            // nonzero | evenodd | inherit
124     case SVGCSS_PROP_FILL_RULE:
125         if (id == SVGCSS_VAL_NONZERO || id == SVGCSS_VAL_EVENODD)
126             valid_primitive = true;
127         break;
128
129     case SVGCSS_PROP_STROKE_MITERLIMIT:   // <miterlimit> | inherit
130         valid_primitive = validUnit(value, FInteger|FNonNeg, false);
131         break;
132
133     case SVGCSS_PROP_STROKE_LINEJOIN:   // miter | round | bevel | inherit
134         if (id == SVGCSS_VAL_MITER || id == CSS_VAL_ROUND || id == SVGCSS_VAL_BEVEL)
135             valid_primitive = true;
136         break;
137
138     case SVGCSS_PROP_STROKE_LINECAP:    // butt | round | square | inherit
139         if (id == SVGCSS_VAL_BUTT || id == CSS_VAL_ROUND || id == CSS_VAL_SQUARE)
140             valid_primitive = true;
141         break;
142
143     case SVGCSS_PROP_STROKE_OPACITY:   // <opacity-value> | inherit
144     case SVGCSS_PROP_FILL_OPACITY:
145     case SVGCSS_PROP_STOP_OPACITY:
146     case SVGCSS_PROP_FLOOD_OPACITY:
147         valid_primitive = (!id && validUnit(value, FNumber|FPercent, false));
148         break;
149
150     case SVGCSS_PROP_SHAPE_RENDERING:
151     // auto | optimizeSpeed | crispEdges | geometricPrecision | inherit
152         if (id == CSS_VAL_AUTO || id == SVGCSS_VAL_OPTIMIZESPEED ||
153             id == SVGCSS_VAL_CRISPEDGES || id == SVGCSS_VAL_GEOMETRICPRECISION)
154             valid_primitive = true;
155         break;
156
157     case SVGCSS_PROP_TEXT_RENDERING:   // auto | optimizeSpeed | optimizeLegibility | geometricPrecision | inherit
158         if (id == CSS_VAL_AUTO || id == SVGCSS_VAL_OPTIMIZESPEED || id == SVGCSS_VAL_OPTIMIZELEGIBILITY ||
159        id == SVGCSS_VAL_GEOMETRICPRECISION)
160             valid_primitive = true;
161         break;
162
163     case SVGCSS_PROP_IMAGE_RENDERING:  // auto | optimizeSpeed |
164     case SVGCSS_PROP_COLOR_RENDERING:  // optimizeQuality | inherit
165         if (id == CSS_VAL_AUTO || id == SVGCSS_VAL_OPTIMIZESPEED ||
166             id == SVGCSS_VAL_OPTIMIZEQUALITY)
167             valid_primitive = true;
168         break;
169
170     case SVGCSS_PROP_COLOR_PROFILE: // auto | sRGB | <name> | <uri> inherit
171         if (id == CSS_VAL_AUTO || id == SVGCSS_VAL_SRGB)
172             valid_primitive = true;
173         break;
174
175     case SVGCSS_PROP_COLOR_INTERPOLATION:   // auto | sRGB | linearRGB | inherit
176     case SVGCSS_PROP_COLOR_INTERPOLATION_FILTERS:  
177         if (id == CSS_VAL_AUTO || id == SVGCSS_VAL_SRGB || id == SVGCSS_VAL_LINEARRGB)
178             valid_primitive = true;
179         break;
180
181     /* Start of supported CSS properties with validation. This is needed for parseShortHand to work
182      * correctly and allows optimization in applyRule(..)
183      */
184
185     case SVGCSS_PROP_POINTER_EVENTS:
186     // none | visiblePainted | visibleFill | visibleStroke | visible |
187     // painted | fill | stroke | none | all | inherit
188         if (id == CSS_VAL_VISIBLE || id == CSS_VAL_NONE ||
189           (id >= SVGCSS_VAL_VISIBLEPAINTED && id <= SVGCSS_VAL_ALL))
190             valid_primitive = true;
191         break;
192
193     case SVGCSS_PROP_TEXT_ANCHOR:    // start | middle | end | inherit
194         if (id == CSS_VAL_START || id == CSS_VAL_MIDDLE || id == CSS_VAL_END)
195             valid_primitive = true;
196         break;
197
198     case SVGCSS_PROP_GLYPH_ORIENTATION_VERTICAL: // auto | <angle> | inherit
199         if (id == CSS_VAL_AUTO) {
200             valid_primitive = true;
201             break;
202         }
203         /* fallthrough intentional */
204     case SVGCSS_PROP_GLYPH_ORIENTATION_HORIZONTAL: // <angle> | inherit
205         if (value->unit == CSSPrimitiveValue::CSS_DEG)
206             parsedValue = new CSSPrimitiveValue(value->fValue, CSSPrimitiveValue::CSS_DEG);
207         else if (value->unit == CSSPrimitiveValue::CSS_GRAD)
208             parsedValue = new CSSPrimitiveValue(value->fValue, CSSPrimitiveValue::CSS_GRAD);
209         else if (value->unit == CSSPrimitiveValue::CSS_RAD)
210             parsedValue = new CSSPrimitiveValue(value->fValue, CSSPrimitiveValue::CSS_RAD);
211         break;
212
213     case SVGCSS_PROP_FILL:                 // <paint> | inherit
214     case SVGCSS_PROP_STROKE:               // <paint> | inherit
215         {
216             if (id == CSS_VAL_NONE)
217                 parsedValue = new SVGPaint(SVGPaint::SVG_PAINTTYPE_NONE);
218             else if (id == SVGCSS_VAL_CURRENTCOLOR)
219                 parsedValue = new SVGPaint(SVGPaint::SVG_PAINTTYPE_CURRENTCOLOR);
220             else if (value->unit == CSSPrimitiveValue::CSS_URI)
221                 parsedValue = new SVGPaint(SVGPaint::SVG_PAINTTYPE_URI, domString(value->string).impl());
222             else
223                 parsedValue = parseSVGPaint();
224
225             if (parsedValue)
226                 valueList->next();
227         }
228         break;
229
230     case CSS_PROP_COLOR:                // <color> | inherit
231         if ((id >= CSS_VAL_AQUA && id <= CSS_VAL_WINDOWTEXT) ||
232            (id >= SVGCSS_VAL_ALICEBLUE && id <= SVGCSS_VAL_YELLOWGREEN))
233             parsedValue = new SVGColor(domString(value->string).impl());
234         else
235             parsedValue = parseSVGColor();
236
237         if (parsedValue)
238             valueList->next();
239         break;
240
241     case SVGCSS_PROP_STOP_COLOR: // TODO : icccolor
242     case SVGCSS_PROP_FLOOD_COLOR:
243     case SVGCSS_PROP_LIGHTING_COLOR:
244         if ((id >= CSS_VAL_AQUA && id <= CSS_VAL_WINDOWTEXT) ||
245            (id >= SVGCSS_VAL_ALICEBLUE && id <= SVGCSS_VAL_YELLOWGREEN))
246             parsedValue = new SVGColor(domString(value->string).impl());
247         else if (id == SVGCSS_VAL_CURRENTCOLOR)
248             parsedValue = new SVGColor(SVGColor::SVG_COLORTYPE_CURRENTCOLOR);
249         else // TODO : svgcolor (iccColor)
250             parsedValue = parseSVGColor();
251
252         if (parsedValue)
253             valueList->next();
254
255         break;
256
257     case SVGCSS_PROP_WRITING_MODE:
258     // lr-tb | rl_tb | tb-rl | lr | rl | tb | inherit
259         if (id >= SVGCSS_VAL_LR_TB && id <= SVGCSS_VAL_TB)
260             valid_primitive = true;
261         break;
262
263     case SVGCSS_PROP_STROKE_WIDTH:         // <length> | inherit
264     case SVGCSS_PROP_STROKE_DASHOFFSET:
265         valid_primitive = validUnit(value, FLength | FPercent, false);
266         break;
267     case SVGCSS_PROP_STROKE_DASHARRAY:     // none | <dasharray> | inherit
268         if (id == CSS_VAL_NONE)
269             valid_primitive = true;
270         else
271             parsedValue = parseSVGStrokeDasharray();
272
273         break;
274
275     case SVGCSS_PROP_KERNING:              // auto | normal | <length> | inherit
276         if (id == CSS_VAL_AUTO)
277             valid_primitive = true;
278         else
279             valid_primitive = validUnit(value, FLength, false);
280         break;
281
282     case SVGCSS_PROP_CLIP_PATH:    // <uri> | none | inherit
283     case SVGCSS_PROP_FILTER:
284         if (id == CSS_VAL_NONE)
285             valid_primitive = true;
286         else if (value->unit == CSSPrimitiveValue::CSS_URI) {
287             parsedValue = new CSSPrimitiveValue(domString(value->string), (CSSPrimitiveValue::UnitTypes) value->unit);
288             if (parsedValue)
289                 valueList->next();
290         }
291         break;
292
293     /* shorthand properties */
294     case SVGCSS_PROP_MARKER:
295     {
296         const int properties[3] = { SVGCSS_PROP_MARKER_START,
297                                     SVGCSS_PROP_MARKER_MID,
298                                     SVGCSS_PROP_MARKER_END };
299         return parseShorthand(propId, properties, 3, important);
300     }
301     default:
302         return false;
303     }
304
305     if (valid_primitive) {
306         if (id != 0)
307             parsedValue = new CSSPrimitiveValue(id);
308         else if (value->unit == CSSPrimitiveValue::CSS_STRING)
309             parsedValue = new CSSPrimitiveValue(domString(value->string), (CSSPrimitiveValue::UnitTypes) value->unit);
310         else if (value->unit >= CSSPrimitiveValue::CSS_NUMBER && value->unit <= CSSPrimitiveValue::CSS_KHZ)
311             parsedValue = new CSSPrimitiveValue(value->fValue, (CSSPrimitiveValue::UnitTypes) value->unit);
312         else if (value->unit >= Value::Q_EMS)
313             parsedValue = new CSSQuirkPrimitiveValue(value->fValue, CSSPrimitiveValue::CSS_EMS);
314         valueList->next();
315     }
316     if (parsedValue) {
317         if (!valueList->current() || inShorthand()) {
318             addProperty(propId, parsedValue, important);
319             return true;
320         }
321         delete parsedValue;
322     }
323     return false;
324 }
325
326 CSSValue* CSSParser::parseSVGStrokeDasharray()
327 {
328     CSSValueList* ret = new CSSValueList;
329     Value* value = valueList->current();
330     bool valid_primitive = true;
331     while(valid_primitive && value) {
332         valid_primitive = validUnit(value, FLength | FPercent |FNonNeg, false);
333         if (value->id != 0)
334             ret->append(new CSSPrimitiveValue(value->id));
335         else if (value->unit == CSSPrimitiveValue::CSS_STRING)
336             ret->append(new CSSPrimitiveValue(domString(value->string), (CSSPrimitiveValue::UnitTypes) value->unit));
337         else if (value->unit >= CSSPrimitiveValue::CSS_NUMBER && value->unit <= CSSPrimitiveValue::CSS_KHZ)
338             ret->append(new CSSPrimitiveValue(value->fValue, (CSSPrimitiveValue::UnitTypes) value->unit));
339         value = valueList->next();
340         if (value && value->unit == Value::Operator && value->iValue == ',')
341             value = valueList->next();
342     }
343     if (!valid_primitive) {
344         delete ret;
345         ret = 0;
346     }
347
348     return ret;
349 }
350
351 CSSValue* CSSParser::parseSVGPaint()
352 {
353     Value* value = valueList->current();
354     if (!strict && value->unit == CSSPrimitiveValue::CSS_NUMBER &&
355        value->fValue >= 0. && value->fValue < 1000000.) {
356         String str = String::sprintf("%06d", (int)(value->fValue+.5));
357         return new SVGPaint(SVGPaint::SVG_PAINTTYPE_RGBCOLOR, String(), str);
358     } else if (value->unit == CSSPrimitiveValue::CSS_RGBCOLOR) {
359         String str = "#" + domString(value->string);
360         return new SVGPaint(SVGPaint::SVG_PAINTTYPE_RGBCOLOR, String(), str);
361     } else if (value->unit == CSSPrimitiveValue::CSS_IDENT ||
362            (!strict && value->unit == CSSPrimitiveValue::CSS_DIMENSION))
363         return new SVGPaint(SVGPaint::SVG_PAINTTYPE_RGBCOLOR, String(), domString(value->string));
364     else if (value->unit == Value::Function && value->function->args != 0 &&
365             domString(value->function->name).lower() == "rgb(") {
366         ValueList* args = value->function->args;
367         Value* v = args->current();
368         if (!validUnit(v, FInteger|FPercent, true))
369             return 0;
370         int r = (int) (v->fValue * (v->unit == CSSPrimitiveValue::CSS_PERCENTAGE ? 256./100. : 1.));
371         v = args->next();
372         if (v->unit != Value::Operator && v->iValue != ',')
373             return 0;
374         v = args->next();
375         if (!validUnit(v, FInteger|FPercent, true))
376             return 0;
377         int g = (int) (v->fValue * (v->unit == CSSPrimitiveValue::CSS_PERCENTAGE ? 256./100. : 1.));
378         v = args->next();
379         if (v->unit != Value::Operator && v->iValue != ',')
380             return 0;
381         v = args->next();
382         if (!validUnit(v, FInteger|FPercent, true))
383             return 0;
384         int b = (int) (v->fValue * (v->unit == CSSPrimitiveValue::CSS_PERCENTAGE ? 256./100. : 1.));
385         r = max(0, min(255, r));
386         g = max(0, min(255, g));
387         b = max(0, min(255, b));
388         
389         return new SVGPaint(SVGPaint::SVG_PAINTTYPE_RGBCOLOR, String(), String::sprintf("rgb(%d, %d, %d)", r, g, b).impl());
390     }
391     else
392         return 0;
393
394     return new SVGPaint();
395 }
396
397 CSSValue* CSSParser::parseSVGColor()
398 {
399     Value* value = valueList->current();
400     if (!strict && value->unit == CSSPrimitiveValue::CSS_NUMBER && value->fValue >= 0. && value->fValue < 1000000.)
401         return new SVGColor(String::sprintf("%06d", (int)(value->fValue+.5)).impl());
402     else if (value->unit == CSSPrimitiveValue::CSS_RGBCOLOR) {
403         String str = "#" + domString(value->string);
404         return new SVGColor(str.impl());
405     } else if (value->unit == CSSPrimitiveValue::CSS_IDENT || (!strict && value->unit == CSSPrimitiveValue::CSS_DIMENSION))
406         return new SVGColor(domString(value->string).impl());
407     else if (value->unit == Value::Function && value->function->args != 0 && domString(value->function->name).lower() == "rgb(") {
408         ValueList* args = value->function->args;
409         Value* v = args->current();
410         if (!validUnit(v, FInteger|FPercent, true))
411             return 0;
412         int r = (int) (v->fValue * (v->unit == CSSPrimitiveValue::CSS_PERCENTAGE ? 256./100. : 1.));
413         v = args->next();
414         if (v->unit != Value::Operator && v->iValue != ',')
415             return 0;
416         v = args->next();
417         if (!validUnit(v, FInteger|FPercent, true))
418             return 0;
419         int g = (int) (v->fValue * (v->unit == CSSPrimitiveValue::CSS_PERCENTAGE ? 256./100. : 1.));
420         v = args->next();
421         if (v->unit != Value::Operator && v->iValue != ',')
422             return 0;
423         v = args->next();
424         if (!validUnit(v, FInteger|FPercent, true))
425             return 0;
426         int b = (int) (v->fValue * (v->unit == CSSPrimitiveValue::CSS_PERCENTAGE ? 256./100. : 1.));
427         r = max(0, min(255, r));
428         g = max(0, min(255, g));
429         b = max(0, min(255, b));
430         
431         return new SVGColor(String::sprintf("rgb(%d, %d, %d)", r, g, b).impl());
432     }
433     else
434         return 0;
435
436     return new SVGPaint();
437 }
438
439 }
440
441 #endif // SVG_SUPPORT
442
443 // vim:ts=4:noet