1 /* 2 * Copyright (c) 2003, 2006, 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.io.File; 29 import java.awt.Font; 30 import java.util.Collection; 31 import java.util.HashMap; 32 import java.util.concurrent.ConcurrentHashMap; 33 import java.util.Locale; 34 35 public class FontFamily { 36 37 private static ConcurrentHashMap<String, FontFamily> 38 familyNameMap = new ConcurrentHashMap<String, FontFamily>(); 39 private static HashMap<String, FontFamily> allLocaleNames; 40 41 protected String familyName; 42 protected Font2D plain; 43 protected Font2D bold; 44 protected Font2D italic; 45 protected Font2D bolditalic; 46 protected boolean logicalFont = false; 47 protected int familyRank; 48 49 public static FontFamily getFamily(String name) { 50 return familyNameMap.get(name.toLowerCase(Locale.ENGLISH)); 51 } 52 53 public static String[] getAllFamilyNames() { 54 return null; 55 } 56 57 /* Only for use by FontManager.deRegisterBadFont(..). 58 * If this was the only font in the family, the family is removed 59 * from the map 60 */ 61 static void remove(Font2D font2D) { 62 63 String name = font2D.getFamilyName(Locale.ENGLISH); 64 FontFamily family = getFamily(name); 65 if (family == null) { 66 return; 67 } 68 if (family.plain == font2D) { 69 family.plain = null; 70 } 71 if (family.bold == font2D) { 72 family.bold = null; 73 } 74 if (family.italic == font2D) { 75 family.italic = null; 76 } 77 if (family.bolditalic == font2D) { 78 family.bolditalic = null; 79 } 80 if (family.plain == null && family.bold == null && 81 family.plain == null && family.bold == null) { 82 familyNameMap.remove(name); 83 } 84 } 85 86 public FontFamily(String name, boolean isLogFont, int rank) { 87 logicalFont = isLogFont; 88 familyName = name; 89 familyRank = rank; 90 familyNameMap.put(name.toLowerCase(Locale.ENGLISH), this); 91 } 92 93 /* Create a family for created fonts which aren't listed in the 94 * main map. 95 */ 96 FontFamily(String name) { 97 logicalFont = false; 98 familyName = name; 99 familyRank = Font2D.DEFAULT_RANK; 100 } 101 102 public String getFamilyName() { 103 return familyName; 104 } 105 106 public int getRank() { 107 return familyRank; 108 } 109 110 private boolean isFromSameSource(Font2D font) { 111 if (!(font instanceof FileFont)) { 112 return false; 113 } 114 115 FileFont existingFont = null; 116 if (plain instanceof FileFont) { 117 existingFont = (FileFont)plain; 118 } else if (bold instanceof FileFont) { 119 existingFont = (FileFont)bold; 120 } else if (italic instanceof FileFont) { 121 existingFont = (FileFont)italic; 122 } else if (bolditalic instanceof FileFont) { 123 existingFont = (FileFont)bolditalic; 124 } 125 // A family isn't created until there's a font. 126 // So if we didn't find a file font it means this 127 // isn't a file-based family. 128 if (existingFont == null) { 129 return false; 130 } 131 File existDir = (new File(existingFont.platName)).getParentFile(); 132 133 FileFont newFont = (FileFont)font; 134 File newDir = (new File(newFont.platName)).getParentFile(); 135 return java.util.Objects.equals(newDir, existDir); 136 } 137 138 /* 139 * We want a family to be of the same width and prefer medium/normal width. 140 * Once we find a particular width we accept more of the same width 141 * until we find one closer to normal when we 'evict' all existing fonts. 142 * So once we see a 'normal' width font we evict all members that are not 143 * normal width and then accept only new ones that are normal width. 144 * 145 * Once a font passes the width test we subject it to the weight test. 146 * For Plain we target the weight the closest that is <= NORMAL (400) 147 * For Bold we target the weight that is closest to BOLD (700). 148 * 149 * In the future, rather than discarding these fonts, we should 150 * extend the family to include these so lookups on these properties 151 * can locate them, as presently they will only be located by full name 152 * based lookup. 153 */ 154 155 private int familyWidth = 0; 156 private boolean preferredWidth(Font2D font) { 157 158 int newWidth = font.getWidth(); 159 160 if (familyWidth == 0) { 161 familyWidth = newWidth; 162 return true; 163 } 164 165 if (newWidth == familyWidth) { 166 return true; 167 } 168 169 if (Math.abs(Font2D.FWIDTH_NORMAL - newWidth) < 170 Math.abs(Font2D.FWIDTH_NORMAL - familyWidth)) 171 { 172 if (FontUtilities.debugFonts()) { 173 FontUtilities.getLogger().info( 174 "Found more preferred width. New width = " + newWidth + 175 " Old width = " + familyWidth + " in font " + font + 176 " nulling out fonts plain: " + plain + " bold: " + bold + 177 " italic: " + italic + " bolditalic: " + bolditalic); 178 } 179 familyWidth = newWidth; 180 plain = bold = italic = bolditalic = null; 181 return true; 182 } else if (FontUtilities.debugFonts()) { 183 FontUtilities.getLogger().info( 184 "Family rejecting font " + font + 185 " of less preferred width " + newWidth); 186 } 187 return false; 188 } 189 190 private boolean closerWeight(Font2D currFont, Font2D font, int style) { 191 if (familyWidth != font.getWidth()) { 192 return false; 193 } 194 195 if (currFont == null) { 196 return true; 197 } 198 199 if (FontUtilities.debugFonts()) { 200 FontUtilities.getLogger().info( 201 "New weight for style " + style + ". Curr.font=" + currFont + 202 " New font="+font+" Curr.weight="+ + currFont.getWeight()+ 203 " New weight="+font.getWeight()); 204 } 205 206 int newWeight = font.getWeight(); 207 switch (style) { 208 case Font.PLAIN: 209 case Font.ITALIC: 210 return (newWeight <= Font2D.FWEIGHT_NORMAL && 211 newWeight > currFont.getWeight()); 212 213 case Font.BOLD: 214 case Font.BOLD|Font.ITALIC: 215 return (Math.abs(newWeight - Font2D.FWEIGHT_BOLD) < 216 Math.abs(currFont.getWeight() - Font2D.FWEIGHT_BOLD)); 217 218 default: 219 return false; 220 } 221 } 222 223 public void setFont(Font2D font, int style) { 224 225 if (FontUtilities.isLogging()) { 226 FontUtilities.getLogger().info( 227 "Request to add " + font + " with style " + style + 228 " to family " + this); 229 } 230 /* Allow a lower-rank font only if its a file font 231 * from the exact same source as any previous font. 232 */ 233 if ((font.getRank() > familyRank) && !isFromSameSource(font)) { 234 if (FontUtilities.isLogging()) { 235 FontUtilities.getLogger() 236 .warning("Rejecting adding " + font + 237 " of lower rank " + font.getRank() + 238 " to family " + this + 239 " of rank " + familyRank); 240 } 241 return; 242 } 243 244 switch (style) { 245 246 case Font.PLAIN: 247 if (preferredWidth(font) && closerWeight(plain, font, style)) { 248 plain = font; 249 } 250 break; 251 252 case Font.BOLD: 253 if (preferredWidth(font) && closerWeight(bold, font, style)) { 254 bold = font; 255 } 256 break; 257 258 case Font.ITALIC: 259 if (preferredWidth(font) && closerWeight(italic, font, style)) { 260 italic = font; 261 } 262 break; 263 264 case Font.BOLD|Font.ITALIC: 265 if (preferredWidth(font) && closerWeight(bolditalic, font, style)) { 266 bolditalic = font; 267 } 268 break; 269 270 default: 271 break; 272 } 273 } 274 275 public Font2D getFontWithExactStyleMatch(int style) { 276 277 switch (style) { 278 279 case Font.PLAIN: 280 return plain; 281 282 case Font.BOLD: 283 return bold; 284 285 case Font.ITALIC: 286 return italic; 287 288 case Font.BOLD|Font.ITALIC: 289 return bolditalic; 290 291 default: 292 return null; 293 } 294 } 295 296 /* REMIND: if the callers of this method are operating in an 297 * environment in which not all fonts are registered, the returned 298 * font may be a algorithmically styled one, where in fact if loadfonts 299 * were executed, a styled font may be located. Our present "solution" 300 * to this is to register all fonts in a directory and assume that this 301 * registered all the styles of a font, since they would all be in the 302 * same location. 303 */ 304 public Font2D getFont(int style) { 305 306 switch (style) { 307 308 case Font.PLAIN: 309 return plain; 310 311 case Font.BOLD: 312 if (bold != null) { 313 return bold; 314 } else if (plain != null && plain.canDoStyle(style)) { 315 return plain; 316 } else { 317 return null; 318 } 319 320 case Font.ITALIC: 321 if (italic != null) { 322 return italic; 323 } else if (plain != null && plain.canDoStyle(style)) { 324 return plain; 325 } else { 326 return null; 327 } 328 329 case Font.BOLD|Font.ITALIC: 330 if (bolditalic != null) { 331 return bolditalic; 332 } else if (italic != null && italic.canDoStyle(style)) { 333 return italic; 334 } else if (bold != null && bold.canDoStyle(style)) { 335 return italic; 336 } else if (plain != null && plain.canDoStyle(style)) { 337 return plain; 338 } else { 339 return null; 340 } 341 default: 342 return null; 343 } 344 } 345 346 /* Only to be called if getFont(style) returns null 347 * This method will only return null if the family is completely empty! 348 * Note that it assumes the font of the style you need isn't in the 349 * family. The logic here is that if we must substitute something 350 * it might as well be from the same family. 351 */ 352 Font2D getClosestStyle(int style) { 353 354 switch (style) { 355 /* if you ask for a plain font try to return a non-italic one, 356 * then a italic one, finally a bold italic one */ 357 case Font.PLAIN: 358 if (bold != null) { 359 return bold; 360 } else if (italic != null) { 361 return italic; 362 } else { 363 return bolditalic; 364 } 365 366 /* if you ask for a bold font try to return a non-italic one, 367 * then a bold italic one, finally an italic one */ 368 case Font.BOLD: 369 if (plain != null) { 370 return plain; 371 } else if (bolditalic != null) { 372 return bolditalic; 373 } else { 374 return italic; 375 } 376 377 /* if you ask for a italic font try to return a bold italic one, 378 * then a plain one, finally an bold one */ 379 case Font.ITALIC: 380 if (bolditalic != null) { 381 return bolditalic; 382 } else if (plain != null) { 383 return plain; 384 } else { 385 return bold; 386 } 387 388 case Font.BOLD|Font.ITALIC: 389 if (italic != null) { 390 return italic; 391 } else if (bold != null) { 392 return bold; 393 } else { 394 return plain; 395 } 396 } 397 return null; 398 } 399 400 /* Font may have localized names. Store these in a separate map, so 401 * that only clients who use these names need be affected. 402 */ 403 static synchronized void addLocaleNames(FontFamily family, String[] names){ 404 if (allLocaleNames == null) { 405 allLocaleNames = new HashMap<String, FontFamily>(); 406 } 407 for (int i=0; i<names.length; i++) { 408 allLocaleNames.put(names[i].toLowerCase(), family); 409 } 410 } 411 412 public static synchronized FontFamily getLocaleFamily(String name) { 413 if (allLocaleNames == null) { 414 return null; 415 } 416 return allLocaleNames.get(name.toLowerCase()); 417 } 418 419 public static FontFamily[] getAllFontFamilies() { 420 Collection<FontFamily> families = familyNameMap.values(); 421 return families.toArray(new FontFamily[0]); 422 } 423 424 public String toString() { 425 return 426 "Font family: " + familyName + 427 " plain="+plain+ 428 " bold=" + bold + 429 " italic=" + italic + 430 " bolditalic=" + bolditalic; 431 432 } 433 434 } --- EOF ---