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