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