1 /* 2 * Copyright (c) 2011, 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 sun.font; 27 28 import java.awt.*; 29 import java.io.File; 30 import java.security.AccessController; 31 import java.security.PrivilegedAction; 32 import java.util.ArrayList; 33 import java.util.HashMap; 34 import java.util.Hashtable; 35 import java.util.Locale; 36 import java.util.TreeMap; 37 import java.util.Vector; 38 39 import javax.swing.plaf.FontUIResource; 40 41 import sun.awt.FontConfiguration; 42 import sun.awt.HeadlessToolkit; 43 import sun.awt.util.ThreadGroupUtils; 44 import sun.lwawt.macosx.*; 45 46 public final class CFontManager extends SunFontManager { 47 private FontConfigManager fcManager = null; 48 private static Hashtable<String, Font2D> genericFonts = new Hashtable<String, Font2D>(); 49 50 @Override 51 protected FontConfiguration createFontConfiguration() { 52 FontConfiguration fc = new CFontConfiguration(this); 53 fc.init(); 54 return fc; 55 } 56 57 @Override 58 public FontConfiguration createFontConfiguration(boolean preferLocaleFonts, 59 boolean preferPropFonts) 60 { 61 return new CFontConfiguration(this, preferLocaleFonts, preferPropFonts); 62 } 63 64 /* 65 * Returns an array of two strings. The first element is the 66 * name of the font. The second element is the file name. 67 */ 68 @Override 69 protected String[] getDefaultPlatformFont() { 70 return new String[]{"Lucida Grande", 71 "/System/Library/Fonts/LucidaGrande.ttc"}; 72 } 73 74 // This is a way to register any kind of Font2D, not just files and composites. 75 public static Font2D[] getGenericFonts() { 76 return genericFonts.values().toArray(new Font2D[0]); 77 } 78 79 public Font2D registerGenericFont(Font2D f) 80 { 81 return registerGenericFont(f, false); 82 } 83 public Font2D registerGenericFont(Font2D f, boolean logicalFont) 84 { 85 int rank = 4; 86 87 String fontName = f.fullName; 88 String familyName = f.familyName; 89 90 if (fontName == null || "".equals(fontName)) { 91 return null; 92 } 93 94 // logical fonts always need to be added to the family 95 // plus they never need to be added to the generic font list 96 // or the fullNameToFont table since they are covers for 97 // already existing fonts in this list 98 if (logicalFont || !genericFonts.containsKey(fontName)) { 99 if (FontUtilities.debugFonts()) { 100 FontUtilities.getLogger().info("Add to Family "+familyName + 101 ", Font " + fontName + " rank="+rank); 102 } 103 FontFamily family = FontFamily.getFamily(familyName); 104 if (family == null) { 105 family = new FontFamily(familyName, false, rank); 106 family.setFont(f, f.style); 107 } else if (family.getRank() >= rank) { 108 family.setFont(f, f.style); 109 } 110 if (!logicalFont) 111 { 112 genericFonts.put(fontName, f); 113 fullNameToFont.put(fontName.toLowerCase(Locale.ENGLISH), f); 114 } 115 return f; 116 } else { 117 return genericFonts.get(fontName); 118 } 119 } 120 121 @Override 122 public Font2D[] getRegisteredFonts() { 123 Font2D[] regFonts = super.getRegisteredFonts(); 124 125 // Add in the Mac OS X native fonts 126 Font2D[] genericFonts = getGenericFonts(); 127 Font2D[] allFonts = new Font2D[regFonts.length+genericFonts.length]; 128 System.arraycopy(regFonts, 0, allFonts, 0, regFonts.length); 129 System.arraycopy(genericFonts, 0, allFonts, regFonts.length, genericFonts.length); 130 131 return allFonts; 132 } 133 134 @Override 135 protected void addNativeFontFamilyNames(TreeMap<String, String> familyNames, Locale requestedLocale) { 136 Font2D[] genericfonts = getGenericFonts(); 137 for (int i=0; i < genericfonts.length; i++) { 138 if (!(genericfonts[i] instanceof NativeFont)) { 139 String name = genericfonts[i].getFamilyName(requestedLocale); 140 familyNames.put(name.toLowerCase(requestedLocale), name); 141 } 142 } 143 } 144 145 @Override 146 public Font2D createFont2D(File fontFile, int fontFormat, boolean isCopy, CreatedFontTracker tracker) throws FontFormatException { 147 148 String fontFilePath = fontFile.getPath(); 149 Font2D font2D = null; 150 final File fFile = fontFile; 151 final CreatedFontTracker _tracker = tracker; 152 try { 153 switch (fontFormat) { 154 case Font.TRUETYPE_FONT: 155 font2D = new TrueTypeFont(fontFilePath, null, 0, true); 156 break; 157 case Font.TYPE1_FONT: 158 font2D = new Type1Font(fontFilePath, null, isCopy); 159 break; 160 default: 161 throw new FontFormatException("Unrecognised Font Format"); 162 } 163 } catch (FontFormatException e) { 164 if (isCopy) { 165 java.security.AccessController.doPrivileged( 166 new java.security.PrivilegedAction<Object>() { 167 public Object run() { 168 if (_tracker != null) { 169 _tracker.subBytes((int)fFile.length()); 170 } 171 fFile.delete(); 172 return null; 173 } 174 }); 175 } 176 throw(e); 177 } 178 if (isCopy) { 179 FileFont.setFileToRemove(font2D, fontFile, tracker); 180 synchronized (FontManager.class) { 181 182 if (tmpFontFiles == null) { 183 tmpFontFiles = new Vector<File>(); 184 } 185 tmpFontFiles.add(fontFile); 186 187 if (fileCloser == null) { 188 final Runnable fileCloserRunnable = new Runnable() { 189 public void run() { 190 java.security.AccessController.doPrivileged( 191 new java.security.PrivilegedAction<Object>() { 192 public Object run() { 193 194 for (int i=0;i<CHANNELPOOLSIZE;i++) { 195 if (fontFileCache[i] != null) { 196 try { 197 fontFileCache[i].close(); 198 } catch (Exception e) {} 199 } 200 } 201 if (tmpFontFiles != null) { 202 File[] files = new File[tmpFontFiles.size()]; 203 files = tmpFontFiles.toArray(files); 204 for (int f=0; f<files.length;f++) { 205 try { 206 files[f].delete(); 207 } catch (Exception e) {} 208 } 209 } 210 return null; 211 } 212 }); 213 } 214 }; 215 AccessController.doPrivileged( 216 (PrivilegedAction<Void>) () -> { 217 /* The thread must be a member of a thread group 218 * which will not get GCed before VM exit. 219 * Make its parent the top-level thread group. 220 */ 221 ThreadGroup rootTG = ThreadGroupUtils.getRootThreadGroup(); 222 fileCloser = new Thread(rootTG, fileCloserRunnable); 223 fileCloser.setContextClassLoader(null); 224 Runtime.getRuntime().addShutdownHook(fileCloser); 225 return null; 226 } 227 ); 228 } 229 } 230 } 231 return font2D; 232 } 233 234 /* 235 public synchronized FontConfigManager getFontConfigManager() { 236 if (fcManager == null) { 237 fcManager = new FontConfigManager(); 238 } 239 return fcManager; 240 } 241 */ 242 243 protected void registerFontsInDir(String dirName, boolean useJavaRasterizer, int fontRank, boolean defer, boolean resolveSymLinks) { 244 loadNativeDirFonts(dirName); 245 super.registerFontsInDir(dirName, useJavaRasterizer, fontRank, defer, resolveSymLinks); 246 } 247 248 private native void loadNativeDirFonts(String dirName); 249 private native void loadNativeFonts(); 250 251 void registerFont(String fontName, String fontFamilyName) { 252 final CFont font = new CFont(fontName, fontFamilyName); 253 254 registerGenericFont(font); 255 } 256 257 void registerItalicDerived() { 258 FontFamily[] famArr = FontFamily.getAllFontFamilies(); 259 for (int i=0; i<famArr.length; i++) { 260 FontFamily family = famArr[i]; 261 262 Font2D f2dPlain = family.getFont(Font.PLAIN); 263 if (f2dPlain != null && !(f2dPlain instanceof CFont)) continue; 264 Font2D f2dBold = family.getFont(Font.BOLD); 265 if (f2dBold != null && !(f2dBold instanceof CFont)) continue; 266 Font2D f2dItalic = family.getFont(Font.ITALIC); 267 if (f2dItalic != null && !(f2dItalic instanceof CFont)) continue; 268 Font2D f2dBoldItalic = family.getFont(Font.BOLD|Font.ITALIC); 269 if (f2dBoldItalic != null && !(f2dBoldItalic instanceof CFont)) continue; 270 271 CFont plain = (CFont)f2dPlain; 272 CFont bold = (CFont)f2dBold; 273 CFont italic = (CFont)f2dItalic; 274 CFont boldItalic = (CFont)f2dBoldItalic; 275 276 if (bold == null) bold = plain; 277 if (plain == null && bold == null) continue; 278 if (italic != null && boldItalic != null) continue; 279 if (plain != null && italic == null) { 280 registerGenericFont(plain.createItalicVariant(), true); 281 CFont f = plain.createItalicVariant(); 282 registerGenericFont(f, true); 283 } 284 if (bold != null && boldItalic == null) { 285 registerGenericFont(bold.createItalicVariant(), true); 286 CFont f = bold.createItalicVariant(); 287 registerGenericFont(f, true); 288 } 289 } 290 } 291 292 Object waitForFontsToBeLoaded = new Object(); 293 private boolean loadedAllFonts = false; 294 295 public void loadFonts() 296 { 297 synchronized(waitForFontsToBeLoaded) 298 { 299 super.loadFonts(); 300 java.security.AccessController.doPrivileged( 301 new java.security.PrivilegedAction<Object>() { 302 public Object run() { 303 if (!loadedAllFonts) { 304 loadNativeFonts(); 305 registerItalicDerived(); 306 loadedAllFonts = true; 307 } 308 return null; 309 } 310 } 311 ); 312 313 String defaultFont = "Lucida Grande"; 314 String defaultFallback = "Lucida Sans"; 315 316 setupLogicalFonts("Dialog", defaultFont, defaultFallback); 317 setupLogicalFonts("Serif", "Times", "Lucida Bright"); 318 setupLogicalFonts("SansSerif", defaultFont, defaultFallback); 319 setupLogicalFonts("Monospaced", "Menlo", "Lucida Sans Typewriter"); 320 setupLogicalFonts("DialogInput", defaultFont, defaultFallback); 321 } 322 } 323 324 protected void setupLogicalFonts(String logicalName, String realName, String fallbackName) { 325 FontFamily realFamily = getFontFamilyWithExtraTry(logicalName, realName, fallbackName); 326 327 cloneStyledFont(realFamily, logicalName, Font.PLAIN); 328 cloneStyledFont(realFamily, logicalName, Font.BOLD); 329 cloneStyledFont(realFamily, logicalName, Font.ITALIC); 330 cloneStyledFont(realFamily, logicalName, Font.BOLD | Font.ITALIC); 331 } 332 333 protected FontFamily getFontFamilyWithExtraTry(String logicalName, String realName, String fallbackName){ 334 FontFamily family = getFontFamily(realName, fallbackName); 335 if (family != null) return family; 336 337 // at this point, we recognize that we probably needed a fallback font 338 super.loadFonts(); 339 340 family = getFontFamily(realName, fallbackName); 341 if (family != null) return family; 342 343 System.err.println("Warning: the fonts \"" + realName + "\" and \"" + fallbackName + "\" are not available for the Java logical font \"" + logicalName + "\", which may have unexpected appearance or behavior. Re-enable the \""+ realName +"\" font to remove this warning."); 344 return null; 345 } 346 347 protected FontFamily getFontFamily(String realName, String fallbackName){ 348 FontFamily family = FontFamily.getFamily(realName); 349 if (family != null) return family; 350 351 family = FontFamily.getFamily(fallbackName); 352 if (family != null){ 353 System.err.println("Warning: the font \"" + realName + "\" is not available, so \"" + fallbackName + "\" has been substituted, but may have unexpected appearance or behavor. Re-enable the \""+ realName +"\" font to remove this warning."); 354 return family; 355 } 356 357 return null; 358 } 359 360 protected boolean cloneStyledFont(FontFamily realFamily, String logicalFamilyName, int style) { 361 if (realFamily == null) return false; 362 363 Font2D realFont = realFamily.getFontWithExactStyleMatch(style); 364 if (realFont == null || !(realFont instanceof CFont)) return false; 365 366 CFont newFont = new CFont((CFont)realFont, logicalFamilyName); 367 registerGenericFont(newFont, true); 368 369 return true; 370 } 371 372 @Override 373 public String getFontPath(boolean noType1Fonts) { 374 // In the case of the Cocoa toolkit, since we go through NSFont, we don't need to register /Library/Fonts 375 Toolkit tk = Toolkit.getDefaultToolkit(); 376 if (tk instanceof HeadlessToolkit) { 377 tk = ((HeadlessToolkit)tk).getUnderlyingToolkit(); 378 } 379 if (tk instanceof LWCToolkit) { 380 return ""; 381 } 382 383 // X11 case 384 return "/Library/Fonts"; 385 } 386 387 @Override 388 protected FontUIResource getFontConfigFUIR( 389 String family, int style, int size) 390 { 391 String mappedName = FontUtilities.mapFcName(family); 392 if (mappedName == null) { 393 mappedName = "sansserif"; 394 } 395 return new FontUIResource(mappedName, style, size); 396 } 397 398 // Only implemented on Windows 399 @Override 400 protected void populateFontFileNameMap(HashMap<String, String> fontToFileMap, HashMap<String, String> fontToFamilyNameMap, 401 HashMap<String, ArrayList<String>> familyToFontListMap, Locale locale) {} 402 }