PageRenderTime 71ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

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

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