1 /* 2 * Copyright 2000-2008 Sun Microsystems, Inc. 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. Sun designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 22 * CA 95054 USA or visit www.sun.com if you need additional information or 23 * have any questions. 24 */ 25 26 package sun.awt; 27 28 import java.awt.Font; 29 import java.io.DataInputStream; 30 import java.io.DataOutputStream; 31 import java.io.File; 32 import java.io.FileInputStream; 33 import java.io.FileOutputStream; 34 import java.io.InputStream; 35 import java.io.IOException; 36 import java.io.OutputStream; 37 import java.nio.charset.Charset; 38 import java.nio.charset.CharsetEncoder; 39 import java.security.AccessController; 40 import java.security.PrivilegedAction; 41 import java.util.logging.Logger; 42 import java.util.HashMap; 43 import java.util.HashSet; 44 import java.util.Hashtable; 45 import java.util.Iterator; 46 import java.util.Locale; 47 import java.util.Map.Entry; 48 import java.util.Properties; 49 import java.util.Set; 50 import java.util.Vector; 51 import sun.font.CompositeFontDescriptor; 52 import sun.java2d.SunGraphicsEnvironment; 53 54 /** 55 * Provides the definitions of the five logical fonts: Serif, SansSerif, 56 * Monospaced, Dialog, and DialogInput. The necessary information 57 * is obtained from fontconfig files. 58 */ 59 public abstract class FontConfiguration { 60 61 //static global runtime env 62 protected static String osVersion; 63 protected static String osName; 64 protected static String encoding; // canonical name of default nio charset 65 protected static Locale startupLocale = null; 66 protected static Hashtable localeMap = null; 67 private static FontConfiguration fontConfig; 68 private static Logger logger; 69 protected static boolean isProperties = true; 70 71 protected SunGraphicsEnvironment environment; 72 protected boolean preferLocaleFonts; 73 protected boolean preferPropFonts; 74 75 private File fontConfigFile; 76 private boolean foundOsSpecificFile; 77 private boolean inited; 78 private String javaLib; 79 80 /* A default FontConfiguration must be created before an alternate 81 * one to ensure proper static initialisation takes place. 82 */ 83 public FontConfiguration(SunGraphicsEnvironment environment) { 84 if (SunGraphicsEnvironment.debugFonts && logger == null) { 85 logger = Logger.getLogger("sun.awt.FontConfiguration"); 86 } 87 this.environment = environment; 88 setOsNameAndVersion(); /* static initialization */ 89 setEncoding(); /* static initialization */ 90 /* Separating out the file location from the rest of the 91 * initialisation, so the caller has the option of doing 92 * something else if a suitable file isn't found. 93 */ 94 findFontConfigFile(); 95 } 96 97 public synchronized boolean init() { 98 if (!inited) { 99 this.preferLocaleFonts = false; 100 this.preferPropFonts = false; 101 setFontConfiguration(); 102 readFontConfigFile(fontConfigFile); 103 initFontConfig(); 104 inited = true; 105 } 106 return true; 107 } 108 109 public FontConfiguration(SunGraphicsEnvironment environment, 110 boolean preferLocaleFonts, 111 boolean preferPropFonts) { 112 this.environment = environment; 113 this.preferLocaleFonts = preferLocaleFonts; 114 this.preferPropFonts = preferPropFonts; 115 /* fontConfig should be initialised by default constructor, and 116 * its data tables can be shared, since readFontConfigFile doesn't 117 * update any other state. Also avoid a doPrivileged block. 118 */ 119 initFontConfig(); 120 } 121 122 /** 123 * Fills in this instance's osVersion and osName members. By 124 * default uses the system properties os.name and os.version; 125 * subclasses may override. 126 */ 127 protected void setOsNameAndVersion() { 128 osName = System.getProperty("os.name"); 129 osVersion = System.getProperty("os.version"); 130 } 131 132 private void setEncoding() { 133 encoding = Charset.defaultCharset().name(); 134 startupLocale = SunToolkit.getStartupLocale(); 135 } 136 137 ///////////////////////////////////////////////////////////////////// 138 // methods for loading the FontConfig file // 139 ///////////////////////////////////////////////////////////////////// 140 141 public boolean foundOsSpecificFile() { 142 return foundOsSpecificFile; 143 } 144 145 /* Smoke test to see if we can trust this configuration by testing if 146 * the first slot of a composite font maps to an installed file. 147 */ 148 public boolean fontFilesArePresent() { 149 init(); 150 short fontNameID = compFontNameIDs[0][0][0]; 151 short fileNameID = getComponentFileID(fontNameID); 152 final String fileName = mapFileName(getComponentFileName(fileNameID)); 153 Boolean exists = (Boolean)java.security.AccessController.doPrivileged( 154 new java.security.PrivilegedAction() { 155 public Object run() { 156 try { 157 File f = new File(fileName); 158 return Boolean.valueOf(f.exists()); 159 } 160 catch (Exception e) { 161 return false; 162 } 163 } 164 }); 165 return exists.booleanValue(); 166 } 167 168 private void findFontConfigFile() { 169 170 foundOsSpecificFile = true; // default assumption. 171 String javaHome = System.getProperty("java.home"); 172 if (javaHome == null) { 173 throw new Error("java.home property not set"); 174 } 175 javaLib = javaHome + File.separator + "lib"; 176 String userConfigFile = System.getProperty("sun.awt.fontconfig"); 177 if (userConfigFile != null) { 178 fontConfigFile = new File(userConfigFile); 179 } else { 180 fontConfigFile = findFontConfigFile(javaLib); 181 } 182 } 183 184 private void readFontConfigFile(File f) { 185 /* This is invoked here as readFontConfigFile is only invoked 186 * once per VM, and always in a privileged context, thus the 187 * directory containing installed fall back fonts is accessed 188 * from this context 189 */ 190 getInstalledFallbackFonts(javaLib); 191 192 if (f != null) { 193 try { 194 FileInputStream in = new FileInputStream(f.getPath()); 195 if (isProperties) { 196 loadProperties(in); 197 } else { 198 loadBinary(in); 199 } 200 in.close(); 201 if (SunGraphicsEnvironment.debugFonts) { 202 logger.config("Read logical font configuration from " + f); 203 } 204 } catch (IOException e) { 205 if (SunGraphicsEnvironment.debugFonts) { 206 logger.config("Failed to read logical font configuration from " + f); 207 } 208 } 209 } 210 String version = getVersion(); 211 if (!"1".equals(version) && SunGraphicsEnvironment.debugFonts) { 212 logger.config("Unsupported fontconfig version: " + version); 213 } 214 } 215 216 protected void getInstalledFallbackFonts(String javaLib) { 217 String fallbackDirName = javaLib + File.separator + 218 "fonts" + File.separator + "fallback"; 219 220 File fallbackDir = new File(fallbackDirName); 221 if (fallbackDir.exists() && fallbackDir.isDirectory()) { 222 String[] ttfs = fallbackDir.list(SunGraphicsEnvironment.ttFilter); 223 String[] t1s = fallbackDir.list(SunGraphicsEnvironment.t1Filter); 224 int numTTFs = (ttfs == null) ? 0 : ttfs.length; 225 int numT1s = (t1s == null) ? 0 : t1s.length; 226 int len = numTTFs + numT1s; 227 if (numTTFs + numT1s == 0) { 228 return; 229 } 230 installedFallbackFontFiles = new String[len]; 231 for (int i=0; i<numTTFs; i++) { 232 installedFallbackFontFiles[i] = 233 fallbackDir + File.separator + ttfs[i]; 234 } 235 for (int i=0; i<numT1s; i++) { 236 installedFallbackFontFiles[i+numTTFs] = 237 fallbackDir + File.separator + t1s[i]; 238 } 239 environment.registerFontsInDir(fallbackDirName); 240 } 241 } 242 243 private File findImpl(String fname) { 244 File f = new File(fname + ".properties"); 245 if (f.canRead()) { 246 isProperties = true; 247 return f; 248 } 249 f = new File(fname + ".bfc"); 250 if (f.canRead()) { 251 isProperties = false; 252 return f; 253 } 254 return null; 255 } 256 257 private File findFontConfigFile(String javaLib) { 258 String baseName = javaLib + File.separator + "fontconfig"; 259 File configFile; 260 if (osVersion != null && osName != null) { 261 configFile = findImpl(baseName + "." + osName + "." + osVersion); 262 if (configFile != null) { 263 return configFile; 264 } 265 } 266 if (osName != null) { 267 configFile = findImpl(baseName + "." + osName); 268 if (configFile != null) { 269 return configFile; 270 } 271 } 272 if (osVersion != null) { 273 configFile = findImpl(baseName + "." + osVersion); 274 if (configFile != null) { 275 return configFile; 276 } 277 } 278 foundOsSpecificFile = false; 279 280 configFile = findImpl(baseName); 281 if (configFile != null) { 282 return configFile; 283 } 284 return null; 285 } 286 287 /* Initialize the internal data tables from binary format font 288 * configuration file. 289 */ 290 public static void loadBinary(InputStream inStream) throws IOException { 291 DataInputStream in = new DataInputStream(inStream); 292 head = readShortTable(in, HEAD_LENGTH); 293 int[] tableSizes = new int[INDEX_TABLEEND]; 294 for (int i = 0; i < INDEX_TABLEEND; i++) { 295 tableSizes[i] = head[i + 1] - head[i]; 296 } 297 table_scriptIDs = readShortTable(in, tableSizes[INDEX_scriptIDs]); 298 table_scriptFonts = readShortTable(in, tableSizes[INDEX_scriptFonts]); 299 table_elcIDs = readShortTable(in, tableSizes[INDEX_elcIDs]); 300 table_sequences = readShortTable(in, tableSizes[INDEX_sequences]); 301 table_fontfileNameIDs = readShortTable(in, tableSizes[INDEX_fontfileNameIDs]); 302 table_componentFontNameIDs = readShortTable(in, tableSizes[INDEX_componentFontNameIDs]); 303 table_filenames = readShortTable(in, tableSizes[INDEX_filenames]); 304 table_awtfontpaths = readShortTable(in, tableSizes[INDEX_awtfontpaths]); 305 table_exclusions = readShortTable(in, tableSizes[INDEX_exclusions]); 306 table_proportionals = readShortTable(in, tableSizes[INDEX_proportionals]); 307 table_scriptFontsMotif = readShortTable(in, tableSizes[INDEX_scriptFontsMotif]); 308 table_alphabeticSuffix = readShortTable(in, tableSizes[INDEX_alphabeticSuffix]); 309 table_stringIDs = readShortTable(in, tableSizes[INDEX_stringIDs]); 310 311 //StringTable cache 312 stringCache = new String[table_stringIDs.length + 1]; 313 314 int len = tableSizes[INDEX_stringTable]; 315 byte[] bb = new byte[len * 2]; 316 table_stringTable = new char[len]; 317 in.read(bb); 318 int i = 0, j = 0; 319 while (i < len) { 320 table_stringTable[i++] = (char)(bb[j++] << 8 | (bb[j++] & 0xff)); 321 } 322 if (verbose) { 323 dump(); 324 } 325 } 326 327 /* Generate a binary format font configuration from internal data 328 * tables. 329 */ 330 public static void saveBinary(OutputStream out) throws IOException { 331 DataOutputStream dataOut = new DataOutputStream(out); 332 writeShortTable(dataOut, head); 333 writeShortTable(dataOut, table_scriptIDs); 334 writeShortTable(dataOut, table_scriptFonts); 335 writeShortTable(dataOut, table_elcIDs); 336 writeShortTable(dataOut, table_sequences); 337 writeShortTable(dataOut, table_fontfileNameIDs); 338 writeShortTable(dataOut, table_componentFontNameIDs); 339 writeShortTable(dataOut, table_filenames); 340 writeShortTable(dataOut, table_awtfontpaths); 341 writeShortTable(dataOut, table_exclusions); 342 writeShortTable(dataOut, table_proportionals); 343 writeShortTable(dataOut, table_scriptFontsMotif); 344 writeShortTable(dataOut, table_alphabeticSuffix); 345 writeShortTable(dataOut, table_stringIDs); 346 //stringTable 347 dataOut.writeChars(new String(table_stringTable)); 348 out.close(); 349 if (verbose) { 350 dump(); 351 } 352 sanityCheck(); 353 } 354 355 //private static boolean loadingProperties; 356 private static short stringIDNum; 357 private static short[] stringIDs; 358 private static StringBuilder stringTable; 359 360 public static void loadProperties(InputStream in) throws IOException { 361 //loadingProperties = true; 362 //StringID starts from "1", "0" is reserved for "not defined" 363 stringIDNum = 1; 364 stringIDs = new short[1000]; 365 stringTable = new StringBuilder(4096); 366 367 if (verbose && logger == null) { 368 logger = Logger.getLogger("sun.awt.FontConfiguration"); 369 } 370 new PropertiesHandler().load(in); 371 372 //loadingProperties = false; 373 stringIDs = null; 374 stringTable = null; 375 } 376 377 378 ///////////////////////////////////////////////////////////////////// 379 // methods for initializing the FontConfig // 380 ///////////////////////////////////////////////////////////////////// 381 382 /** 383 * set initLocale, initEncoding and initELC for this FontConfig object 384 * currently we just simply use the startup locale and encoding 385 */ 386 private void initFontConfig() { 387 initLocale = startupLocale; 388 initEncoding = encoding; 389 if (preferLocaleFonts && !willReorderForStartupLocale()) { 390 preferLocaleFonts = false; 391 } 392 initELC = getInitELC(); 393 initAllComponentFonts(); 394 } 395 396 //"ELC" stands for "Encoding.Language.Country". This method returns 397 //the ID of the matched elc setting of "initLocale" in elcIDs table. 398 //If no match is found, it returns the default ID, which is 399 //"NULL.NULL.NULL" in elcIDs table. 400 private short getInitELC() { 401 if (initELC != -1) { 402 return initELC; 403 } 404 HashMap <String, Integer> elcIDs = new HashMap<String, Integer>(); 405 for (int i = 0; i < table_elcIDs.length; i++) { 406 elcIDs.put(getString(table_elcIDs[i]), i); 407 } 408 String language = initLocale.getLanguage(); 409 String country = initLocale.getCountry(); 410 String elc; 411 if (elcIDs.containsKey(elc=initEncoding + "." + language + "." + country) 412 || elcIDs.containsKey(elc=initEncoding + "." + language) 413 || elcIDs.containsKey(elc=initEncoding)) { 414 initELC = elcIDs.get(elc).shortValue(); 415 } else { 416 initELC = elcIDs.get("NULL.NULL.NULL").shortValue(); 417 } 418 int i = 0; 419 while (i < table_alphabeticSuffix.length) { 420 if (initELC == table_alphabeticSuffix[i]) { 421 alphabeticSuffix = getString(table_alphabeticSuffix[i + 1]); 422 return initELC; 423 } 424 i += 2; 425 } 426 return initELC; 427 } 428 429 public static boolean verbose; 430 private short initELC = -1; 431 private Locale initLocale; 432 private String initEncoding; 433 private String alphabeticSuffix; 434 435 private short[][][] compFontNameIDs = new short[NUM_FONTS][NUM_STYLES][]; 436 private int[][][] compExclusions = new int[NUM_FONTS][][]; 437 private int[] compCoreNum = new int[NUM_FONTS]; 438 439 private Set<Short> coreFontNameIDs = new HashSet<Short>(); 440 private Set<Short> fallbackFontNameIDs = new HashSet<Short>(); 441 442 private void initAllComponentFonts() { 443 short[] fallbackScripts = getFallbackScripts(); 444 for (int fontIndex = 0; fontIndex < NUM_FONTS; fontIndex++) { 445 short[] coreScripts = getCoreScripts(fontIndex); 446 compCoreNum[fontIndex] = coreScripts.length; 447 /* 448 System.out.println("coreScriptID=" + table_sequences[initELC * 5 + fontIndex]); 449 for (int i = 0; i < coreScripts.length; i++) { 450 System.out.println(" " + i + " :" + getString(table_scriptIDs[coreScripts[i]])); 451 } 452 */ 453 //init exclusionRanges 454 int[][] exclusions = new int[coreScripts.length][]; 455 for (int i = 0; i < coreScripts.length; i++) { 456 exclusions[i] = getExclusionRanges(coreScripts[i]); 457 } 458 compExclusions[fontIndex] = exclusions; 459 //init componentFontNames 460 for (int styleIndex = 0; styleIndex < NUM_STYLES; styleIndex++) { 461 int index; 462 short[] nameIDs = new short[coreScripts.length + fallbackScripts.length]; 463 //core 464 for (index = 0; index < coreScripts.length; index++) { 465 nameIDs[index] = getComponentFontID(coreScripts[index], 466 fontIndex, styleIndex); 467 if (preferLocaleFonts && localeMap != null && 468 sun.font.FontManager.usingAlternateFontforJALocales()) { 469 nameIDs[index] = remapLocaleMap(fontIndex, styleIndex, 470 coreScripts[index], nameIDs[index]); 471 } 472 if (preferPropFonts) { 473 nameIDs[index] = remapProportional(fontIndex, nameIDs[index]); 474 } 475 //System.out.println("nameid=" + nameIDs[index]); 476 coreFontNameIDs.add(nameIDs[index]); 477 } 478 //fallback 479 for (int i = 0; i < fallbackScripts.length; i++) { 480 short id = getComponentFontID(fallbackScripts[i], 481 fontIndex, styleIndex); 482 if (preferLocaleFonts && localeMap != null && 483 sun.font.FontManager.usingAlternateFontforJALocales()) { 484 id = remapLocaleMap(fontIndex, styleIndex, fallbackScripts[i], id); 485 } 486 if (preferPropFonts) { 487 id = remapProportional(fontIndex, id); 488 } 489 if (contains(nameIDs, id, index)) { 490 continue; 491 } 492 /* 493 System.out.println("fontIndex=" + fontIndex + ", styleIndex=" + styleIndex 494 + ", fbIndex=" + i + ",fbS=" + fallbackScripts[i] + ", id=" + id); 495 */ 496 fallbackFontNameIDs.add(id); 497 nameIDs[index++] = id; 498 } 499 if (index < nameIDs.length) { 500 short[] newNameIDs = new short[index]; 501 System.arraycopy(nameIDs, 0, newNameIDs, 0, index); 502 nameIDs = newNameIDs; 503 } 504 compFontNameIDs[fontIndex][styleIndex] = nameIDs; 505 } 506 } 507 } 508 509 private short remapLocaleMap(int fontIndex, int styleIndex, short scriptID, short fontID) { 510 String scriptName = getString(table_scriptIDs[scriptID]); 511 512 String value = (String)localeMap.get(scriptName); 513 if (value == null) { 514 String fontName = fontNames[fontIndex]; 515 String styleName = styleNames[styleIndex]; 516 value = (String)localeMap.get(fontName + "." + styleName + "." + scriptName); 517 } 518 if (value == null) { 519 return fontID; 520 } 521 522 for (int i = 0; i < table_componentFontNameIDs.length; i++) { 523 String name = getString(table_componentFontNameIDs[i]); 524 if (value.equalsIgnoreCase(name)) { 525 fontID = (short)i; 526 break; 527 } 528 } 529 return fontID; 530 } 531 532 public static boolean hasMonoToPropMap() { 533 return table_proportionals != null && table_proportionals.length != 0; 534 } 535 536 private short remapProportional(int fontIndex, short id) { 537 if (preferPropFonts && 538 table_proportionals.length != 0 && 539 fontIndex != 2 && //"monospaced" 540 fontIndex != 4) { //"dialoginput" 541 int i = 0; 542 while (i < table_proportionals.length) { 543 if (table_proportionals[i] == id) { 544 return table_proportionals[i + 1]; 545 } 546 i += 2; 547 } 548 } 549 return id; 550 } 551 552 ///////////////////////////////////////////////////////////////////// 553 // Methods for handling font and style names // 554 ///////////////////////////////////////////////////////////////////// 555 protected static final int NUM_FONTS = 5; 556 protected static final int NUM_STYLES = 4; 557 protected static final String[] fontNames 558 = {"serif", "sansserif", "monospaced", "dialog", "dialoginput"}; 559 protected static final String[] publicFontNames 560 = {Font.SERIF, Font.SANS_SERIF, Font.MONOSPACED, Font.DIALOG, 561 Font.DIALOG_INPUT}; 562 protected static final String[] styleNames 563 = {"plain", "bold", "italic", "bolditalic"}; 564 565 /** 566 * Checks whether the given font family name is a valid logical font name. 567 * The check is case insensitive. 568 */ 569 public static boolean isLogicalFontFamilyName(String fontName) { 570 return isLogicalFontFamilyNameLC(fontName.toLowerCase(Locale.ENGLISH)); 571 } 572 573 /** 574 * Checks whether the given font family name is a valid logical font name. 575 * The check is case sensitive. 576 */ 577 public static boolean isLogicalFontFamilyNameLC(String fontName) { 578 for (int i = 0; i < fontNames.length; i++) { 579 if (fontName.equals(fontNames[i])) { 580 return true; 581 } 582 } 583 return false; 584 } 585 586 /** 587 * Checks whether the given style name is a valid logical font style name. 588 */ 589 private static boolean isLogicalFontStyleName(String styleName) { 590 for (int i = 0; i < styleNames.length; i++) { 591 if (styleName.equals(styleNames[i])) { 592 return true; 593 } 594 } 595 return false; 596 } 597 598 /** 599 * Checks whether the given font face name is a valid logical font name. 600 * The check is case insensitive. 601 */ 602 public static boolean isLogicalFontFaceName(String fontName) { 603 return isLogicalFontFaceNameLC(fontName.toLowerCase(Locale.ENGLISH)); 604 } 605 606 /** 607 * Checks whether the given font face name is a valid logical font name. 608 * The check is case sensitive. 609 */ 610 public static boolean isLogicalFontFaceNameLC(String fontName) { 611 int period = fontName.indexOf('.'); 612 if (period >= 0) { 613 String familyName = fontName.substring(0, period); 614 String styleName = fontName.substring(period + 1); 615 return isLogicalFontFamilyName(familyName) && 616 isLogicalFontStyleName(styleName); 617 } else { 618 return isLogicalFontFamilyName(fontName); 619 } 620 } 621 622 protected static int getFontIndex(String fontName) { 623 return getArrayIndex(fontNames, fontName); 624 } 625 626 protected static int getStyleIndex(String styleName) { 627 return getArrayIndex(styleNames, styleName); 628 } 629 630 private static int getArrayIndex(String[] names, String name) { 631 for (int i = 0; i < names.length; i++) { 632 if (name.equals(names[i])) { 633 return i; 634 } 635 } 636 assert false; 637 return 0; 638 } 639 640 protected static int getStyleIndex(int style) { 641 switch (style) { 642 case Font.PLAIN: 643 return 0; 644 case Font.BOLD: 645 return 1; 646 case Font.ITALIC: 647 return 2; 648 case Font.BOLD | Font.ITALIC: 649 return 3; 650 default: 651 return 0; 652 } 653 } 654 655 protected static String getFontName(int fontIndex) { 656 return fontNames[fontIndex]; 657 } 658 659 protected static String getStyleName(int styleIndex) { 660 return styleNames[styleIndex]; 661 } 662 663 /** 664 * Returns the font face name for the given logical font 665 * family name and style. 666 * The style argument is interpreted as in java.awt.Font.Font. 667 */ 668 public static String getLogicalFontFaceName(String familyName, int style) { 669 assert isLogicalFontFamilyName(familyName); 670 return familyName.toLowerCase(Locale.ENGLISH) + "." + getStyleString(style); 671 } 672 673 /** 674 * Returns the string typically used in properties files 675 * for the given style. 676 * The style argument is interpreted as in java.awt.Font.Font. 677 */ 678 public static String getStyleString(int style) { 679 return getStyleName(getStyleIndex(style)); 680 } 681 682 /** 683 * Returns a fallback name for the given font name. For a few known 684 * font names, matching logical font names are returned. For all 685 * other font names, defaultFallback is returned. 686 * defaultFallback differs between AWT and 2D. 687 */ 688 public abstract String getFallbackFamilyName(String fontName, String defaultFallback); 689 690 /** 691 * Returns the 1.1 equivalent for some old 1.0 font family names for 692 * which we need to maintain compatibility in some configurations. 693 * Returns null for other font names. 694 */ 695 protected String getCompatibilityFamilyName(String fontName) { 696 fontName = fontName.toLowerCase(Locale.ENGLISH); 697 if (fontName.equals("timesroman")) { 698 return "serif"; 699 } else if (fontName.equals("helvetica")) { 700 return "sansserif"; 701 } else if (fontName.equals("courier")) { 702 return "monospaced"; 703 } 704 return null; 705 } 706 707 protected static String[] installedFallbackFontFiles = null; 708 709 /** 710 * Maps a file name given in the font configuration file 711 * to a format appropriate for the platform. 712 */ 713 protected String mapFileName(String fileName) { 714 return fileName; 715 } 716 717 ////////////////////////////////////////////////////////////////////// 718 // reordering // 719 ////////////////////////////////////////////////////////////////////// 720 721 /* Mappings from file encoding to font config name for font supporting 722 * the corresponding language. This is filled in by initReorderMap() 723 */ 724 protected HashMap reorderMap = null; 725 726 /* Platform-specific mappings */ 727 protected abstract void initReorderMap(); 728 729 /* Move item at index "src" to "dst", shuffling all values in 730 * between down 731 */ 732 private void shuffle(String[] seq, int src, int dst) { 733 if (dst >= src) { 734 return; 735 } 736 String tmp = seq[src]; 737 for (int i=src; i>dst; i--) { 738 seq[i] = seq[i-1]; 739 } 740 seq[dst] = tmp; 741 } 742 743 /* Called to determine if there's a re-order sequence for this locale/ 744 * encoding. If there's none then the caller can "bail" and avoid 745 * unnecessary work 746 */ 747 public static boolean willReorderForStartupLocale() { 748 return getReorderSequence() != null; 749 } 750 751 private static Object getReorderSequence() { 752 if (fontConfig.reorderMap == null) { 753 fontConfig.initReorderMap(); 754 } 755 HashMap reorderMap = fontConfig.reorderMap; 756 757 /* Find the most specific mapping */ 758 String language = startupLocale.getLanguage(); 759 String country = startupLocale.getCountry(); 760 Object val = reorderMap.get(encoding + "." + language + "." + country); 761 if (val == null) { 762 val = reorderMap.get(encoding + "." + language); 763 } 764 if (val == null) { 765 val = reorderMap.get(encoding); 766 } 767 return val; 768 } 769 770 /* This method reorders the sequence such that the matches for the 771 * file encoding are moved ahead of other elements. 772 * If an encoding uses more than one font, they are all moved up. 773 */ 774 private void reorderSequenceForLocale(String[] seq) { 775 Object val = getReorderSequence(); 776 if (val instanceof String) { 777 for (int i=0; i< seq.length; i++) { 778 if (seq[i].equals(val)) { 779 shuffle(seq, i, 0); 780 return; 781 } 782 } 783 } else if (val instanceof String[]) { 784 String[] fontLangs = (String[])val; 785 for (int l=0; l<fontLangs.length;l++) { 786 for (int i=0; i<seq.length;i++) { 787 if (seq[i].equals(fontLangs[l])) { 788 shuffle(seq, i, l); 789 } 790 } 791 } 792 } 793 } 794 795 private static Vector splitSequence(String sequence) { 796 //String.split would be more convenient, but incurs big performance penalty 797 Vector parts = new Vector(); 798 int start = 0; 799 int end; 800 while ((end = sequence.indexOf(',', start)) >= 0) { 801 parts.add(sequence.substring(start, end)); 802 start = end + 1; 803 } 804 if (sequence.length() > start) { 805 parts.add(sequence.substring(start, sequence.length())); 806 } 807 return parts; 808 } 809 810 protected String[] split(String sequence) { 811 Vector v = splitSequence(sequence); 812 return (String[])v.toArray(new String[0]); 813 } 814 815 //////////////////////////////////////////////////////////////////////// 816 // Methods for extracting information from the fontconfig data for AWT// 817 //////////////////////////////////////////////////////////////////////// 818 private Hashtable charsetRegistry = new Hashtable(5); 819 820 /** 821 * Returns FontDescriptors describing the physical fonts used for the 822 * given logical font name and style. The font name is interpreted 823 * in a case insensitive way. 824 * The style argument is interpreted as in java.awt.Font.Font. 825 */ 826 public FontDescriptor[] getFontDescriptors(String fontName, int style) { 827 assert isLogicalFontFamilyName(fontName); 828 fontName = fontName.toLowerCase(Locale.ENGLISH); 829 int fontIndex = getFontIndex(fontName); 830 int styleIndex = getStyleIndex(style); 831 return getFontDescriptors(fontIndex, styleIndex); 832 } 833 private FontDescriptor[][][] fontDescriptors = 834 new FontDescriptor[NUM_FONTS][NUM_STYLES][]; 835 836 private FontDescriptor[] getFontDescriptors(int fontIndex, int styleIndex) { 837 FontDescriptor[] descriptors = fontDescriptors[fontIndex][styleIndex]; 838 if (descriptors == null) { 839 descriptors = buildFontDescriptors(fontIndex, styleIndex); 840 fontDescriptors[fontIndex][styleIndex] = descriptors; 841 } 842 return descriptors; 843 } 844 845 private FontDescriptor[] buildFontDescriptors(int fontIndex, int styleIndex) { 846 String fontName = fontNames[fontIndex]; 847 String styleName = styleNames[styleIndex]; 848 849 short[] scriptIDs = getCoreScripts(fontIndex); 850 short[] nameIDs = compFontNameIDs[fontIndex][styleIndex]; 851 String[] sequence = new String[scriptIDs.length]; 852 String[] names = new String[scriptIDs.length]; 853 for (int i = 0; i < sequence.length; i++) { 854 names[i] = getComponentFontName(nameIDs[i]); 855 sequence[i] = getScriptName(scriptIDs[i]); 856 if (alphabeticSuffix != null && "alphabetic".equals(sequence[i])) { 857 sequence[i] = sequence[i] + "/" + alphabeticSuffix; 858 } 859 } 860 int[][] fontExclusionRanges = compExclusions[fontIndex]; 861 862 FontDescriptor[] descriptors = new FontDescriptor[names.length]; 863 864 for (int i = 0; i < names.length; i++) { 865 String awtFontName; 866 String encoding; 867 868 awtFontName = makeAWTFontName(names[i], sequence[i]); 869 870 // look up character encoding 871 encoding = getEncoding(names[i], sequence[i]); 872 if (encoding == null) { 873 encoding = "default"; 874 } 875 CharsetEncoder enc 876 = getFontCharsetEncoder(encoding.trim(), awtFontName); 877 878 // we already have the exclusion ranges 879 int[] exclusionRanges = fontExclusionRanges[i]; 880 881 // create descriptor 882 descriptors[i] = new FontDescriptor(awtFontName, enc, exclusionRanges); 883 } 884 return descriptors; 885 } 886 887 /** 888 * Returns the AWT font name for the given platform font name and 889 * character subset. 890 */ 891 protected String makeAWTFontName(String platformFontName, 892 String characterSubsetName) { 893 return platformFontName; 894 } 895 896 /** 897 * Returns the java.io name of the platform character encoding for the 898 * given AWT font name and character subset. May return "default" 899 * to indicate that getDefaultFontCharset should be called to obtain 900 * a charset encoder. 901 */ 902 protected abstract String getEncoding(String awtFontName, 903 String characterSubsetName); 904 905 private CharsetEncoder getFontCharsetEncoder(final String charsetName, 906 String fontName) { 907 908 Charset fc = null; 909 if (charsetName.equals("default")) { 910 fc = (Charset) charsetRegistry.get(fontName); 911 } else { 912 fc = (Charset) charsetRegistry.get(charsetName); 913 } 914 if (fc != null) { 915 return fc.newEncoder(); 916 } 917 918 if (!charsetName.startsWith("sun.awt.") && !charsetName.equals("default")) { 919 fc = Charset.forName(charsetName); 920 } else { 921 Class fcc = (Class) AccessController.doPrivileged(new PrivilegedAction() { 922 public Object run() { 923 try { 924 return Class.forName(charsetName, true, 925 Thread.currentThread().getContextClassLoader()); 926 } catch (ClassNotFoundException e) { 927 } 928 return null; 929 } 930 }); 931 932 if (fcc != null) { 933 try { 934 fc = (Charset) fcc.newInstance(); 935 } catch (Exception e) { 936 } 937 } 938 } 939 if (fc == null) { 940 fc = getDefaultFontCharset(fontName); 941 } 942 943 if (charsetName.equals("default")){ 944 charsetRegistry.put(fontName, fc); 945 } else { 946 charsetRegistry.put(charsetName, fc); 947 } 948 return fc.newEncoder(); 949 } 950 951 protected abstract Charset getDefaultFontCharset( 952 String fontName); 953 954 /* This retrieves the platform font directories (path) calculated 955 * by setAWTFontPathSequence(String[]). The default implementation 956 * returns null, its expected that X11 platforms may return 957 * non-null. 958 */ 959 public HashSet<String> getAWTFontPathSet() { 960 return null; 961 } 962 963 //////////////////////////////////////////////////////////////////////// 964 // methods for extracting information from the fontconfig data for 2D // 965 //////////////////////////////////////////////////////////////////////// 966 967 /** 968 * Returns an array of composite font descriptors for all logical font 969 * faces. 970 * If the font configuration file doesn't specify Lucida Sans Regular 971 * or the given fallback font as component fonts, they are added here. 972 */ 973 public CompositeFontDescriptor[] get2DCompositeFontInfo() { 974 CompositeFontDescriptor[] result = 975 new CompositeFontDescriptor[NUM_FONTS * NUM_STYLES]; 976 String defaultFontFile = environment.getDefaultFontFile(); 977 String defaultFontFaceName = environment.getDefaultFontFaceName(); 978 979 for (int fontIndex = 0; fontIndex < NUM_FONTS; fontIndex++) { 980 String fontName = publicFontNames[fontIndex]; 981 982 // determine exclusion ranges for font 983 // AWT uses separate exclusion range array per component font. 984 // 2D packs all range boundaries into one array. 985 // Both use separate entries for lower and upper boundary. 986 int[][] exclusions = compExclusions[fontIndex]; 987 int numExclusionRanges = 0; 988 for (int i = 0; i < exclusions.length; i++) { 989 numExclusionRanges += exclusions[i].length; 990 } 991 int[] exclusionRanges = new int[numExclusionRanges]; 992 int[] exclusionRangeLimits = new int[exclusions.length]; 993 int exclusionRangeIndex = 0; 994 int exclusionRangeLimitIndex = 0; 995 for (int i = 0; i < exclusions.length; i++) { 996 int[] componentRanges = exclusions[i]; 997 for (int j = 0; j < componentRanges.length; ) { 998 int value = componentRanges[j]; 999 exclusionRanges[exclusionRangeIndex++] = componentRanges[j++]; 1000 exclusionRanges[exclusionRangeIndex++] = componentRanges[j++]; 1001 } 1002 exclusionRangeLimits[i] = exclusionRangeIndex; 1003 } 1004 // other info is per style 1005 for (int styleIndex = 0; styleIndex < NUM_STYLES; styleIndex++) { 1006 int maxComponentFontCount = compFontNameIDs[fontIndex][styleIndex].length; 1007 boolean sawDefaultFontFile = false; 1008 // fall back fonts listed in the lib/fonts/fallback directory 1009 if (installedFallbackFontFiles != null) { 1010 maxComponentFontCount += installedFallbackFontFiles.length; 1011 } 1012 String faceName = fontName + "." + styleNames[styleIndex]; 1013 1014 // determine face names and file names of component fonts 1015 String[] componentFaceNames = new String[maxComponentFontCount]; 1016 String[] componentFileNames = new String[maxComponentFontCount]; 1017 1018 int index; 1019 for (index = 0; index < compFontNameIDs[fontIndex][styleIndex].length; index++) { 1020 short fontNameID = compFontNameIDs[fontIndex][styleIndex][index]; 1021 short fileNameID = getComponentFileID(fontNameID); 1022 componentFaceNames[index] = getFaceNameFromComponentFontName(getComponentFontName(fontNameID)); 1023 componentFileNames[index] = mapFileName(getComponentFileName(fileNameID)); 1024 if (componentFileNames[index] == null || 1025 needToSearchForFile(componentFileNames[index])) { 1026 componentFileNames[index] = getFileNameFromComponentFontName(getComponentFontName(fontNameID)); 1027 } 1028 if (!sawDefaultFontFile && 1029 defaultFontFile.equals(componentFileNames[index])) { 1030 sawDefaultFontFile = true; 1031 } 1032 /* 1033 System.out.println(publicFontNames[fontIndex] + "." + styleNames[styleIndex] + "." 1034 + getString(table_scriptIDs[coreScripts[index]]) + "=" + componentFileNames[index]); 1035 */ 1036 } 1037 1038 //"Lucida Sans Regular" is not in the list, we add it here 1039 if (!sawDefaultFontFile) { 1040 int len = 0; 1041 if (installedFallbackFontFiles != null) { 1042 len = installedFallbackFontFiles.length; 1043 } 1044 if (index + len == maxComponentFontCount) { 1045 String[] newComponentFaceNames = new String[maxComponentFontCount + 1]; 1046 System.arraycopy(componentFaceNames, 0, newComponentFaceNames, 0, index); 1047 componentFaceNames = newComponentFaceNames; 1048 String[] newComponentFileNames = new String[maxComponentFontCount + 1]; 1049 System.arraycopy(componentFileNames, 0, newComponentFileNames, 0, index); 1050 componentFileNames = newComponentFileNames; 1051 } 1052 componentFaceNames[index] = defaultFontFaceName; 1053 componentFileNames[index] = defaultFontFile; 1054 index++; 1055 } 1056 1057 if (installedFallbackFontFiles != null) { 1058 for (int ifb=0; ifb<installedFallbackFontFiles.length; ifb++) { 1059 componentFaceNames[index] = null; 1060 componentFileNames[index] = installedFallbackFontFiles[ifb]; 1061 index++; 1062 } 1063 } 1064 1065 if (index < maxComponentFontCount) { 1066 String[] newComponentFaceNames = new String[index]; 1067 System.arraycopy(componentFaceNames, 0, newComponentFaceNames, 0, index); 1068 componentFaceNames = newComponentFaceNames; 1069 String[] newComponentFileNames = new String[index]; 1070 System.arraycopy(componentFileNames, 0, newComponentFileNames, 0, index); 1071 componentFileNames = newComponentFileNames; 1072 } 1073 // exclusion range limit array length must match component face name 1074 // array length - native code relies on this 1075 1076 int[] clippedExclusionRangeLimits = exclusionRangeLimits; 1077 if (index != clippedExclusionRangeLimits.length) { 1078 int len = exclusionRangeLimits.length; 1079 clippedExclusionRangeLimits = new int[index]; 1080 System.arraycopy(exclusionRangeLimits, 0, clippedExclusionRangeLimits, 0, len); 1081 //padding for various fallback fonts 1082 for (int i = len; i < index; i++) { 1083 clippedExclusionRangeLimits[i] = exclusionRanges.length; 1084 } 1085 } 1086 /* 1087 System.out.println(faceName + ":"); 1088 for (int i = 0; i < componentFileNames.length; i++) { 1089 System.out.println(" " + componentFaceNames[i] 1090 + " -> " + componentFileNames[i]); 1091 } 1092 */ 1093 result[fontIndex * NUM_STYLES + styleIndex] 1094 = new CompositeFontDescriptor( 1095 faceName, 1096 compCoreNum[fontIndex], 1097 componentFaceNames, 1098 componentFileNames, 1099 exclusionRanges, 1100 clippedExclusionRangeLimits); 1101 } 1102 } 1103 return result; 1104 } 1105 1106 protected abstract String getFaceNameFromComponentFontName(String componentFontName); 1107 protected abstract String getFileNameFromComponentFontName(String componentFontName); 1108 1109 /* 1110 public class 2dFont { 1111 public String platformName; 1112 public String fontfileName; 1113 } 1114 private 2dFont [] componentFonts = null; 1115 */ 1116 1117 /* Used on Linux to test if a file referenced in a font configuration 1118 * file exists in the location that is expected. If it does, no need 1119 * to search for it. If it doesn't then unless its a fallback font, 1120 * return that expensive code should be invoked to search for the font. 1121 */ 1122 HashMap<String, Boolean> existsMap; 1123 public boolean needToSearchForFile(String fileName) { 1124 if (!environment.isLinux) { 1125 return false; 1126 } else if (existsMap == null) { 1127 existsMap = new HashMap<String, Boolean>(); 1128 } 1129 Boolean exists = existsMap.get(fileName); 1130 if (exists == null) { 1131 /* call getNumberCoreFonts() to ensure these are initialised, and 1132 * if this file isn't for a core component, ie, is a for a fallback 1133 * font which very typically isn't available, then can't afford 1134 * to take the start-up penalty to search for it. 1135 */ 1136 getNumberCoreFonts(); 1137 if (!coreFontFileNames.contains(fileName)) { 1138 exists = Boolean.TRUE; 1139 } else { 1140 exists = Boolean.valueOf((new File(fileName)).exists()); 1141 existsMap.put(fileName, exists); 1142 if (SunGraphicsEnvironment.debugFonts && 1143 exists == Boolean.FALSE) { 1144 logger.warning("Couldn't locate font file " + fileName); 1145 } 1146 } 1147 } 1148 return exists == Boolean.FALSE; 1149 } 1150 1151 private int numCoreFonts = -1; 1152 private String[] componentFonts = null; 1153 HashMap <String, String> filenamesMap = new HashMap<String, String>(); 1154 HashSet <String> coreFontFileNames = new HashSet<String>(); 1155 1156 /* Return the number of core fonts. Note this isn't thread safe but 1157 * a calling thread can call this and getPlatformFontNames() in either 1158 * order. 1159 */ 1160 public int getNumberCoreFonts() { 1161 if (numCoreFonts == -1) { 1162 numCoreFonts = coreFontNameIDs.size(); 1163 Short[] emptyShortArray = new Short[0]; 1164 Short[] core = coreFontNameIDs.toArray(emptyShortArray); 1165 Short[] fallback = fallbackFontNameIDs.toArray(emptyShortArray); 1166 1167 int numFallbackFonts = 0; 1168 int i; 1169 for (i = 0; i < fallback.length; i++) { 1170 if (coreFontNameIDs.contains(fallback[i])) { 1171 fallback[i] = null; 1172 continue; 1173 } 1174 numFallbackFonts++; 1175 } 1176 componentFonts = new String[numCoreFonts + numFallbackFonts]; 1177 String filename = null; 1178 for (i = 0; i < core.length; i++) { 1179 short fontid = core[i]; 1180 short fileid = getComponentFileID(fontid); 1181 componentFonts[i] = getComponentFontName(fontid); 1182 String compFileName = getComponentFileName(fileid); 1183 if (compFileName != null) { 1184 coreFontFileNames.add(compFileName); 1185 } 1186 filenamesMap.put(componentFonts[i], mapFileName(compFileName)); 1187 } 1188 for (int j = 0; j < fallback.length; j++) { 1189 if (fallback[j] != null) { 1190 short fontid = fallback[j]; 1191 short fileid = getComponentFileID(fontid); 1192 componentFonts[i] = getComponentFontName(fontid); 1193 filenamesMap.put(componentFonts[i], 1194 mapFileName(getComponentFileName(fileid))); 1195 i++; 1196 } 1197 } 1198 } 1199 return numCoreFonts; 1200 } 1201 1202 /* Return all platform font names used by this font configuration. 1203 * The first getNumberCoreFonts() entries are guaranteed to be the 1204 * core fonts - ie no fall back only fonts. 1205 */ 1206 public String[] getPlatformFontNames() { 1207 if (numCoreFonts == -1) { 1208 getNumberCoreFonts(); 1209 } 1210 return componentFonts; 1211 } 1212 1213 /** 1214 * Returns a file name for the physical font represented by this platform font name, 1215 * if the font configuration has such information available, or null if the 1216 * information is unavailable. The file name returned is just a hint; a null return 1217 * value doesn't necessarily mean that the font is unavailable, nor does a non-null 1218 * return value guarantee that the file exists and contains the physical font. 1219 * The file name can be an absolute or a relative path name. 1220 */ 1221 public String getFileNameFromPlatformName(String platformName) { 1222 // get2DCompositeFontInfo 1223 // -> getFileNameFromComponentfontName() (W/M) 1224 // -> getFileNameFromPlatformName() 1225 // it's a waste of time on Win32, but I have to give X11 a chance to 1226 // call getFileNameFromXLFD() 1227 return filenamesMap.get(platformName); 1228 } 1229 1230 /** 1231 * Returns a configuration specific path to be appended to the font 1232 * search path. 1233 */ 1234 public String getExtraFontPath() { 1235 return getString(head[INDEX_appendedfontpath]); 1236 } 1237 1238 public String getVersion() { 1239 return getString(head[INDEX_version]); 1240 } 1241 1242 /* subclass support */ 1243 protected static FontConfiguration getFontConfiguration() { 1244 return fontConfig; 1245 } 1246 1247 protected void setFontConfiguration() { 1248 fontConfig = this; /* static initialization */ 1249 } 1250 1251 ////////////////////////////////////////////////////////////////////// 1252 // FontConfig data tables and the index constants in binary file // 1253 ////////////////////////////////////////////////////////////////////// 1254 /* The binary font configuration file begins with a short[] "head", which 1255 * contains the offsets to the starts of the individual data table which 1256 * immediately follow. Teh current implemention includes the tables shown 1257 * below. 1258 * 1259 * (00) table_scriptIDs :stringIDs of all defined CharacterSubsetNames 1260 * (01) table_scriptFonts :scriptID x fontIndex x styleIndex-> 1261 * PlatformFontNameID mapping. Each scriptID might 1262 * have 1 or 20 entries depends on if it is defined 1263 * via a "allfonts.CharacterSubsetname" or a list of 1264 * "LogicalFontName.StyleName.CharacterSubsetName" 1265 * entries, positive entry means it's a "allfonts" 1266 * entry, a negative value means this is a offset to 1267 * a NUM_FONTS x NUM_STYLES subtable. 1268 * (02) table_elcIDs :stringIDs of all defined ELC names, string 1269 * "NULL.NULL.NULL" is used for "default" 1270 * (03) table_sequences :elcID x logicalFont -> scriptIDs table defined 1271 * by "sequence.allfonts/LogicalFontName.ELC" in 1272 * font configuration file, each "elcID" has 1273 * NUM_FONTS (5) entries in this table. 1274 * (04) table_fontfileNameIDs 1275 * :stringIDs of all defined font file names 1276 * (05) table_componentFontNameIDs 1277 * :stringIDs of all defined PlatformFontNames 1278 * (06) table_filenames :platformFontNamesID->fontfileNameID mapping 1279 * table, the index is the platformFontNamesID. 1280 * (07) table_awtfontpaths :CharacterSubsetNames->awtfontpaths mapping table, 1281 * the index is the CharacterSubsetName's stringID 1282 * and content is the stringID of awtfontpath. 1283 * (08) table_exclusions :scriptID -> exclusionRanges mapping table, 1284 * the index is the scriptID and the content is 1285 a id of an exclusionRanges int[]. 1286 * (09) table_proportionals:list of pairs of PlatformFontNameIDs, stores 1287 * the replacement info defined by "proportional" 1288 * keyword. 1289 * (10) table_scriptFontsMotif 1290 * :same as (01) except this table stores the 1291 * info defined with ".motif" keyword 1292 * (11) table_alphabeticSuffix 1293 * :elcID -> stringID of alphabetic/XXXX entries 1294 * (12) table_stringIDs :The index of this table is the string ID, the 1295 * content is the "start index" of this string in 1296 * stringTable, use the start index of next entry 1297 * as the "end index". 1298 * (13) table_stringTable :The real storage of all character strings defined 1299 * /used this font configuration, need a pair of 1300 * "start" and "end" indices to access. 1301 * (14) reserved 1302 * (15) table_fallbackScripts 1303 * :stringIDs of fallback CharacterSubsetnames, stored 1304 * in the order of they are defined in sequence.fallback. 1305 * (16) table_appendedfontpath 1306 * :stringtID of the "appendedfontpath" defined. 1307 * (17) table_version :stringID of the version number of this fontconfig file. 1308 */ 1309 private static final int HEAD_LENGTH = 20; 1310 private static final int INDEX_scriptIDs = 0; 1311 private static final int INDEX_scriptFonts = 1; 1312 private static final int INDEX_elcIDs = 2; 1313 private static final int INDEX_sequences = 3; 1314 private static final int INDEX_fontfileNameIDs = 4; 1315 private static final int INDEX_componentFontNameIDs = 5; 1316 private static final int INDEX_filenames = 6; 1317 private static final int INDEX_awtfontpaths = 7; 1318 private static final int INDEX_exclusions = 8; 1319 private static final int INDEX_proportionals = 9; 1320 private static final int INDEX_scriptFontsMotif = 10; 1321 private static final int INDEX_alphabeticSuffix = 11; 1322 private static final int INDEX_stringIDs = 12; 1323 private static final int INDEX_stringTable = 13; 1324 private static final int INDEX_TABLEEND = 14; 1325 private static final int INDEX_fallbackScripts = 15; 1326 private static final int INDEX_appendedfontpath = 16; 1327 private static final int INDEX_version = 17; 1328 1329 private static short[] head; 1330 private static short[] table_scriptIDs; 1331 private static short[] table_scriptFonts; 1332 private static short[] table_elcIDs; 1333 private static short[] table_sequences; 1334 private static short[] table_fontfileNameIDs; 1335 private static short[] table_componentFontNameIDs; 1336 private static short[] table_filenames; 1337 protected static short[] table_awtfontpaths; 1338 private static short[] table_exclusions; 1339 private static short[] table_proportionals; 1340 private static short[] table_scriptFontsMotif; 1341 private static short[] table_alphabeticSuffix; 1342 private static short[] table_stringIDs; 1343 private static char[] table_stringTable; 1344 1345 private static void sanityCheck() { 1346 int errors = 0; 1347 1348 //This method will only be called during build time, do we 1349 //need do PrivilegedAction? 1350 String osName = (String)java.security.AccessController.doPrivileged( 1351 new java.security.PrivilegedAction() { 1352 public Object run() { 1353 return System.getProperty("os.name"); 1354 } 1355 }); 1356 1357 //componentFontNameID starts from "1" 1358 for (int ii = 1; ii < table_filenames.length; ii++) { 1359 if (table_filenames[ii] == -1) { 1360 System.out.println("\n Warning: " 1361 + "<filename." 1362 + getString(table_componentFontNameIDs[ii]) 1363 + "> entry is missing!!!"); 1364 if (!osName.contains("Linux")) { 1365 errors++; 1366 } 1367 } 1368 } 1369 for (int ii = 0; ii < table_scriptIDs.length; ii++) { 1370 short fid = table_scriptFonts[ii]; 1371 if (fid == 0) { 1372 System.out.println("\n Error: <allfonts." 1373 + getString(table_scriptIDs[ii]) 1374 + "> entry is missing!!!"); 1375 errors++; 1376 continue; 1377 } else if (fid < 0) { 1378 fid = (short)-fid; 1379 for (int iii = 0; iii < NUM_FONTS; iii++) { 1380 for (int iij = 0; iij < NUM_STYLES; iij++) { 1381 int jj = iii * NUM_STYLES + iij; 1382 short ffid = table_scriptFonts[fid + jj]; 1383 if (ffid == 0) { 1384 System.out.println("\n Error: <" 1385 + getFontName(iii) + "." 1386 + getStyleName(iij) + "." 1387 + getString(table_scriptIDs[ii]) 1388 + "> entry is missing!!!"); 1389 errors++; 1390 } 1391 } 1392 } 1393 } 1394 } 1395 if ("SunOS".equals(osName)) { 1396 for (int ii = 0; ii < table_awtfontpaths.length; ii++) { 1397 if (table_awtfontpaths[ii] == 0) { 1398 String script = getString(table_scriptIDs[ii]); 1399 if (script.contains("lucida") || 1400 script.contains("dingbats") || 1401 script.contains("symbol")) { 1402 continue; 1403 } 1404 System.out.println("\nError: " 1405 + "<awtfontpath." 1406 + script 1407 + "> entry is missing!!!"); 1408 errors++; 1409 } 1410 } 1411 } 1412 if (errors != 0) { 1413 System.out.println("!!THERE ARE " + errors + " ERROR(S) IN " 1414 + "THE FONTCONFIG FILE, PLEASE CHECK ITS CONTENT!!\n"); 1415 System.exit(1); 1416 1417 } 1418 } 1419 1420 //dump the fontconfig data tables 1421 private static void dump() { 1422 System.out.println("\n----Head Table------------"); 1423 for (int ii = 0; ii < HEAD_LENGTH; ii++) { 1424 System.out.println(" " + ii + " : " + head[ii]); 1425 } 1426 System.out.println("\n----scriptIDs-------------"); 1427 printTable(table_scriptIDs, 0); 1428 System.out.println("\n----scriptFonts----------------"); 1429 for (int ii = 0; ii < table_scriptIDs.length; ii++) { 1430 short fid = table_scriptFonts[ii]; 1431 if (fid >= 0) { 1432 System.out.println(" allfonts." 1433 + getString(table_scriptIDs[ii]) 1434 + "=" 1435 + getString(table_componentFontNameIDs[fid])); 1436 } 1437 } 1438 for (int ii = 0; ii < table_scriptIDs.length; ii++) { 1439 short fid = table_scriptFonts[ii]; 1440 if (fid < 0) { 1441 fid = (short)-fid; 1442 for (int iii = 0; iii < NUM_FONTS; iii++) { 1443 for (int iij = 0; iij < NUM_STYLES; iij++) { 1444 int jj = iii * NUM_STYLES + iij; 1445 short ffid = table_scriptFonts[fid + jj]; 1446 System.out.println(" " 1447 + getFontName(iii) + "." 1448 + getStyleName(iij) + "." 1449 + getString(table_scriptIDs[ii]) 1450 + "=" 1451 + getString(table_componentFontNameIDs[ffid])); 1452 } 1453 } 1454 1455 } 1456 } 1457 System.out.println("\n----elcIDs----------------"); 1458 printTable(table_elcIDs, 0); 1459 System.out.println("\n----sequences-------------"); 1460 for (int ii = 0; ii< table_elcIDs.length; ii++) { 1461 System.out.println(" " + ii + "/" + getString((short)table_elcIDs[ii])); 1462 short[] ss = getShortArray(table_sequences[ii * NUM_FONTS + 0]); 1463 for (int jj = 0; jj < ss.length; jj++) { 1464 System.out.println(" " + getString((short)table_scriptIDs[ss[jj]])); 1465 } 1466 } 1467 System.out.println("\n----fontfileNameIDs-------"); 1468 printTable(table_fontfileNameIDs, 0); 1469 1470 System.out.println("\n----componentFontNameIDs--"); 1471 printTable(table_componentFontNameIDs, 1); 1472 System.out.println("\n----filenames-------------"); 1473 for (int ii = 0; ii < table_filenames.length; ii++) { 1474 if (table_filenames[ii] == -1) { 1475 System.out.println(" " + ii + " : null"); 1476 } else { 1477 System.out.println(" " + ii + " : " 1478 + getString(table_fontfileNameIDs[table_filenames[ii]])); 1479 } 1480 } 1481 System.out.println("\n----awtfontpaths---------"); 1482 for (int ii = 0; ii < table_awtfontpaths.length; ii++) { 1483 System.out.println(" " + getString(table_scriptIDs[ii]) 1484 + " : " 1485 + getString(table_awtfontpaths[ii])); 1486 } 1487 System.out.println("\n----proportionals--------"); 1488 for (int ii = 0; ii < table_proportionals.length; ii++) { 1489 System.out.println(" " 1490 + getString((short)table_componentFontNameIDs[table_proportionals[ii++]]) 1491 + " -> " 1492 + getString((short)table_componentFontNameIDs[table_proportionals[ii]])); 1493 } 1494 int i = 0; 1495 System.out.println("\n----alphabeticSuffix----"); 1496 while (i < table_alphabeticSuffix.length) { 1497 System.out.println(" " + getString(table_elcIDs[table_alphabeticSuffix[i++]]) 1498 + " -> " + getString(table_alphabeticSuffix[i++])); 1499 } 1500 System.out.println("\n----String Table---------"); 1501 System.out.println(" stringID: Num =" + table_stringIDs.length); 1502 System.out.println(" stringTable: Size=" + table_stringTable.length * 2); 1503 1504 System.out.println("\n----fallbackScriptIDs---"); 1505 short[] fbsIDs = getShortArray(head[INDEX_fallbackScripts]); 1506 for (int ii = 0; ii < fbsIDs.length; ii++) { 1507 System.out.println(" " + getString(table_scriptIDs[fbsIDs[ii]])); 1508 } 1509 System.out.println("\n----appendedfontpath-----"); 1510 System.out.println(" " + getString(head[INDEX_appendedfontpath])); 1511 System.out.println("\n----Version--------------"); 1512 System.out.println(" " + getString(head[INDEX_version])); 1513 } 1514 1515 1516 ////////////////////////////////////////////////////////////////////// 1517 // Data table access methods // 1518 ////////////////////////////////////////////////////////////////////// 1519 1520 /* Return the fontID of the platformFontName defined in this font config 1521 * by "LogicalFontName.StyleName.CharacterSubsetName" entry or 1522 * "allfonts.CharacterSubsetName" entry in properties format fc file. 1523 */ 1524 protected static short getComponentFontID(short scriptID, int fontIndex, int styleIndex) { 1525 short fid = table_scriptFonts[scriptID]; 1526 //System.out.println("fid=" + fid + "/ scriptID=" + scriptID + ", fi=" + fontIndex + ", si=" + styleIndex); 1527 if (fid >= 0) { 1528 //"allfonts" 1529 return fid; 1530 } else { 1531 return table_scriptFonts[-fid + fontIndex * NUM_STYLES + styleIndex]; 1532 } 1533 } 1534 1535 /* Same as getCompoentFontID() except this method returns the fontID define by 1536 * "xxxx.motif" entry. 1537 */ 1538 protected static short getComponentFontIDMotif(short scriptID, int fontIndex, int styleIndex) { 1539 if (table_scriptFontsMotif.length == 0) { 1540 return 0; 1541 } 1542 short fid = table_scriptFontsMotif[scriptID]; 1543 if (fid >= 0) { 1544 //"allfonts" > 0 or "not defined" == 0 1545 return fid; 1546 } else { 1547 return table_scriptFontsMotif[-fid + fontIndex * NUM_STYLES + styleIndex]; 1548 } 1549 } 1550 1551 private static int[] getExclusionRanges(short scriptID) { 1552 short exID = table_exclusions[scriptID]; 1553 if (exID == 0) { 1554 return EMPTY_INT_ARRAY; 1555 } else { 1556 char[] exChar = getString(exID).toCharArray(); 1557 int[] exInt = new int[exChar.length / 2]; 1558 int i = 0; 1559 for (int j = 0; j < exInt.length; j++) { 1560 exInt[j] = (exChar[i++] << 16) + (exChar[i++] & 0xffff); 1561 } 1562 return exInt; 1563 } 1564 } 1565 1566 private static boolean contains(short IDs[], short id, int limit) { 1567 for (int i = 0; i < limit; i++) { 1568 if (IDs[i] == id) { 1569 return true; 1570 } 1571 } 1572 return false; 1573 } 1574 1575 /* Return the PlatformFontName from its fontID*/ 1576 protected static String getComponentFontName(short id) { 1577 if (id < 0) { 1578 return null; 1579 } 1580 return getString(table_componentFontNameIDs[id]); 1581 } 1582 1583 private static String getComponentFileName(short id) { 1584 if (id < 0) { 1585 return null; 1586 } 1587 return getString(table_fontfileNameIDs[id]); 1588 } 1589 1590 //componentFontID -> componentFileID 1591 private static short getComponentFileID(short nameID) { 1592 return table_filenames[nameID]; 1593 } 1594 1595 private static String getScriptName(short scriptID) { 1596 return getString(table_scriptIDs[scriptID]); 1597 } 1598 1599 private HashMap<String, Short> reorderScripts; 1600 protected short[] getCoreScripts(int fontIndex) { 1601 short elc = getInitELC(); 1602 /* 1603 System.out.println("getCoreScripts: elc=" + elc + ", fontIndex=" + fontIndex); 1604 short[] ss = getShortArray(table_sequences[elc * NUM_FONTS + fontIndex]); 1605 for (int i = 0; i < ss.length; i++) { 1606 System.out.println(" " + getString((short)table_scriptIDs[ss[i]])); 1607 } 1608 */ 1609 short[] scripts = getShortArray(table_sequences[elc * NUM_FONTS + fontIndex]); 1610 if (preferLocaleFonts) { 1611 if (reorderScripts == null) { 1612 reorderScripts = new HashMap<String, Short>(); 1613 } 1614 String[] ss = new String[scripts.length]; 1615 for (int i = 0; i < ss.length; i++) { 1616 ss[i] = getScriptName(scripts[i]); 1617 reorderScripts.put(ss[i], scripts[i]); 1618 } 1619 reorderSequenceForLocale(ss); 1620 for (int i = 0; i < ss.length; i++) { 1621 scripts[i] = reorderScripts.get(ss[i]); 1622 } 1623 } 1624 return scripts; 1625 } 1626 1627 private static short[] getFallbackScripts() { 1628 return getShortArray(head[INDEX_fallbackScripts]); 1629 } 1630 1631 private static void printTable(short[] list, int start) { 1632 for (int i = start; i < list.length; i++) { 1633 System.out.println(" " + i + " : " + getString(list[i])); 1634 } 1635 } 1636 1637 private static short[] readShortTable(DataInputStream in, int len ) 1638 throws IOException { 1639 if (len == 0) { 1640 return EMPTY_SHORT_ARRAY; 1641 } 1642 short[] data = new short[len]; 1643 byte[] bb = new byte[len * 2]; 1644 in.read(bb); 1645 int i = 0,j = 0; 1646 while (i < len) { 1647 data[i++] = (short)(bb[j++] << 8 | (bb[j++] & 0xff)); 1648 } 1649 return data; 1650 } 1651 1652 private static void writeShortTable(DataOutputStream out, short[] data) 1653 throws IOException { 1654 for (int i = 0; i < data.length; i++) { 1655 out.writeShort(data[i]); 1656 } 1657 } 1658 1659 private static short[] toList(HashMap map) { 1660 short[] list = new short[map.size()]; 1661 for (int i = 0; i < list.length; i++) { 1662 list[i] = -1; 1663 } 1664 Iterator iterator = map.entrySet().iterator(); 1665 while (iterator.hasNext()) { 1666 Entry<String, Short> entry = (Entry <String, Short>)iterator.next(); 1667 list[entry.getValue().shortValue()] = getStringID(entry.getKey()); 1668 } 1669 return list; 1670 } 1671 1672 //runtime cache 1673 private static String[] stringCache; 1674 protected static String getString(short stringID) { 1675 if (stringID == 0) 1676 return null; 1677 /* 1678 if (loadingProperties) { 1679 return stringTable.substring(stringIDs[stringID], 1680 stringIDs[stringID+1]); 1681 } 1682 */ 1683 //sync if we want it to be MT-enabled 1684 if (stringCache[stringID] == null){ 1685 stringCache[stringID] = 1686 new String (table_stringTable, 1687 table_stringIDs[stringID], 1688 table_stringIDs[stringID+1] - table_stringIDs[stringID]); 1689 } 1690 return stringCache[stringID]; 1691 } 1692 1693 private static short[] getShortArray(short shortArrayID) { 1694 String s = getString(shortArrayID); 1695 char[] cc = s.toCharArray(); 1696 short[] ss = new short[cc.length]; 1697 for (int i = 0; i < cc.length; i++) { 1698 ss[i] = (short)(cc[i] & 0xffff); 1699 } 1700 return ss; 1701 } 1702 1703 private static short getStringID(String s) { 1704 if (s == null) { 1705 return (short)0; 1706 } 1707 short pos0 = (short)stringTable.length(); 1708 stringTable.append(s); 1709 short pos1 = (short)stringTable.length(); 1710 1711 stringIDs[stringIDNum] = pos0; 1712 stringIDs[stringIDNum + 1] = pos1; 1713 stringIDNum++; 1714 if (stringIDNum + 1 >= stringIDs.length) { 1715 short[] tmp = new short[stringIDNum + 1000]; 1716 System.arraycopy(stringIDs, 0, tmp, 0, stringIDNum); 1717 stringIDs = tmp; 1718 } 1719 return (short)(stringIDNum - 1); 1720 } 1721 1722 private static short getShortArrayID(short sa[]) { 1723 char[] cc = new char[sa.length]; 1724 for (int i = 0; i < sa.length; i ++) { 1725 cc[i] = (char)sa[i]; 1726 } 1727 String s = new String(cc); 1728 return getStringID(s); 1729 } 1730 1731 //utility "empty" objects 1732 private static final int[] EMPTY_INT_ARRAY = new int[0]; 1733 private static final String[] EMPTY_STRING_ARRAY = new String[0]; 1734 private static final short[] EMPTY_SHORT_ARRAY = new short[0]; 1735 private static final String UNDEFINED_COMPONENT_FONT = "unknown"; 1736 1737 ////////////////////////////////////////////////////////////////////////// 1738 //Convert the FontConfig data in Properties file to binary data tables // 1739 ////////////////////////////////////////////////////////////////////////// 1740 static class PropertiesHandler { 1741 public void load(InputStream in) throws IOException { 1742 initLogicalNameStyle(); 1743 initHashMaps(); 1744 FontProperties fp = new FontProperties(); 1745 fp.load(in); 1746 initBinaryTable(); 1747 } 1748 1749 private void initBinaryTable() { 1750 //(0) 1751 head = new short[HEAD_LENGTH]; 1752 head[INDEX_scriptIDs] = (short)HEAD_LENGTH; 1753 1754 table_scriptIDs = toList(scriptIDs); 1755 //(1)a: scriptAllfonts scriptID/allfonts -> componentFontNameID 1756 // b: scriptFonts scriptID -> componentFontNameID[20] 1757 //if we have a "allfonts.script" def, then we just put 1758 //the "-platformFontID" value in the slot, otherwise the slot 1759 //value is "offset" which "offset" is where 20 entries located 1760 //in the table attached. 1761 head[INDEX_scriptFonts] = (short)(head[INDEX_scriptIDs] + table_scriptIDs.length); 1762 int len = table_scriptIDs.length + scriptFonts.size() * 20; 1763 table_scriptFonts = new short[len]; 1764 1765 Iterator iterator = scriptAllfonts.entrySet().iterator(); 1766 while (iterator.hasNext()) { 1767 Entry<Short, Short> entry = (Entry <Short, Short>)iterator.next(); 1768 table_scriptFonts[entry.getKey().intValue()] = (short)entry.getValue().shortValue(); 1769 } 1770 int off = table_scriptIDs.length; 1771 iterator = scriptFonts.entrySet().iterator(); 1772 while (iterator.hasNext()) { 1773 Entry<Short, Short[]> entry = (Entry <Short, Short[]>)iterator.next(); 1774 table_scriptFonts[entry.getKey().intValue()] = (short)-off; 1775 Short[] v = entry.getValue(); 1776 int i = 0; 1777 while (i < 20) { 1778 if (v[i] != null) { 1779 table_scriptFonts[off++] = v[i].shortValue(); 1780 } else { 1781 table_scriptFonts[off++] = 0; 1782 } 1783 i++; 1784 } 1785 } 1786 1787 //(2) 1788 head[INDEX_elcIDs] = (short)(head[INDEX_scriptFonts] + table_scriptFonts.length); 1789 table_elcIDs = toList(elcIDs); 1790 1791 //(3) sequences elcID -> XXXX[1|5] -> scriptID[] 1792 head[INDEX_sequences] = (short)(head[INDEX_elcIDs] + table_elcIDs.length); 1793 table_sequences = new short[elcIDs.size() * NUM_FONTS]; 1794 iterator = sequences.entrySet().iterator(); 1795 while (iterator.hasNext()) { 1796 Entry<Short, short[]> entry = (Entry <Short, short[]>)iterator.next(); 1797 //table_sequences[entry.getKey().intValue()] = (short)-off; 1798 int k = entry.getKey().intValue(); 1799 short[] v = entry.getValue(); 1800 /* 1801 System.out.println("elc=" + k + "/" + getString((short)table_elcIDs[k])); 1802 short[] ss = getShortArray(v[0]); 1803 for (int i = 0; i < ss.length; i++) { 1804 System.out.println(" " + getString((short)table_scriptIDs[ss[i]])); 1805 } 1806 */ 1807 if (v.length == 1) { 1808 //the "allfonts" entries 1809 for (int i = 0; i < NUM_FONTS; i++) { 1810 table_sequences[k * NUM_FONTS + i] = v[0]; 1811 } 1812 } else { 1813 for (int i = 0; i < NUM_FONTS; i++) { 1814 table_sequences[k * NUM_FONTS + i] = v[i]; 1815 } 1816 } 1817 } 1818 //(4) 1819 head[INDEX_fontfileNameIDs] = (short)(head[INDEX_sequences] + table_sequences.length); 1820 table_fontfileNameIDs = toList(fontfileNameIDs); 1821 1822 //(5) 1823 head[INDEX_componentFontNameIDs] = (short)(head[INDEX_fontfileNameIDs] + table_fontfileNameIDs.length); 1824 table_componentFontNameIDs = toList(componentFontNameIDs); 1825 1826 //(6)componentFontNameID -> filenameID 1827 head[INDEX_filenames] = (short)(head[INDEX_componentFontNameIDs] + table_componentFontNameIDs.length); 1828 table_filenames = new short[table_componentFontNameIDs.length]; 1829 for (int i = 0; i < table_filenames.length; i++) { 1830 table_filenames[i] = -1; 1831 } 1832 iterator = filenames.entrySet().iterator(); 1833 while (iterator.hasNext()) { 1834 Entry<Short, Short> entry = (Entry <Short, Short>)iterator.next(); 1835 table_filenames[entry.getKey().shortValue()] = entry.getValue().shortValue(); 1836 } 1837 1838 //(7)scriptID-> awtfontpath 1839 //the paths are stored as scriptID -> stringID in awtfontpahts 1840 head[INDEX_awtfontpaths] = (short)(head[INDEX_filenames] + table_filenames.length); 1841 table_awtfontpaths = new short[table_scriptIDs.length]; 1842 iterator = awtfontpaths.entrySet().iterator(); 1843 while (iterator.hasNext()) { 1844 Entry<Short, Short> entry = (Entry <Short, Short>)iterator.next(); 1845 table_awtfontpaths[entry.getKey().shortValue()] = entry.getValue().shortValue(); 1846 } 1847 1848 //(8)exclusions 1849 head[INDEX_exclusions] = (short)(head[INDEX_awtfontpaths] + table_awtfontpaths.length); 1850 table_exclusions = new short[scriptIDs.size()]; 1851 iterator = exclusions.entrySet().iterator(); 1852 while (iterator.hasNext()) { 1853 Entry<Short, int[]> entry = (Entry <Short, int[]>)iterator.next(); 1854 int[] exI = entry.getValue(); 1855 char[] exC = new char[exI.length * 2]; 1856 int j = 0; 1857 for (int i = 0; i < exI.length; i++) { 1858 exC[j++] = (char) (exI[i] >> 16); 1859 exC[j++] = (char) (exI[i] & 0xffff); 1860 } 1861 table_exclusions[entry.getKey().shortValue()] = getStringID(new String (exC)); 1862 } 1863 //(9)proportionals 1864 head[INDEX_proportionals] = (short)(head[INDEX_exclusions] + table_exclusions.length); 1865 table_proportionals = new short[proportionals.size() * 2]; 1866 iterator = proportionals.entrySet().iterator(); 1867 int j = 0; 1868 while (iterator.hasNext()) { 1869 Entry<Short, Short> entry = (Entry <Short, Short>)iterator.next(); 1870 table_proportionals[j++] = entry.getKey().shortValue(); 1871 table_proportionals[j++] = entry.getValue().shortValue(); 1872 } 1873 1874 //(10) see (1) for info, the only difference is "xxx.motif" 1875 head[INDEX_scriptFontsMotif] = (short)(head[INDEX_proportionals] + table_proportionals.length); 1876 if (scriptAllfontsMotif.size() != 0 || scriptFontsMotif.size() != 0) { 1877 len = table_scriptIDs.length + scriptFontsMotif.size() * 20; 1878 table_scriptFontsMotif = new short[len]; 1879 1880 iterator = scriptAllfontsMotif.entrySet().iterator(); 1881 while (iterator.hasNext()) { 1882 Entry<Short, Short> entry = (Entry <Short, Short>)iterator.next(); 1883 table_scriptFontsMotif[entry.getKey().intValue()] = 1884 (short)entry.getValue().shortValue(); 1885 } 1886 off = table_scriptIDs.length; 1887 iterator = scriptFontsMotif.entrySet().iterator(); 1888 while (iterator.hasNext()) { 1889 Entry<Short, Short[]> entry = (Entry <Short, Short[]>)iterator.next(); 1890 table_scriptFontsMotif[entry.getKey().intValue()] = (short)-off; 1891 Short[] v = entry.getValue(); 1892 int i = 0; 1893 while (i < 20) { 1894 if (v[i] != null) { 1895 table_scriptFontsMotif[off++] = v[i].shortValue(); 1896 } else { 1897 table_scriptFontsMotif[off++] = 0; 1898 } 1899 i++; 1900 } 1901 } 1902 } else { 1903 table_scriptFontsMotif = EMPTY_SHORT_ARRAY; 1904 } 1905 1906 //(11)short[] alphabeticSuffix 1907 head[INDEX_alphabeticSuffix] = (short)(head[INDEX_scriptFontsMotif] + table_scriptFontsMotif.length); 1908 table_alphabeticSuffix = new short[alphabeticSuffix.size() * 2]; 1909 iterator = alphabeticSuffix.entrySet().iterator(); 1910 j = 0; 1911 while (iterator.hasNext()) { 1912 Entry<Short, Short> entry = (Entry <Short, Short>)iterator.next(); 1913 table_alphabeticSuffix[j++] = entry.getKey().shortValue(); 1914 table_alphabeticSuffix[j++] = entry.getValue().shortValue(); 1915 } 1916 1917 //(15)short[] fallbackScriptIDs; just put the ID in head 1918 head[INDEX_fallbackScripts] = getShortArrayID(fallbackScriptIDs); 1919 1920 //(16)appendedfontpath 1921 head[INDEX_appendedfontpath] = getStringID(appendedfontpath); 1922 1923 //(17)version 1924 head[INDEX_version] = getStringID(version); 1925 1926 //(12)short[] StringIDs 1927 head[INDEX_stringIDs] = (short)(head[INDEX_alphabeticSuffix] + table_alphabeticSuffix.length); 1928 table_stringIDs = new short[stringIDNum + 1]; 1929 System.arraycopy(stringIDs, 0, table_stringIDs, 0, stringIDNum + 1); 1930 1931 //(13)StringTable 1932 head[INDEX_stringTable] = (short)(head[INDEX_stringIDs] + stringIDNum + 1); 1933 table_stringTable = stringTable.toString().toCharArray(); 1934 //(14) 1935 head[INDEX_TABLEEND] = (short)(head[INDEX_stringTable] + stringTable.length()); 1936 1937 //StringTable cache 1938 stringCache = new String[table_stringIDs.length]; 1939 } 1940 1941 ////////////////////////////////////////////// 1942 private HashMap<String, Short> scriptIDs; 1943 //elc -> Encoding.Language.Country 1944 private HashMap<String, Short> elcIDs; 1945 //componentFontNameID starts from "1", "0" reserves for "undefined" 1946 private HashMap<String, Short> componentFontNameIDs; 1947 private HashMap<String, Short> fontfileNameIDs; 1948 private HashMap<String, Integer> logicalFontIDs; 1949 private HashMap<String, Integer> fontStyleIDs; 1950 1951 //componentFontNameID -> fontfileNameID 1952 private HashMap<Short, Short> filenames; 1953 1954 //elcID -> allfonts/logicalFont -> scriptID list 1955 //(1)if we have a "allfonts", then the length of the 1956 // value array is "1", otherwise it's 5, each font 1957 // must have their own individual entry. 1958 //scriptID list "short[]" is stored as an ID 1959 private HashMap<Short, short[]> sequences; 1960 1961 //scriptID ->logicFontID/fontStyleID->componentFontNameID, 1962 //a 20-entry array (5-name x 4-style) for each script 1963 private HashMap<Short, Short[]> scriptFonts; 1964 1965 //scriptID -> componentFontNameID 1966 private HashMap<Short, Short> scriptAllfonts; 1967 1968 //scriptID -> exclusionRanges[] 1969 private HashMap<Short, int[]> exclusions; 1970 1971 //scriptID -> fontpath 1972 private HashMap<Short, Short> awtfontpaths; 1973 1974 //fontID -> fontID 1975 private HashMap<Short, Short> proportionals; 1976 1977 //scriptID -> componentFontNameID 1978 private HashMap<Short, Short> scriptAllfontsMotif; 1979 1980 //scriptID ->logicFontID/fontStyleID->componentFontNameID, 1981 private HashMap<Short, Short[]> scriptFontsMotif; 1982 1983 //elcID -> stringID of alphabetic/XXXX 1984 private HashMap<Short, Short> alphabeticSuffix; 1985 1986 private short[] fallbackScriptIDs; 1987 private String version; 1988 private String appendedfontpath; 1989 1990 private void initLogicalNameStyle() { 1991 logicalFontIDs = new HashMap<String, Integer>(); 1992 fontStyleIDs = new HashMap<String, Integer>(); 1993 logicalFontIDs.put("serif", 0); 1994 logicalFontIDs.put("sansserif", 1); 1995 logicalFontIDs.put("monospaced", 2); 1996 logicalFontIDs.put("dialog", 3); 1997 logicalFontIDs.put("dialoginput",4); 1998 fontStyleIDs.put("plain", 0); 1999 fontStyleIDs.put("bold", 1); 2000 fontStyleIDs.put("italic", 2); 2001 fontStyleIDs.put("bolditalic", 3); 2002 } 2003 2004 private void initHashMaps() { 2005 scriptIDs = new HashMap<String, Short>(); 2006 elcIDs = new HashMap<String, Short>(); 2007 componentFontNameIDs = new HashMap<String, Short>(); 2008 /*Init these tables to allow componentFontNameID, fontfileNameIDs 2009 to start from "1". 2010 */ 2011 componentFontNameIDs.put("", Short.valueOf((short)0)); 2012 2013 fontfileNameIDs = new HashMap<String, Short>(); 2014 filenames = new HashMap<Short, Short>(); 2015 sequences = new HashMap<Short, short[]>(); 2016 scriptFonts = new HashMap<Short, Short[]>(); 2017 scriptAllfonts = new HashMap<Short, Short>(); 2018 exclusions = new HashMap<Short, int[]>(); 2019 awtfontpaths = new HashMap<Short, Short>(); 2020 proportionals = new HashMap<Short, Short>(); 2021 scriptFontsMotif = new HashMap<Short, Short[]>(); 2022 scriptAllfontsMotif = new HashMap<Short, Short>(); 2023 alphabeticSuffix = new HashMap<Short, Short>(); 2024 fallbackScriptIDs = EMPTY_SHORT_ARRAY; 2025 /* 2026 version 2027 appendedfontpath 2028 */ 2029 } 2030 2031 private int[] parseExclusions(String key, String exclusions) { 2032 if (exclusions == null) { 2033 return EMPTY_INT_ARRAY; 2034 } 2035 // range format is xxxx-XXXX,yyyyyy-YYYYYY,..... 2036 int numExclusions = 1; 2037 int pos = 0; 2038 while ((pos = exclusions.indexOf(',', pos)) != -1) { 2039 numExclusions++; 2040 pos++; 2041 } 2042 int[] exclusionRanges = new int[numExclusions * 2]; 2043 pos = 0; 2044 int newPos = 0; 2045 for (int j = 0; j < numExclusions * 2; ) { 2046 String lower, upper; 2047 int lo = 0, up = 0; 2048 try { 2049 newPos = exclusions.indexOf('-', pos); 2050 lower = exclusions.substring(pos, newPos); 2051 pos = newPos + 1; 2052 newPos = exclusions.indexOf(',', pos); 2053 if (newPos == -1) { 2054 newPos = exclusions.length(); 2055 } 2056 upper = exclusions.substring(pos, newPos); 2057 pos = newPos + 1; 2058 int lowerLength = lower.length(); 2059 int upperLength = upper.length(); 2060 if (lowerLength != 4 && lowerLength != 6 2061 || upperLength != 4 && upperLength != 6) { 2062 throw new Exception(); 2063 } 2064 lo = Integer.parseInt(lower, 16); 2065 up = Integer.parseInt(upper, 16); 2066 if (lo > up) { 2067 throw new Exception(); 2068 } 2069 } catch (Exception e) { 2070 if (SunGraphicsEnvironment.debugFonts && logger != null) { 2071 logger.config("Failed parsing " + key + 2072 " property of font configuration."); 2073 2074 } 2075 return EMPTY_INT_ARRAY; 2076 } 2077 exclusionRanges[j++] = lo; 2078 exclusionRanges[j++] = up; 2079 } 2080 return exclusionRanges; 2081 } 2082 2083 private Short getID(HashMap<String, Short> map, String key) { 2084 Short ret = map.get(key); 2085 if ( ret == null) { 2086 map.put(key, (short)map.size()); 2087 return map.get(key); 2088 } 2089 return ret; 2090 } 2091 2092 class FontProperties extends Properties { 2093 public synchronized Object put(Object k, Object v) { 2094 parseProperty((String)k, (String)v); 2095 return null; 2096 } 2097 } 2098 2099 private void parseProperty(String key, String value) { 2100 if (key.startsWith("filename.")) { 2101 //the only special case is "MingLiu_HKSCS" which has "_" in its 2102 //facename, we dont want to replace the "_" with " " 2103 key = key.substring(9); 2104 if (!"MingLiU_HKSCS".equals(key)) { 2105 key = key.replace('_', ' '); 2106 } 2107 Short faceID = getID(componentFontNameIDs, key); 2108 Short fileID = getID(fontfileNameIDs, value); 2109 //System.out.println("faceID=" + faceID + "/" + key + " -> " 2110 // + "fileID=" + fileID + "/" + value); 2111 filenames.put(faceID, fileID); 2112 } else if (key.startsWith("exclusion.")) { 2113 key = key.substring(10); 2114 exclusions.put(getID(scriptIDs,key), parseExclusions(key,value)); 2115 } else if (key.startsWith("sequence.")) { 2116 key = key.substring(9); 2117 boolean hasDefault = false; 2118 boolean has1252 = false; 2119 2120 //get the scriptID list 2121 String[] ss = (String[])splitSequence(value).toArray(EMPTY_STRING_ARRAY); 2122 short [] sa = new short[ss.length]; 2123 for (int i = 0; i < ss.length; i++) { 2124 if ("alphabetic/default".equals(ss[i])) { 2125 //System.out.println(key + " -> " + ss[i]); 2126 ss[i] = "alphabetic"; 2127 hasDefault = true; 2128 } else if ("alphabetic/1252".equals(ss[i])) { 2129 //System.out.println(key + " -> " + ss[i]); 2130 ss[i] = "alphabetic"; 2131 has1252 = true; 2132 } 2133 sa[i] = getID(scriptIDs, ss[i]).shortValue(); 2134 //System.out.println("scriptID=" + si[i] + "/" + ss[i]); 2135 } 2136 //convert the "short[] -> string -> stringID" 2137 short scriptArrayID = getShortArrayID(sa); 2138 Short elcID = null; 2139 int dot = key.indexOf('.'); 2140 if (dot == -1) { 2141 if ("fallback".equals(key)) { 2142 fallbackScriptIDs = sa; 2143 return; 2144 } 2145 if ("allfonts".equals(key)) { 2146 elcID = getID(elcIDs, "NULL.NULL.NULL"); 2147 } else { 2148 if (logger != null) { 2149 logger.config("Error sequence def: <sequence." + key + ">"); 2150 } 2151 return; 2152 } 2153 } else { 2154 elcID = getID(elcIDs, key.substring(dot + 1)); 2155 //System.out.println("elcID=" + elcID + "/" + key.substring(dot + 1)); 2156 key = key.substring(0, dot); 2157 } 2158 short[] scriptArrayIDs = null; 2159 if ("allfonts".equals(key)) { 2160 scriptArrayIDs = new short[1]; 2161 scriptArrayIDs[0] = scriptArrayID; 2162 } else { 2163 scriptArrayIDs = sequences.get(elcID); 2164 if (scriptArrayIDs == null) { 2165 scriptArrayIDs = new short[5]; 2166 } 2167 Integer fid = logicalFontIDs.get(key); 2168 if (fid == null) { 2169 if (logger != null) { 2170 logger.config("Unrecognizable logicfont name " + key); 2171 } 2172 return; 2173 } 2174 //System.out.println("sequence." + key + "/" + id); 2175 scriptArrayIDs[fid.intValue()] = scriptArrayID; 2176 } 2177 sequences.put(elcID, scriptArrayIDs); 2178 if (hasDefault) { 2179 alphabeticSuffix.put(elcID, getStringID("default")); 2180 } else 2181 if (has1252) { 2182 alphabeticSuffix.put(elcID, getStringID("1252")); 2183 } 2184 } else if (key.startsWith("allfonts.")) { 2185 key = key.substring(9); 2186 if (key.endsWith(".motif")) { 2187 key = key.substring(0, key.length() - 6); 2188 //System.out.println("motif: all." + key + "=" + value); 2189 scriptAllfontsMotif.put(getID(scriptIDs,key), getID(componentFontNameIDs,value)); 2190 } else { 2191 scriptAllfonts.put(getID(scriptIDs,key), getID(componentFontNameIDs,value)); 2192 } 2193 } else if (key.startsWith("awtfontpath.")) { 2194 key = key.substring(12); 2195 //System.out.println("scriptID=" + getID(scriptIDs, key) + "/" + key); 2196 awtfontpaths.put(getID(scriptIDs, key), getStringID(value)); 2197 } else if ("version".equals(key)) { 2198 version = value; 2199 } else if ("appendedfontpath".equals(key)) { 2200 appendedfontpath = value; 2201 } else if (key.startsWith("proportional.")) { 2202 key = key.substring(13).replace('_', ' '); 2203 //System.out.println(key + "=" + value); 2204 proportionals.put(getID(componentFontNameIDs, key), 2205 getID(componentFontNameIDs, value)); 2206 } else { 2207 //"name.style.script(.motif)", we dont care anything else 2208 int dot1, dot2; 2209 boolean isMotif = false; 2210 2211 dot1 = key.indexOf('.'); 2212 if (dot1 == -1) { 2213 if (logger != null) { 2214 logger.config("Failed parsing " + key + 2215 " property of font configuration."); 2216 2217 } 2218 return; 2219 } 2220 dot2 = key.indexOf('.', dot1 + 1); 2221 if (dot2 == -1) { 2222 if (logger != null) { 2223 logger.config("Failed parsing " + key + 2224 " property of font configuration."); 2225 2226 } 2227 return; 2228 } 2229 if (key.endsWith(".motif")) { 2230 key = key.substring(0, key.length() - 6); 2231 isMotif = true; 2232 //System.out.println("motif: " + key + "=" + value); 2233 } 2234 Integer nameID = logicalFontIDs.get(key.substring(0, dot1)); 2235 Integer styleID = fontStyleIDs.get(key.substring(dot1+1, dot2)); 2236 Short scriptID = getID(scriptIDs, key.substring(dot2 + 1)); 2237 if (nameID == null || styleID == null) { 2238 if (logger != null) { 2239 logger.config("unrecognizable logicfont name/style at " + key); 2240 } 2241 return; 2242 } 2243 Short[] pnids; 2244 if (isMotif) { 2245 pnids = scriptFontsMotif.get(scriptID); 2246 } else { 2247 pnids = scriptFonts.get(scriptID); 2248 } 2249 if (pnids == null) { 2250 pnids = new Short[20]; 2251 } 2252 pnids[nameID.intValue() * NUM_STYLES + styleID.intValue()] 2253 = getID(componentFontNameIDs, value); 2254 /* 2255 System.out.println("key=" + key + "/<" + nameID + "><" + styleID 2256 + "><" + scriptID + ">=" + value 2257 + "/" + getID(componentFontNameIDs, value)); 2258 */ 2259 if (isMotif) { 2260 scriptFontsMotif.put(scriptID, pnids); 2261 } else { 2262 scriptFonts.put(scriptID, pnids); 2263 } 2264 } 2265 } 2266 } 2267 }