1 /*
2
3    Licensed to the Apache Software Foundation (ASF) under one or more
4    contributor license agreements.  See the NOTICE file distributed with
5    this work for additional information regarding copyright ownership.
6    The ASF licenses this file to You under the Apache License, Version 2.0
7    (the "License"); you may not use this file except in compliance with
8    the License.  You may obtain a copy of the License at
9
10        http://www.apache.org/licenses/LICENSE-2.0
11
12    Unless required by applicable law or agreed to in writing, software
13    distributed under the License is distributed on an "AS IS" BASIS,
14    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15    See the License for the specific language governing permissions and
16    limitations under the License.
17
18  */

19 package org.apache.batik.css.engine.value.svg;
20
21 import org.apache.batik.css.engine.CSSEngine;
22 import org.apache.batik.css.engine.CSSStylableElement;
23 import org.apache.batik.css.engine.StyleMap;
24 import org.apache.batik.css.engine.value.ListValue;
25 import org.apache.batik.css.engine.value.Value;
26 import org.apache.batik.css.engine.value.ValueManager;
27 import org.apache.batik.css.engine.value.svg12.CIELCHColor;
28 import org.apache.batik.css.engine.value.svg12.CIELabColor;
29 import org.apache.batik.css.engine.value.svg12.DeviceColor;
30 import org.apache.batik.css.engine.value.svg12.ICCNamedColor;
31 import org.apache.batik.util.CSSConstants;
32 import org.apache.batik.util.SVGTypes;
33
34 import org.w3c.css.sac.LexicalUnit;
35 import org.w3c.dom.DOMException;
36 import org.w3c.dom.css.CSSValue;
37
38 /**
39  * This class provides a manager for the SVGColor property values.
40  *
41  * @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a>
42  * @version $Id: SVGColorManager.java 1733416 2016-03-03 07:07:13Z gadams $
43  */

