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
20 package org.apache.batik.css.engine.value.css2;
21
22 import java.util.HashSet;
23 import java.util.Set;
24
25 import org.apache.batik.css.engine.CSSEngine;
26 import org.apache.batik.css.engine.value.ValueManager;
27 import org.apache.batik.css.engine.value.IdentifierManager;
28 import org.apache.batik.css.engine.value.AbstractValueFactory;
29 import org.apache.batik.css.engine.value.ShorthandManager;
30 import org.apache.batik.css.engine.value.StringMap;
31 import org.apache.batik.css.parser.CSSLexicalUnit;
32 import org.apache.batik.util.CSSConstants;
33 import org.w3c.css.sac.LexicalUnit;
34
35 /**
36  * This class provides support for the CSS2 'font' shorthand property.
37  *
38  * The form of this property is:
39  *     [ [ <font-style> || <font-variant> || <font-weight> ]?
40  *         <font-size> [ / <line-height> ]? <font-family> ] |
41  *       caption | icon | menu | message-box | small-caption |
42  *       status-bar | inherit
43  *
44  *  It is worth noting that there is a potential ambiguity
45  * between font-size and font-weight since in SVG they can both
46  * be unitless.  This is solved by considering the 'last' number
47  * before an 'ident' or '/' to be font-size and any preceeding
48  * number to be font-weight.
49  *
50  * @author <a href="mailto:deweese@apache.org">deweese</a>
51  * @version $Id: FontShorthandManager.java 1808001 2017-09-11 09:51:29Z ssteiner $
52  */

