3098 "monospace:bold:roman", |
3099 "monospace:bold:roman", |
3099 "monospace:regular:italic", |
3100 "monospace:regular:italic", |
3100 "monospace:bold:italic", |
3101 "monospace:bold:italic", |
3101 }; |
3102 }; |
3102 |
3103 |
3103 /* This class is just a data structure. |
3104 /* These next three classes are just data structures. |
3104 */ |
3105 */ |
3105 private static class FontConfigInfo { |
3106 static class FontConfigFont { |
|
3107 String familyName; // eg Bitstream Vera Sans |
|
3108 String styleStr; // eg Bold |
|
3109 String fullName; // eg Bitstream Vera Sans Bold |
|
3110 String fontFile; // eg /usr/X11/lib/fonts/foo.ttf |
|
3111 } |
|
3112 |
|
3113 static class FcCompFont { |
3106 String fcName; // eg sans |
3114 String fcName; // eg sans |
3107 String fcFamily; // eg sans |
3115 String fcFamily; // eg sans |
3108 String jdkName; // eg sansserif |
3116 String jdkName; // eg sansserif |
3109 int style; // eg 0=PLAIN |
3117 int style; // eg 0=PLAIN |
3110 String familyName; // eg Bitstream Vera Sans |
3118 FontConfigFont firstFont; |
3111 String fontFile; // eg /usr/X11/lib/fonts/foo.ttf |
3119 FontConfigFont[] allFonts; |
3112 //boolean preferBitmaps; // if embedded bitmaps preferred over AA |
3120 //boolean preferBitmaps; // if embedded bitmaps preferred over AA |
3113 CompositeFont compFont; // null if not yet created/known. |
3121 CompositeFont compFont; // null if not yet created/known. |
3114 } |
3122 } |
3115 |
3123 |
|
3124 static class FontConfigInfo { |
|
3125 int fcVersion; |
|
3126 String[] cacheDirs = new String[4]; |
|
3127 } |
3116 |
3128 |
3117 private static String getFCLocaleStr() { |
3129 private static String getFCLocaleStr() { |
3118 Locale l = SunToolkit.getStartupLocale(); |
3130 Locale l = SunToolkit.getStartupLocale(); |
3119 String localeStr = l.getLanguage(); |
3131 String localeStr = l.getLanguage(); |
3120 String country = l.getCountry(); |
3132 String country = l.getCountry(); |
3121 if (!country.equals("")) { |
3133 if (!country.equals("")) { |
3122 localeStr = localeStr + "-" + country; |
3134 localeStr = localeStr + "-" + country; |
3123 } |
3135 } |
3124 return localeStr; |
3136 return localeStr; |
3125 } |
3137 } |
|
3138 |
|
3139 /* This does cause the native libfontconfig to be loaded and unloaded, |
|
3140 * but it does not incur the overhead of initialisation of its |
|
3141 * data structures, so shouldn't have a measurable impact. |
|
3142 */ |
|
3143 public static native int getFontConfigVersion(); |
3126 |
3144 |
3127 private static native int |
3145 private static native int |
3128 getFontConfigAASettings(String locale, String fcFamily); |
3146 getFontConfigAASettings(String locale, String fcFamily); |
3129 |
3147 |
3130 /* This is public solely so that for debugging purposes it can be called |
3148 /* This is public solely so that for debugging purposes it can be called |
3155 */ |
3173 */ |
3156 public static Object getFontConfigAAHint() { |
3174 public static Object getFontConfigAAHint() { |
3157 return getFontConfigAAHint("sans"); |
3175 return getFontConfigAAHint("sans"); |
3158 } |
3176 } |
3159 |
3177 |
|
3178 /* This is populated by native */ |
|
3179 private static final FontConfigInfo fcInfo = new FontConfigInfo(); |
|
3180 |
3160 /* This array has the array elements created in Java code and is |
3181 /* This array has the array elements created in Java code and is |
3161 * passed down to native to be filled in. |
3182 * passed down to native to be filled in. |
3162 */ |
3183 */ |
3163 private static FontConfigInfo[] fontConfigFonts; |
3184 private static FcCompFont[] fontConfigFonts; |
3164 |
3185 |
3165 /* Return an array of FontConfigInfo structs describing the primary |
3186 /* Return an array of FcCompFont structs describing the primary |
3166 * font located for each of fontconfig/GTK/Pango's logical font names. |
3187 * font located for each of fontconfig/GTK/Pango's logical font names. |
3167 */ |
3188 */ |
3168 private static native void getFontConfig(String locale, |
3189 private static native void getFontConfig(String locale, |
3169 FontConfigInfo[] fonts); |
3190 FontConfigInfo fcInfo, |
3170 |
3191 FcCompFont[] fonts, |
|
3192 boolean includeFallbacks); |
|
3193 |
|
3194 static void populateFontConfig(FcCompFont[] fcInfo) { |
|
3195 fontConfigFonts = fcInfo; |
|
3196 } |
|
3197 |
|
3198 static FcCompFont[] loadFontConfig() { |
|
3199 initFontConfigFonts(true); |
|
3200 return fontConfigFonts; |
|
3201 } |
|
3202 |
|
3203 static FontConfigInfo getFontConfigInfo() { |
|
3204 initFontConfigFonts(true); |
|
3205 return fcInfo; |
|
3206 } |
3171 |
3207 |
3172 /* This can be made public if it's needed to force a re-read |
3208 /* This can be made public if it's needed to force a re-read |
3173 * rather than using the cached values. The re-read would be needed |
3209 * rather than using the cached values. The re-read would be needed |
3174 * only if some event signalled that the fontconfig has changed. |
3210 * only if some event signalled that the fontconfig has changed. |
3175 * In that event this method would need to return directly the array |
3211 * In that event this method would need to return directly the array |
3176 * to be used by the caller in case it subsequently changed. |
3212 * to be used by the caller in case it subsequently changed. |
3177 */ |
3213 */ |
3178 private static void initFontConfigFonts() { |
3214 private static synchronized void |
|
3215 initFontConfigFonts(boolean includeFallbacks) { |
3179 |
3216 |
3180 if (fontConfigFonts != null) { |
3217 if (fontConfigFonts != null) { |
3181 return; |
3218 if (!includeFallbacks || (fontConfigFonts[0].allFonts != null)) { |
3182 } |
3219 return; |
3183 |
3220 } |
3184 if (isWindows) { |
3221 } |
|
3222 |
|
3223 if (isWindows || fontConfigFailed) { |
3185 return; |
3224 return; |
3186 } |
3225 } |
3187 |
3226 |
3188 long t0 = 0; |
3227 long t0 = 0; |
3189 if (logging) { |
3228 if (logging) { |
3190 t0 = System.currentTimeMillis(); |
3229 t0 = System.nanoTime(); |
3191 } |
3230 } |
3192 |
3231 |
3193 FontConfigInfo[] fontArr = new FontConfigInfo[fontConfigNames.length]; |
3232 FcCompFont[] fontArr = new FcCompFont[fontConfigNames.length]; |
3194 for (int i = 0; i< fontArr.length; i++) { |
3233 for (int i = 0; i< fontArr.length; i++) { |
3195 fontArr[i] = new FontConfigInfo(); |
3234 fontArr[i] = new FcCompFont(); |
3196 fontArr[i].fcName = fontConfigNames[i]; |
3235 fontArr[i].fcName = fontConfigNames[i]; |
3197 int colonPos = fontArr[i].fcName.indexOf(':'); |
3236 int colonPos = fontArr[i].fcName.indexOf(':'); |
3198 fontArr[i].fcFamily = fontArr[i].fcName.substring(0, colonPos); |
3237 fontArr[i].fcFamily = fontArr[i].fcName.substring(0, colonPos); |
3199 fontArr[i].jdkName = mapFcName(fontArr[i].fcFamily); |
3238 fontArr[i].jdkName = mapFcName(fontArr[i].fcFamily); |
3200 fontArr[i].style = i % 4; // depends on array order. |
3239 fontArr[i].style = i % 4; // depends on array order. |
3201 } |
3240 } |
3202 getFontConfig(getFCLocaleStr(), fontArr); |
3241 getFontConfig(getFCLocaleStr(), fcInfo, fontArr, includeFallbacks); |
|
3242 /* If don't find anything (eg no libfontconfig), then just return */ |
|
3243 for (int i = 0; i< fontArr.length; i++) { |
|
3244 FcCompFont fci = fontArr[i]; |
|
3245 if (fci.firstFont == null) { |
|
3246 if (logging) { |
|
3247 logger.info("Fontconfig returned no fonts."); |
|
3248 } |
|
3249 fontConfigFailed = true; |
|
3250 return; |
|
3251 } |
|
3252 } |
3203 fontConfigFonts = fontArr; |
3253 fontConfigFonts = fontArr; |
3204 |
3254 |
3205 if (logging) { |
3255 if (logging) { |
3206 long t1 = System.currentTimeMillis(); |
3256 long t1 = System.nanoTime(); |
3207 logger.info("Time spent accessing fontconfig="+(t1-t0)+"ms."); |
3257 logger.info("Time spent accessing fontconfig="+ |
|
3258 (t1-t0)/1000000+"ms."); |
3208 |
3259 |
3209 for (int i = 0; i< fontConfigFonts.length; i++) { |
3260 for (int i = 0; i< fontConfigFonts.length; i++) { |
3210 FontConfigInfo fci = fontConfigFonts[i]; |
3261 FcCompFont fci = fontConfigFonts[i]; |
3211 logger.info("FC font " + fci.fcName+" maps to family " + |
3262 logger.info("FC font " + fci.fcName+" maps to family " + |
3212 fci.familyName + " in file " + fci.fontFile); |
3263 fci.firstFont.familyName + |
3213 } |
3264 " in file " + fci.firstFont.fontFile); |
3214 } |
3265 if (fci.allFonts != null) { |
3215 } |
3266 for (int f=0;f<fci.allFonts.length;f++) { |
3216 |
3267 FontConfigFont fcf = fci.allFonts[f]; |
3217 private static PhysicalFont registerFromFcInfo(FontConfigInfo fcInfo) { |
3268 logger.info("Family=" + fcf.familyName + |
|
3269 " Style="+ fcf.styleStr + |
|
3270 " Fullname="+fcf.fullName + |
|
3271 " File="+fcf.fontFile); |
|
3272 } |
|
3273 } |
|
3274 } |
|
3275 } |
|
3276 } |
|
3277 |
|
3278 private static PhysicalFont registerFromFcInfo(FcCompFont fcInfo) { |
3218 |
3279 |
3219 /* If it's a TTC file we need to know that as we will need to |
3280 /* If it's a TTC file we need to know that as we will need to |
3220 * make sure we return the right font */ |
3281 * make sure we return the right font */ |
3221 int offset = fcInfo.fontFile.length()-4; |
3282 String fontFile = fcInfo.firstFont.fontFile; |
|
3283 int offset = fontFile.length()-4; |
3222 if (offset <= 0) { |
3284 if (offset <= 0) { |
3223 return null; |
3285 return null; |
3224 } |
3286 } |
3225 String ext = fcInfo.fontFile.substring(offset).toLowerCase(); |
3287 String ext = fontFile.substring(offset).toLowerCase(); |
3226 boolean isTTC = ext.equals(".ttc"); |
3288 boolean isTTC = ext.equals(".ttc"); |
3227 |
3289 |
3228 /* If this file is already registered, can just return its font. |
3290 /* If this file is already registered, can just return its font. |
3229 * However we do need to check in case it's a TTC as we need |
3291 * However we do need to check in case it's a TTC as we need |
3230 * a specific font, so rather than directly returning it, let |
3292 * a specific font, so rather than directly returning it, let |
3231 * findFont2D resolve that. |
3293 * findFont2D resolve that. |
3232 */ |
3294 */ |
3233 PhysicalFont physFont = registeredFontFiles.get(fcInfo.fontFile); |
3295 PhysicalFont physFont = registeredFontFiles.get(fontFile); |
3234 if (physFont != null) { |
3296 if (physFont != null) { |
3235 if (isTTC) { |
3297 if (isTTC) { |
3236 Font2D f2d = findFont2D(fcInfo.familyName, |
3298 Font2D f2d = findFont2D(fcInfo.firstFont.familyName, |
3237 fcInfo.style, NO_FALLBACK); |
3299 fcInfo.style, NO_FALLBACK); |
3238 if (f2d instanceof PhysicalFont) { /* paranoia */ |
3300 if (f2d instanceof PhysicalFont) { /* paranoia */ |
3239 return (PhysicalFont)f2d; |
3301 return (PhysicalFont)f2d; |
3240 } else { |
3302 } else { |
3241 return null; |
3303 return null; |
3247 |
3309 |
3248 /* If the font may hide a JRE font (eg fontconfig says it is |
3310 /* If the font may hide a JRE font (eg fontconfig says it is |
3249 * Lucida Sans), we want to use the JRE version, so make it |
3311 * Lucida Sans), we want to use the JRE version, so make it |
3250 * point to the JRE font. |
3312 * point to the JRE font. |
3251 */ |
3313 */ |
3252 physFont = findJREDeferredFont(fcInfo.familyName, fcInfo.style); |
3314 physFont = findJREDeferredFont(fcInfo.firstFont.familyName, |
|
3315 fcInfo.style); |
3253 |
3316 |
3254 /* It is also possible the font file is on the "deferred" list, |
3317 /* It is also possible the font file is on the "deferred" list, |
3255 * in which case we can just initialise it now. |
3318 * in which case we can just initialise it now. |
3256 */ |
3319 */ |
3257 if (physFont == null && |
3320 if (physFont == null && |
3258 deferredFontFiles.get(fcInfo.fontFile) != null) { |
3321 deferredFontFiles.get(fontFile) != null) |
3259 physFont = initialiseDeferredFont(fcInfo.fontFile); |
3322 { |
|
3323 physFont = initialiseDeferredFont(fcInfo.firstFont.fontFile); |
3260 /* use findFont2D to get the right font from TTC's */ |
3324 /* use findFont2D to get the right font from TTC's */ |
3261 if (physFont != null) { |
3325 if (physFont != null) { |
3262 if (isTTC) { |
3326 if (isTTC) { |
3263 Font2D f2d = findFont2D(fcInfo.familyName, |
3327 Font2D f2d = findFont2D(fcInfo.firstFont.familyName, |
3264 fcInfo.style, NO_FALLBACK); |
3328 fcInfo.style, NO_FALLBACK); |
3265 if (f2d instanceof PhysicalFont) { /* paranoia */ |
3329 if (f2d instanceof PhysicalFont) { /* paranoia */ |
3266 return (PhysicalFont)f2d; |
3330 return (PhysicalFont)f2d; |
3267 } else { |
3331 } else { |
3268 return null; |
3332 return null; |
3343 } else { |
3407 } else { |
3344 info[1] = dirs[0]; |
3408 info[1] = dirs[0]; |
3345 } |
3409 } |
3346 info[1] = info[1] + File.separator + "arial.ttf"; |
3410 info[1] = info[1] + File.separator + "arial.ttf"; |
3347 } else { |
3411 } else { |
3348 initFontConfigFonts(); |
3412 initFontConfigFonts(false); |
3349 for (int i=0; i<fontConfigFonts.length; i++) { |
3413 for (int i=0; i<fontConfigFonts.length; i++) { |
3350 if ("sans".equals(fontConfigFonts[i].fcFamily) && |
3414 if ("sans".equals(fontConfigFonts[i].fcFamily) && |
3351 0 == fontConfigFonts[i].style) { |
3415 0 == fontConfigFonts[i].style) { |
3352 info[0] = fontConfigFonts[i].familyName; |
3416 info[0] = fontConfigFonts[i].firstFont.familyName; |
3353 info[1] = fontConfigFonts[i].fontFile; |
3417 info[1] = fontConfigFonts[i].firstFont.fontFile; |
3354 break; |
3418 break; |
3355 } |
3419 } |
3356 } |
3420 } |
3357 /* Absolute last ditch attempt in the face of fontconfig problems. |
3421 /* Absolute last ditch attempt in the face of fontconfig problems. |
3358 * If we didn't match, pick the first, or just make something |
3422 * If we didn't match, pick the first, or just make something |
3359 * up so we don't NPE. |
3423 * up so we don't NPE. |
3360 */ |
3424 */ |
3361 if (info[0] == null) { |
3425 if (info[0] == null) { |
3362 if (fontConfigFonts.length > 0 && |
3426 if (fontConfigFonts.length > 0 && |
3363 fontConfigFonts[0].fontFile != null) { |
3427 fontConfigFonts[0].firstFont.fontFile != null) { |
3364 info[0] = fontConfigFonts[0].familyName; |
3428 info[0] = fontConfigFonts[0].firstFont.familyName; |
3365 info[1] = fontConfigFonts[0].fontFile; |
3429 info[1] = fontConfigFonts[0].firstFont.fontFile; |
3366 } else { |
3430 } else { |
3367 info[0] = "Dialog"; |
3431 info[0] = "Dialog"; |
3368 info[1] = "/dialog.ttf"; |
3432 info[1] = "/dialog.ttf"; |
3369 } |
3433 } |
3370 } |
3434 } |
3371 } |
3435 } |
3372 defaultPlatformFont = info; |
3436 defaultPlatformFont = info; |
3373 return defaultPlatformFont; |
3437 return defaultPlatformFont; |
3374 } |
3438 } |
3375 |
3439 |
3376 private FontConfigInfo getFontConfigInfo() { |
3440 private FcCompFont getFcCompFont() { |
3377 initFontConfigFonts(); |
3441 initFontConfigFonts(false); |
3378 for (int i=0; i<fontConfigFonts.length; i++) { |
3442 for (int i=0; i<fontConfigFonts.length; i++) { |
3379 if ("sans".equals(fontConfigFonts[i].fcFamily) && |
3443 if ("sans".equals(fontConfigFonts[i].fcFamily) && |
3380 0 == fontConfigFonts[i].style) { |
3444 0 == fontConfigFonts[i].style) { |
3381 return fontConfigFonts[i]; |
3445 return fontConfigFonts[i]; |
3382 } |
3446 } |
3430 * regular JDK composite. |
3496 * regular JDK composite. |
3431 * Algorithmically styled fonts won't match on exact style, so |
3497 * Algorithmically styled fonts won't match on exact style, so |
3432 * will fall through this code, but the regisration code will |
3498 * will fall through this code, but the regisration code will |
3433 * find that file already registered and return its font. |
3499 * find that file already registered and return its font. |
3434 */ |
3500 */ |
3435 FontFamily family = FontFamily.getFamily(fcInfo.familyName); |
3501 FontFamily family = FontFamily.getFamily(fcInfo.firstFont.familyName); |
3436 PhysicalFont physFont = null; |
3502 PhysicalFont physFont = null; |
3437 if (family != null) { |
3503 if (family != null) { |
3438 Font2D f2D = family.getFontWithExactStyleMatch(fcInfo.style); |
3504 Font2D f2D = family.getFontWithExactStyleMatch(fcInfo.style); |
3439 if (f2D instanceof PhysicalFont) { |
3505 if (f2D instanceof PhysicalFont) { |
3440 physFont = (PhysicalFont)f2D; |
3506 physFont = (PhysicalFont)f2D; |
3441 } |
3507 } |
3442 } |
3508 } |
3443 |
3509 |
3444 if (physFont == null || !fcInfo.fontFile.equals(physFont.platName)) { |
3510 if (physFont == null || |
|
3511 !fcInfo.firstFont.fontFile.equals(physFont.platName)) { |
3445 physFont = registerFromFcInfo(fcInfo); |
3512 physFont = registerFromFcInfo(fcInfo); |
3446 if (physFont == null) { |
3513 if (physFont == null) { |
3447 return (fcInfo.compFont = jdkFont); |
3514 return (fcInfo.compFont = jdkFont); |
3448 } |
3515 } |
3449 family = FontFamily.getFamily(physFont.getFamilyName(null)); |
3516 family = FontFamily.getFamily(physFont.getFamilyName(null)); |