PageRenderTime 64ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/src/share/classes/sun/awt/FontConfiguration.java

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