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