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