44 public class SVGColorManager extends ColorManager {
45
46     /**
47      * The name of the handled property.
48      */

49     protected String property;
50
51     /**
52      * The default value.
53      */

54     protected Value defaultValue;
55
56     /**
57      * Creates a new SVGColorManager.
58      * The default value is black.
59      */

60     public SVGColorManager(String prop) {
61         this(prop, SVGValueConstants.BLACK_RGB_VALUE);
62     }
63
64     /**
65      * Creates a new SVGColorManager.
66      */

67     public SVGColorManager(String prop, Value v) {
68         property = prop;
69         defaultValue = v;
70     }
71
72     /**
73      * Implements {@link ValueManager#isInheritedProperty()}.
74      */

75     public boolean isInheritedProperty() {
76         return false;
77     }
78
79     /**
80      * Implements {@link ValueManager#isAnimatableProperty()}.
81      */

82     public boolean isAnimatableProperty() {
83         return true;
84     }
85
86     /**
87      * Implements {@link ValueManager#isAdditiveProperty()}.
88      */

89     public boolean isAdditiveProperty() {
90         return true;
91     }
92
93     /**
94      * Implements {@link ValueManager#getPropertyType()}.
95      */

96     public int getPropertyType() {
97         return SVGTypes.TYPE_COLOR;
98     }
99
100     /**
101      * Implements {@link ValueManager#getPropertyName()}.
102      */

103     public String getPropertyName() {
104         return property;
105     }
106
107
108     /**
109      * Implements {@link
110      * org.apache.batik.css.engine.value.ValueManager#getDefaultValue()}.
111      */

112     public Value getDefaultValue() {
113         return defaultValue;
114     }
115
116     /**
117      * Implements {@link ValueManager#createValue(LexicalUnit,CSSEngine)}.
118      */

119     public Value createValue(LexicalUnit lu, CSSEngine engine)
120         throws DOMException {
121         if (lu.getLexicalUnitType() == LexicalUnit.SAC_IDENT) {
122             if (lu.getStringValue().equalsIgnoreCase
123                 (CSSConstants.CSS_CURRENTCOLOR_VALUE)) {
124                 return SVGValueConstants.CURRENTCOLOR_VALUE;
125             }
126         }
127         Value v = super.createValue(lu, engine);
128         lu = lu.getNextLexicalUnit();
129         if (lu == null) {
130             return v;
131         }
132
133         //If we have more content here, there is a color function after the sRGB color.
134         if (lu.getLexicalUnitType() != LexicalUnit.SAC_FUNCTION) {
135             throw createInvalidLexicalUnitDOMException
136                 (lu.getLexicalUnitType());
137         }
138
139         ListValue result = new ListValue(' ');
140         result.append(v);
141
142         Value colorValue = parseColorFunction(lu, v);
143         if (colorValue != null) {
144             result.append(colorValue);
145         } else {
146             return v; //use sRGB fallback if an unsupported color function is encountered
147         }
148         return result;
149     }
150
151     private Value parseColorFunction(LexicalUnit lu, Value v) {
152         String functionName = lu.getFunctionName();
153         if (functionName.equalsIgnoreCase(ICCColor.ICC_COLOR_FUNCTION)) {
154             return createICCColorValue(lu, v);
155         }
156         return parseColor12Function(lu, v);
157     }
158
159     private Value parseColor12Function(LexicalUnit lu, Value v) {
160         String functionName = lu.getFunctionName();
161         if (functionName.equalsIgnoreCase(ICCNamedColor.ICC_NAMED_COLOR_FUNCTION)) {
162             return createICCNamedColorValue(lu, v);
163         } else if (functionName.equalsIgnoreCase(CIELabColor.CIE_LAB_COLOR_FUNCTION)) {
164             return createCIELabColorValue(lu, v);
165         } else if (functionName.equalsIgnoreCase(CIELCHColor.CIE_LCH_COLOR_FUNCTION)) {
166             return createCIELCHColorValue(lu, v);
167         } else if (functionName.equalsIgnoreCase(DeviceColor.DEVICE_CMYK_COLOR_FUNCTION)) {
168             return createDeviceColorValue(lu, v, 4);
169         } else if (functionName.equalsIgnoreCase(DeviceColor.DEVICE_RGB_COLOR_FUNCTION)) {
170             return createDeviceColorValue(lu, v, 3);
171         } else if (functionName.equalsIgnoreCase(DeviceColor.DEVICE_GRAY_COLOR_FUNCTION)) {
172             return createDeviceColorValue(lu, v, 1);
173         } else if (functionName.equalsIgnoreCase(DeviceColor.DEVICE_NCHANNEL_COLOR_FUNCTION)) {
174             return createDeviceColorValue(lu, v, 0);
175         }
176         return null;
177     }
178
179     private Value createICCColorValue(LexicalUnit lu, Value v) {
180         lu = lu.getParameters();
181         expectIdent(lu);
182
183         ICCColor icc = new ICCColor(lu.getStringValue());
184
185         lu = lu.getNextLexicalUnit();
186         while (lu != null) {
187             expectComma(lu);
188             lu = lu.getNextLexicalUnit();
189             icc.append(getColorValue(lu));
190             lu = lu.getNextLexicalUnit();
191         }
192         return icc;
193     }
194
195     private Value createICCNamedColorValue(LexicalUnit lu, Value v) {
196         lu = lu.getParameters();
197         expectIdent(lu);
198         String profileName = lu.getStringValue();
199
200         lu = lu.getNextLexicalUnit();
201         expectComma(lu);
202         lu = lu.getNextLexicalUnit();
203         expectIdent(lu);
204         String colorName = lu.getStringValue();
205
206         ICCNamedColor icc = new ICCNamedColor(profileName, colorName);
207
208         lu = lu.getNextLexicalUnit();
209         return icc;
210     }
211
212     private Value createCIELabColorValue(LexicalUnit lu, Value v) {
213         lu = lu.getParameters();
214         float l = getColorValue(lu);
215         lu = lu.getNextLexicalUnit();
216         expectComma(lu);
217         lu = lu.getNextLexicalUnit();
218         float a = getColorValue(lu);
219         lu = lu.getNextLexicalUnit();
220         expectComma(lu);
221         lu = lu.getNextLexicalUnit();
222         float b = getColorValue(lu);
223
224         CIELabColor icc = new CIELabColor(l, a, b);
225
226         lu = lu.getNextLexicalUnit();
227         return icc;
228     }
229
230     private Value createCIELCHColorValue(LexicalUnit lu, Value v) {
231         lu = lu.getParameters();
232         float l = getColorValue(lu);
233         lu = lu.getNextLexicalUnit();
234         expectComma(lu);
235         lu = lu.getNextLexicalUnit();
236         float c = getColorValue(lu);
237         lu = lu.getNextLexicalUnit();
238         expectComma(lu);
239         lu = lu.getNextLexicalUnit();
240         float h = getColorValue(lu);
241
242         CIELCHColor icc = new CIELCHColor(l, c, h);
243
244         lu = lu.getNextLexicalUnit();
245         return icc;
246     }
247
248     private Value createDeviceColorValue(LexicalUnit lu, Value v, int expectedComponents) {
249         lu = lu.getParameters();
250
251         boolean nChannel = (expectedComponents <= 0);
252         DeviceColor col = new DeviceColor(nChannel);
253
254         col.append(getColorValue(lu));
255         LexicalUnit lastUnit = lu;
256         lu = lu.getNextLexicalUnit();
257         while (lu != null) {
258             expectComma(lu);
259             lu = lu.getNextLexicalUnit();
260             col.append(getColorValue(lu));
261             lastUnit = lu;
262             lu = lu.getNextLexicalUnit();
263         }
264         if (!nChannel && expectedComponents != col.getNumberOfColors()) {
265             throw createInvalidLexicalUnitDOMException(lastUnit.getLexicalUnitType());
266         }
267         return col;
268     }
269
270     private void expectIdent(LexicalUnit lu) {
271         if (lu.getLexicalUnitType() != LexicalUnit.SAC_IDENT) {
272             throw createInvalidLexicalUnitDOMException
273                 (lu.getLexicalUnitType());
274         }
275     }
276
277     private void expectComma(LexicalUnit lu) {
278         if (lu.getLexicalUnitType() != LexicalUnit.SAC_OPERATOR_COMMA) {
279             throw createInvalidLexicalUnitDOMException
280                 (lu.getLexicalUnitType());
281         }
282     }
283
284     private void expectNonNull(LexicalUnit lu) {
285         if (lu == null) {
286             throw createInvalidLexicalUnitDOMException((short)-1);
287         }
288     }
289
290     /**
291      * Implements {@link
292      * ValueManager#computeValue(CSSStylableElement,String,CSSEngine,int,StyleMap,Value)}.
293      */

294     public Value computeValue(CSSStylableElement elt,
295                               String pseudo,
296                               CSSEngine engine,
297                               int idx,
298                               StyleMap sm,
299                               Value value) {
300         if (value == SVGValueConstants.CURRENTCOLOR_VALUE) {
301             sm.putColorRelative(idx, true);
302
303             int ci = engine.getColorIndex();
304             return engine.getComputedStyle(elt, pseudo, ci);
305         }
306         if (value.getCssValueType() == CSSValue.CSS_VALUE_LIST) {
307             ListValue lv = (ListValue)value;
308             Value v = lv.item(0);
309             Value t = super.computeValue(elt, pseudo, engine, idx, sm, v);
310             if (t != v) {
311                 ListValue result = new ListValue(' ');
312                 result.append(t);
313                 result.append(lv.item(1));
314                 return result;
315             }
316             return value;
317         }
318         return super.computeValue(elt, pseudo, engine, idx, sm, value);
319     }
320
321     /**
322      * Creates a float value usable as a component of an RGBColor.
323      */

324     protected float getColorValue(LexicalUnit lu) {
325         expectNonNull(lu);
326         switch (lu.getLexicalUnitType()) {
327         case LexicalUnit.SAC_INTEGER:
328             return lu.getIntegerValue();
329         case LexicalUnit.SAC_REAL:
330             return lu.getFloatValue();
331         }
332         throw createInvalidLexicalUnitDOMException(lu.getLexicalUnitType());
333     }
334 }
335