53 public class FontShorthandManager
54     extends AbstractValueFactory
55     implements ShorthandManager {
56
57     public FontShorthandManager() { }
58
59     /**
60      * Implements {@link ValueManager#getPropertyName()}.
61      */

62     public String getPropertyName() {
63         return CSSConstants.CSS_FONT_PROPERTY;
64     }
65
66     /**
67      * Implements {@link ShorthandManager#isAnimatableProperty()}.
68      */

69     public boolean isAnimatableProperty() {
70         return true;
71     }
72
73     /**
74      * Implements {@link ValueManager#isAdditiveProperty()}.
75      */

76     public boolean isAdditiveProperty() {
77         return false;
78     }
79
80     static LexicalUnit NORMAL_LU = CSSLexicalUnit.createString
81         (LexicalUnit.SAC_IDENT, CSSConstants.CSS_NORMAL_VALUE, null);
82     static LexicalUnit BOLD_LU = CSSLexicalUnit.createString
83         (LexicalUnit.SAC_IDENT, CSSConstants.CSS_BOLD_VALUE, null);
84
85     static LexicalUnit MEDIUM_LU = CSSLexicalUnit.createString
86         (LexicalUnit.SAC_IDENT, CSSConstants.CSS_MEDIUM_VALUE, null);
87
88     static LexicalUnit SZ_10PT_LU = CSSLexicalUnit.createFloat
89         (LexicalUnit.SAC_POINT, 10, null);
90     static LexicalUnit SZ_8PT_LU = CSSLexicalUnit.createFloat
91         (LexicalUnit.SAC_POINT, 8, null);
92
93
94     static LexicalUnit FONT_FAMILY_LU;
95     static {
96         LexicalUnit lu;
97         FONT_FAMILY_LU = CSSLexicalUnit.createString
98             (LexicalUnit.SAC_IDENT, "Dialog"null);
99         lu = CSSLexicalUnit.createString
100             (LexicalUnit.SAC_IDENT, "Helvetica", FONT_FAMILY_LU);
101         CSSLexicalUnit.createString
102             (LexicalUnit.SAC_IDENT,
103              CSSConstants.CSS_SANS_SERIF_VALUE, lu);
104     }
105
106     protected static final Set values = new HashSet();
107     static {
108         values.add(CSSConstants.CSS_CAPTION_VALUE);
109         values.add(CSSConstants.CSS_ICON_VALUE);
110         values.add(CSSConstants.CSS_MENU_VALUE);
111         values.add(CSSConstants.CSS_MESSAGE_BOX_VALUE);
112         values.add(CSSConstants.CSS_SMALL_CAPTION_VALUE);
113         values.add(CSSConstants.CSS_STATUS_BAR_VALUE);
114     }
115
116     public void handleSystemFont(CSSEngine eng,
117                                  ShorthandManager.PropertyHandler ph,
118                                  String s,
119                                  boolean imp) {
120
121         LexicalUnit fontStyle   = NORMAL_LU;
122         LexicalUnit fontVariant = NORMAL_LU;
123         LexicalUnit fontWeight  = NORMAL_LU;
124         LexicalUnit lineHeight  = NORMAL_LU;
125         LexicalUnit fontFamily  = FONT_FAMILY_LU;
126
127         LexicalUnit fontSize;
128         if (s.equals(CSSConstants.CSS_SMALL_CAPTION_VALUE)) {
129             fontSize = SZ_8PT_LU;
130         } else {
131             fontSize = SZ_10PT_LU;
132         }
133         ph.property(CSSConstants.CSS_FONT_FAMILY_PROPERTY,  fontFamily,  imp);
134         ph.property(CSSConstants.CSS_FONT_STYLE_PROPERTY,   fontStyle,   imp);
135         ph.property(CSSConstants.CSS_FONT_VARIANT_PROPERTY, fontVariant, imp);
136         ph.property(CSSConstants.CSS_FONT_WEIGHT_PROPERTY,  fontWeight,  imp);
137         ph.property(CSSConstants.CSS_FONT_SIZE_PROPERTY,    fontSize,    imp);
138         ph.property(CSSConstants.CSS_LINE_HEIGHT_PROPERTY,  lineHeight,  imp);
139     }
140
141     /**
142      * Implements {@link ShorthandManager#setValues(CSSEngine,ShorthandManager.PropertyHandler,LexicalUnit,boolean)}.
143      */

144     public void setValues(CSSEngine eng,
145                           ShorthandManager.PropertyHandler ph,
146                           LexicalUnit lu,
147                           boolean imp) {
148         switch (lu.getLexicalUnitType()) {
149         case LexicalUnit.SAC_INHERIT: return;
150         case LexicalUnit.SAC_IDENT: {
151             String s = lu.getStringValue().toLowerCase();
152             if (values.contains(s)) {
153                 handleSystemFont(eng, ph, s, imp);
154                 return;
155             }
156         }
157         }
158
159         LexicalUnit fontStyle   = null;
160         LexicalUnit fontVariant = null;
161         LexicalUnit fontWeight  = null;
162         LexicalUnit fontSize    = null;
163         LexicalUnit lineHeight  = null;
164         LexicalUnit fontFamily  = null;
165
166         ValueManager[]vMgrs = eng.getValueManagers();
167         int fst, fv, fw, fsz, lh;
168         fst = eng.getPropertyIndex(CSSConstants.CSS_FONT_STYLE_PROPERTY);
169         fv  = eng.getPropertyIndex(CSSConstants.CSS_FONT_VARIANT_PROPERTY);
170         fw  = eng.getPropertyIndex(CSSConstants.CSS_FONT_WEIGHT_PROPERTY);
171         fsz = eng.getPropertyIndex(CSSConstants.CSS_FONT_SIZE_PROPERTY);
172         lh  = eng.getPropertyIndex(CSSConstants.CSS_LINE_HEIGHT_PROPERTY);
173
174         IdentifierManager fstVM = (IdentifierManager)vMgrs[fst];
175         IdentifierManager fvVM  = (IdentifierManager)vMgrs[fv];
176         IdentifierManager fwVM  = (IdentifierManager)vMgrs[fw];
177         FontSizeManager   fszVM = (FontSizeManager)vMgrs[fsz];
178
179         StringMap fstSM = fstVM.getIdentifiers();
180         StringMap fvSM  = fvVM.getIdentifiers();
181         StringMap fwSM  = fwVM.getIdentifiers();
182         StringMap fszSM = fszVM.getIdentifiers();
183
184
185         // Check for font-style, font-variant, & font-weight
186         // These are all optional.
187
188         boolean svwDone= false;
189         LexicalUnit intLU = null;
190         while (!svwDone && (lu != null)) {
191             switch (lu.getLexicalUnitType()) {
192             case LexicalUnit.SAC_IDENT: {
193                 String s = lu.getStringValue().toLowerCase().intern();
194                 if (fontStyle == null && fstSM.get(s) != null) {
195                     fontStyle = lu;
196                     if (intLU != null) {
197                         if (fontWeight == null) {
198                             fontWeight = intLU;
199                             intLU = null;
200                         } else {
201                             throw createInvalidLexicalUnitDOMException
202                                 (intLU.getLexicalUnitType());
203                         }
204                     }
205                     break;
206                 }
207
208                 if (fontVariant == null && fvSM.get(s) != null) {
209                     fontVariant = lu;
210                     if (intLU != null) {
211                         if (fontWeight == null) {
212                             fontWeight = intLU;
213                             intLU = null;
214                         } else {
215                             throw createInvalidLexicalUnitDOMException
216                                 (intLU.getLexicalUnitType());
217                         }
218                     }
219                     break;
220                 }
221
222                 if (intLU == null && fontWeight == null
223                         && fwSM.get(s) != null) {
224                     fontWeight = lu;
225                     break;
226                 }
227
228                 svwDone = true;
229                 break;
230             }
231             case LexicalUnit.SAC_INTEGER:
232                 if (intLU == null && fontWeight == null) {
233                     intLU = lu;
234                     break;
235                 }
236                 svwDone = true;
237                 break;
238
239             default// All other must be size,'/line-height', family
240                 svwDone = true;
241                 break;
242             }
243             if (!svwDone) lu = lu.getNextLexicalUnit();
244         }
245
246         // Must have font-size.
247         if (lu == null)
248             throw createMalformedLexicalUnitDOMException();
249
250         // Now we need to get font-size
251         switch (lu.getLexicalUnitType()) {
252         case LexicalUnit.SAC_IDENT: {
253             String s= lu.getStringValue().toLowerCase().intern();
254             if (fszSM.get(s) != null) {
255                 fontSize = lu; // This is a font-size ident.
256                 lu = lu.getNextLexicalUnit();
257             }
258         }
259             break;
260
261         case LexicalUnit.SAC_EM:
262         case LexicalUnit.SAC_EX:
263         case LexicalUnit.SAC_PIXEL:
264         case LexicalUnit.SAC_CENTIMETER:
265         case LexicalUnit.SAC_MILLIMETER:
266         case LexicalUnit.SAC_INCH:
267         case LexicalUnit.SAC_POINT:
268         case LexicalUnit.SAC_PICA:
269         case LexicalUnit.SAC_INTEGER:
270         case LexicalUnit.SAC_REAL:
271         case LexicalUnit.SAC_PERCENTAGE:
272             fontSize = lu;
273             lu = lu.getNextLexicalUnit();
274             break;
275         }
276
277
278         if (fontSize == null) {
279             // We must have a font-size so see if we can use intLU...
280             if (intLU != null) {
281                 fontSize = intLU;  // Yup!
282                 intLU = null;
283             } else {
284                 throw createInvalidLexicalUnitDOMException
285                     (lu.getLexicalUnitType());
286             }
287         }
288
289         if (intLU != null) {
290             // We have a intLU left see if we can use it as font-weight
291             if (fontWeight == null) {
292                 fontWeight = intLU; // use intLU as font-weight.
293             } else {
294                 // we have an 'extra' integer in property.
295                 throw createInvalidLexicalUnitDOMException
296                     (intLU.getLexicalUnitType());
297             }
298         }
299
300         // Must have Font-Family, so if it's null now we are done!
301         if (lu == null)
302             throw createMalformedLexicalUnitDOMException();
303
304         // Now at this point we want to look for
305         // line-height.
306         switch (lu.getLexicalUnitType()) {
307         case LexicalUnit.SAC_OPERATOR_SLASH: // we have line-height
308             lu = lu.getNextLexicalUnit();
309             if (lu == null// OOPS!
310                 throw createMalformedLexicalUnitDOMException();
311             lineHeight = lu;
312             lu = lu.getNextLexicalUnit();
313             break;
314         }
315
316         // Must have Font-Family, so if it's null now we are done!
317         if (lu == null)
318             throw createMalformedLexicalUnitDOMException();
319         fontFamily = lu;
320
321         if (fontStyle   == null) fontStyle   = NORMAL_LU;
322         if (fontVariant == null) fontVariant = NORMAL_LU;
323         if (fontWeight  == null) fontWeight  = NORMAL_LU;
324         if (lineHeight  == null) lineHeight  = NORMAL_LU;
325
326         ph.property(CSSConstants.CSS_FONT_FAMILY_PROPERTY,  fontFamily,  imp);
327         ph.property(CSSConstants.CSS_FONT_STYLE_PROPERTY,   fontStyle,   imp);
328         ph.property(CSSConstants.CSS_FONT_VARIANT_PROPERTY, fontVariant, imp);
329         ph.property(CSSConstants.CSS_FONT_WEIGHT_PROPERTY,  fontWeight,  imp);
330         ph.property(CSSConstants.CSS_FONT_SIZE_PROPERTY,    fontSize,    imp);
331         if (lh != -1) {
332             ph.property(CSSConstants.CSS_LINE_HEIGHT_PROPERTY,
333                         lineHeight,  imp);
334         }
335     }
336 }
337