0001: /*
0002: * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved.
0003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004: *
0005: * This code is free software; you can redistribute it and/or modify it
0006: * under the terms of the GNU General Public License version 2 only, as
0007: * published by the Free Software Foundation. Sun designates this
0008: * particular file as subject to the "Classpath" exception as provided
0009: * by Sun in the LICENSE file that accompanied this code.
0010: *
0011: * This code is distributed in the hope that it will be useful, but WITHOUT
0012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014: * version 2 for more details (a copy is included in the LICENSE file that
0015: * accompanied this code).
0016: *
0017: * You should have received a copy of the GNU General Public License version
0018: * 2 along with this work; if not, write to the Free Software Foundation,
0019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020: *
0021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022: * CA 95054 USA or visit www.sun.com if you need additional information or
0023: * have any questions.
0024: */
0025:
0026: package sun.awt;
0027:
0028: import java.awt.Font;
0029: import java.io.DataInputStream;
0030: import java.io.DataOutputStream;
0031: import java.io.File;
0032: import java.io.FileInputStream;
0033: import java.io.FileOutputStream;
0034: import java.io.InputStream;
0035: import java.io.IOException;
0036: import java.io.OutputStream;
0037: import java.nio.charset.Charset;
0038: import java.nio.charset.CharsetEncoder;
0039: import java.security.AccessController;
0040: import java.security.PrivilegedAction;
0041: import java.util.logging.Logger;
0042: import java.util.HashMap;
0043: import java.util.HashSet;
0044: import java.util.Hashtable;
0045: import java.util.Iterator;
0046: import java.util.Locale;
0047: import java.util.Map.Entry;
0048: import java.util.Properties;
0049: import java.util.Set;
0050: import java.util.Vector;
0051: import sun.font.CompositeFontDescriptor;
0052: import sun.java2d.SunGraphicsEnvironment;
0053:
0054: /**
0055: * Provides the definitions of the five logical fonts: Serif, SansSerif,
0056: * Monospaced, Dialog, and DialogInput. The necessary information
0057: * is obtained from fontconfig files.
0058: */
0059: public abstract class FontConfiguration {
0060:
0061: //static global runtime env
0062: protected static String osVersion;
0063: protected static String osName;
0064: protected static String encoding; // canonical name of default nio charset
0065: protected static Locale startupLocale = null;
0066: protected static Hashtable localeMap = null;
0067: private static FontConfiguration fontConfig;
0068: private static Logger logger;
0069: protected static boolean isProperties = true;
0070:
0071: protected SunGraphicsEnvironment environment;
0072: protected boolean preferLocaleFonts;
0073: protected boolean preferPropFonts;
0074:
0075: /* A default FontConfiguration must be created before an alternate
0076: * one to ensure proper static initialisation takes place.
0077: */
0078: public FontConfiguration(SunGraphicsEnvironment environment) {
0079: if (SunGraphicsEnvironment.debugFonts && logger == null) {
0080: logger = Logger.getLogger("sun.awt.FontConfiguration");
0081: }
0082: this .environment = environment;
0083: this .preferLocaleFonts = false;
0084: this .preferPropFonts = false;
0085: setOsNameAndVersion(); /* static initialization */
0086: setEncoding(); /* static initialization */
0087: fontConfig = this ; /* static initialization */
0088:
0089: readFontConfigFile();
0090: initFontConfig();
0091: }
0092:
0093: public FontConfiguration(SunGraphicsEnvironment environment,
0094: boolean preferLocaleFonts, boolean preferPropFonts) {
0095: this .environment = environment;
0096: this .preferLocaleFonts = preferLocaleFonts;
0097: this .preferPropFonts = preferPropFonts;
0098: /* fontConfig should be initialised by default constructor, and
0099: * its data tables can be shared, since readFontConfigFile doesn't
0100: * update any other state. Also avoid a doPrivileged block.
0101: */
0102: initFontConfig();
0103: }
0104:
0105: /**
0106: * Fills in this instance's osVersion and osName members. By
0107: * default uses the system properties os.name and os.version;
0108: * subclasses may override.
0109: */
0110: protected void setOsNameAndVersion() {
0111: osName = System.getProperty("os.name");
0112: osVersion = System.getProperty("os.version");
0113: }
0114:
0115: private void setEncoding() {
0116: encoding = Charset.defaultCharset().name();
0117: startupLocale = SunToolkit.getStartupLocale();
0118: }
0119:
0120: /////////////////////////////////////////////////////////////////////
0121: // methods for loading the FontConfig file //
0122: /////////////////////////////////////////////////////////////////////
0123: private void readFontConfigFile() {
0124: // Find fontconfig file
0125: File f = null;
0126: String javaHome = System.getProperty("java.home");
0127: if (javaHome == null) {
0128: throw new Error("java.home property not set");
0129: }
0130: String javaLib = javaHome + File.separator + "lib";
0131: String userConfigFile = System
0132: .getProperty("sun.awt.fontconfig");
0133: if (userConfigFile != null) {
0134: f = new File(userConfigFile);
0135: } else {
0136: f = findFontConfigFile(javaLib);
0137: }
0138:
0139: /* This is invoked here as readFontConfigFile is only invoked
0140: * once per VM, and always in a privileged context, thus the
0141: * directory containing installed fall back fonts is accessed
0142: * from this context
0143: */
0144: getInstalledFallbackFonts(javaLib);
0145:
0146: if (f != null) {
0147: try {
0148: FileInputStream in = new FileInputStream(f.getPath());
0149: if (isProperties) {
0150: loadProperties(in);
0151: } else {
0152: loadBinary(in);
0153: }
0154: in.close();
0155: if (SunGraphicsEnvironment.debugFonts) {
0156: logger
0157: .config("Read logical font configuration from "
0158: + f);
0159: }
0160: } catch (IOException e) {
0161: if (SunGraphicsEnvironment.debugFonts) {
0162: logger
0163: .config("Failed to read logical font configuration from "
0164: + f);
0165: }
0166: }
0167: }
0168: String version = getVersion();
0169: if (!"1".equals(version) && SunGraphicsEnvironment.debugFonts) {
0170: logger.config("Unsupported fontconfig version: " + version);
0171: }
0172: }
0173:
0174: private void getInstalledFallbackFonts(String javaLib) {
0175: String fallbackDirName = javaLib + File.separator + "fonts"
0176: + File.separator + "fallback";
0177:
0178: File fallbackDir = new File(fallbackDirName);
0179: if (fallbackDir.exists() && fallbackDir.isDirectory()) {
0180: String[] ttfs = fallbackDir
0181: .list(SunGraphicsEnvironment.ttFilter);
0182: String[] t1s = fallbackDir
0183: .list(SunGraphicsEnvironment.t1Filter);
0184: int numTTFs = (ttfs == null) ? 0 : ttfs.length;
0185: int numT1s = (t1s == null) ? 0 : t1s.length;
0186: int len = numTTFs + numT1s;
0187: if (numTTFs + numT1s == 0) {
0188: return;
0189: }
0190: installedFallbackFontFiles = new String[len];
0191: for (int i = 0; i < numTTFs; i++) {
0192: installedFallbackFontFiles[i] = fallbackDir
0193: + File.separator + ttfs[i];
0194: }
0195: for (int i = 0; i < numT1s; i++) {
0196: installedFallbackFontFiles[i + numTTFs] = fallbackDir
0197: + File.separator + t1s[i];
0198: }
0199: environment.registerFontsInDir(fallbackDirName);
0200: }
0201: }
0202:
0203: private File findImpl(String fname) {
0204: File f = new File(fname + ".properties");
0205: if (f.canRead()) {
0206: isProperties = true;
0207: return f;
0208: }
0209: f = new File(fname + ".bfc");
0210: if (f.canRead()) {
0211: isProperties = false;
0212: return f;
0213: }
0214: return null;
0215: }
0216:
0217: private File findFontConfigFile(String javaLib) {
0218: String baseName = javaLib + File.separator + "fontconfig";
0219: File configFile;
0220: if (osVersion != null && osName != null) {
0221: configFile = findImpl(baseName + "." + osName + "."
0222: + osVersion);
0223: if (configFile != null) {
0224: return configFile;
0225: }
0226: }
0227: if (osName != null) {
0228: configFile = findImpl(baseName + "." + osName);
0229: if (configFile != null) {
0230: return configFile;
0231: }
0232: }
0233: if (osVersion != null) {
0234: configFile = findImpl(baseName + "." + osVersion);
0235: if (configFile != null) {
0236: return configFile;
0237: }
0238: }
0239: configFile = findImpl(baseName);
0240: if (configFile != null) {
0241: return configFile;
0242: }
0243: return null;
0244: }
0245:
0246: /* Initialize the internal data tables from binary format font
0247: * configuration file.
0248: */
0249: public static void loadBinary(InputStream inStream)
0250: throws IOException {
0251: DataInputStream in = new DataInputStream(inStream);
0252: head = readShortTable(in, HEAD_LENGTH);
0253: int[] tableSizes = new int[INDEX_TABLEEND];
0254: for (int i = 0; i < INDEX_TABLEEND; i++) {
0255: tableSizes[i] = head[i + 1] - head[i];
0256: }
0257: table_scriptIDs = readShortTable(in,
0258: tableSizes[INDEX_scriptIDs]);
0259: table_scriptFonts = readShortTable(in,
0260: tableSizes[INDEX_scriptFonts]);
0261: table_elcIDs = readShortTable(in, tableSizes[INDEX_elcIDs]);
0262: table_sequences = readShortTable(in,
0263: tableSizes[INDEX_sequences]);
0264: table_fontfileNameIDs = readShortTable(in,
0265: tableSizes[INDEX_fontfileNameIDs]);
0266: table_componentFontNameIDs = readShortTable(in,
0267: tableSizes[INDEX_componentFontNameIDs]);
0268: table_filenames = readShortTable(in,
0269: tableSizes[INDEX_filenames]);
0270: table_awtfontpaths = readShortTable(in,
0271: tableSizes[INDEX_awtfontpaths]);
0272: table_exclusions = readShortTable(in,
0273: tableSizes[INDEX_exclusions]);
0274: table_proportionals = readShortTable(in,
0275: tableSizes[INDEX_proportionals]);
0276: table_scriptFontsMotif = readShortTable(in,
0277: tableSizes[INDEX_scriptFontsMotif]);
0278: table_alphabeticSuffix = readShortTable(in,
0279: tableSizes[INDEX_alphabeticSuffix]);
0280: table_stringIDs = readShortTable(in,
0281: tableSizes[INDEX_stringIDs]);
0282:
0283: //StringTable cache
0284: stringCache = new String[table_stringIDs.length + 1];
0285:
0286: int len = tableSizes[INDEX_stringTable];
0287: byte[] bb = new byte[len * 2];
0288: table_stringTable = new char[len];
0289: in.read(bb);
0290: int i = 0, j = 0;
0291: while (i < len) {
0292: table_stringTable[i++] = (char) (bb[j++] << 8 | (bb[j++] & 0xff));
0293: }
0294: if (verbose) {
0295: dump();
0296: }
0297: }
0298:
0299: /* Generate a binary format font configuration from internal data
0300: * tables.
0301: */
0302: public static void saveBinary(OutputStream out) throws IOException {
0303: DataOutputStream dataOut = new DataOutputStream(out);
0304: writeShortTable(dataOut, head);
0305: writeShortTable(dataOut, table_scriptIDs);
0306: writeShortTable(dataOut, table_scriptFonts);
0307: writeShortTable(dataOut, table_elcIDs);
0308: writeShortTable(dataOut, table_sequences);
0309: writeShortTable(dataOut, table_fontfileNameIDs);
0310: writeShortTable(dataOut, table_componentFontNameIDs);
0311: writeShortTable(dataOut, table_filenames);
0312: writeShortTable(dataOut, table_awtfontpaths);
0313: writeShortTable(dataOut, table_exclusions);
0314: writeShortTable(dataOut, table_proportionals);
0315: writeShortTable(dataOut, table_scriptFontsMotif);
0316: writeShortTable(dataOut, table_alphabeticSuffix);
0317: writeShortTable(dataOut, table_stringIDs);
0318: //stringTable
0319: dataOut.writeChars(new String(table_stringTable));
0320: out.close();
0321: if (verbose) {
0322: dump();
0323: }
0324: sanityCheck();
0325: }
0326:
0327: //private static boolean loadingProperties;
0328: private static short stringIDNum;
0329: private static short[] stringIDs;
0330: private static StringBuilder stringTable;
0331:
0332: public static void loadProperties(InputStream in)
0333: throws IOException {
0334: //loadingProperties = true;
0335: //StringID starts from "1", "0" is reserved for "not defined"
0336: stringIDNum = 1;
0337: stringIDs = new short[1000];
0338: stringTable = new StringBuilder(4096);
0339:
0340: if (verbose && logger == null) {
0341: logger = Logger.getLogger("sun.awt.FontConfiguration");
0342: }
0343: new PropertiesHandler().load(in);
0344:
0345: //loadingProperties = false;
0346: stringIDs = null;
0347: stringTable = null;
0348: }
0349:
0350: /////////////////////////////////////////////////////////////////////
0351: // methods for initializing the FontConfig //
0352: /////////////////////////////////////////////////////////////////////
0353:
0354: /**
0355: * set initLocale, initEncoding and initELC for this FontConfig object
0356: * currently we just simply use the startup locale and encoding
0357: */
0358: private void initFontConfig() {
0359: initLocale = startupLocale;
0360: initEncoding = encoding;
0361: if (preferLocaleFonts && !willReorderForStartupLocale()) {
0362: preferLocaleFonts = false;
0363: }
0364: initELC = getInitELC();
0365: initAllComponentFonts();
0366: }
0367:
0368: //"ELC" stands for "Encoding.Language.Country". This method returns
0369: //the ID of the matched elc setting of "initLocale" in elcIDs table.
0370: //If no match is found, it returns the default ID, which is
0371: //"NULL.NULL.NULL" in elcIDs table.
0372: private short getInitELC() {
0373: if (initELC != -1) {
0374: return initELC;
0375: }
0376: HashMap<String, Integer> elcIDs = new HashMap<String, Integer>();
0377: for (int i = 0; i < table_elcIDs.length; i++) {
0378: elcIDs.put(getString(table_elcIDs[i]), i);
0379: }
0380: String language = initLocale.getLanguage();
0381: String country = initLocale.getCountry();
0382: String elc;
0383: if (elcIDs.containsKey(elc = initEncoding + "." + language
0384: + "." + country)
0385: || elcIDs.containsKey(elc = initEncoding + "."
0386: + language)
0387: || elcIDs.containsKey(elc = initEncoding)) {
0388: initELC = elcIDs.get(elc).shortValue();
0389: } else {
0390: initELC = elcIDs.get("NULL.NULL.NULL").shortValue();
0391: }
0392: int i = 0;
0393: while (i < table_alphabeticSuffix.length) {
0394: if (initELC == table_alphabeticSuffix[i]) {
0395: alphabeticSuffix = getString(table_alphabeticSuffix[i + 1]);
0396: return initELC;
0397: }
0398: i += 2;
0399: }
0400: return initELC;
0401: }
0402:
0403: public static boolean verbose;
0404: private short initELC = -1;
0405: private Locale initLocale;
0406: private String initEncoding;
0407: private String alphabeticSuffix;
0408:
0409: private short[][][] compFontNameIDs = new short[NUM_FONTS][NUM_STYLES][];
0410: private int[][][] compExclusions = new int[NUM_FONTS][][];
0411: private int[] compCoreNum = new int[NUM_FONTS];
0412:
0413: private Set<Short> coreFontNameIDs = new HashSet<Short>();
0414: private Set<Short> fallbackFontNameIDs = new HashSet<Short>();
0415:
0416: private void initAllComponentFonts() {
0417: short[] fallbackScripts = getFallbackScripts();
0418: for (int fontIndex = 0; fontIndex < NUM_FONTS; fontIndex++) {
0419: short[] coreScripts = getCoreScripts(fontIndex);
0420: compCoreNum[fontIndex] = coreScripts.length;
0421: /*
0422: System.out.println("coreScriptID=" + table_sequences[initELC * 5 + fontIndex]);
0423: for (int i = 0; i < coreScripts.length; i++) {
0424: System.out.println(" " + i + " :" + getString(table_scriptIDs[coreScripts[i]]));
0425: }
0426: */
0427: //init exclusionRanges
0428: int[][] exclusions = new int[coreScripts.length][];
0429: for (int i = 0; i < coreScripts.length; i++) {
0430: exclusions[i] = getExclusionRanges(coreScripts[i]);
0431: }
0432: compExclusions[fontIndex] = exclusions;
0433: //init componentFontNames
0434: for (int styleIndex = 0; styleIndex < NUM_STYLES; styleIndex++) {
0435: int index;
0436: short[] nameIDs = new short[coreScripts.length
0437: + fallbackScripts.length];
0438: //core
0439: for (index = 0; index < coreScripts.length; index++) {
0440: nameIDs[index] = getComponentFontID(
0441: coreScripts[index], fontIndex, styleIndex);
0442: if (preferLocaleFonts
0443: && localeMap != null
0444: && sun.font.FontManager
0445: .usingAlternateFontforJALocales()) {
0446: nameIDs[index] = remapLocaleMap(fontIndex,
0447: styleIndex, coreScripts[index],
0448: nameIDs[index]);
0449: }
0450: if (preferPropFonts) {
0451: nameIDs[index] = remapProportional(fontIndex,
0452: nameIDs[index]);
0453: }
0454: //System.out.println("nameid=" + nameIDs[index]);
0455: coreFontNameIDs.add(nameIDs[index]);
0456: }
0457: //fallback
0458: for (int i = 0; i < fallbackScripts.length; i++) {
0459: short id = getComponentFontID(fallbackScripts[i],
0460: fontIndex, styleIndex);
0461: if (preferLocaleFonts
0462: && localeMap != null
0463: && sun.font.FontManager
0464: .usingAlternateFontforJALocales()) {
0465: id = remapLocaleMap(fontIndex, styleIndex,
0466: fallbackScripts[i], id);
0467: }
0468: if (preferPropFonts) {
0469: id = remapProportional(fontIndex, id);
0470: }
0471: if (contains(nameIDs, id, index)) {
0472: continue;
0473: }
0474: /*
0475: System.out.println("fontIndex=" + fontIndex + ", styleIndex=" + styleIndex
0476: + ", fbIndex=" + i + ",fbS=" + fallbackScripts[i] + ", id=" + id);
0477: */
0478: fallbackFontNameIDs.add(id);
0479: nameIDs[index++] = id;
0480: }
0481: if (index < nameIDs.length) {
0482: short[] newNameIDs = new short[index];
0483: System.arraycopy(nameIDs, 0, newNameIDs, 0, index);
0484: nameIDs = newNameIDs;
0485: }
0486: compFontNameIDs[fontIndex][styleIndex] = nameIDs;
0487: }
0488: }
0489: }
0490:
0491: private short remapLocaleMap(int fontIndex, int styleIndex,
0492: short scriptID, short fontID) {
0493: String scriptName = getString(table_scriptIDs[scriptID]);
0494:
0495: String value = (String) localeMap.get(scriptName);
0496: if (value == null) {
0497: String fontName = fontNames[fontIndex];
0498: String styleName = styleNames[styleIndex];
0499: value = (String) localeMap.get(fontName + "." + styleName
0500: + "." + scriptName);
0501: }
0502: if (value == null) {
0503: return fontID;
0504: }
0505:
0506: for (int i = 0; i < table_componentFontNameIDs.length; i++) {
0507: String name = getString(table_componentFontNameIDs[i]);
0508: if (value.equalsIgnoreCase(name)) {
0509: fontID = (short) i;
0510: break;
0511: }
0512: }
0513: return fontID;
0514: }
0515:
0516: public static boolean hasMonoToPropMap() {
0517: return table_proportionals != null
0518: && table_proportionals.length != 0;
0519: }
0520:
0521: private short remapProportional(int fontIndex, short id) {
0522: if (preferPropFonts && table_proportionals.length != 0
0523: && fontIndex != 2 && //"monospaced"
0524: fontIndex != 4) { //"dialoginput"
0525: int i = 0;
0526: while (i < table_proportionals.length) {
0527: if (table_proportionals[i] == id) {
0528: return table_proportionals[i + 1];
0529: }
0530: i += 2;
0531: }
0532: }
0533: return id;
0534: }
0535:
0536: /////////////////////////////////////////////////////////////////////
0537: // Methods for handling font and style names //
0538: /////////////////////////////////////////////////////////////////////
0539: protected static final int NUM_FONTS = 5;
0540: protected static final int NUM_STYLES = 4;
0541: private static final String[] fontNames = { "serif", "sansserif",
0542: "monospaced", "dialog", "dialoginput" };
0543: private static final String[] publicFontNames = { Font.SERIF,
0544: Font.SANS_SERIF, Font.MONOSPACED, Font.DIALOG,
0545: Font.DIALOG_INPUT };
0546: private static final String[] styleNames = { "plain", "bold",
0547: "italic", "bolditalic" };
0548:
0549: /**
0550: * Checks whether the given font family name is a valid logical font name.
0551: * The check is case insensitive.
0552: */
0553: public static boolean isLogicalFontFamilyName(String fontName) {
0554: return isLogicalFontFamilyNameLC(fontName
0555: .toLowerCase(Locale.ENGLISH));
0556: }
0557:
0558: /**
0559: * Checks whether the given font family name is a valid logical font name.
0560: * The check is case sensitive.
0561: */
0562: public static boolean isLogicalFontFamilyNameLC(String fontName) {
0563: for (int i = 0; i < fontNames.length; i++) {
0564: if (fontName.equals(fontNames[i])) {
0565: return true;
0566: }
0567: }
0568: return false;
0569: }
0570:
0571: /**
0572: * Checks whether the given style name is a valid logical font style name.
0573: */
0574: private static boolean isLogicalFontStyleName(String styleName) {
0575: for (int i = 0; i < styleNames.length; i++) {
0576: if (styleName.equals(styleNames[i])) {
0577: return true;
0578: }
0579: }
0580: return false;
0581: }
0582:
0583: /**
0584: * Checks whether the given font face name is a valid logical font name.
0585: * The check is case insensitive.
0586: */
0587: public static boolean isLogicalFontFaceName(String fontName) {
0588: return isLogicalFontFaceNameLC(fontName
0589: .toLowerCase(Locale.ENGLISH));
0590: }
0591:
0592: /**
0593: * Checks whether the given font face name is a valid logical font name.
0594: * The check is case sensitive.
0595: */
0596: public static boolean isLogicalFontFaceNameLC(String fontName) {
0597: int period = fontName.indexOf('.');
0598: if (period >= 0) {
0599: String familyName = fontName.substring(0, period);
0600: String styleName = fontName.substring(period + 1);
0601: return isLogicalFontFamilyName(familyName)
0602: && isLogicalFontStyleName(styleName);
0603: } else {
0604: return isLogicalFontFamilyName(fontName);
0605: }
0606: }
0607:
0608: protected static int getFontIndex(String fontName) {
0609: return getArrayIndex(fontNames, fontName);
0610: }
0611:
0612: protected static int getStyleIndex(String styleName) {
0613: return getArrayIndex(styleNames, styleName);
0614: }
0615:
0616: private static int getArrayIndex(String[] names, String name) {
0617: for (int i = 0; i < names.length; i++) {
0618: if (name.equals(names[i])) {
0619: return i;
0620: }
0621: }
0622: assert false;
0623: return 0;
0624: }
0625:
0626: protected static int getStyleIndex(int style) {
0627: switch (style) {
0628: case Font.PLAIN:
0629: return 0;
0630: case Font.BOLD:
0631: return 1;
0632: case Font.ITALIC:
0633: return 2;
0634: case Font.BOLD | Font.ITALIC:
0635: return 3;
0636: default:
0637: return 0;
0638: }
0639: }
0640:
0641: protected static String getFontName(int fontIndex) {
0642: return fontNames[fontIndex];
0643: }
0644:
0645: protected static String getStyleName(int styleIndex) {
0646: return styleNames[styleIndex];
0647: }
0648:
0649: /**
0650: * Returns the font face name for the given logical font
0651: * family name and style.
0652: * The style argument is interpreted as in java.awt.Font.Font.
0653: */
0654: public static String getLogicalFontFaceName(String familyName,
0655: int style) {
0656: assert isLogicalFontFamilyName(familyName);
0657: return familyName.toLowerCase(Locale.ENGLISH) + "."
0658: + getStyleString(style);
0659: }
0660:
0661: /**
0662: * Returns the string typically used in properties files
0663: * for the given style.
0664: * The style argument is interpreted as in java.awt.Font.Font.
0665: */
0666: public static String getStyleString(int style) {
0667: return getStyleName(getStyleIndex(style));
0668: }
0669:
0670: /**
0671: * Returns a fallback name for the given font name. For a few known
0672: * font names, matching logical font names are returned. For all
0673: * other font names, defaultFallback is returned.
0674: * defaultFallback differs between AWT and 2D.
0675: */
0676: public abstract String getFallbackFamilyName(String fontName,
0677: String defaultFallback);
0678:
0679: /**
0680: * Returns the 1.1 equivalent for some old 1.0 font family names for
0681: * which we need to maintain compatibility in some configurations.
0682: * Returns null for other font names.
0683: */
0684: protected String getCompatibilityFamilyName(String fontName) {
0685: fontName = fontName.toLowerCase(Locale.ENGLISH);
0686: if (fontName.equals("timesroman")) {
0687: return "serif";
0688: } else if (fontName.equals("helvetica")) {
0689: return "sansserif";
0690: } else if (fontName.equals("courier")) {
0691: return "monospaced";
0692: }
0693: return null;
0694: }
0695:
0696: private static String[] installedFallbackFontFiles = null;
0697:
0698: /**
0699: * Maps a file name given in the font configuration file
0700: * to a format appropriate for the platform.
0701: */
0702: protected String mapFileName(String fileName) {
0703: return fileName;
0704: }
0705:
0706: //////////////////////////////////////////////////////////////////////
0707: // reordering //
0708: //////////////////////////////////////////////////////////////////////
0709:
0710: /* Mappings from file encoding to font config name for font supporting
0711: * the corresponding language. This is filled in by initReorderMap()
0712: */
0713: protected HashMap reorderMap = null;
0714:
0715: /* Platform-specific mappings */
0716: protected abstract void initReorderMap();
0717:
0718: /* Move item at index "src" to "dst", shuffling all values in
0719: * between down
0720: */
0721: private void shuffle(String[] seq, int src, int dst) {
0722: if (dst >= src) {
0723: return;
0724: }
0725: String tmp = seq[src];
0726: for (int i = src; i > dst; i--) {
0727: seq[i] = seq[i - 1];
0728: }
0729: seq[dst] = tmp;
0730: }
0731:
0732: /* Called to determine if there's a re-order sequence for this locale/
0733: * encoding. If there's none then the caller can "bail" and avoid
0734: * unnecessary work
0735: */
0736: public static boolean willReorderForStartupLocale() {
0737: return getReorderSequence() != null;
0738: }
0739:
0740: private static Object getReorderSequence() {
0741: if (fontConfig.reorderMap == null) {
0742: fontConfig.initReorderMap();
0743: }
0744: HashMap reorderMap = fontConfig.reorderMap;
0745:
0746: /* Find the most specific mapping */
0747: String language = startupLocale.getLanguage();
0748: String country = startupLocale.getCountry();
0749: Object val = reorderMap.get(encoding + "." + language + "."
0750: + country);
0751: if (val == null) {
0752: val = reorderMap.get(encoding + "." + language);
0753: }
0754: if (val == null) {
0755: val = reorderMap.get(encoding);
0756: }
0757: return val;
0758: }
0759:
0760: /* This method reorders the sequence such that the matches for the
0761: * file encoding are moved ahead of other elements.
0762: * If an encoding uses more than one font, they are all moved up.
0763: */
0764: private void reorderSequenceForLocale(String[] seq) {
0765: Object val = getReorderSequence();
0766: if (val instanceof String) {
0767: for (int i = 0; i < seq.length; i++) {
0768: if (seq[i].equals(val)) {
0769: shuffle(seq, i, 0);
0770: return;
0771: }
0772: }
0773: } else if (val instanceof String[]) {
0774: String[] fontLangs = (String[]) val;
0775: for (int l = 0; l < fontLangs.length; l++) {
0776: for (int i = 0; i < seq.length; i++) {
0777: if (seq[i].equals(fontLangs[l])) {
0778: shuffle(seq, i, l);
0779: }
0780: }
0781: }
0782: }
0783: }
0784:
0785: private static Vector splitSequence(String sequence) {
0786: //String.split would be more convenient, but incurs big performance penalty
0787: Vector parts = new Vector();
0788: int start = 0;
0789: int end;
0790: while ((end = sequence.indexOf(',', start)) >= 0) {
0791: parts.add(sequence.substring(start, end));
0792: start = end + 1;
0793: }
0794: if (sequence.length() > start) {
0795: parts.add(sequence.substring(start, sequence.length()));
0796: }
0797: return parts;
0798: }
0799:
0800: protected String[] split(String sequence) {
0801: Vector v = splitSequence(sequence);
0802: return (String[]) v.toArray(new String[0]);
0803: }
0804:
0805: ////////////////////////////////////////////////////////////////////////
0806: // Methods for extracting information from the fontconfig data for AWT//
0807: ////////////////////////////////////////////////////////////////////////
0808: private Hashtable charsetRegistry = new Hashtable(5);
0809:
0810: /**
0811: * Returns FontDescriptors describing the physical fonts used for the
0812: * given logical font name and style. The font name is interpreted
0813: * in a case insensitive way.
0814: * The style argument is interpreted as in java.awt.Font.Font.
0815: */
0816: public FontDescriptor[] getFontDescriptors(String fontName,
0817: int style) {
0818: assert isLogicalFontFamilyName(fontName);
0819: fontName = fontName.toLowerCase(Locale.ENGLISH);
0820: int fontIndex = getFontIndex(fontName);
0821: int styleIndex = getStyleIndex(style);
0822: return getFontDescriptors(fontIndex, styleIndex);
0823: }
0824:
0825: private FontDescriptor[][][] fontDescriptors = new FontDescriptor[NUM_FONTS][NUM_STYLES][];
0826:
0827: private FontDescriptor[] getFontDescriptors(int fontIndex,
0828: int styleIndex) {
0829: FontDescriptor[] descriptors = fontDescriptors[fontIndex][styleIndex];
0830: if (descriptors == null) {
0831: descriptors = buildFontDescriptors(fontIndex, styleIndex);
0832: fontDescriptors[fontIndex][styleIndex] = descriptors;
0833: }
0834: return descriptors;
0835: }
0836:
0837: private FontDescriptor[] buildFontDescriptors(int fontIndex,
0838: int styleIndex) {
0839: String fontName = fontNames[fontIndex];
0840: String styleName = styleNames[styleIndex];
0841:
0842: short[] scriptIDs = getCoreScripts(fontIndex);
0843: short[] nameIDs = compFontNameIDs[fontIndex][styleIndex];
0844: String[] sequence = new String[scriptIDs.length];
0845: String[] names = new String[scriptIDs.length];
0846: for (int i = 0; i < sequence.length; i++) {
0847: names[i] = getComponentFontName(nameIDs[i]);
0848: sequence[i] = getScriptName(scriptIDs[i]);
0849: if (alphabeticSuffix != null
0850: && "alphabetic".equals(sequence[i])) {
0851: sequence[i] = sequence[i] + "/" + alphabeticSuffix;
0852: }
0853: }
0854: int[][] fontExclusionRanges = compExclusions[fontIndex];
0855:
0856: FontDescriptor[] descriptors = new FontDescriptor[names.length];
0857:
0858: for (int i = 0; i < names.length; i++) {
0859: String awtFontName;
0860: String encoding;
0861:
0862: awtFontName = makeAWTFontName(names[i], sequence[i]);
0863:
0864: // look up character encoding
0865: encoding = getEncoding(names[i], sequence[i]);
0866: if (encoding == null) {
0867: encoding = "default";
0868: }
0869: CharsetEncoder enc = getFontCharsetEncoder(encoding.trim(),
0870: awtFontName);
0871:
0872: // we already have the exclusion ranges
0873: int[] exclusionRanges = fontExclusionRanges[i];
0874:
0875: // create descriptor
0876: descriptors[i] = new FontDescriptor(awtFontName, enc,
0877: exclusionRanges);
0878: }
0879: return descriptors;
0880: }
0881:
0882: /**
0883: * Returns the AWT font name for the given platform font name and
0884: * character subset.
0885: */
0886: protected String makeAWTFontName(String platformFontName,
0887: String characterSubsetName) {
0888: return platformFontName;
0889: }
0890:
0891: /**
0892: * Returns the java.io name of the platform character encoding for the
0893: * given AWT font name and character subset. May return "default"
0894: * to indicate that getDefaultFontCharset should be called to obtain
0895: * a charset encoder.
0896: */
0897: protected abstract String getEncoding(String awtFontName,
0898: String characterSubsetName);
0899:
0900: private CharsetEncoder getFontCharsetEncoder(
0901: final String charsetName, String fontName) {
0902:
0903: Charset fc = null;
0904: if (charsetName.equals("default")) {
0905: fc = (Charset) charsetRegistry.get(fontName);
0906: } else {
0907: fc = (Charset) charsetRegistry.get(charsetName);
0908: }
0909: if (fc != null) {
0910: return fc.newEncoder();
0911: }
0912:
0913: if (!charsetName.startsWith("sun.awt.")
0914: && !charsetName.equals("default")) {
0915: fc = Charset.forName(charsetName);
0916: } else {
0917: Class fcc = (Class) AccessController
0918: .doPrivileged(new PrivilegedAction() {
0919: public Object run() {
0920: try {
0921: return Class
0922: .forName(
0923: charsetName,
0924: true,
0925: Thread
0926: .currentThread()
0927: .getContextClassLoader());
0928: } catch (ClassNotFoundException e) {
0929: }
0930: return null;
0931: }
0932: });
0933:
0934: if (fcc != null) {
0935: try {
0936: fc = (Charset) fcc.newInstance();
0937: } catch (Exception e) {
0938: }
0939: }
0940: }
0941: if (fc == null) {
0942: fc = getDefaultFontCharset(fontName);
0943: }
0944:
0945: if (charsetName.equals("default")) {
0946: charsetRegistry.put(fontName, fc);
0947: } else {
0948: charsetRegistry.put(charsetName, fc);
0949: }
0950: return fc.newEncoder();
0951: }
0952:
0953: protected abstract Charset getDefaultFontCharset(String fontName);
0954:
0955: /* This retrieves the platform font directories (path) calculated
0956: * by setAWTFontPathSequence(String[]). The default implementation
0957: * returns null, its expected that X11 platforms may return
0958: * non-null.
0959: */
0960: public HashSet<String> getAWTFontPathSet() {
0961: return null;
0962: }
0963:
0964: ////////////////////////////////////////////////////////////////////////
0965: // methods for extracting information from the fontconfig data for 2D //
0966: ////////////////////////////////////////////////////////////////////////
0967:
0968: /**
0969: * Returns an array of composite font descriptors for all logical font
0970: * faces.
0971: * If the font configuration file doesn't specify Lucida Sans Regular
0972: * or the given fallback font as component fonts, they are added here.
0973: */
0974: public CompositeFontDescriptor[] get2DCompositeFontInfo() {
0975: CompositeFontDescriptor[] result = new CompositeFontDescriptor[NUM_FONTS
0976: * NUM_STYLES];
0977: String defaultFontFile = environment.getDefaultFontFile();
0978: String defaultFontFaceName = environment
0979: .getDefaultFontFaceName();
0980:
0981: for (int fontIndex = 0; fontIndex < NUM_FONTS; fontIndex++) {
0982: String fontName = publicFontNames[fontIndex];
0983:
0984: // determine exclusion ranges for font
0985: // AWT uses separate exclusion range array per component font.
0986: // 2D packs all range boundaries into one array.
0987: // Both use separate entries for lower and upper boundary.
0988: int[][] exclusions = compExclusions[fontIndex];
0989: int numExclusionRanges = 0;
0990: for (int i = 0; i < exclusions.length; i++) {
0991: numExclusionRanges += exclusions[i].length;
0992: }
0993: int[] exclusionRanges = new int[numExclusionRanges];
0994: int[] exclusionRangeLimits = new int[exclusions.length];
0995: int exclusionRangeIndex = 0;
0996: int exclusionRangeLimitIndex = 0;
0997: for (int i = 0; i < exclusions.length; i++) {
0998: int[] componentRanges = exclusions[i];
0999: for (int j = 0; j < componentRanges.length;) {
1000: int value = componentRanges[j];
1001: exclusionRanges[exclusionRangeIndex++] = componentRanges[j++];
1002: exclusionRanges[exclusionRangeIndex++] = componentRanges[j++];
1003: }
1004: exclusionRangeLimits[i] = exclusionRangeIndex;
1005: }
1006: // other info is per style
1007: for (int styleIndex = 0; styleIndex < NUM_STYLES; styleIndex++) {
1008: int maxComponentFontCount = compFontNameIDs[fontIndex][styleIndex].length;
1009: boolean sawDefaultFontFile = false;
1010: // fall back fonts listed in the lib/fonts/fallback directory
1011: if (installedFallbackFontFiles != null) {
1012: maxComponentFontCount += installedFallbackFontFiles.length;
1013: }
1014: String faceName = fontName + "."
1015: + styleNames[styleIndex];
1016:
1017: // determine face names and file names of component fonts
1018: String[] componentFaceNames = new String[maxComponentFontCount];
1019: String[] componentFileNames = new String[maxComponentFontCount];
1020:
1021: int index;
1022: for (index = 0; index < compFontNameIDs[fontIndex][styleIndex].length; index++) {
1023: short fontNameID = compFontNameIDs[fontIndex][styleIndex][index];
1024: short fileNameID = getComponentFileID(fontNameID);
1025: componentFaceNames[index] = getFaceNameFromComponentFontName(getComponentFontName(fontNameID));
1026: componentFileNames[index] = mapFileName(getComponentFileName(fileNameID));
1027: if (componentFileNames[index] == null
1028: || needToSearchForFile(componentFileNames[index])) {
1029: componentFileNames[index] = getFileNameFromComponentFontName(getComponentFontName(fontNameID));
1030: }
1031: if (!sawDefaultFontFile
1032: && defaultFontFile
1033: .equals(componentFileNames[index])) {
1034: sawDefaultFontFile = true;
1035: }
1036: /*
1037: System.out.println(publicFontNames[fontIndex] + "." + styleNames[styleIndex] + "."
1038: + getString(table_scriptIDs[coreScripts[index]]) + "=" + componentFileNames[index]);
1039: */
1040: }
1041:
1042: //"Lucida Sans Regular" is not in the list, we add it here
1043: if (!sawDefaultFontFile) {
1044: int len = 0;
1045: if (installedFallbackFontFiles != null) {
1046: len = installedFallbackFontFiles.length;
1047: }
1048: if (index + len == maxComponentFontCount) {
1049: String[] newComponentFaceNames = new String[maxComponentFontCount + 1];
1050: System.arraycopy(componentFaceNames, 0,
1051: newComponentFaceNames, 0, index);
1052: componentFaceNames = newComponentFaceNames;
1053: String[] newComponentFileNames = new String[maxComponentFontCount + 1];
1054: System.arraycopy(componentFileNames, 0,
1055: newComponentFileNames, 0, index);
1056: componentFileNames = newComponentFileNames;
1057: }
1058: componentFaceNames[index] = defaultFontFaceName;
1059: componentFileNames[index] = defaultFontFile;
1060: index++;
1061: }
1062:
1063: if (installedFallbackFontFiles != null) {
1064: for (int ifb = 0; ifb < installedFallbackFontFiles.length; ifb++) {
1065: componentFaceNames[index] = null;
1066: componentFileNames[index] = installedFallbackFontFiles[ifb];
1067: index++;
1068: }
1069: }
1070:
1071: if (index < maxComponentFontCount) {
1072: String[] newComponentFaceNames = new String[index];
1073: System.arraycopy(componentFaceNames, 0,
1074: newComponentFaceNames, 0, index);
1075: componentFaceNames = newComponentFaceNames;
1076: String[] newComponentFileNames = new String[index];
1077: System.arraycopy(componentFileNames, 0,
1078: newComponentFileNames, 0, index);
1079: componentFileNames = newComponentFileNames;
1080: }
1081: // exclusion range limit array length must match component face name
1082: // array length - native code relies on this
1083:
1084: int[] clippedExclusionRangeLimits = exclusionRangeLimits;
1085: if (index != clippedExclusionRangeLimits.length) {
1086: int len = exclusionRangeLimits.length;
1087: clippedExclusionRangeLimits = new int[index];
1088: System.arraycopy(exclusionRangeLimits, 0,
1089: clippedExclusionRangeLimits, 0, len);
1090: //padding for various fallback fonts
1091: for (int i = len; i < index; i++) {
1092: clippedExclusionRangeLimits[i] = exclusionRanges.length;
1093: }
1094: }
1095: /*
1096: System.out.println(faceName + ":");
1097: for (int i = 0; i < componentFileNames.length; i++) {
1098: System.out.println(" " + componentFaceNames[i]
1099: + " -> " + componentFileNames[i]);
1100: }
1101: */
1102: result[fontIndex * NUM_STYLES + styleIndex] = new CompositeFontDescriptor(
1103: faceName, compCoreNum[fontIndex],
1104: componentFaceNames, componentFileNames,
1105: exclusionRanges, clippedExclusionRangeLimits);
1106: }
1107: }
1108: return result;
1109: }
1110:
1111: protected abstract String getFaceNameFromComponentFontName(
1112: String componentFontName);
1113:
1114: protected abstract String getFileNameFromComponentFontName(
1115: String componentFontName);
1116:
1117: /*
1118: public class 2dFont {
1119: public String platformName;
1120: public String fontfileName;
1121: }
1122: private 2dFont [] componentFonts = null;
1123: */
1124:
1125: /* Used on Linux to test if a file referenced in a font configuration
1126: * file exists in the location that is expected. If it does, no need
1127: * to search for it. If it doesn't then unless its a fallback font,
1128: * return that expensive code should be invoked to search for the font.
1129: */
1130: HashMap<String, Boolean> existsMap;
1131:
1132: public boolean needToSearchForFile(String fileName) {
1133: if (!environment.isLinux) {
1134: return false;
1135: } else if (existsMap == null) {
1136: existsMap = new HashMap<String, Boolean>();
1137: }
1138: Boolean exists = existsMap.get(fileName);
1139: if (exists == null) {
1140: /* call getNumberCoreFonts() to ensure these are initialised, and
1141: * if this file isn't for a core component, ie, is a for a fallback
1142: * font which very typically isn't available, then can't afford
1143: * to take the start-up penalty to search for it.
1144: */
1145: getNumberCoreFonts();
1146: if (!coreFontFileNames.contains(fileName)) {
1147: exists = Boolean.TRUE;
1148: } else {
1149: exists = Boolean.valueOf((new File(fileName)).exists());
1150: existsMap.put(fileName, exists);
1151: if (SunGraphicsEnvironment.debugFonts
1152: && exists == Boolean.FALSE) {
1153: logger.warning("Couldn't locate font file "
1154: + fileName);
1155: }
1156: }
1157: }
1158: return exists == Boolean.FALSE;
1159: }
1160:
1161: private int numCoreFonts = -1;
1162: private String[] componentFonts = null;
1163: HashMap<String, String> filenamesMap = new HashMap<String, String>();
1164: HashSet<String> coreFontFileNames = new HashSet<String>();
1165:
1166: /* Return the number of core fonts. Note this isn't thread safe but
1167: * a calling thread can call this and getPlatformFontNames() in either
1168: * order.
1169: */
1170: public int getNumberCoreFonts() {
1171: if (numCoreFonts == -1) {
1172: numCoreFonts = coreFontNameIDs.size();
1173: Short[] emptyShortArray = new Short[0];
1174: Short[] core = coreFontNameIDs.toArray(emptyShortArray);
1175: Short[] fallback = fallbackFontNameIDs
1176: .toArray(emptyShortArray);
1177:
1178: int numFallbackFonts = 0;
1179: int i;
1180: for (i = 0; i < fallback.length; i++) {
1181: if (coreFontNameIDs.contains(fallback[i])) {
1182: fallback[i] = null;
1183: continue;
1184: }
1185: numFallbackFonts++;
1186: }
1187: componentFonts = new String[numCoreFonts + numFallbackFonts];
1188: String filename = null;
1189: for (i = 0; i < core.length; i++) {
1190: short fontid = core[i];
1191: short fileid = getComponentFileID(fontid);
1192: componentFonts[i] = getComponentFontName(fontid);
1193: String compFileName = getComponentFileName(fileid);
1194: if (compFileName != null) {
1195: coreFontFileNames.add(compFileName);
1196: }
1197: filenamesMap.put(componentFonts[i],
1198: mapFileName(compFileName));
1199: }
1200: for (int j = 0; j < fallback.length; j++) {
1201: if (fallback[j] != null) {
1202: short fontid = fallback[j];
1203: short fileid = getComponentFileID(fontid);
1204: componentFonts[i] = getComponentFontName(fontid);
1205: filenamesMap.put(componentFonts[i],
1206: mapFileName(getComponentFileName(fileid)));
1207: i++;
1208: }
1209: }
1210: }
1211: return numCoreFonts;
1212: }
1213:
1214: /* Return all platform font names used by this font configuration.
1215: * The first getNumberCoreFonts() entries are guaranteed to be the
1216: * core fonts - ie no fall back only fonts.
1217: */
1218: public String[] getPlatformFontNames() {
1219: if (numCoreFonts == -1) {
1220: getNumberCoreFonts();
1221: }
1222: return componentFonts;
1223: }
1224:
1225: /**
1226: * Returns a file name for the physical font represented by this platform font name,
1227: * if the font configuration has such information available, or null if the
1228: * information is unavailable. The file name returned is just a hint; a null return
1229: * value doesn't necessarily mean that the font is unavailable, nor does a non-null
1230: * return value guarantee that the file exists and contains the physical font.
1231: * The file name can be an absolute or a relative path name.
1232: */
1233: public String getFileNameFromPlatformName(String platformName) {
1234: // get2DCompositeFontInfo
1235: // -> getFileNameFromComponentfontName() (W/M)
1236: // -> getFileNameFromPlatformName()
1237: // it's a waste of time on Win32, but I have to give X11 a chance to
1238: // call getFileNameFromXLFD()
1239: return filenamesMap.get(platformName);
1240: }
1241:
1242: /**
1243: * Returns a configuration specific path to be appended to the font
1244: * search path.
1245: */
1246: public String getExtraFontPath() {
1247: return getString(head[INDEX_appendedfontpath]);
1248: }
1249:
1250: public String getVersion() {
1251: return getString(head[INDEX_version]);
1252: }
1253:
1254: /* subclass support */
1255: protected static FontConfiguration getFontConfiguration() {
1256: return fontConfig;
1257: }
1258:
1259: //////////////////////////////////////////////////////////////////////
1260: // FontConfig data tables and the index constants in binary file //
1261: //////////////////////////////////////////////////////////////////////
1262: /* The binary font configuration file begins with a short[] "head", which
1263: * contains the offsets to the starts of the individual data table which
1264: * immediately follow. Teh current implemention includes the tables shown
1265: * below.
1266: *
1267: * (00) table_scriptIDs :stringIDs of all defined CharacterSubsetNames
1268: * (01) table_scriptFonts :scriptID x fontIndex x styleIndex->
1269: * PlatformFontNameID mapping. Each scriptID might
1270: * have 1 or 20 entries depends on if it is defined
1271: * via a "allfonts.CharacterSubsetname" or a list of
1272: * "LogicalFontName.StyleName.CharacterSubsetName"
1273: * entries, positive entry means it's a "allfonts"
1274: * entry, a negative value means this is a offset to
1275: * a NUM_FONTS x NUM_STYLES subtable.
1276: * (02) table_elcIDs :stringIDs of all defined ELC names, string
1277: * "NULL.NULL.NULL" is used for "default"
1278: * (03) table_sequences :elcID x logicalFont -> scriptIDs table defined
1279: * by "sequence.allfonts/LogicalFontName.ELC" in
1280: * font configuration file, each "elcID" has
1281: * NUM_FONTS (5) entries in this table.
1282: * (04) table_fontfileNameIDs
1283: * :stringIDs of all defined font file names
1284: * (05) table_componentFontNameIDs
1285: * :stringIDs of all defined PlatformFontNames
1286: * (06) table_filenames :platformFontNamesID->fontfileNameID mapping
1287: * table, the index is the platformFontNamesID.
1288: * (07) table_awtfontpaths :CharacterSubsetNames->awtfontpaths mapping table,
1289: * the index is the CharacterSubsetName's stringID
1290: * and content is the stringID of awtfontpath.
1291: * (08) table_exclusions :scriptID -> exclusionRanges mapping table,
1292: * the index is the scriptID and the content is
1293: a id of an exclusionRanges int[].
1294: * (09) table_proportionals:list of pairs of PlatformFontNameIDs, stores
1295: * the replacement info defined by "proportional"
1296: * keyword.
1297: * (10) table_scriptFontsMotif
1298: * :same as (01) except this table stores the
1299: * info defined with ".motif" keyword
1300: * (11) table_alphabeticSuffix
1301: * :elcID -> stringID of alphabetic/XXXX entries
1302: * (12) table_stringIDs :The index of this table is the string ID, the
1303: * content is the "start index" of this string in
1304: * stringTable, use the start index of next entry
1305: * as the "end index".
1306: * (13) table_stringTable :The real storage of all character strings defined
1307: * /used this font configuration, need a pair of
1308: * "start" and "end" indices to access.
1309: * (14) reserved
1310: * (15) table_fallbackScripts
1311: * :stringIDs of fallback CharacterSubsetnames, stored
1312: * in the order of they are defined in sequence.fallback.
1313: * (16) table_appendedfontpath
1314: * :stringtID of the "appendedfontpath" defined.
1315: * (17) table_version :stringID of the version number of this fontconfig file.
1316: */
1317: private static final int HEAD_LENGTH = 20;
1318: private static final int INDEX_scriptIDs = 0;
1319: private static final int INDEX_scriptFonts = 1;
1320: private static final int INDEX_elcIDs = 2;
1321: private static final int INDEX_sequences = 3;
1322: private static final int INDEX_fontfileNameIDs = 4;
1323: private static final int INDEX_componentFontNameIDs = 5;
1324: private static final int INDEX_filenames = 6;
1325: private static final int INDEX_awtfontpaths = 7;
1326: private static final int INDEX_exclusions = 8;
1327: private static final int INDEX_proportionals = 9;
1328: private static final int INDEX_scriptFontsMotif = 10;
1329: private static final int INDEX_alphabeticSuffix = 11;
1330: private static final int INDEX_stringIDs = 12;
1331: private static final int INDEX_stringTable = 13;
1332: private static final int INDEX_TABLEEND = 14;
1333: private static final int INDEX_fallbackScripts = 15;
1334: private static final int INDEX_appendedfontpath = 16;
1335: private static final int INDEX_version = 17;
1336:
1337: private static short[] head;
1338: private static short[] table_scriptIDs;
1339: private static short[] table_scriptFonts;
1340: private static short[] table_elcIDs;
1341: private static short[] table_sequences;
1342: private static short[] table_fontfileNameIDs;
1343: private static short[] table_componentFontNameIDs;
1344: private static short[] table_filenames;
1345: protected static short[] table_awtfontpaths;
1346: private static short[] table_exclusions;
1347: private static short[] table_proportionals;
1348: private static short[] table_scriptFontsMotif;
1349: private static short[] table_alphabeticSuffix;
1350: private static short[] table_stringIDs;
1351: private static char[] table_stringTable;
1352:
1353: private static void sanityCheck() {
1354: int errors = 0;
1355:
1356: //This method will only be called during build time, do we
1357: //need do PrivilegedAction?
1358: String osName = (String) java.security.AccessController
1359: .doPrivileged(new java.security.PrivilegedAction() {
1360: public Object run() {
1361: return System.getProperty("os.name");
1362: }
1363: });
1364:
1365: //componentFontNameID starts from "1"
1366: for (int ii = 1; ii < table_filenames.length; ii++) {
1367: if (table_filenames[ii] == -1) {
1368: System.out.println("\n Warning: " + "<filename."
1369: + getString(table_componentFontNameIDs[ii])
1370: + "> entry is missing!!!");
1371: if (!osName.contains("Linux")) {
1372: errors++;
1373: }
1374: }
1375: }
1376: for (int ii = 0; ii < table_scriptIDs.length; ii++) {
1377: short fid = table_scriptFonts[ii];
1378: if (fid == 0) {
1379: System.out.println("\n Error: <allfonts."
1380: + getString(table_scriptIDs[ii])
1381: + "> entry is missing!!!");
1382: errors++;
1383: continue;
1384: } else if (fid < 0) {
1385: fid = (short) -fid;
1386: for (int iii = 0; iii < NUM_FONTS; iii++) {
1387: for (int iij = 0; iij < NUM_STYLES; iij++) {
1388: int jj = iii * NUM_STYLES + iij;
1389: short ffid = table_scriptFonts[fid + jj];
1390: if (ffid == 0) {
1391: System.out.println("\n Error: <"
1392: + getFontName(iii) + "."
1393: + getStyleName(iij) + "."
1394: + getString(table_scriptIDs[ii])
1395: + "> entry is missing!!!");
1396: errors++;
1397: }
1398: }
1399: }
1400: }
1401: }
1402: if ("SunOS".equals(osName)) {
1403: for (int ii = 0; ii < table_awtfontpaths.length; ii++) {
1404: if (table_awtfontpaths[ii] == 0) {
1405: String script = getString(table_scriptIDs[ii]);
1406: if (script.contains("lucida")
1407: || script.contains("dingbats")
1408: || script.contains("symbol")) {
1409: continue;
1410: }
1411: System.out.println("\nError: " + "<awtfontpath."
1412: + script + "> entry is missing!!!");
1413: errors++;
1414: }
1415: }
1416: }
1417: if (errors != 0) {
1418: System.out
1419: .println("!!THERE ARE "
1420: + errors
1421: + " ERROR(S) IN "
1422: + "THE FONTCONFIG FILE, PLEASE CHECK ITS CONTENT!!\n");
1423: System.exit(1);
1424:
1425: }
1426: }
1427:
1428: //dump the fontconfig data tables
1429: private static void dump() {
1430: System.out.println("\n----Head Table------------");
1431: for (int ii = 0; ii < HEAD_LENGTH; ii++) {
1432: System.out.println(" " + ii + " : " + head[ii]);
1433: }
1434: System.out.println("\n----scriptIDs-------------");
1435: printTable(table_scriptIDs, 0);
1436: System.out.println("\n----scriptFonts----------------");
1437: for (int ii = 0; ii < table_scriptIDs.length; ii++) {
1438: short fid = table_scriptFonts[ii];
1439: if (fid >= 0) {
1440: System.out.println(" allfonts."
1441: + getString(table_scriptIDs[ii]) + "="
1442: + getString(table_componentFontNameIDs[fid]));
1443: }
1444: }
1445: for (int ii = 0; ii < table_scriptIDs.length; ii++) {
1446: short fid = table_scriptFonts[ii];
1447: if (fid < 0) {
1448: fid = (short) -fid;
1449: for (int iii = 0; iii < NUM_FONTS; iii++) {
1450: for (int iij = 0; iij < NUM_STYLES; iij++) {
1451: int jj = iii * NUM_STYLES + iij;
1452: short ffid = table_scriptFonts[fid + jj];
1453: System.out
1454: .println(" "
1455: + getFontName(iii)
1456: + "."
1457: + getStyleName(iij)
1458: + "."
1459: + getString(table_scriptIDs[ii])
1460: + "="
1461: + getString(table_componentFontNameIDs[ffid]));
1462: }
1463: }
1464:
1465: }
1466: }
1467: System.out.println("\n----elcIDs----------------");
1468: printTable(table_elcIDs, 0);
1469: System.out.println("\n----sequences-------------");
1470: for (int ii = 0; ii < table_elcIDs.length; ii++) {
1471: System.out.println(" " + ii + "/"
1472: + getString((short) table_elcIDs[ii]));
1473: short[] ss = getShortArray(table_sequences[ii * NUM_FONTS
1474: + 0]);
1475: for (int jj = 0; jj < ss.length; jj++) {
1476: System.out.println(" "
1477: + getString((short) table_scriptIDs[ss[jj]]));
1478: }
1479: }
1480: System.out.println("\n----fontfileNameIDs-------");
1481: printTable(table_fontfileNameIDs, 0);
1482:
1483: System.out.println("\n----componentFontNameIDs--");
1484: printTable(table_componentFontNameIDs, 1);
1485: System.out.println("\n----filenames-------------");
1486: for (int ii = 0; ii < table_filenames.length; ii++) {
1487: if (table_filenames[ii] == -1) {
1488: System.out.println(" " + ii + " : null");
1489: } else {
1490: System.out
1491: .println(" "
1492: + ii
1493: + " : "
1494: + getString(table_fontfileNameIDs[table_filenames[ii]]));
1495: }
1496: }
1497: System.out.println("\n----awtfontpaths---------");
1498: for (int ii = 0; ii < table_awtfontpaths.length; ii++) {
1499: System.out.println(" " + getString(table_scriptIDs[ii])
1500: + " : " + getString(table_awtfontpaths[ii]));
1501: }
1502: System.out.println("\n----proportionals--------");
1503: for (int ii = 0; ii < table_proportionals.length; ii++) {
1504: System.out
1505: .println(" "
1506: + getString((short) table_componentFontNameIDs[table_proportionals[ii++]])
1507: + " -> "
1508: + getString((short) table_componentFontNameIDs[table_proportionals[ii]]));
1509: }
1510: int i = 0;
1511: System.out.println("\n----alphabeticSuffix----");
1512: while (i < table_alphabeticSuffix.length) {
1513: System.out
1514: .println(" "
1515: + getString(table_elcIDs[table_alphabeticSuffix[i++]])
1516: + " -> "
1517: + getString(table_alphabeticSuffix[i++]));
1518: }
1519: System.out.println("\n----String Table---------");
1520: System.out.println(" stringID: Num ="
1521: + table_stringIDs.length);
1522: System.out.println(" stringTable: Size="
1523: + table_stringTable.length * 2);
1524:
1525: System.out.println("\n----fallbackScriptIDs---");
1526: short[] fbsIDs = getShortArray(head[INDEX_fallbackScripts]);
1527: for (int ii = 0; ii < fbsIDs.length; ii++) {
1528: System.out.println(" "
1529: + getString(table_scriptIDs[fbsIDs[ii]]));
1530: }
1531: System.out.println("\n----appendedfontpath-----");
1532: System.out.println(" "
1533: + getString(head[INDEX_appendedfontpath]));
1534: System.out.println("\n----Version--------------");
1535: System.out.println(" " + getString(head[INDEX_version]));
1536: }
1537:
1538: //////////////////////////////////////////////////////////////////////
1539: // Data table access methods //
1540: //////////////////////////////////////////////////////////////////////
1541:
1542: /* Return the fontID of the platformFontName defined in this font config
1543: * by "LogicalFontName.StyleName.CharacterSubsetName" entry or
1544: * "allfonts.CharacterSubsetName" entry in properties format fc file.
1545: */
1546: protected static short getComponentFontID(short scriptID,
1547: int fontIndex, int styleIndex) {
1548: short fid = table_scriptFonts[scriptID];
1549: //System.out.println("fid=" + fid + "/ scriptID=" + scriptID + ", fi=" + fontIndex + ", si=" + styleIndex);
1550: if (fid >= 0) {
1551: //"allfonts"
1552: return fid;
1553: } else {
1554: return table_scriptFonts[-fid + fontIndex * NUM_STYLES
1555: + styleIndex];
1556: }
1557: }
1558:
1559: /* Same as getCompoentFontID() except this method returns the fontID define by
1560: * "xxxx.motif" entry.
1561: */
1562: protected static short getComponentFontIDMotif(short scriptID,
1563: int fontIndex, int styleIndex) {
1564: if (table_scriptFontsMotif.length == 0) {
1565: return 0;
1566: }
1567: short fid = table_scriptFontsMotif[scriptID];
1568: if (fid >= 0) {
1569: //"allfonts" > 0 or "not defined" == 0
1570: return fid;
1571: } else {
1572: return table_scriptFontsMotif[-fid + fontIndex * NUM_STYLES
1573: + styleIndex];
1574: }
1575: }
1576:
1577: private static int[] getExclusionRanges(short scriptID) {
1578: short exID = table_exclusions[scriptID];
1579: if (exID == 0) {
1580: return EMPTY_INT_ARRAY;
1581: } else {
1582: char[] exChar = getString(exID).toCharArray();
1583: int[] exInt = new int[exChar.length / 2];
1584: int i = 0;
1585: for (int j = 0; j < exInt.length; j++) {
1586: exInt[j] = (exChar[i++] << 16) + (exChar[i++] & 0xffff);
1587: }
1588: return exInt;
1589: }
1590: }
1591:
1592: private static boolean contains(short IDs[], short id, int limit) {
1593: for (int i = 0; i < limit; i++) {
1594: if (IDs[i] == id) {
1595: return true;
1596: }
1597: }
1598: return false;
1599: }
1600:
1601: /* Return the PlatformFontName from its fontID*/
1602: protected static String getComponentFontName(short id) {
1603: if (id < 0) {
1604: return null;
1605: }
1606: return getString(table_componentFontNameIDs[id]);
1607: }
1608:
1609: private static String getComponentFileName(short id) {
1610: if (id < 0) {
1611: return null;
1612: }
1613: return getString(table_fontfileNameIDs[id]);
1614: }
1615:
1616: //componentFontID -> componentFileID
1617: private static short getComponentFileID(short nameID) {
1618: return table_filenames[nameID];
1619: }
1620:
1621: private static String getScriptName(short scriptID) {
1622: return getString(table_scriptIDs[scriptID]);
1623: }
1624:
1625: private HashMap<String, Short> reorderScripts;
1626:
1627: protected short[] getCoreScripts(int fontIndex) {
1628: short elc = getInitELC();
1629: /*
1630: System.out.println("getCoreScripts: elc=" + elc + ", fontIndex=" + fontIndex);
1631: short[] ss = getShortArray(table_sequences[elc * NUM_FONTS + fontIndex]);
1632: for (int i = 0; i < ss.length; i++) {
1633: System.out.println(" " + getString((short)table_scriptIDs[ss[i]]));
1634: }
1635: */
1636: short[] scripts = getShortArray(table_sequences[elc * NUM_FONTS
1637: + fontIndex]);
1638: if (preferLocaleFonts) {
1639: if (reorderScripts == null) {
1640: reorderScripts = new HashMap<String, Short>();
1641: }
1642: String[] ss = new String[scripts.length];
1643: for (int i = 0; i < ss.length; i++) {
1644: ss[i] = getScriptName(scripts[i]);
1645: reorderScripts.put(ss[i], scripts[i]);
1646: }
1647: reorderSequenceForLocale(ss);
1648: for (int i = 0; i < ss.length; i++) {
1649: scripts[i] = reorderScripts.get(ss[i]);
1650: }
1651: }
1652: return scripts;
1653: }
1654:
1655: private static short[] getFallbackScripts() {
1656: return getShortArray(head[INDEX_fallbackScripts]);
1657: }
1658:
1659: private static void printTable(short[] list, int start) {
1660: for (int i = start; i < list.length; i++) {
1661: System.out.println(" " + i + " : " + getString(list[i]));
1662: }
1663: }
1664:
1665: private static short[] readShortTable(DataInputStream in, int len)
1666: throws IOException {
1667: if (len == 0) {
1668: return EMPTY_SHORT_ARRAY;
1669: }
1670: short[] data = new short[len];
1671: byte[] bb = new byte[len * 2];
1672: in.read(bb);
1673: int i = 0, j = 0;
1674: while (i < len) {
1675: data[i++] = (short) (bb[j++] << 8 | (bb[j++] & 0xff));
1676: }
1677: return data;
1678: }
1679:
1680: private static void writeShortTable(DataOutputStream out,
1681: short[] data) throws IOException {
1682: for (int i = 0; i < data.length; i++) {
1683: out.writeShort(data[i]);
1684: }
1685: }
1686:
1687: private static short[] toList(HashMap map) {
1688: short[] list = new short[map.size()];
1689: for (int i = 0; i < list.length; i++) {
1690: list[i] = -1;
1691: }
1692: Iterator iterator = map.entrySet().iterator();
1693: while (iterator.hasNext()) {
1694: Entry<String, Short> entry = (Entry<String, Short>) iterator
1695: .next();
1696: list[entry.getValue().shortValue()] = getStringID(entry
1697: .getKey());
1698: }
1699: return list;
1700: }
1701:
1702: //runtime cache
1703: private static String[] stringCache;
1704:
1705: protected static String getString(short stringID) {
1706: if (stringID == 0)
1707: return null;
1708: /*
1709: if (loadingProperties) {
1710: return stringTable.substring(stringIDs[stringID],
1711: stringIDs[stringID+1]);
1712: }
1713: */
1714: //sync if we want it to be MT-enabled
1715: if (stringCache[stringID] == null) {
1716: stringCache[stringID] = new String(table_stringTable,
1717: table_stringIDs[stringID],
1718: table_stringIDs[stringID + 1]
1719: - table_stringIDs[stringID]);
1720: }
1721: return stringCache[stringID];
1722: }
1723:
1724: private static short[] getShortArray(short shortArrayID) {
1725: String s = getString(shortArrayID);
1726: char[] cc = s.toCharArray();
1727: short[] ss = new short[cc.length];
1728: for (int i = 0; i < cc.length; i++) {
1729: ss[i] = (short) (cc[i] & 0xffff);
1730: }
1731: return ss;
1732: }
1733:
1734: private static short getStringID(String s) {
1735: if (s == null) {
1736: return (short) 0;
1737: }
1738: short pos0 = (short) stringTable.length();
1739: stringTable.append(s);
1740: short pos1 = (short) stringTable.length();
1741:
1742: stringIDs[stringIDNum] = pos0;
1743: stringIDs[stringIDNum + 1] = pos1;
1744: stringIDNum++;
1745: if (stringIDNum + 1 >= stringIDs.length) {
1746: short[] tmp = new short[stringIDNum + 1000];
1747: System.arraycopy(stringIDs, 0, tmp, 0, stringIDNum);
1748: stringIDs = tmp;
1749: }
1750: return (short) (stringIDNum - 1);
1751: }
1752:
1753: private static short getShortArrayID(short sa[]) {
1754: char[] cc = new char[sa.length];
1755: for (int i = 0; i < sa.length; i++) {
1756: cc[i] = (char) sa[i];
1757: }
1758: String s = new String(cc);
1759: return getStringID(s);
1760: }
1761:
1762: //utility "empty" objects
1763: private static final int[] EMPTY_INT_ARRAY = new int[0];
1764: private static final String[] EMPTY_STRING_ARRAY = new String[0];
1765: private static final short[] EMPTY_SHORT_ARRAY = new short[0];
1766: private static final String UNDEFINED_COMPONENT_FONT = "unknown";
1767:
1768: //////////////////////////////////////////////////////////////////////////
1769: //Convert the FontConfig data in Properties file to binary data tables //
1770: //////////////////////////////////////////////////////////////////////////
1771: static class PropertiesHandler {
1772: public void load(InputStream in) throws IOException {
1773: initLogicalNameStyle();
1774: initHashMaps();
1775: FontProperties fp = new FontProperties();
1776: fp.load(in);
1777: initBinaryTable();
1778: }
1779:
1780: private void initBinaryTable() {
1781: //(0)
1782: head = new short[HEAD_LENGTH];
1783: head[INDEX_scriptIDs] = (short) HEAD_LENGTH;
1784:
1785: table_scriptIDs = toList(scriptIDs);
1786: //(1)a: scriptAllfonts scriptID/allfonts -> componentFontNameID
1787: // b: scriptFonts scriptID -> componentFontNameID[20]
1788: //if we have a "allfonts.script" def, then we just put
1789: //the "-platformFontID" value in the slot, otherwise the slot
1790: //value is "offset" which "offset" is where 20 entries located
1791: //in the table attached.
1792: head[INDEX_scriptFonts] = (short) (head[INDEX_scriptIDs] + table_scriptIDs.length);
1793: int len = table_scriptIDs.length + scriptFonts.size() * 20;
1794: table_scriptFonts = new short[len];
1795:
1796: Iterator iterator = scriptAllfonts.entrySet().iterator();
1797: while (iterator.hasNext()) {
1798: Entry<Short, Short> entry = (Entry<Short, Short>) iterator
1799: .next();
1800: table_scriptFonts[entry.getKey().intValue()] = (short) entry
1801: .getValue().shortValue();
1802: }
1803: int off = table_scriptIDs.length;
1804: iterator = scriptFonts.entrySet().iterator();
1805: while (iterator.hasNext()) {
1806: Entry<Short, Short[]> entry = (Entry<Short, Short[]>) iterator
1807: .next();
1808: table_scriptFonts[entry.getKey().intValue()] = (short) -off;
1809: Short[] v = entry.getValue();
1810: int i = 0;
1811: while (i < 20) {
1812: if (v[i] != null) {
1813: table_scriptFonts[off++] = v[i].shortValue();
1814: } else {
1815: table_scriptFonts[off++] = 0;
1816: }
1817: i++;
1818: }
1819: }
1820:
1821: //(2)
1822: head[INDEX_elcIDs] = (short) (head[INDEX_scriptFonts] + table_scriptFonts.length);
1823: table_elcIDs = toList(elcIDs);
1824:
1825: //(3) sequences elcID -> XXXX[1|5] -> scriptID[]
1826: head[INDEX_sequences] = (short) (head[INDEX_elcIDs] + table_elcIDs.length);
1827: table_sequences = new short[elcIDs.size() * NUM_FONTS];
1828: iterator = sequences.entrySet().iterator();
1829: while (iterator.hasNext()) {
1830: Entry<Short, short[]> entry = (Entry<Short, short[]>) iterator
1831: .next();
1832: //table_sequences[entry.getKey().intValue()] = (short)-off;
1833: int k = entry.getKey().intValue();
1834: short[] v = entry.getValue();
1835: /*
1836: System.out.println("elc=" + k + "/" + getString((short)table_elcIDs[k]));
1837: short[] ss = getShortArray(v[0]);
1838: for (int i = 0; i < ss.length; i++) {
1839: System.out.println(" " + getString((short)table_scriptIDs[ss[i]]));
1840: }
1841: */
1842: if (v.length == 1) {
1843: //the "allfonts" entries
1844: for (int i = 0; i < NUM_FONTS; i++) {
1845: table_sequences[k * NUM_FONTS + i] = v[0];
1846: }
1847: } else {
1848: for (int i = 0; i < NUM_FONTS; i++) {
1849: table_sequences[k * NUM_FONTS + i] = v[i];
1850: }
1851: }
1852: }
1853: //(4)
1854: head[INDEX_fontfileNameIDs] = (short) (head[INDEX_sequences] + table_sequences.length);
1855: table_fontfileNameIDs = toList(fontfileNameIDs);
1856:
1857: //(5)
1858: head[INDEX_componentFontNameIDs] = (short) (head[INDEX_fontfileNameIDs] + table_fontfileNameIDs.length);
1859: table_componentFontNameIDs = toList(componentFontNameIDs);
1860:
1861: //(6)componentFontNameID -> filenameID
1862: head[INDEX_filenames] = (short) (head[INDEX_componentFontNameIDs] + table_componentFontNameIDs.length);
1863: table_filenames = new short[table_componentFontNameIDs.length];
1864: for (int i = 0; i < table_filenames.length; i++) {
1865: table_filenames[i] = -1;
1866: }
1867: iterator = filenames.entrySet().iterator();
1868: while (iterator.hasNext()) {
1869: Entry<Short, Short> entry = (Entry<Short, Short>) iterator
1870: .next();
1871: table_filenames[entry.getKey().shortValue()] = entry
1872: .getValue().shortValue();
1873: }
1874:
1875: //(7)scriptID-> awtfontpath
1876: //the paths are stored as scriptID -> stringID in awtfontpahts
1877: head[INDEX_awtfontpaths] = (short) (head[INDEX_filenames] + table_filenames.length);
1878: table_awtfontpaths = new short[table_scriptIDs.length];
1879: iterator = awtfontpaths.entrySet().iterator();
1880: while (iterator.hasNext()) {
1881: Entry<Short, Short> entry = (Entry<Short, Short>) iterator
1882: .next();
1883: table_awtfontpaths[entry.getKey().shortValue()] = entry
1884: .getValue().shortValue();
1885: }
1886:
1887: //(8)exclusions
1888: head[INDEX_exclusions] = (short) (head[INDEX_awtfontpaths] + table_awtfontpaths.length);
1889: table_exclusions = new short[scriptIDs.size()];
1890: iterator = exclusions.entrySet().iterator();
1891: while (iterator.hasNext()) {
1892: Entry<Short, int[]> entry = (Entry<Short, int[]>) iterator
1893: .next();
1894: int[] exI = entry.getValue();
1895: char[] exC = new char[exI.length * 2];
1896: int j = 0;
1897: for (int i = 0; i < exI.length; i++) {
1898: exC[j++] = (char) (exI[i] >> 16);
1899: exC[j++] = (char) (exI[i] & 0xffff);
1900: }
1901: table_exclusions[entry.getKey().shortValue()] = getStringID(new String(
1902: exC));
1903: }
1904: //(9)proportionals
1905: head[INDEX_proportionals] = (short) (head[INDEX_exclusions] + table_exclusions.length);
1906: table_proportionals = new short[proportionals.size() * 2];
1907: iterator = proportionals.entrySet().iterator();
1908: int j = 0;
1909: while (iterator.hasNext()) {
1910: Entry<Short, Short> entry = (Entry<Short, Short>) iterator
1911: .next();
1912: table_proportionals[j++] = entry.getKey().shortValue();
1913: table_proportionals[j++] = entry.getValue()
1914: .shortValue();
1915: }
1916:
1917: //(10) see (1) for info, the only difference is "xxx.motif"
1918: head[INDEX_scriptFontsMotif] = (short) (head[INDEX_proportionals] + table_proportionals.length);
1919: if (scriptAllfontsMotif.size() != 0
1920: || scriptFontsMotif.size() != 0) {
1921: len = table_scriptIDs.length + scriptFontsMotif.size()
1922: * 20;
1923: table_scriptFontsMotif = new short[len];
1924:
1925: iterator = scriptAllfontsMotif.entrySet().iterator();
1926: while (iterator.hasNext()) {
1927: Entry<Short, Short> entry = (Entry<Short, Short>) iterator
1928: .next();
1929: table_scriptFontsMotif[entry.getKey().intValue()] = (short) entry
1930: .getValue().shortValue();
1931: }
1932: off = table_scriptIDs.length;
1933: iterator = scriptFontsMotif.entrySet().iterator();
1934: while (iterator.hasNext()) {
1935: Entry<Short, Short[]> entry = (Entry<Short, Short[]>) iterator
1936: .next();
1937: table_scriptFontsMotif[entry.getKey().intValue()] = (short) -off;
1938: Short[] v = entry.getValue();
1939: int i = 0;
1940: while (i < 20) {
1941: if (v[i] != null) {
1942: table_scriptFontsMotif[off++] = v[i]
1943: .shortValue();
1944: } else {
1945: table_scriptFontsMotif[off++] = 0;
1946: }
1947: i++;
1948: }
1949: }
1950: } else {
1951: table_scriptFontsMotif = EMPTY_SHORT_ARRAY;
1952: }
1953:
1954: //(11)short[] alphabeticSuffix
1955: head[INDEX_alphabeticSuffix] = (short) (head[INDEX_scriptFontsMotif] + table_scriptFontsMotif.length);
1956: table_alphabeticSuffix = new short[alphabeticSuffix.size() * 2];
1957: iterator = alphabeticSuffix.entrySet().iterator();
1958: j = 0;
1959: while (iterator.hasNext()) {
1960: Entry<Short, Short> entry = (Entry<Short, Short>) iterator
1961: .next();
1962: table_alphabeticSuffix[j++] = entry.getKey()
1963: .shortValue();
1964: table_alphabeticSuffix[j++] = entry.getValue()
1965: .shortValue();
1966: }
1967:
1968: //(15)short[] fallbackScriptIDs; just put the ID in head
1969: head[INDEX_fallbackScripts] = getShortArrayID(fallbackScriptIDs);
1970:
1971: //(16)appendedfontpath
1972: head[INDEX_appendedfontpath] = getStringID(appendedfontpath);
1973:
1974: //(17)version
1975: head[INDEX_version] = getStringID(version);
1976:
1977: //(12)short[] StringIDs
1978: head[INDEX_stringIDs] = (short) (head[INDEX_alphabeticSuffix] + table_alphabeticSuffix.length);
1979: table_stringIDs = new short[stringIDNum + 1];
1980: System.arraycopy(stringIDs, 0, table_stringIDs, 0,
1981: stringIDNum + 1);
1982:
1983: //(13)StringTable
1984: head[INDEX_stringTable] = (short) (head[INDEX_stringIDs]
1985: + stringIDNum + 1);
1986: table_stringTable = stringTable.toString().toCharArray();
1987: //(14)
1988: head[INDEX_TABLEEND] = (short) (head[INDEX_stringTable] + stringTable
1989: .length());
1990:
1991: //StringTable cache
1992: stringCache = new String[table_stringIDs.length];
1993: }
1994:
1995: //////////////////////////////////////////////
1996: private HashMap<String, Short> scriptIDs;
1997: //elc -> Encoding.Language.Country
1998: private HashMap<String, Short> elcIDs;
1999: //componentFontNameID starts from "1", "0" reserves for "undefined"
2000: private HashMap<String, Short> componentFontNameIDs;
2001: private HashMap<String, Short> fontfileNameIDs;
2002: private HashMap<String, Integer> logicalFontIDs;
2003: private HashMap<String, Integer> fontStyleIDs;
2004:
2005: //componentFontNameID -> fontfileNameID
2006: private HashMap<Short, Short> filenames;
2007:
2008: //elcID -> allfonts/logicalFont -> scriptID list
2009: //(1)if we have a "allfonts", then the length of the
2010: // value array is "1", otherwise it's 5, each font
2011: // must have their own individual entry.
2012: //scriptID list "short[]" is stored as an ID
2013: private HashMap<Short, short[]> sequences;
2014:
2015: //scriptID ->logicFontID/fontStyleID->componentFontNameID,
2016: //a 20-entry array (5-name x 4-style) for each script
2017: private HashMap<Short, Short[]> scriptFonts;
2018:
2019: //scriptID -> componentFontNameID
2020: private HashMap<Short, Short> scriptAllfonts;
2021:
2022: //scriptID -> exclusionRanges[]
2023: private HashMap<Short, int[]> exclusions;
2024:
2025: //scriptID -> fontpath
2026: private HashMap<Short, Short> awtfontpaths;
2027:
2028: //fontID -> fontID
2029: private HashMap<Short, Short> proportionals;
2030:
2031: //scriptID -> componentFontNameID
2032: private HashMap<Short, Short> scriptAllfontsMotif;
2033:
2034: //scriptID ->logicFontID/fontStyleID->componentFontNameID,
2035: private HashMap<Short, Short[]> scriptFontsMotif;
2036:
2037: //elcID -> stringID of alphabetic/XXXX
2038: private HashMap<Short, Short> alphabeticSuffix;
2039:
2040: private short[] fallbackScriptIDs;
2041: private String version;
2042: private String appendedfontpath;
2043:
2044: private void initLogicalNameStyle() {
2045: logicalFontIDs = new HashMap<String, Integer>();
2046: fontStyleIDs = new HashMap<String, Integer>();
2047: logicalFontIDs.put("serif", 0);
2048: logicalFontIDs.put("sansserif", 1);
2049: logicalFontIDs.put("monospaced", 2);
2050: logicalFontIDs.put("dialog", 3);
2051: logicalFontIDs.put("dialoginput", 4);
2052: fontStyleIDs.put("plain", 0);
2053: fontStyleIDs.put("bold", 1);
2054: fontStyleIDs.put("italic", 2);
2055: fontStyleIDs.put("bolditalic", 3);
2056: }
2057:
2058: private void initHashMaps() {
2059: scriptIDs = new HashMap<String, Short>();
2060: elcIDs = new HashMap<String, Short>();
2061: componentFontNameIDs = new HashMap<String, Short>();
2062: /*Init these tables to allow componentFontNameID, fontfileNameIDs
2063: to start from "1".
2064: */
2065: componentFontNameIDs.put("", new Short((short) 0));
2066:
2067: fontfileNameIDs = new HashMap<String, Short>();
2068: filenames = new HashMap<Short, Short>();
2069: sequences = new HashMap<Short, short[]>();
2070: scriptFonts = new HashMap<Short, Short[]>();
2071: scriptAllfonts = new HashMap<Short, Short>();
2072: exclusions = new HashMap<Short, int[]>();
2073: awtfontpaths = new HashMap<Short, Short>();
2074: proportionals = new HashMap<Short, Short>();
2075: scriptFontsMotif = new HashMap<Short, Short[]>();
2076: scriptAllfontsMotif = new HashMap<Short, Short>();
2077: alphabeticSuffix = new HashMap<Short, Short>();
2078: fallbackScriptIDs = EMPTY_SHORT_ARRAY;
2079: /*
2080: version
2081: appendedfontpath
2082: */
2083: }
2084:
2085: private int[] parseExclusions(String key, String exclusions) {
2086: if (exclusions == null) {
2087: return EMPTY_INT_ARRAY;
2088: }
2089: // range format is xxxx-XXXX,yyyyyy-YYYYYY,.....
2090: int numExclusions = 1;
2091: int pos = 0;
2092: while ((pos = exclusions.indexOf(',', pos)) != -1) {
2093: numExclusions++;
2094: pos++;
2095: }
2096: int[] exclusionRanges = new int[numExclusions * 2];
2097: pos = 0;
2098: int newPos = 0;
2099: for (int j = 0; j < numExclusions * 2;) {
2100: String lower, upper;
2101: int lo = 0, up = 0;
2102: try {
2103: newPos = exclusions.indexOf('-', pos);
2104: lower = exclusions.substring(pos, newPos);
2105: pos = newPos + 1;
2106: newPos = exclusions.indexOf(',', pos);
2107: if (newPos == -1) {
2108: newPos = exclusions.length();
2109: }
2110: upper = exclusions.substring(pos, newPos);
2111: pos = newPos + 1;
2112: int lowerLength = lower.length();
2113: int upperLength = upper.length();
2114: if (lowerLength != 4 && lowerLength != 6
2115: || upperLength != 4 && upperLength != 6) {
2116: throw new Exception();
2117: }
2118: lo = Integer.parseInt(lower, 16);
2119: up = Integer.parseInt(upper, 16);
2120: if (lo > up) {
2121: throw new Exception();
2122: }
2123: } catch (Exception e) {
2124: if (SunGraphicsEnvironment.debugFonts
2125: && logger != null) {
2126: logger.config("Failed parsing " + key
2127: + " property of font configuration.");
2128:
2129: }
2130: return EMPTY_INT_ARRAY;
2131: }
2132: exclusionRanges[j++] = lo;
2133: exclusionRanges[j++] = up;
2134: }
2135: return exclusionRanges;
2136: }
2137:
2138: private Short getID(HashMap<String, Short> map, String key) {
2139: Short ret = map.get(key);
2140: if (ret == null) {
2141: map.put(key, (short) map.size());
2142: return map.get(key);
2143: }
2144: return ret;
2145: }
2146:
2147: class FontProperties extends Properties {
2148: public synchronized Object put(Object k, Object v) {
2149: parseProperty((String) k, (String) v);
2150: return null;
2151: }
2152: }
2153:
2154: private void parseProperty(String key, String value) {
2155: if (key.startsWith("filename.")) {
2156: //the only special case is "MingLiu_HKSCS" which has "_" in its
2157: //facename, we dont want to replace the "_" with " "
2158: key = key.substring(9);
2159: if (!"MingLiU_HKSCS".equals(key)) {
2160: key = key.replace('_', ' ');
2161: }
2162: Short faceID = getID(componentFontNameIDs, key);
2163: Short fileID = getID(fontfileNameIDs, value);
2164: //System.out.println("faceID=" + faceID + "/" + key + " -> "
2165: // + "fileID=" + fileID + "/" + value);
2166: filenames.put(faceID, fileID);
2167: } else if (key.startsWith("exclusion.")) {
2168: key = key.substring(10);
2169: exclusions.put(getID(scriptIDs, key), parseExclusions(
2170: key, value));
2171: } else if (key.startsWith("sequence.")) {
2172: key = key.substring(9);
2173: boolean hasDefault = false;
2174: boolean has1252 = false;
2175:
2176: //get the scriptID list
2177: String[] ss = (String[]) splitSequence(value).toArray(
2178: EMPTY_STRING_ARRAY);
2179: short[] sa = new short[ss.length];
2180: for (int i = 0; i < ss.length; i++) {
2181: if ("alphabetic/default".equals(ss[i])) {
2182: //System.out.println(key + " -> " + ss[i]);
2183: ss[i] = "alphabetic";
2184: hasDefault = true;
2185: } else if ("alphabetic/1252".equals(ss[i])) {
2186: //System.out.println(key + " -> " + ss[i]);
2187: ss[i] = "alphabetic";
2188: has1252 = true;
2189: }
2190: sa[i] = getID(scriptIDs, ss[i]).shortValue();
2191: //System.out.println("scriptID=" + si[i] + "/" + ss[i]);
2192: }
2193: //convert the "short[] -> string -> stringID"
2194: short scriptArrayID = getShortArrayID(sa);
2195: Short elcID = null;
2196: int dot = key.indexOf('.');
2197: if (dot == -1) {
2198: if ("fallback".equals(key)) {
2199: fallbackScriptIDs = sa;
2200: return;
2201: }
2202: if ("allfonts".equals(key)) {
2203: elcID = getID(elcIDs, "NULL.NULL.NULL");
2204: } else {
2205: if (logger != null) {
2206: logger
2207: .config("Error sequence def: <sequence."
2208: + key + ">");
2209: }
2210: return;
2211: }
2212: } else {
2213: elcID = getID(elcIDs, key.substring(dot + 1));
2214: //System.out.println("elcID=" + elcID + "/" + key.substring(dot + 1));
2215: key = key.substring(0, dot);
2216: }
2217: short[] scriptArrayIDs = null;
2218: if ("allfonts".equals(key)) {
2219: scriptArrayIDs = new short[1];
2220: scriptArrayIDs[0] = scriptArrayID;
2221: } else {
2222: scriptArrayIDs = sequences.get(elcID);
2223: if (scriptArrayIDs == null) {
2224: scriptArrayIDs = new short[5];
2225: }
2226: Integer fid = logicalFontIDs.get(key);
2227: if (fid == null) {
2228: if (logger != null) {
2229: logger
2230: .config("Unrecognizable logicfont name "
2231: + key);
2232: }
2233: return;
2234: }
2235: //System.out.println("sequence." + key + "/" + id);
2236: scriptArrayIDs[fid.intValue()] = scriptArrayID;
2237: }
2238: sequences.put(elcID, scriptArrayIDs);
2239: if (hasDefault) {
2240: alphabeticSuffix.put(elcID, getStringID("default"));
2241: } else if (has1252) {
2242: alphabeticSuffix.put(elcID, getStringID("1252"));
2243: }
2244: } else if (key.startsWith("allfonts.")) {
2245: key = key.substring(9);
2246: if (key.endsWith(".motif")) {
2247: key = key.substring(0, key.length() - 6);
2248: //System.out.println("motif: all." + key + "=" + value);
2249: scriptAllfontsMotif.put(getID(scriptIDs, key),
2250: getID(componentFontNameIDs, value));
2251: } else {
2252: scriptAllfonts.put(getID(scriptIDs, key), getID(
2253: componentFontNameIDs, value));
2254: }
2255: } else if (key.startsWith("awtfontpath.")) {
2256: key = key.substring(12);
2257: //System.out.println("scriptID=" + getID(scriptIDs, key) + "/" + key);
2258: awtfontpaths.put(getID(scriptIDs, key),
2259: getStringID(value));
2260: } else if ("version".equals(key)) {
2261: version = value;
2262: } else if ("appendedfontpath".equals(key)) {
2263: appendedfontpath = value;
2264: } else if (key.startsWith("proportional.")) {
2265: key = key.substring(13).replace('_', ' ');
2266: //System.out.println(key + "=" + value);
2267: proportionals.put(getID(componentFontNameIDs, key),
2268: getID(componentFontNameIDs, value));
2269: } else {
2270: //"name.style.script(.motif)", we dont care anything else
2271: int dot1, dot2;
2272: boolean isMotif = false;
2273:
2274: dot1 = key.indexOf('.');
2275: if (dot1 == -1) {
2276: if (logger != null) {
2277: logger.config("Failed parsing " + key
2278: + " property of font configuration.");
2279:
2280: }
2281: return;
2282: }
2283: dot2 = key.indexOf('.', dot1 + 1);
2284: if (dot2 == -1) {
2285: if (logger != null) {
2286: logger.config("Failed parsing " + key
2287: + " property of font configuration.");
2288:
2289: }
2290: return;
2291: }
2292: if (key.endsWith(".motif")) {
2293: key = key.substring(0, key.length() - 6);
2294: isMotif = true;
2295: //System.out.println("motif: " + key + "=" + value);
2296: }
2297: Integer nameID = logicalFontIDs.get(key.substring(0,
2298: dot1));
2299: Integer styleID = fontStyleIDs.get(key.substring(
2300: dot1 + 1, dot2));
2301: Short scriptID = getID(scriptIDs, key
2302: .substring(dot2 + 1));
2303: if (nameID == null || styleID == null) {
2304: if (logger != null) {
2305: logger
2306: .config("unrecognizable logicfont name/style at "
2307: + key);
2308: }
2309: return;
2310: }
2311: Short[] pnids;
2312: if (isMotif) {
2313: pnids = scriptFontsMotif.get(scriptID);
2314: } else {
2315: pnids = scriptFonts.get(scriptID);
2316: }
2317: if (pnids == null) {
2318: pnids = new Short[20];
2319: }
2320: pnids[nameID.intValue() * NUM_STYLES
2321: + styleID.intValue()] = getID(
2322: componentFontNameIDs, value);
2323: /*
2324: System.out.println("key=" + key + "/<" + nameID + "><" + styleID
2325: + "><" + scriptID + ">=" + value
2326: + "/" + getID(componentFontNameIDs, value));
2327: */
2328: if (isMotif) {
2329: scriptFontsMotif.put(scriptID, pnids);
2330: } else {
2331: scriptFonts.put(scriptID, pnids);
2332: }
2333: }
2334: }
2335: }
2336: }
|