OSDN Git Service

Remove stale temporary ASEC containers
[android-x86/frameworks-base.git] / tools / layoutlib / bridge / src / com / android / layoutlib / bridge / FontLoader.java
1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.android.layoutlib.bridge;
18
19 import org.xml.sax.Attributes;
20 import org.xml.sax.SAXException;
21 import org.xml.sax.helpers.DefaultHandler;
22
23 import android.graphics.Typeface;
24
25 import java.awt.Font;
26 import java.awt.FontFormatException;
27 import java.io.File;
28 import java.io.FileInputStream;
29 import java.io.FileNotFoundException;
30 import java.io.IOException;
31 import java.util.ArrayList;
32 import java.util.Collections;
33 import java.util.HashMap;
34 import java.util.HashSet;
35 import java.util.List;
36 import java.util.Map;
37 import java.util.Set;
38
39 import javax.xml.parsers.ParserConfigurationException;
40 import javax.xml.parsers.SAXParser;
41 import javax.xml.parsers.SAXParserFactory;
42
43 /**
44  * Provides {@link Font} object to the layout lib.
45  * <p/>
46  * The fonts are loaded from the SDK directory. Family/style mapping is done by parsing the
47  * fonts.xml file located alongside the ttf files.
48  */
49 public final class FontLoader {
50     private static final String FONTS_DEFINITIONS = "fonts.xml";
51
52     private static final String NODE_FONTS = "fonts";
53     private static final String NODE_FONT = "font";
54     private static final String NODE_NAME = "name";
55     private static final String NODE_FALLBACK = "fallback";
56
57     private static final String ATTR_TTF = "ttf";
58
59     private static final String FONT_EXT = ".ttf";
60
61     private static final String[] FONT_STYLE_DEFAULT = { "", "-Regular" };
62     private static final String[] FONT_STYLE_BOLD = { "-Bold" };
63     private static final String[] FONT_STYLE_ITALIC = { "-Italic" };
64     private static final String[] FONT_STYLE_BOLDITALIC = { "-BoldItalic" };
65
66     // list of font style, in the order matching the Typeface Font style
67     private static final String[][] FONT_STYLES = {
68         FONT_STYLE_DEFAULT,
69         FONT_STYLE_BOLD,
70         FONT_STYLE_ITALIC,
71         FONT_STYLE_BOLDITALIC
72     };
73
74     private final Map<String, String> mFamilyToTtf = new HashMap<String, String>();
75     private final Map<String, Map<Integer, Font>> mTtfToFontMap =
76         new HashMap<String, Map<Integer, Font>>();
77
78     private List<Font> mFallBackFonts = null;
79
80     public static FontLoader create(String fontOsLocation) {
81         try {
82             SAXParserFactory parserFactory = SAXParserFactory.newInstance();
83                 parserFactory.setNamespaceAware(true);
84
85             SAXParser parser = parserFactory.newSAXParser();
86             File f = new File(fontOsLocation + File.separator + FONTS_DEFINITIONS);
87
88             FontDefinitionParser definitionParser = new FontDefinitionParser(
89                     fontOsLocation + File.separator);
90             parser.parse(new FileInputStream(f), definitionParser);
91
92             return definitionParser.getFontLoader();
93         } catch (ParserConfigurationException e) {
94             // return null below
95         } catch (SAXException e) {
96             // return null below
97         } catch (FileNotFoundException e) {
98             // return null below
99         } catch (IOException e) {
100             // return null below
101         }
102
103         return null;
104     }
105
106     private FontLoader(List<FontInfo> fontList, List<String> fallBackList) {
107         for (FontInfo info : fontList) {
108             for (String family : info.families) {
109                 mFamilyToTtf.put(family, info.ttf);
110             }
111         }
112
113         ArrayList<Font> list = new ArrayList<Font>();
114         for (String path : fallBackList) {
115             File f = new File(path + FONT_EXT);
116             if (f.isFile()) {
117                 try {
118                     Font font = Font.createFont(Font.TRUETYPE_FONT, f);
119                     if (font != null) {
120                         list.add(font);
121                     }
122                 } catch (FontFormatException e) {
123                     // skip this font name
124                 } catch (IOException e) {
125                     // skip this font name
126                 }
127             }
128         }
129
130         mFallBackFonts = Collections.unmodifiableList(list);
131     }
132
133     public List<Font> getFallBackFonts() {
134         return mFallBackFonts;
135     }
136
137     public synchronized Font getFont(String family, int[] style) {
138         if (family == null) {
139             return null;
140         }
141
142         // get the ttf name from the family
143         String ttf = mFamilyToTtf.get(family);
144
145         if (ttf == null) {
146             return null;
147         }
148
149         // get the font from the ttf
150         Map<Integer, Font> styleMap = mTtfToFontMap.get(ttf);
151
152         if (styleMap == null) {
153             styleMap = new HashMap<Integer, Font>();
154             mTtfToFontMap.put(ttf, styleMap);
155         }
156
157         Font f = styleMap.get(style);
158
159         if (f != null) {
160             return f;
161         }
162
163         // if it doesn't exist, we create it, and we can't, we try with a simpler style
164         switch (style[0]) {
165             case Typeface.NORMAL:
166                 f = getFont(ttf, FONT_STYLES[Typeface.NORMAL]);
167                 break;
168             case Typeface.BOLD:
169             case Typeface.ITALIC:
170                 f = getFont(ttf, FONT_STYLES[style[0]]);
171                 if (f == null) {
172                     f = getFont(ttf, FONT_STYLES[Typeface.NORMAL]);
173                     style[0] = Typeface.NORMAL;
174                 }
175                 break;
176             case Typeface.BOLD_ITALIC:
177                 f = getFont(ttf, FONT_STYLES[style[0]]);
178                 if (f == null) {
179                     f = getFont(ttf, FONT_STYLES[Typeface.BOLD]);
180                     if (f != null) {
181                         style[0] = Typeface.BOLD;
182                     } else {
183                         f = getFont(ttf, FONT_STYLES[Typeface.ITALIC]);
184                         if (f != null) {
185                             style[0] = Typeface.ITALIC;
186                         } else {
187                             f = getFont(ttf, FONT_STYLES[Typeface.NORMAL]);
188                             style[0] = Typeface.NORMAL;
189                         }
190                     }
191                 }
192                 break;
193         }
194
195         if (f != null) {
196             styleMap.put(style[0], f);
197             return f;
198         }
199
200         return null;
201     }
202
203     private Font getFont(String ttf, String[] fontFileSuffix) {
204         for (String suffix : fontFileSuffix) {
205             String name = ttf + suffix + FONT_EXT;
206
207             File f = new File(name);
208             if (f.isFile()) {
209                 try {
210                     Font font = Font.createFont(Font.TRUETYPE_FONT, f);
211                     if (font != null) {
212                         return font;
213                     }
214                 } catch (FontFormatException e) {
215                     // skip this font name
216                 } catch (IOException e) {
217                     // skip this font name
218                 }
219             }
220         }
221
222         return null;
223     }
224
225     private final static class FontInfo {
226         String ttf;
227         final Set<String> families;
228
229         FontInfo() {
230             families = new HashSet<String>();
231         }
232     }
233
234     private final static class FontDefinitionParser extends DefaultHandler {
235         private final String mOsFontsLocation;
236
237         private FontInfo mFontInfo = null;
238         private final StringBuilder mBuilder = new StringBuilder();
239         private List<FontInfo> mFontList;
240         private List<String> mFallBackList;
241
242         private FontDefinitionParser(String osFontsLocation) {
243             super();
244             mOsFontsLocation = osFontsLocation;
245         }
246
247         FontLoader getFontLoader() {
248             return new FontLoader(mFontList, mFallBackList);
249         }
250
251         /* (non-Javadoc)
252          * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
253          */
254         @Override
255         public void startElement(String uri, String localName, String name, Attributes attributes)
256                 throws SAXException {
257             if (NODE_FONTS.equals(localName)) {
258                 mFontList = new ArrayList<FontInfo>();
259                 mFallBackList = new ArrayList<String>();
260             } else if (NODE_FONT.equals(localName)) {
261                 if (mFontList != null) {
262                     String ttf = attributes.getValue(ATTR_TTF);
263                     if (ttf != null) {
264                         mFontInfo = new FontInfo();
265                         mFontInfo.ttf = mOsFontsLocation + ttf;
266                         mFontList.add(mFontInfo);
267                     }
268                 }
269             } else if (NODE_NAME.equals(localName)) {
270                 // do nothing, we'll handle the name in the endElement
271             } else if (NODE_FALLBACK.equals(localName)) {
272                 if (mFallBackList != null) {
273                     String ttf = attributes.getValue(ATTR_TTF);
274                     if (ttf != null) {
275                         mFallBackList.add(mOsFontsLocation + ttf);
276                     }
277                 }
278             }
279
280             mBuilder.setLength(0);
281
282             super.startElement(uri, localName, name, attributes);
283         }
284
285         /* (non-Javadoc)
286          * @see org.xml.sax.helpers.DefaultHandler#characters(char[], int, int)
287          */
288         @Override
289         public void characters(char[] ch, int start, int length) throws SAXException {
290             mBuilder.append(ch, start, length);
291         }
292
293         /* (non-Javadoc)
294          * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
295          */
296         @Override
297         public void endElement(String uri, String localName, String name) throws SAXException {
298             if (NODE_FONTS.equals(localName)) {
299                 // top level, do nothing
300             } else if (NODE_FONT.equals(localName)) {
301                 mFontInfo = null;
302             } else if (NODE_NAME.equals(localName)) {
303                 // handle a new name for an existing Font Info
304                 if (mFontInfo != null) {
305                     String family = trimXmlWhitespaces(mBuilder.toString());
306                     mFontInfo.families.add(family);
307                 }
308             } else if (NODE_FALLBACK.equals(localName)) {
309                 // nothing to do here.
310             }
311         }
312
313         private String trimXmlWhitespaces(String value) {
314             if (value == null) {
315                 return null;
316             }
317
318             // look for carriage return and replace all whitespace around it by just 1 space.
319             int index;
320
321             while ((index = value.indexOf('\n')) != -1) {
322                 // look for whitespace on each side
323                 int left = index - 1;
324                 while (left >= 0) {
325                     if (Character.isWhitespace(value.charAt(left))) {
326                         left--;
327                     } else {
328                         break;
329                     }
330                 }
331
332                 int right = index + 1;
333                 int count = value.length();
334                 while (right < count) {
335                     if (Character.isWhitespace(value.charAt(right))) {
336                         right++;
337                     } else {
338                         break;
339                     }
340                 }
341
342                 // remove all between left and right (non inclusive) and replace by a single space.
343                 String leftString = null;
344                 if (left >= 0) {
345                     leftString = value.substring(0, left + 1);
346                 }
347                 String rightString = null;
348                 if (right < count) {
349                     rightString = value.substring(right);
350                 }
351
352                 if (leftString != null) {
353                     value = leftString;
354                     if (rightString != null) {
355                         value += " " + rightString;
356                     }
357                 } else {
358                     value = rightString != null ? rightString : "";
359                 }
360             }
361
362             // now we un-escape the string
363             int length = value.length();
364             char[] buffer = value.toCharArray();
365
366             for (int i = 0 ; i < length ; i++) {
367                 if (buffer[i] == '\\') {
368                     if (buffer[i+1] == 'n') {
369                         // replace the char with \n
370                         buffer[i+1] = '\n';
371                     }
372
373                     // offset the rest of the buffer since we go from 2 to 1 char
374                     System.arraycopy(buffer, i+1, buffer, i, length - i - 1);
375                     length--;
376                 }
377             }
378
379             return new String(buffer, 0, length);
380         }
381
382     }
383 }