1 /* 2 * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.javafx.font; 27 28 import java.io.File; 29 import java.io.FileInputStream; 30 import java.io.FileNotFoundException; 31 import java.io.IOException; 32 import java.io.InputStream; 33 import java.security.AccessController; 34 import java.security.PrivilegedAction; 35 import java.util.ArrayList; 36 import java.util.HashMap; 37 import java.util.List; 38 import java.util.Locale; 39 40 import javax.xml.parsers.SAXParser; 41 import javax.xml.parsers.SAXParserFactory; 42 43 import org.xml.sax.Attributes; 44 import org.xml.sax.SAXException; 45 import org.xml.sax.helpers.DefaultHandler; 46 47 import com.sun.glass.utils.NativeLibLoader; 48 49 /** 50 * Class AndroidFontFinder reads font descriptor from 51 * /system/etc/system_fonts.xml. If that file doesn't exist it is replaced by 52 * embedded font descriptor {@link com/sun/t2k/android_system_fonts.xml} which 53 * defines some basic mappings based on best guess which fonts are mandatory on 54 * platforms lower than 4.0 and how they map to typefaces. 55 */ 56 class AndroidFontFinder { 57 58 private final static String SYSTEM_FONT_NAME = "sans serif"; 59 private final static float SYSTEM_FONT_SIZE = 16.0f; 60 61 final static String fontDescriptor_2_X_Path = "/com/sun/javafx/font/android_system_fonts.xml"; 62 final static String fontDescriptor_4_X_Path = "/system/etc/system_fonts.xml"; 63 final static String systemFontsDir = "/system/fonts"; 64 65 static { 66 AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 67 NativeLibLoader.loadLibrary("javafx_font"); 68 return null; 69 }); 70 } 71 72 public static String getSystemFont() { 73 return SYSTEM_FONT_NAME; 74 } 75 76 public static float getSystemFontSize() { 77 return SYSTEM_FONT_SIZE; 78 } 79 80 public static String getSystemFontsDir() { 81 return systemFontsDir; 82 } 83 84 private static boolean parse_2_X_SystemDefaultFonts( 85 final HashMap<String, String> fontToFileMap, 86 final HashMap<String, String> fontToFamilyNameMap, 87 final HashMap<String, ArrayList<String>> familyToFontListMap) { 88 89 InputStream is = AndroidFontFinder.class 90 .getResourceAsStream(fontDescriptor_2_X_Path); 91 if (is == null) { 92 System.err 93 .println("Resource not found: " + fontDescriptor_2_X_Path); 94 return false; 95 } 96 return parseSystemDefaultFonts(is, fontToFileMap, fontToFamilyNameMap, 97 familyToFontListMap); 98 } 99 100 private static boolean parse_4_X_SystemDefaultFonts( 101 final HashMap<String, String> fontToFileMap, 102 final HashMap<String, String> fontToFamilyNameMap, 103 final HashMap<String, ArrayList<String>> familyToFontListMap) { 104 File iFile = new File(fontDescriptor_4_X_Path); 105 try { 106 return parseSystemDefaultFonts(new FileInputStream(iFile), 107 fontToFileMap, fontToFamilyNameMap, familyToFontListMap); 108 109 } catch (FileNotFoundException e) { 110 System.err.println("File not found: " + fontDescriptor_4_X_Path); 111 } 112 return false; 113 } 114 115 private static boolean parseSystemDefaultFonts(final InputStream is, 116 final HashMap<String, String> fontToFileMap, 117 final HashMap<String, String> fontToFamilyNameMap, 118 final HashMap<String, ArrayList<String>> familyToFontListMap) { 119 120 try { 121 122 SAXParserFactory factory = SAXParserFactory.newInstance(); 123 SAXParser saxParser = factory.newSAXParser(); 124 125 DefaultHandler handler = new DefaultHandler() { 126 private final static char DASH = '-'; 127 private final static String FAMILY = "family"; 128 129 private final static String FILE = "file"; 130 private final static String FILESET = "fileset"; 131 private final static String NAME = "name"; 132 private final static String NAMESET = "nameset"; 133 private final static char SPACE = ' '; 134 final List<String> filesets = new ArrayList<>(); 135 136 boolean inFamily = false; 137 boolean inFile = false; 138 boolean inFileset = false; 139 boolean inName = false; 140 boolean inNameset = false; 141 142 private final List<String> namesets = new ArrayList<>(); 143 private final String[] styles = new String[] { 144 "regular", "bold", "italic", "bold italic" }; 145 146 public void characters(char[] ch, int start, int length) 147 throws SAXException { 148 if (inName) { 149 String nameset = new String(ch, start, length) 150 .toLowerCase(); 151 namesets.add(nameset); 152 } else if (inFile) { 153 String fileset = new String(ch, start, length); 154 filesets.add(fileset); 155 } 156 } 157 158 public void endElement(String uri, String localName, 159 String qName) throws SAXException { 160 if (qName.equalsIgnoreCase(FAMILY)) { 161 for (String family : namesets) { 162 int i = 0; 163 String familyName = family.replace(DASH, SPACE); 164 for (String file : filesets) { 165 String fullName = familyName + " " + styles[i]; 166 String fullFile = systemFontsDir 167 + File.separator + file; 168 File f = new File(fullFile); 169 if (!f.exists() || !f.canRead()) { 170 continue; 171 } 172 fontToFileMap.put(fullName, fullFile); 173 fontToFamilyNameMap.put(fullName, familyName); 174 ArrayList<String> list = familyToFontListMap 175 .get(familyName); 176 if (list == null) { 177 list = new ArrayList<String>(); 178 familyToFontListMap.put(familyName, list); 179 } 180 list.add(fullName); 181 i++; 182 } 183 } 184 inFamily = false; 185 } else if (qName.equalsIgnoreCase(NAMESET)) { 186 inNameset = false; 187 } else if (qName.equalsIgnoreCase(FILESET)) { 188 inFileset = false; 189 } else if (qName.equalsIgnoreCase(NAME)) { 190 inName = false; 191 } else if (qName.equalsIgnoreCase(FILE)) { 192 inFile = false; 193 } 194 } 195 196 @Override 197 public void startElement(String uri, String localName, 198 String qName, Attributes attributes) 199 throws SAXException { 200 if (qName.equalsIgnoreCase(FAMILY)) { 201 inFamily = true; 202 namesets.clear(); 203 filesets.clear(); 204 } else if (qName.equalsIgnoreCase(NAMESET)) { 205 inNameset = true; 206 } else if (qName.equalsIgnoreCase(FILESET)) { 207 inFileset = true; 208 } else if (qName.equalsIgnoreCase(NAME)) { 209 inName = true; 210 } else if (qName.equalsIgnoreCase(FILE)) { 211 inFile = true; 212 } 213 } 214 };// DefaultHandler 215 216 saxParser.parse(is, handler); 217 return true; 218 219 } catch (IOException e) { 220 System.err.println("Failed to load default fonts descriptor: " 221 + fontDescriptor_4_X_Path); 222 } catch (Exception e) { 223 System.err.println("Failed parsing default fonts descriptor;"); 224 e.printStackTrace(); 225 } 226 return false; 227 } 228 229 public static void populateFontFileNameMap( 230 HashMap<String, String> fontToFileMap, 231 HashMap<String, String> fontToFamilyNameMap, 232 HashMap<String, ArrayList<String>> familyToFontListMap, 233 Locale locale) { 234 235 if (fontToFileMap == null || fontToFamilyNameMap == null 236 || familyToFontListMap == null) { 237 return; 238 } 239 if (locale == null) { 240 locale = Locale.ENGLISH; 241 } 242 243 boolean systemFonts_4_X_DescriptorFound = parse_4_X_SystemDefaultFonts( 244 fontToFileMap, fontToFamilyNameMap, familyToFontListMap); 245 if (!systemFonts_4_X_DescriptorFound) { 246 parse_2_X_SystemDefaultFonts(fontToFileMap, fontToFamilyNameMap, 247 familyToFontListMap); 248 } 249 } 250 }