0N/A/*
3909N/A * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.font;
0N/A
0N/Aimport java.awt.Font;
0N/Aimport java.awt.FontFormatException;
0N/Aimport java.awt.GraphicsEnvironment;
0N/Aimport java.awt.geom.Point2D;
0N/Aimport java.io.FileNotFoundException;
0N/Aimport java.io.IOException;
0N/Aimport java.io.RandomAccessFile;
0N/Aimport java.io.UnsupportedEncodingException;
0N/Aimport java.nio.ByteBuffer;
0N/Aimport java.nio.CharBuffer;
0N/Aimport java.nio.IntBuffer;
0N/Aimport java.nio.ShortBuffer;
0N/Aimport java.nio.channels.ClosedChannelException;
0N/Aimport java.nio.channels.FileChannel;
1686N/Aimport java.util.HashMap;
0N/Aimport java.util.HashSet;
1686N/Aimport java.util.Map;
0N/Aimport java.util.Locale;
0N/Aimport sun.java2d.Disposer;
0N/Aimport sun.java2d.DisposerRecord;
0N/A
0N/A/**
0N/A * TrueTypeFont is not called SFntFont because it is not expected
0N/A * to handle all types that may be housed in a such a font file.
0N/A * If additional types are supported later, it may make sense to
0N/A * create an SFnt superclass. Eg to handle sfnt-housed postscript fonts.
0N/A * OpenType fonts are handled by this class, and possibly should be
0N/A * represented by a subclass.
0N/A * An instance stores some information from the font file to faciliate
0N/A * faster access. File size, the table directory and the names of the font
0N/A * are the most important of these. It amounts to approx 400 bytes
0N/A * for a typical font. Systems with mutiple locales sometimes have up to 400
0N/A * font files, and an app which loads all font files would need around
0N/A * 160Kbytes. So storing any more info than this would be expensive.
0N/A */
0N/Apublic class TrueTypeFont extends FileFont {
0N/A
0N/A /* -- Tags for required TrueType tables */
0N/A public static final int cmapTag = 0x636D6170; // 'cmap'
0N/A public static final int glyfTag = 0x676C7966; // 'glyf'
0N/A public static final int headTag = 0x68656164; // 'head'
0N/A public static final int hheaTag = 0x68686561; // 'hhea'
0N/A public static final int hmtxTag = 0x686D7478; // 'hmtx'
0N/A public static final int locaTag = 0x6C6F6361; // 'loca'
0N/A public static final int maxpTag = 0x6D617870; // 'maxp'
0N/A public static final int nameTag = 0x6E616D65; // 'name'
0N/A public static final int postTag = 0x706F7374; // 'post'
0N/A public static final int os_2Tag = 0x4F532F32; // 'OS/2'
0N/A
0N/A /* -- Tags for opentype related tables */
0N/A public static final int GDEFTag = 0x47444546; // 'GDEF'
0N/A public static final int GPOSTag = 0x47504F53; // 'GPOS'
0N/A public static final int GSUBTag = 0x47535542; // 'GSUB'
0N/A public static final int mortTag = 0x6D6F7274; // 'mort'
0N/A
0N/A /* -- Tags for non-standard tables */
0N/A public static final int fdscTag = 0x66647363; // 'fdsc' - gxFont descriptor
0N/A public static final int fvarTag = 0x66766172; // 'fvar' - gxFont variations
0N/A public static final int featTag = 0x66656174; // 'feat' - layout features
0N/A public static final int EBLCTag = 0x45424C43; // 'EBLC' - embedded bitmaps
0N/A public static final int gaspTag = 0x67617370; // 'gasp' - hint/smooth sizes
0N/A
0N/A /* -- Other tags */
0N/A public static final int ttcfTag = 0x74746366; // 'ttcf' - TTC file
0N/A public static final int v1ttTag = 0x00010000; // 'v1tt' - Version 1 TT font
0N/A public static final int trueTag = 0x74727565; // 'true' - Version 2 TT font
752N/A public static final int ottoTag = 0x4f54544f; // 'otto' - OpenType font
0N/A
0N/A /* -- ID's used in the 'name' table */
0N/A public static final int MS_PLATFORM_ID = 3;
0N/A /* MS locale id for US English is the "default" */
0N/A public static final short ENGLISH_LOCALE_ID = 0x0409; // 1033 decimal
0N/A public static final int FAMILY_NAME_ID = 1;
0N/A // public static final int STYLE_WEIGHT_ID = 2; // currently unused.
0N/A public static final int FULL_NAME_ID = 4;
0N/A public static final int POSTSCRIPT_NAME_ID = 6;
0N/A
1686N/A private static final short US_LCID = 0x0409; // US English - default
1686N/A
1686N/A private static Map<String, Short> lcidMap;
0N/A
0N/A class DirectoryEntry {
0N/A int tag;
0N/A int offset;
0N/A int length;
0N/A }
0N/A
0N/A /* There is a pool which limits the number of fd's that are in
0N/A * use. Normally fd's are closed as they are replaced in the pool.
0N/A * But if an instance of this class becomes unreferenced, then there
0N/A * needs to be a way to close the fd. A finalize() method could do this,
0N/A * but using the Disposer class will ensure its called in a more timely
0N/A * manner. This is not something which should be relied upon to free
0N/A * fd's - its a safeguard.
0N/A */
0N/A private static class TTDisposerRecord implements DisposerRecord {
0N/A
0N/A FileChannel channel = null;
0N/A
0N/A public synchronized void dispose() {
0N/A try {
0N/A if (channel != null) {
0N/A channel.close();
0N/A }
0N/A } catch (IOException e) {
0N/A } finally {
0N/A channel = null;
0N/A }
0N/A }
0N/A }
0N/A
0N/A TTDisposerRecord disposerRecord = new TTDisposerRecord();
0N/A
0N/A /* > 0 only if this font is a part of a collection */
0N/A int fontIndex = 0;
0N/A
0N/A /* Number of fonts in this collection. ==1 if not a collection */
0N/A int directoryCount = 1;
0N/A
0N/A /* offset in file of table directory for this font */
0N/A int directoryOffset; // 12 if its not a collection.
0N/A
0N/A /* number of table entries in the directory/offsets table */
0N/A int numTables;
0N/A
0N/A /* The contents of the the directory/offsets table */
0N/A DirectoryEntry []tableDirectory;
0N/A
0N/A// protected byte []gposTable = null;
0N/A// protected byte []gdefTable = null;
0N/A// protected byte []gsubTable = null;
0N/A// protected byte []mortTable = null;
0N/A// protected boolean hintsTabledChecked = false;
0N/A// protected boolean containsHintsTable = false;
0N/A
0N/A /* These fields are set from os/2 table info. */
0N/A private boolean supportsJA;
0N/A private boolean supportsCJK;
0N/A
1284N/A /* These are for faster access to the name of the font as
1284N/A * typically exposed via API to applications.
1284N/A */
1284N/A private Locale nameLocale;
1284N/A private String localeFamilyName;
1284N/A private String localeFullName;
1284N/A
0N/A /**
0N/A * - does basic verification of the file
0N/A * - reads the header table for this font (within a collection)
0N/A * - reads the names (full, family).
0N/A * - determines the style of the font.
0N/A * - initializes the CMAP
0N/A * @throws FontFormatException - if the font can't be opened
0N/A * or fails verification, or there's no usable cmap
0N/A */
1686N/A public TrueTypeFont(String platname, Object nativeNames, int fIndex,
0N/A boolean javaRasterizer)
0N/A throws FontFormatException {
0N/A super(platname, nativeNames);
0N/A useJavaRasterizer = javaRasterizer;
0N/A fontRank = Font2D.TTF_RANK;
1124N/A try {
1124N/A verify();
1124N/A init(fIndex);
1124N/A } catch (Throwable t) {
1124N/A close();
1124N/A if (t instanceof FontFormatException) {
1124N/A throw (FontFormatException)t;
1124N/A } else {
1124N/A throw new FontFormatException("Unexpected runtime exception.");
1124N/A }
1124N/A }
0N/A Disposer.addObjectRecord(this, disposerRecord);
0N/A }
0N/A
0N/A /* Enable natives just for fonts picked up from the platform that
0N/A * may have external bitmaps on Solaris. Could do this just for
0N/A * the fonts that are specified in font configuration files which
0N/A * would lighten the burden (think about that).
0N/A * The EBLCTag is used to skip natives for fonts that contain embedded
0N/A * bitmaps as there's no need to use X11 for those fonts.
0N/A * Skip all the latin fonts as they don't need this treatment.
0N/A * Further refine this to fonts that are natively accessible (ie
0N/A * as PCF bitmap fonts on the X11 font path).
0N/A * This method is called when creating the first strike for this font.
0N/A */
1686N/A @Override
0N/A protected boolean checkUseNatives() {
0N/A if (checkedNatives) {
0N/A return useNatives;
0N/A }
1686N/A if (!FontUtilities.isSolaris || useJavaRasterizer ||
1686N/A FontUtilities.useT2K || nativeNames == null ||
0N/A getDirectoryEntry(EBLCTag) != null ||
0N/A GraphicsEnvironment.isHeadless()) {
0N/A checkedNatives = true;
0N/A return false; /* useNatives is false */
0N/A } else if (nativeNames instanceof String) {
0N/A String name = (String)nativeNames;
0N/A /* Don't do do this for Latin fonts */
0N/A if (name.indexOf("8859") > 0) {
0N/A checkedNatives = true;
0N/A return false;
0N/A } else if (NativeFont.hasExternalBitmaps(name)) {
0N/A nativeFonts = new NativeFont[1];
0N/A try {
0N/A nativeFonts[0] = new NativeFont(name, true);
0N/A /* If reach here we have an non-latin font that has
0N/A * external bitmaps and we successfully created it.
0N/A */
0N/A useNatives = true;
0N/A } catch (FontFormatException e) {
0N/A nativeFonts = null;
0N/A }
0N/A }
0N/A } else if (nativeNames instanceof String[]) {
0N/A String[] natNames = (String[])nativeNames;
0N/A int numNames = natNames.length;
0N/A boolean externalBitmaps = false;
0N/A for (int nn = 0; nn < numNames; nn++) {
0N/A if (natNames[nn].indexOf("8859") > 0) {
0N/A checkedNatives = true;
0N/A return false;
0N/A } else if (NativeFont.hasExternalBitmaps(natNames[nn])) {
0N/A externalBitmaps = true;
0N/A }
0N/A }
0N/A if (!externalBitmaps) {
0N/A checkedNatives = true;
0N/A return false;
0N/A }
0N/A useNatives = true;
0N/A nativeFonts = new NativeFont[numNames];
0N/A for (int nn = 0; nn < numNames; nn++) {
0N/A try {
0N/A nativeFonts[nn] = new NativeFont(natNames[nn], true);
0N/A } catch (FontFormatException e) {
0N/A useNatives = false;
0N/A nativeFonts = null;
0N/A }
0N/A }
0N/A }
0N/A if (useNatives) {
0N/A glyphToCharMap = new char[getMapper().getNumGlyphs()];
0N/A }
0N/A checkedNatives = true;
0N/A return useNatives;
0N/A }
0N/A
0N/A
0N/A /* This is intended to be called, and the returned value used,
0N/A * from within a block synchronized on this font object.
0N/A * ie the channel returned may be nulled out at any time by "close()"
0N/A * unless the caller holds a lock.
0N/A * Deadlock warning: FontManager.addToPool(..) acquires a global lock,
0N/A * which means nested locks may be in effect.
0N/A */
0N/A private synchronized FileChannel open() throws FontFormatException {
0N/A if (disposerRecord.channel == null) {
1686N/A if (FontUtilities.isLogging()) {
1686N/A FontUtilities.getLogger().info("open TTF: " + platName);
0N/A }
0N/A try {
0N/A RandomAccessFile raf = (RandomAccessFile)
0N/A java.security.AccessController.doPrivileged(
0N/A new java.security.PrivilegedAction() {
0N/A public Object run() {
0N/A try {
0N/A return new RandomAccessFile(platName, "r");
0N/A } catch (FileNotFoundException ffne) {
0N/A }
0N/A return null;
0N/A }
0N/A });
0N/A disposerRecord.channel = raf.getChannel();
0N/A fileSize = (int)disposerRecord.channel.size();
1686N/A FontManager fm = FontManagerFactory.getInstance();
1686N/A if (fm instanceof SunFontManager) {
1686N/A ((SunFontManager) fm).addToPool(this);
1686N/A }
0N/A } catch (NullPointerException e) {
0N/A close();
0N/A throw new FontFormatException(e.toString());
0N/A } catch (ClosedChannelException e) {
0N/A /* NIO I/O is interruptible, recurse to retry operation.
0N/A * The call to channel.size() above can throw this exception.
0N/A * Clear interrupts before recursing in case NIO didn't.
0N/A * Note that close() sets disposerRecord.channel to null.
0N/A */
0N/A Thread.interrupted();
0N/A close();
0N/A open();
0N/A } catch (IOException e) {
0N/A close();
0N/A throw new FontFormatException(e.toString());
0N/A }
0N/A }
0N/A return disposerRecord.channel;
0N/A }
0N/A
0N/A protected synchronized void close() {
0N/A disposerRecord.dispose();
0N/A }
0N/A
0N/A
0N/A int readBlock(ByteBuffer buffer, int offset, int length) {
0N/A int bread = 0;
0N/A try {
0N/A synchronized (this) {
0N/A if (disposerRecord.channel == null) {
0N/A open();
0N/A }
0N/A if (offset + length > fileSize) {
0N/A if (offset >= fileSize) {
0N/A /* Since the caller ensures that offset is < fileSize
0N/A * this condition suggests that fileSize is now
0N/A * different than the value we originally provided
0N/A * to native when the scaler was created.
0N/A * Also fileSize is updated every time we
0N/A * open() the file here, but in native the value
0N/A * isn't updated. If the file has changed whilst we
0N/A * are executing we want to bail, not spin.
0N/A */
1686N/A if (FontUtilities.isLogging()) {
0N/A String msg = "Read offset is " + offset +
0N/A " file size is " + fileSize+
0N/A " file is " + platName;
1686N/A FontUtilities.getLogger().severe(msg);
0N/A }
0N/A return -1;
0N/A } else {
0N/A length = fileSize - offset;
0N/A }
0N/A }
0N/A buffer.clear();
0N/A disposerRecord.channel.position(offset);
0N/A while (bread < length) {
0N/A int cnt = disposerRecord.channel.read(buffer);
0N/A if (cnt == -1) {
0N/A String msg = "Unexpected EOF " + this;
0N/A int currSize = (int)disposerRecord.channel.size();
0N/A if (currSize != fileSize) {
0N/A msg += " File size was " + fileSize +
0N/A " and now is " + currSize;
0N/A }
1686N/A if (FontUtilities.isLogging()) {
1686N/A FontUtilities.getLogger().severe(msg);
0N/A }
0N/A // We could still flip() the buffer here because
0N/A // it's possible that we did read some data in
0N/A // an earlier loop, and we probably should
0N/A // return that to the caller. Although if
0N/A // the caller expected 8K of data and we return
0N/A // only a few bytes then maybe it's better instead to
0N/A // set bread = -1 to indicate failure.
0N/A // The following is therefore using arbitrary values
0N/A // but is meant to allow cases where enough
0N/A // data was read to probably continue.
0N/A if (bread > length/2 || bread > 16384) {
0N/A buffer.flip();
1686N/A if (FontUtilities.isLogging()) {
0N/A msg = "Returning " + bread +
0N/A " bytes instead of " + length;
1686N/A FontUtilities.getLogger().severe(msg);
0N/A }
0N/A } else {
0N/A bread = -1;
0N/A }
0N/A throw new IOException(msg);
0N/A }
0N/A bread += cnt;
0N/A }
0N/A buffer.flip();
0N/A if (bread > length) { // possible if buffer.size() > length
0N/A bread = length;
0N/A }
0N/A }
0N/A } catch (FontFormatException e) {
1686N/A if (FontUtilities.isLogging()) {
1696N/A FontUtilities.getLogger().severe(
0N/A "While reading " + platName, e);
0N/A }
0N/A bread = -1; // signal EOF
0N/A deregisterFontAndClearStrikeCache();
0N/A } catch (ClosedChannelException e) {
0N/A /* NIO I/O is interruptible, recurse to retry operation.
0N/A * Clear interrupts before recursing in case NIO didn't.
0N/A */
0N/A Thread.interrupted();
0N/A close();
0N/A return readBlock(buffer, offset, length);
0N/A } catch (IOException e) {
0N/A /* If we did not read any bytes at all and the exception is
0N/A * not a recoverable one (ie is not ClosedChannelException) then
0N/A * we should indicate that there is no point in re-trying.
0N/A * Other than an attempt to read past the end of the file it
0N/A * seems unlikely this would occur as problems opening the
0N/A * file are handled as a FontFormatException.
0N/A */
1686N/A if (FontUtilities.isLogging()) {
1696N/A FontUtilities.getLogger().severe(
0N/A "While reading " + platName, e);
0N/A }
0N/A if (bread == 0) {
0N/A bread = -1; // signal EOF
0N/A deregisterFontAndClearStrikeCache();
0N/A }
0N/A }
0N/A return bread;
0N/A }
0N/A
0N/A ByteBuffer readBlock(int offset, int length) {
0N/A
0N/A ByteBuffer buffer = ByteBuffer.allocate(length);
0N/A try {
0N/A synchronized (this) {
0N/A if (disposerRecord.channel == null) {
0N/A open();
0N/A }
0N/A if (offset + length > fileSize) {
0N/A if (offset > fileSize) {
0N/A return null; // assert?
0N/A } else {
0N/A buffer = ByteBuffer.allocate(fileSize-offset);
0N/A }
0N/A }
0N/A disposerRecord.channel.position(offset);
0N/A disposerRecord.channel.read(buffer);
0N/A buffer.flip();
0N/A }
0N/A } catch (FontFormatException e) {
0N/A return null;
0N/A } catch (ClosedChannelException e) {
0N/A /* NIO I/O is interruptible, recurse to retry operation.
0N/A * Clear interrupts before recursing in case NIO didn't.
0N/A */
0N/A Thread.interrupted();
0N/A close();
0N/A readBlock(buffer, offset, length);
0N/A } catch (IOException e) {
0N/A return null;
0N/A }
0N/A return buffer;
0N/A }
0N/A
0N/A /* This is used by native code which can't allocate a direct byte
0N/A * buffer because of bug 4845371. It, and references to it in native
0N/A * code in scalerMethods.c can be removed once that bug is fixed.
0N/A * 4845371 is now fixed but we'll keep this around as it doesn't cost
0N/A * us anything if its never used/called.
0N/A */
0N/A byte[] readBytes(int offset, int length) {
0N/A ByteBuffer buffer = readBlock(offset, length);
0N/A if (buffer.hasArray()) {
0N/A return buffer.array();
0N/A } else {
0N/A byte[] bufferBytes = new byte[buffer.limit()];
0N/A buffer.get(bufferBytes);
0N/A return bufferBytes;
0N/A }
0N/A }
0N/A
0N/A private void verify() throws FontFormatException {
0N/A open();
0N/A }
0N/A
0N/A /* sizes, in bytes, of TT/TTC header records */
0N/A private static final int TTCHEADERSIZE = 12;
0N/A private static final int DIRECTORYHEADERSIZE = 12;
0N/A private static final int DIRECTORYENTRYSIZE = 16;
0N/A
0N/A protected void init(int fIndex) throws FontFormatException {
0N/A int headerOffset = 0;
0N/A ByteBuffer buffer = readBlock(0, TTCHEADERSIZE);
0N/A try {
0N/A switch (buffer.getInt()) {
0N/A
0N/A case ttcfTag:
0N/A buffer.getInt(); // skip TTC version ID
0N/A directoryCount = buffer.getInt();
0N/A if (fIndex >= directoryCount) {
0N/A throw new FontFormatException("Bad collection index");
0N/A }
0N/A fontIndex = fIndex;
0N/A buffer = readBlock(TTCHEADERSIZE+4*fIndex, 4);
0N/A headerOffset = buffer.getInt();
0N/A break;
0N/A
0N/A case v1ttTag:
0N/A case trueTag:
752N/A case ottoTag:
0N/A break;
0N/A
0N/A default:
3784N/A throw new FontFormatException("Unsupported sfnt " +
3784N/A getPublicFileName());
0N/A }
0N/A
0N/A /* Now have the offset of this TT font (possibly within a TTC)
0N/A * After the TT version/scaler type field, is the short
0N/A * representing the number of tables in the table directory.
0N/A * The table directory begins at 12 bytes after the header.
0N/A * Each table entry is 16 bytes long (4 32-bit ints)
0N/A */
0N/A buffer = readBlock(headerOffset+4, 2);
0N/A numTables = buffer.getShort();
0N/A directoryOffset = headerOffset+DIRECTORYHEADERSIZE;
0N/A ByteBuffer bbuffer = readBlock(directoryOffset,
0N/A numTables*DIRECTORYENTRYSIZE);
0N/A IntBuffer ibuffer = bbuffer.asIntBuffer();
0N/A DirectoryEntry table;
0N/A tableDirectory = new DirectoryEntry[numTables];
0N/A for (int i=0; i<numTables;i++) {
0N/A tableDirectory[i] = table = new DirectoryEntry();
0N/A table.tag = ibuffer.get();
0N/A /* checksum */ ibuffer.get();
0N/A table.offset = ibuffer.get();
0N/A table.length = ibuffer.get();
0N/A if (table.offset + table.length > fileSize) {
0N/A throw new FontFormatException("bad table, tag="+table.tag);
0N/A }
0N/A }
0N/A initNames();
0N/A } catch (Exception e) {
1686N/A if (FontUtilities.isLogging()) {
1686N/A FontUtilities.getLogger().severe(e.toString());
0N/A }
0N/A if (e instanceof FontFormatException) {
0N/A throw (FontFormatException)e;
0N/A } else {
0N/A throw new FontFormatException(e.toString());
0N/A }
0N/A }
0N/A if (familyName == null || fullName == null) {
0N/A throw new FontFormatException("Font name not found");
0N/A }
0N/A /* The os2_Table is needed to gather some info, but we don't
0N/A * want to keep it around (as a field) so obtain it once and
0N/A * pass it to the code that needs it.
0N/A */
0N/A ByteBuffer os2_Table = getTableBuffer(os_2Tag);
0N/A setStyle(os2_Table);
0N/A setCJKSupport(os2_Table);
0N/A }
0N/A
0N/A /* The array index corresponds to a bit offset in the TrueType
0N/A * font's OS/2 compatibility table's code page ranges fields.
0N/A * These are two 32 bit unsigned int fields at offsets 78 and 82.
0N/A * We are only interested in determining if the font supports
0N/A * the windows encodings we expect as the default encoding in
0N/A * supported locales, so we only map the first of these fields.
0N/A */
0N/A static final String encoding_mapping[] = {
0N/A "cp1252", /* 0:Latin 1 */
0N/A "cp1250", /* 1:Latin 2 */
0N/A "cp1251", /* 2:Cyrillic */
0N/A "cp1253", /* 3:Greek */
0N/A "cp1254", /* 4:Turkish/Latin 5 */
0N/A "cp1255", /* 5:Hebrew */
0N/A "cp1256", /* 6:Arabic */
0N/A "cp1257", /* 7:Windows Baltic */
0N/A "", /* 8:reserved for alternate ANSI */
0N/A "", /* 9:reserved for alternate ANSI */
0N/A "", /* 10:reserved for alternate ANSI */
0N/A "", /* 11:reserved for alternate ANSI */
0N/A "", /* 12:reserved for alternate ANSI */
0N/A "", /* 13:reserved for alternate ANSI */
0N/A "", /* 14:reserved for alternate ANSI */
0N/A "", /* 15:reserved for alternate ANSI */
0N/A "ms874", /* 16:Thai */
0N/A "ms932", /* 17:JIS/Japanese */
0N/A "gbk", /* 18:PRC GBK Cp950 */
0N/A "ms949", /* 19:Korean Extended Wansung */
0N/A "ms950", /* 20:Chinese (Taiwan, Hongkong, Macau) */
0N/A "ms1361", /* 21:Korean Johab */
0N/A "", /* 22 */
0N/A "", /* 23 */
0N/A "", /* 24 */
0N/A "", /* 25 */
0N/A "", /* 26 */
0N/A "", /* 27 */
0N/A "", /* 28 */
0N/A "", /* 29 */
0N/A "", /* 30 */
0N/A "", /* 31 */
0N/A };
0N/A
0N/A /* This maps two letter language codes to a Windows code page.
0N/A * Note that eg Cp1252 (the first subarray) is not exactly the same as
0N/A * Latin-1 since Windows code pages are do not necessarily correspond.
0N/A * There are two codepages for zh and ko so if a font supports
0N/A * only one of these ranges then we need to distinguish based on
0N/A * country. So far this only seems to matter for zh.
0N/A * REMIND: Unicode locales such as Hindi do not have a code page so
0N/A * this whole mechansim needs to be revised to map languages to
0N/A * the Unicode ranges either when this fails, or as an additional
0N/A * validating test. Basing it on Unicode ranges should get us away
0N/A * from needing to map to this small and incomplete set of Windows
0N/A * code pages which looks odd on non-Windows platforms.
0N/A */
0N/A private static final String languages[][] = {
0N/A
0N/A /* cp1252/Latin 1 */
0N/A { "en", "ca", "da", "de", "es", "fi", "fr", "is", "it",
0N/A "nl", "no", "pt", "sq", "sv", },
0N/A
0N/A /* cp1250/Latin2 */
0N/A { "cs", "cz", "et", "hr", "hu", "nr", "pl", "ro", "sk",
0N/A "sl", "sq", "sr", },
0N/A
0N/A /* cp1251/Cyrillic */
0N/A { "bg", "mk", "ru", "sh", "uk" },
0N/A
0N/A /* cp1253/Greek*/
0N/A { "el" },
0N/A
0N/A /* cp1254/Turkish,Latin 5 */
0N/A { "tr" },
0N/A
0N/A /* cp1255/Hebrew */
0N/A { "he" },
0N/A
0N/A /* cp1256/Arabic */
0N/A { "ar" },
0N/A
0N/A /* cp1257/Windows Baltic */
0N/A { "et", "lt", "lv" },
0N/A
0N/A /* ms874/Thai */
0N/A { "th" },
0N/A
0N/A /* ms932/Japanese */
0N/A { "ja" },
0N/A
0N/A /* gbk/Chinese (PRC GBK Cp950) */
0N/A { "zh", "zh_CN", },
0N/A
0N/A /* ms949/Korean Extended Wansung */
0N/A { "ko" },
0N/A
0N/A /* ms950/Chinese (Taiwan, Hongkong, Macau) */
0N/A { "zh_HK", "zh_TW", },
0N/A
0N/A /* ms1361/Korean Johab */
0N/A { "ko" },
0N/A };
0N/A
0N/A private static final String codePages[] = {
0N/A "cp1252",
0N/A "cp1250",
0N/A "cp1251",
0N/A "cp1253",
0N/A "cp1254",
0N/A "cp1255",
0N/A "cp1256",
0N/A "cp1257",
0N/A "ms874",
0N/A "ms932",
0N/A "gbk",
0N/A "ms949",
0N/A "ms950",
0N/A "ms1361",
0N/A };
0N/A
0N/A private static String defaultCodePage = null;
0N/A static String getCodePage() {
0N/A
0N/A if (defaultCodePage != null) {
0N/A return defaultCodePage;
0N/A }
0N/A
1686N/A if (FontUtilities.isWindows) {
0N/A defaultCodePage =
0N/A (String)java.security.AccessController.doPrivileged(
0N/A new sun.security.action.GetPropertyAction("file.encoding"));
0N/A } else {
0N/A if (languages.length != codePages.length) {
0N/A throw new InternalError("wrong code pages array length");
0N/A }
0N/A Locale locale = sun.awt.SunToolkit.getStartupLocale();
0N/A
0N/A String language = locale.getLanguage();
0N/A if (language != null) {
0N/A if (language.equals("zh")) {
0N/A String country = locale.getCountry();
0N/A if (country != null) {
0N/A language = language + "_" + country;
0N/A }
0N/A }
0N/A for (int i=0; i<languages.length;i++) {
0N/A for (int l=0;l<languages[i].length; l++) {
0N/A if (language.equals(languages[i][l])) {
0N/A defaultCodePage = codePages[i];
0N/A return defaultCodePage;
0N/A }
0N/A }
0N/A }
0N/A }
0N/A }
0N/A if (defaultCodePage == null) {
0N/A defaultCodePage = "";
0N/A }
0N/A return defaultCodePage;
0N/A }
0N/A
0N/A /* Theoretically, reserved bits must not be set, include symbol bits */
0N/A public static final int reserved_bits1 = 0x80000000;
0N/A public static final int reserved_bits2 = 0x0000ffff;
1686N/A @Override
0N/A boolean supportsEncoding(String encoding) {
0N/A if (encoding == null) {
0N/A encoding = getCodePage();
0N/A }
0N/A if ("".equals(encoding)) {
0N/A return false;
0N/A }
0N/A
0N/A encoding = encoding.toLowerCase();
0N/A
0N/A /* java_props_md.c has a couple of special cases
0N/A * if language packs are installed. In these encodings the
0N/A * fontconfig files pick up different fonts :
0N/A * SimSun-18030 and MingLiU_HKSCS. Since these fonts will
0N/A * indicate they support the base encoding, we need to rewrite
0N/A * these encodings here before checking the map/array.
0N/A */
0N/A if (encoding.equals("gb18030")) {
0N/A encoding = "gbk";
0N/A } else if (encoding.equals("ms950_hkscs")) {
0N/A encoding = "ms950";
0N/A }
0N/A
0N/A ByteBuffer buffer = getTableBuffer(os_2Tag);
0N/A /* required info is at offsets 78 and 82 */
0N/A if (buffer == null || buffer.capacity() < 86) {
0N/A return false;
0N/A }
0N/A
0N/A int range1 = buffer.getInt(78); /* ulCodePageRange1 */
0N/A int range2 = buffer.getInt(82); /* ulCodePageRange2 */
0N/A
0N/A /* This test is too stringent for Arial on Solaris (and perhaps
0N/A * other fonts). Arial has at least one reserved bit set for an
0N/A * unknown reason.
0N/A */
0N/A// if (((range1 & reserved_bits1) | (range2 & reserved_bits2)) != 0) {
0N/A// return false;
0N/A// }
0N/A
0N/A for (int em=0; em<encoding_mapping.length; em++) {
0N/A if (encoding_mapping[em].equals(encoding)) {
0N/A if (((1 << em) & range1) != 0) {
0N/A return true;
0N/A }
0N/A }
0N/A }
0N/A return false;
0N/A }
0N/A
0N/A
0N/A /* Use info in the os_2Table to test CJK support */
0N/A private void setCJKSupport(ByteBuffer os2Table) {
0N/A /* required info is in ulong at offset 46 */
0N/A if (os2Table == null || os2Table.capacity() < 50) {
0N/A return;
0N/A }
0N/A int range2 = os2Table.getInt(46); /* ulUnicodeRange2 */
0N/A
0N/A /* Any of these bits set in the 32-63 range indicate a font with
0N/A * support for a CJK range. We aren't looking at some other bits
0N/A * in the 64-69 range such as half width forms as its unlikely a font
0N/A * would include those and none of these.
0N/A */
0N/A supportsCJK = ((range2 & 0x29bf0000) != 0);
0N/A
0N/A /* This should be generalised, but for now just need to know if
0N/A * Hiragana or Katakana ranges are supported by the font.
0N/A * In the 4 longs representing unicode ranges supported
0N/A * bits 49 & 50 indicate hiragana and katakana
0N/A * This is bits 17 & 18 in the 2nd ulong. If either is supported
0N/A * we presume this is a JA font.
0N/A */
0N/A supportsJA = ((range2 & 0x60000) != 0);
0N/A }
0N/A
0N/A boolean supportsJA() {
0N/A return supportsJA;
0N/A }
0N/A
0N/A ByteBuffer getTableBuffer(int tag) {
0N/A DirectoryEntry entry = null;
0N/A
0N/A for (int i=0;i<numTables;i++) {
0N/A if (tableDirectory[i].tag == tag) {
0N/A entry = tableDirectory[i];
0N/A break;
0N/A }
0N/A }
0N/A if (entry == null || entry.length == 0 ||
0N/A entry.offset+entry.length > fileSize) {
0N/A return null;
0N/A }
0N/A
0N/A int bread = 0;
0N/A ByteBuffer buffer = ByteBuffer.allocate(entry.length);
0N/A synchronized (this) {
0N/A try {
0N/A if (disposerRecord.channel == null) {
0N/A open();
0N/A }
0N/A disposerRecord.channel.position(entry.offset);
0N/A bread = disposerRecord.channel.read(buffer);
0N/A buffer.flip();
0N/A } catch (ClosedChannelException e) {
0N/A /* NIO I/O is interruptible, recurse to retry operation.
0N/A * Clear interrupts before recursing in case NIO didn't.
0N/A */
0N/A Thread.interrupted();
0N/A close();
0N/A return getTableBuffer(tag);
0N/A } catch (IOException e) {
0N/A return null;
0N/A } catch (FontFormatException e) {
0N/A return null;
0N/A }
0N/A
0N/A if (bread < entry.length) {
0N/A return null;
0N/A } else {
0N/A return buffer;
0N/A }
0N/A }
0N/A }
0N/A
0N/A /* NB: is it better to move declaration to Font2D? */
0N/A long getLayoutTableCache() {
0N/A try {
0N/A return getScaler().getLayoutTableCache();
0N/A } catch(FontScalerException fe) {
0N/A return 0L;
0N/A }
0N/A }
0N/A
1686N/A @Override
0N/A byte[] getTableBytes(int tag) {
0N/A ByteBuffer buffer = getTableBuffer(tag);
0N/A if (buffer == null) {
0N/A return null;
0N/A } else if (buffer.hasArray()) {
0N/A try {
0N/A return buffer.array();
0N/A } catch (Exception re) {
0N/A }
0N/A }
0N/A byte []data = new byte[getTableSize(tag)];
0N/A buffer.get(data);
0N/A return data;
0N/A }
0N/A
0N/A int getTableSize(int tag) {
0N/A for (int i=0;i<numTables;i++) {
0N/A if (tableDirectory[i].tag == tag) {
0N/A return tableDirectory[i].length;
0N/A }
0N/A }
0N/A return 0;
0N/A }
0N/A
0N/A int getTableOffset(int tag) {
0N/A for (int i=0;i<numTables;i++) {
0N/A if (tableDirectory[i].tag == tag) {
0N/A return tableDirectory[i].offset;
0N/A }
0N/A }
0N/A return 0;
0N/A }
0N/A
0N/A DirectoryEntry getDirectoryEntry(int tag) {
0N/A for (int i=0;i<numTables;i++) {
0N/A if (tableDirectory[i].tag == tag) {
0N/A return tableDirectory[i];
0N/A }
0N/A }
0N/A return null;
0N/A }
0N/A
301N/A /* Used to determine if this size has embedded bitmaps, which
301N/A * for CJK fonts should be used in preference to LCD glyphs.
301N/A */
301N/A boolean useEmbeddedBitmapsForSize(int ptSize) {
301N/A if (!supportsCJK) {
301N/A return false;
301N/A }
301N/A if (getDirectoryEntry(EBLCTag) == null) {
301N/A return false;
301N/A }
301N/A ByteBuffer eblcTable = getTableBuffer(EBLCTag);
301N/A int numSizes = eblcTable.getInt(4);
301N/A /* The bitmapSizeTable's start at offset of 8.
301N/A * Each bitmapSizeTable entry is 48 bytes.
301N/A * The offset of ppemY in the entry is 45.
301N/A */
301N/A for (int i=0;i<numSizes;i++) {
301N/A int ppemY = eblcTable.get(8+(i*48)+45) &0xff;
301N/A if (ppemY == ptSize) {
301N/A return true;
301N/A }
301N/A }
301N/A return false;
301N/A }
301N/A
0N/A public String getFullName() {
0N/A return fullName;
0N/A }
0N/A
0N/A /* This probably won't get called but is there to support the
0N/A * contract() of setStyle() defined in the superclass.
0N/A */
1686N/A @Override
0N/A protected void setStyle() {
0N/A setStyle(getTableBuffer(os_2Tag));
0N/A }
0N/A
0N/A /* TrueTypeFont can use the fsSelection fields of OS/2 table
0N/A * to determine the style. In the unlikely case that doesn't exist,
0N/A * can use macStyle in the 'head' table but simpler to
0N/A * fall back to super class algorithm of looking for well known string.
0N/A * A very few fonts don't specify this information, but I only
0N/A * came across one: Lucida Sans Thai Typewriter Oblique in
0N/A * /usr/openwin/lib/locale/th_TH/X11/fonts/TrueType/lucidai.ttf
0N/A * that explicitly specified the wrong value. It says its regular.
0N/A * I didn't find any fonts that were inconsistent (ie regular plus some
0N/A * other value).
0N/A */
0N/A private static final int fsSelectionItalicBit = 0x00001;
0N/A private static final int fsSelectionBoldBit = 0x00020;
0N/A private static final int fsSelectionRegularBit = 0x00040;
0N/A private void setStyle(ByteBuffer os_2Table) {
0N/A /* fsSelection is unsigned short at buffer offset 62 */
0N/A if (os_2Table == null || os_2Table.capacity() < 64) {
0N/A super.setStyle();
0N/A return;
0N/A }
0N/A int fsSelection = os_2Table.getChar(62) & 0xffff;
0N/A int italic = fsSelection & fsSelectionItalicBit;
0N/A int bold = fsSelection & fsSelectionBoldBit;
0N/A int regular = fsSelection & fsSelectionRegularBit;
0N/A// System.out.println("platname="+platName+" font="+fullName+
0N/A// " family="+familyName+
0N/A// " R="+regular+" I="+italic+" B="+bold);
0N/A if (regular!=0 && ((italic|bold)!=0)) {
0N/A /* This is inconsistent. Try using the font name algorithm */
0N/A super.setStyle();
0N/A return;
0N/A } else if ((regular|italic|bold) == 0) {
0N/A /* No style specified. Try using the font name algorithm */
0N/A super.setStyle();
0N/A return;
0N/A }
0N/A switch (bold|italic) {
0N/A case fsSelectionItalicBit:
0N/A style = Font.ITALIC;
0N/A break;
0N/A case fsSelectionBoldBit:
1686N/A if (FontUtilities.isSolaris && platName.endsWith("HG-GothicB.ttf")) {
0N/A /* Workaround for Solaris's use of a JA font that's marked as
0N/A * being designed bold, but is used as a PLAIN font.
0N/A */
0N/A style = Font.PLAIN;
0N/A } else {
0N/A style = Font.BOLD;
0N/A }
0N/A break;
0N/A case fsSelectionBoldBit|fsSelectionItalicBit:
0N/A style = Font.BOLD|Font.ITALIC;
0N/A }
0N/A }
0N/A
0N/A private float stSize, stPos, ulSize, ulPos;
0N/A
0N/A private void setStrikethroughMetrics(ByteBuffer os_2Table, int upem) {
0N/A if (os_2Table == null || os_2Table.capacity() < 30 || upem < 0) {
0N/A stSize = .05f;
0N/A stPos = -.4f;
0N/A return;
0N/A }
0N/A ShortBuffer sb = os_2Table.asShortBuffer();
0N/A stSize = sb.get(13) / (float)upem;
0N/A stPos = -sb.get(14) / (float)upem;
0N/A }
0N/A
0N/A private void setUnderlineMetrics(ByteBuffer postTable, int upem) {
0N/A if (postTable == null || postTable.capacity() < 12 || upem < 0) {
0N/A ulSize = .05f;
0N/A ulPos = .1f;
0N/A return;
0N/A }
0N/A ShortBuffer sb = postTable.asShortBuffer();
0N/A ulSize = sb.get(5) / (float)upem;
0N/A ulPos = -sb.get(4) / (float)upem;
0N/A }
0N/A
1686N/A @Override
0N/A public void getStyleMetrics(float pointSize, float[] metrics, int offset) {
1179N/A
1179N/A if (ulSize == 0f && ulPos == 0f) {
1179N/A
1179N/A ByteBuffer head_Table = getTableBuffer(headTag);
1179N/A int upem = -1;
1179N/A if (head_Table != null && head_Table.capacity() >= 18) {
1179N/A ShortBuffer sb = head_Table.asShortBuffer();
1179N/A upem = sb.get(9) & 0xffff;
1179N/A }
1179N/A
1179N/A ByteBuffer os2_Table = getTableBuffer(os_2Tag);
1179N/A setStrikethroughMetrics(os2_Table, upem);
1179N/A
1179N/A ByteBuffer post_Table = getTableBuffer(postTag);
1179N/A setUnderlineMetrics(post_Table, upem);
1179N/A }
1179N/A
0N/A metrics[offset] = stPos * pointSize;
0N/A metrics[offset+1] = stSize * pointSize;
1179N/A
0N/A metrics[offset+2] = ulPos * pointSize;
0N/A metrics[offset+3] = ulSize * pointSize;
0N/A }
0N/A
0N/A private String makeString(byte[] bytes, int len, short encoding) {
0N/A
0N/A /* Check for fonts using encodings 2->6 is just for
0N/A * some old DBCS fonts, apparently mostly on Solaris.
0N/A * Some of these fonts encode ascii names as double-byte characters.
0N/A * ie with a leading zero byte for what properly should be a
0N/A * single byte-char.
0N/A */
0N/A if (encoding >=2 && encoding <= 6) {
0N/A byte[] oldbytes = bytes;
0N/A int oldlen = len;
0N/A bytes = new byte[oldlen];
0N/A len = 0;
0N/A for (int i=0; i<oldlen; i++) {
0N/A if (oldbytes[i] != 0) {
0N/A bytes[len++] = oldbytes[i];
0N/A }
0N/A }
0N/A }
0N/A
0N/A String charset;
0N/A switch (encoding) {
0N/A case 1: charset = "UTF-16"; break; // most common case first.
0N/A case 0: charset = "UTF-16"; break; // symbol uses this
0N/A case 2: charset = "SJIS"; break;
0N/A case 3: charset = "GBK"; break;
0N/A case 4: charset = "MS950"; break;
0N/A case 5: charset = "EUC_KR"; break;
0N/A case 6: charset = "Johab"; break;
0N/A default: charset = "UTF-16"; break;
0N/A }
0N/A
0N/A try {
0N/A return new String(bytes, 0, len, charset);
0N/A } catch (UnsupportedEncodingException e) {
1686N/A if (FontUtilities.isLogging()) {
1686N/A FontUtilities.getLogger().warning(e + " EncodingID=" + encoding);
0N/A }
0N/A return new String(bytes, 0, len);
0N/A } catch (Throwable t) {
0N/A return null;
0N/A }
0N/A }
0N/A
0N/A protected void initNames() {
0N/A
0N/A byte[] name = new byte[256];
0N/A ByteBuffer buffer = getTableBuffer(nameTag);
0N/A
0N/A if (buffer != null) {
0N/A ShortBuffer sbuffer = buffer.asShortBuffer();
0N/A sbuffer.get(); // format - not needed.
0N/A short numRecords = sbuffer.get();
0N/A /* The name table uses unsigned shorts. Many of these
0N/A * are known small values that fit in a short.
0N/A * The values that are sizes or offsets into the table could be
0N/A * greater than 32767, so read and store those as ints
0N/A */
0N/A int stringPtr = sbuffer.get() & 0xffff;
1284N/A
1284N/A nameLocale = sun.awt.SunToolkit.getStartupLocale();
1686N/A short nameLocaleID = getLCIDFromLocale(nameLocale);
1284N/A
0N/A for (int i=0; i<numRecords; i++) {
0N/A short platformID = sbuffer.get();
0N/A if (platformID != MS_PLATFORM_ID) {
0N/A sbuffer.position(sbuffer.position()+5);
0N/A continue; // skip over this record.
0N/A }
0N/A short encodingID = sbuffer.get();
0N/A short langID = sbuffer.get();
0N/A short nameID = sbuffer.get();
0N/A int nameLen = ((int) sbuffer.get()) & 0xffff;
0N/A int namePtr = (((int) sbuffer.get()) & 0xffff) + stringPtr;
1284N/A String tmpName = null;
0N/A switch (nameID) {
0N/A
0N/A case FAMILY_NAME_ID:
0N/A
1284N/A if (familyName == null || langID == ENGLISH_LOCALE_ID ||
1284N/A langID == nameLocaleID)
1284N/A {
0N/A buffer.position(namePtr);
0N/A buffer.get(name, 0, nameLen);
1284N/A tmpName = makeString(name, nameLen, encodingID);
1284N/A
1284N/A if (familyName == null || langID == ENGLISH_LOCALE_ID){
1284N/A familyName = tmpName;
1284N/A }
1284N/A if (langID == nameLocaleID) {
1284N/A localeFamilyName = tmpName;
1284N/A }
0N/A }
0N/A/*
0N/A for (int ii=0;ii<nameLen;ii++) {
0N/A int val = (int)name[ii]&0xff;
0N/A System.err.print(Integer.toHexString(val)+ " ");
0N/A }
0N/A System.err.println();
0N/A System.err.println("familyName="+familyName +
0N/A " nameLen="+nameLen+
0N/A " langID="+langID+ " eid="+encodingID +
0N/A " str len="+familyName.length());
0N/A
0N/A*/
0N/A break;
0N/A
0N/A case FULL_NAME_ID:
0N/A
1284N/A if (fullName == null || langID == ENGLISH_LOCALE_ID ||
1284N/A langID == nameLocaleID)
1284N/A {
0N/A buffer.position(namePtr);
0N/A buffer.get(name, 0, nameLen);
1284N/A tmpName = makeString(name, nameLen, encodingID);
1284N/A
1284N/A if (fullName == null || langID == ENGLISH_LOCALE_ID) {
1284N/A fullName = tmpName;
1284N/A }
1284N/A if (langID == nameLocaleID) {
1284N/A localeFullName = tmpName;
1284N/A }
0N/A }
0N/A break;
0N/A }
0N/A }
1284N/A if (localeFamilyName == null) {
1284N/A localeFamilyName = familyName;
1284N/A }
1284N/A if (localeFullName == null) {
1284N/A localeFullName = fullName;
1284N/A }
0N/A }
0N/A }
0N/A
0N/A /* Return the requested name in the requested locale, for the
0N/A * MS platform ID. If the requested locale isn't found, return US
0N/A * English, if that isn't found, return null and let the caller
0N/A * figure out how to handle that.
0N/A */
0N/A protected String lookupName(short findLocaleID, int findNameID) {
0N/A String foundName = null;
0N/A byte[] name = new byte[1024];
0N/A
0N/A ByteBuffer buffer = getTableBuffer(nameTag);
0N/A if (buffer != null) {
0N/A ShortBuffer sbuffer = buffer.asShortBuffer();
0N/A sbuffer.get(); // format - not needed.
0N/A short numRecords = sbuffer.get();
0N/A
0N/A /* The name table uses unsigned shorts. Many of these
0N/A * are known small values that fit in a short.
0N/A * The values that are sizes or offsets into the table could be
0N/A * greater than 32767, so read and store those as ints
0N/A */
0N/A int stringPtr = ((int) sbuffer.get()) & 0xffff;
0N/A
0N/A for (int i=0; i<numRecords; i++) {
0N/A short platformID = sbuffer.get();
0N/A if (platformID != MS_PLATFORM_ID) {
0N/A sbuffer.position(sbuffer.position()+5);
0N/A continue; // skip over this record.
0N/A }
0N/A short encodingID = sbuffer.get();
0N/A short langID = sbuffer.get();
0N/A short nameID = sbuffer.get();
0N/A int nameLen = ((int) sbuffer.get()) & 0xffff;
0N/A int namePtr = (((int) sbuffer.get()) & 0xffff) + stringPtr;
0N/A if (nameID == findNameID &&
0N/A ((foundName == null && langID == ENGLISH_LOCALE_ID)
0N/A || langID == findLocaleID)) {
0N/A buffer.position(namePtr);
0N/A buffer.get(name, 0, nameLen);
0N/A foundName = makeString(name, nameLen, encodingID);
0N/A if (langID == findLocaleID) {
0N/A return foundName;
0N/A }
0N/A }
0N/A }
0N/A }
0N/A return foundName;
0N/A }
0N/A
0N/A /**
0N/A * @return number of logical fonts. Is "1" for all but TTC files
0N/A */
0N/A public int getFontCount() {
0N/A return directoryCount;
0N/A }
0N/A
0N/A protected synchronized FontScaler getScaler() {
0N/A if (scaler == null) {
1686N/A scaler = FontScaler.getScaler(this, fontIndex,
0N/A supportsCJK, fileSize);
0N/A }
0N/A return scaler;
0N/A }
0N/A
0N/A
0N/A /* Postscript name is rarely requested. Don't waste cycles locating it
0N/A * as part of font creation, nor storage to hold it. Get it only on demand.
0N/A */
1686N/A @Override
0N/A public String getPostscriptName() {
0N/A String name = lookupName(ENGLISH_LOCALE_ID, POSTSCRIPT_NAME_ID);
0N/A if (name == null) {
0N/A return fullName;
0N/A } else {
0N/A return name;
0N/A }
0N/A }
0N/A
1686N/A @Override
0N/A public String getFontName(Locale locale) {
0N/A if (locale == null) {
0N/A return fullName;
1284N/A } else if (locale.equals(nameLocale) && localeFullName != null) {
1284N/A return localeFullName;
0N/A } else {
1686N/A short localeID = getLCIDFromLocale(locale);
0N/A String name = lookupName(localeID, FULL_NAME_ID);
0N/A if (name == null) {
0N/A return fullName;
0N/A } else {
0N/A return name;
0N/A }
0N/A }
0N/A }
0N/A
1686N/A // Return a Microsoft LCID from the given Locale.
1686N/A // Used when getting localized font data.
1686N/A
1686N/A private static void addLCIDMapEntry(Map<String, Short> map,
1686N/A String key, short value) {
1686N/A map.put(key, Short.valueOf(value));
1686N/A }
1686N/A
1686N/A private static synchronized void createLCIDMap() {
1686N/A if (lcidMap != null) {
1686N/A return;
1686N/A }
1686N/A
1686N/A Map<String, Short> map = new HashMap<String, Short>(200);
1686N/A
1686N/A // the following statements are derived from the langIDMap
1686N/A // in src/windows/native/java/lang/java_props_md.c using the following
1686N/A // awk script:
1686N/A // $1~/\/\*/ { next}
1686N/A // $3~/\?\?/ { next }
1686N/A // $3!~/_/ { next }
1686N/A // $1~/0x0409/ { next }
1686N/A // $1~/0x0c0a/ { next }
1686N/A // $1~/0x042c/ { next }
1686N/A // $1~/0x0443/ { next }
1686N/A // $1~/0x0812/ { next }
1686N/A // $1~/0x04/ { print " addLCIDMapEntry(map, " substr($3, 0, 3) "\", (short) " substr($1, 0, 6) ");" ; next }
1686N/A // $3~/,/ { print " addLCIDMapEntry(map, " $3 " (short) " substr($1, 0, 6) ");" ; next }
1686N/A // { print " addLCIDMapEntry(map, " $3 ", (short) " substr($1, 0, 6) ");" ; next }
1686N/A // The lines of this script:
1686N/A // - eliminate comments
1686N/A // - eliminate questionable locales
1686N/A // - eliminate language-only locales
1686N/A // - eliminate the default LCID value
1686N/A // - eliminate a few other unneeded LCID values
1686N/A // - print language-only locale entries for x04* LCID values
1686N/A // (apparently Microsoft doesn't use language-only LCID values -
1686N/A // see http://www.microsoft.com/OpenType/otspec/name.htm
1686N/A // - print complete entries for all other LCID values
1686N/A // Run
1686N/A // awk -f awk-script langIDMap > statements
1686N/A addLCIDMapEntry(map, "ar", (short) 0x0401);
1686N/A addLCIDMapEntry(map, "bg", (short) 0x0402);
1686N/A addLCIDMapEntry(map, "ca", (short) 0x0403);
1686N/A addLCIDMapEntry(map, "zh", (short) 0x0404);
1686N/A addLCIDMapEntry(map, "cs", (short) 0x0405);
1686N/A addLCIDMapEntry(map, "da", (short) 0x0406);
1686N/A addLCIDMapEntry(map, "de", (short) 0x0407);
1686N/A addLCIDMapEntry(map, "el", (short) 0x0408);
1686N/A addLCIDMapEntry(map, "es", (short) 0x040a);
1686N/A addLCIDMapEntry(map, "fi", (short) 0x040b);
1686N/A addLCIDMapEntry(map, "fr", (short) 0x040c);
1686N/A addLCIDMapEntry(map, "iw", (short) 0x040d);
1686N/A addLCIDMapEntry(map, "hu", (short) 0x040e);
1686N/A addLCIDMapEntry(map, "is", (short) 0x040f);
1686N/A addLCIDMapEntry(map, "it", (short) 0x0410);
1686N/A addLCIDMapEntry(map, "ja", (short) 0x0411);
1686N/A addLCIDMapEntry(map, "ko", (short) 0x0412);
1686N/A addLCIDMapEntry(map, "nl", (short) 0x0413);
1686N/A addLCIDMapEntry(map, "no", (short) 0x0414);
1686N/A addLCIDMapEntry(map, "pl", (short) 0x0415);
1686N/A addLCIDMapEntry(map, "pt", (short) 0x0416);
1686N/A addLCIDMapEntry(map, "rm", (short) 0x0417);
1686N/A addLCIDMapEntry(map, "ro", (short) 0x0418);
1686N/A addLCIDMapEntry(map, "ru", (short) 0x0419);
1686N/A addLCIDMapEntry(map, "hr", (short) 0x041a);
1686N/A addLCIDMapEntry(map, "sk", (short) 0x041b);
1686N/A addLCIDMapEntry(map, "sq", (short) 0x041c);
1686N/A addLCIDMapEntry(map, "sv", (short) 0x041d);
1686N/A addLCIDMapEntry(map, "th", (short) 0x041e);
1686N/A addLCIDMapEntry(map, "tr", (short) 0x041f);
1686N/A addLCIDMapEntry(map, "ur", (short) 0x0420);
1686N/A addLCIDMapEntry(map, "in", (short) 0x0421);
1686N/A addLCIDMapEntry(map, "uk", (short) 0x0422);
1686N/A addLCIDMapEntry(map, "be", (short) 0x0423);
1686N/A addLCIDMapEntry(map, "sl", (short) 0x0424);
1686N/A addLCIDMapEntry(map, "et", (short) 0x0425);
1686N/A addLCIDMapEntry(map, "lv", (short) 0x0426);
1686N/A addLCIDMapEntry(map, "lt", (short) 0x0427);
1686N/A addLCIDMapEntry(map, "fa", (short) 0x0429);
1686N/A addLCIDMapEntry(map, "vi", (short) 0x042a);
1686N/A addLCIDMapEntry(map, "hy", (short) 0x042b);
1686N/A addLCIDMapEntry(map, "eu", (short) 0x042d);
1686N/A addLCIDMapEntry(map, "mk", (short) 0x042f);
1686N/A addLCIDMapEntry(map, "tn", (short) 0x0432);
1686N/A addLCIDMapEntry(map, "xh", (short) 0x0434);
1686N/A addLCIDMapEntry(map, "zu", (short) 0x0435);
1686N/A addLCIDMapEntry(map, "af", (short) 0x0436);
1686N/A addLCIDMapEntry(map, "ka", (short) 0x0437);
1686N/A addLCIDMapEntry(map, "fo", (short) 0x0438);
1686N/A addLCIDMapEntry(map, "hi", (short) 0x0439);
1686N/A addLCIDMapEntry(map, "mt", (short) 0x043a);
1686N/A addLCIDMapEntry(map, "se", (short) 0x043b);
1686N/A addLCIDMapEntry(map, "gd", (short) 0x043c);
1686N/A addLCIDMapEntry(map, "ms", (short) 0x043e);
1686N/A addLCIDMapEntry(map, "kk", (short) 0x043f);
1686N/A addLCIDMapEntry(map, "ky", (short) 0x0440);
1686N/A addLCIDMapEntry(map, "sw", (short) 0x0441);
1686N/A addLCIDMapEntry(map, "tt", (short) 0x0444);
1686N/A addLCIDMapEntry(map, "bn", (short) 0x0445);
1686N/A addLCIDMapEntry(map, "pa", (short) 0x0446);
1686N/A addLCIDMapEntry(map, "gu", (short) 0x0447);
1686N/A addLCIDMapEntry(map, "ta", (short) 0x0449);
1686N/A addLCIDMapEntry(map, "te", (short) 0x044a);
1686N/A addLCIDMapEntry(map, "kn", (short) 0x044b);
1686N/A addLCIDMapEntry(map, "ml", (short) 0x044c);
1686N/A addLCIDMapEntry(map, "mr", (short) 0x044e);
1686N/A addLCIDMapEntry(map, "sa", (short) 0x044f);
1686N/A addLCIDMapEntry(map, "mn", (short) 0x0450);
1686N/A addLCIDMapEntry(map, "cy", (short) 0x0452);
1686N/A addLCIDMapEntry(map, "gl", (short) 0x0456);
1686N/A addLCIDMapEntry(map, "dv", (short) 0x0465);
1686N/A addLCIDMapEntry(map, "qu", (short) 0x046b);
1686N/A addLCIDMapEntry(map, "mi", (short) 0x0481);
1686N/A addLCIDMapEntry(map, "ar_IQ", (short) 0x0801);
1686N/A addLCIDMapEntry(map, "zh_CN", (short) 0x0804);
1686N/A addLCIDMapEntry(map, "de_CH", (short) 0x0807);
1686N/A addLCIDMapEntry(map, "en_GB", (short) 0x0809);
1686N/A addLCIDMapEntry(map, "es_MX", (short) 0x080a);
1686N/A addLCIDMapEntry(map, "fr_BE", (short) 0x080c);
1686N/A addLCIDMapEntry(map, "it_CH", (short) 0x0810);
1686N/A addLCIDMapEntry(map, "nl_BE", (short) 0x0813);
1686N/A addLCIDMapEntry(map, "no_NO_NY", (short) 0x0814);
1686N/A addLCIDMapEntry(map, "pt_PT", (short) 0x0816);
1686N/A addLCIDMapEntry(map, "ro_MD", (short) 0x0818);
1686N/A addLCIDMapEntry(map, "ru_MD", (short) 0x0819);
1686N/A addLCIDMapEntry(map, "sr_CS", (short) 0x081a);
1686N/A addLCIDMapEntry(map, "sv_FI", (short) 0x081d);
1686N/A addLCIDMapEntry(map, "az_AZ", (short) 0x082c);
1686N/A addLCIDMapEntry(map, "se_SE", (short) 0x083b);
1686N/A addLCIDMapEntry(map, "ga_IE", (short) 0x083c);
1686N/A addLCIDMapEntry(map, "ms_BN", (short) 0x083e);
1686N/A addLCIDMapEntry(map, "uz_UZ", (short) 0x0843);
1686N/A addLCIDMapEntry(map, "qu_EC", (short) 0x086b);
1686N/A addLCIDMapEntry(map, "ar_EG", (short) 0x0c01);
1686N/A addLCIDMapEntry(map, "zh_HK", (short) 0x0c04);
1686N/A addLCIDMapEntry(map, "de_AT", (short) 0x0c07);
1686N/A addLCIDMapEntry(map, "en_AU", (short) 0x0c09);
1686N/A addLCIDMapEntry(map, "fr_CA", (short) 0x0c0c);
1686N/A addLCIDMapEntry(map, "sr_CS", (short) 0x0c1a);
1686N/A addLCIDMapEntry(map, "se_FI", (short) 0x0c3b);
1686N/A addLCIDMapEntry(map, "qu_PE", (short) 0x0c6b);
1686N/A addLCIDMapEntry(map, "ar_LY", (short) 0x1001);
1686N/A addLCIDMapEntry(map, "zh_SG", (short) 0x1004);
1686N/A addLCIDMapEntry(map, "de_LU", (short) 0x1007);
1686N/A addLCIDMapEntry(map, "en_CA", (short) 0x1009);
1686N/A addLCIDMapEntry(map, "es_GT", (short) 0x100a);
1686N/A addLCIDMapEntry(map, "fr_CH", (short) 0x100c);
1686N/A addLCIDMapEntry(map, "hr_BA", (short) 0x101a);
1686N/A addLCIDMapEntry(map, "ar_DZ", (short) 0x1401);
1686N/A addLCIDMapEntry(map, "zh_MO", (short) 0x1404);
1686N/A addLCIDMapEntry(map, "de_LI", (short) 0x1407);
1686N/A addLCIDMapEntry(map, "en_NZ", (short) 0x1409);
1686N/A addLCIDMapEntry(map, "es_CR", (short) 0x140a);
1686N/A addLCIDMapEntry(map, "fr_LU", (short) 0x140c);
1686N/A addLCIDMapEntry(map, "bs_BA", (short) 0x141a);
1686N/A addLCIDMapEntry(map, "ar_MA", (short) 0x1801);
1686N/A addLCIDMapEntry(map, "en_IE", (short) 0x1809);
1686N/A addLCIDMapEntry(map, "es_PA", (short) 0x180a);
1686N/A addLCIDMapEntry(map, "fr_MC", (short) 0x180c);
1686N/A addLCIDMapEntry(map, "sr_BA", (short) 0x181a);
1686N/A addLCIDMapEntry(map, "ar_TN", (short) 0x1c01);
1686N/A addLCIDMapEntry(map, "en_ZA", (short) 0x1c09);
1686N/A addLCIDMapEntry(map, "es_DO", (short) 0x1c0a);
1686N/A addLCIDMapEntry(map, "sr_BA", (short) 0x1c1a);
1686N/A addLCIDMapEntry(map, "ar_OM", (short) 0x2001);
1686N/A addLCIDMapEntry(map, "en_JM", (short) 0x2009);
1686N/A addLCIDMapEntry(map, "es_VE", (short) 0x200a);
1686N/A addLCIDMapEntry(map, "ar_YE", (short) 0x2401);
1686N/A addLCIDMapEntry(map, "es_CO", (short) 0x240a);
1686N/A addLCIDMapEntry(map, "ar_SY", (short) 0x2801);
1686N/A addLCIDMapEntry(map, "en_BZ", (short) 0x2809);
1686N/A addLCIDMapEntry(map, "es_PE", (short) 0x280a);
1686N/A addLCIDMapEntry(map, "ar_JO", (short) 0x2c01);
1686N/A addLCIDMapEntry(map, "en_TT", (short) 0x2c09);
1686N/A addLCIDMapEntry(map, "es_AR", (short) 0x2c0a);
1686N/A addLCIDMapEntry(map, "ar_LB", (short) 0x3001);
1686N/A addLCIDMapEntry(map, "en_ZW", (short) 0x3009);
1686N/A addLCIDMapEntry(map, "es_EC", (short) 0x300a);
1686N/A addLCIDMapEntry(map, "ar_KW", (short) 0x3401);
1686N/A addLCIDMapEntry(map, "en_PH", (short) 0x3409);
1686N/A addLCIDMapEntry(map, "es_CL", (short) 0x340a);
1686N/A addLCIDMapEntry(map, "ar_AE", (short) 0x3801);
1686N/A addLCIDMapEntry(map, "es_UY", (short) 0x380a);
1686N/A addLCIDMapEntry(map, "ar_BH", (short) 0x3c01);
1686N/A addLCIDMapEntry(map, "es_PY", (short) 0x3c0a);
1686N/A addLCIDMapEntry(map, "ar_QA", (short) 0x4001);
1686N/A addLCIDMapEntry(map, "es_BO", (short) 0x400a);
1686N/A addLCIDMapEntry(map, "es_SV", (short) 0x440a);
1686N/A addLCIDMapEntry(map, "es_HN", (short) 0x480a);
1686N/A addLCIDMapEntry(map, "es_NI", (short) 0x4c0a);
1686N/A addLCIDMapEntry(map, "es_PR", (short) 0x500a);
1686N/A
1686N/A lcidMap = map;
1686N/A }
1686N/A
1686N/A private static short getLCIDFromLocale(Locale locale) {
1686N/A // optimize for common case
1686N/A if (locale.equals(Locale.US)) {
1686N/A return US_LCID;
1686N/A }
1686N/A
1686N/A if (lcidMap == null) {
1686N/A createLCIDMap();
1686N/A }
1686N/A
1686N/A String key = locale.toString();
1686N/A while (!"".equals(key)) {
1686N/A Short lcidObject = (Short) lcidMap.get(key);
1686N/A if (lcidObject != null) {
1686N/A return lcidObject.shortValue();
1686N/A }
1686N/A int pos = key.lastIndexOf('_');
1686N/A if (pos < 1) {
1686N/A return US_LCID;
1686N/A }
1686N/A key = key.substring(0, pos);
1686N/A }
1686N/A
1686N/A return US_LCID;
1686N/A }
1686N/A
1686N/A @Override
0N/A public String getFamilyName(Locale locale) {
0N/A if (locale == null) {
0N/A return familyName;
1284N/A } else if (locale.equals(nameLocale) && localeFamilyName != null) {
1284N/A return localeFamilyName;
0N/A } else {
1686N/A short localeID = getLCIDFromLocale(locale);
0N/A String name = lookupName(localeID, FAMILY_NAME_ID);
0N/A if (name == null) {
1686N/A return familyName;
0N/A } else {
0N/A return name;
0N/A }
0N/A }
0N/A }
0N/A
0N/A public CharToGlyphMapper getMapper() {
0N/A if (mapper == null) {
0N/A mapper = new TrueTypeGlyphMapper(this);
0N/A }
0N/A return mapper;
0N/A }
0N/A
0N/A /* This duplicates initNames() but that has to run fast as its used
0N/A * during typical start-up and the information here is likely never
0N/A * needed.
0N/A */
0N/A protected void initAllNames(int requestedID, HashSet names) {
0N/A
0N/A byte[] name = new byte[256];
0N/A ByteBuffer buffer = getTableBuffer(nameTag);
0N/A
0N/A if (buffer != null) {
0N/A ShortBuffer sbuffer = buffer.asShortBuffer();
0N/A sbuffer.get(); // format - not needed.
0N/A short numRecords = sbuffer.get();
0N/A
0N/A /* The name table uses unsigned shorts. Many of these
0N/A * are known small values that fit in a short.
0N/A * The values that are sizes or offsets into the table could be
0N/A * greater than 32767, so read and store those as ints
0N/A */
0N/A int stringPtr = ((int) sbuffer.get()) & 0xffff;
0N/A for (int i=0; i<numRecords; i++) {
0N/A short platformID = sbuffer.get();
0N/A if (platformID != MS_PLATFORM_ID) {
0N/A sbuffer.position(sbuffer.position()+5);
0N/A continue; // skip over this record.
0N/A }
0N/A short encodingID = sbuffer.get();
0N/A short langID = sbuffer.get();
0N/A short nameID = sbuffer.get();
0N/A int nameLen = ((int) sbuffer.get()) & 0xffff;
0N/A int namePtr = (((int) sbuffer.get()) & 0xffff) + stringPtr;
0N/A
0N/A if (nameID == requestedID) {
0N/A buffer.position(namePtr);
0N/A buffer.get(name, 0, nameLen);
0N/A names.add(makeString(name, nameLen, encodingID));
0N/A }
0N/A }
0N/A }
0N/A }
0N/A
0N/A String[] getAllFamilyNames() {
0N/A HashSet aSet = new HashSet();
0N/A try {
0N/A initAllNames(FAMILY_NAME_ID, aSet);
0N/A } catch (Exception e) {
0N/A /* In case of malformed font */
0N/A }
0N/A return (String[])aSet.toArray(new String[0]);
0N/A }
0N/A
0N/A String[] getAllFullNames() {
0N/A HashSet aSet = new HashSet();
0N/A try {
0N/A initAllNames(FULL_NAME_ID, aSet);
0N/A } catch (Exception e) {
0N/A /* In case of malformed font */
0N/A }
0N/A return (String[])aSet.toArray(new String[0]);
0N/A }
0N/A
0N/A /* Used by the OpenType engine for mark positioning.
0N/A */
1686N/A @Override
0N/A Point2D.Float getGlyphPoint(long pScalerContext,
0N/A int glyphCode, int ptNumber) {
0N/A try {
0N/A return getScaler().getGlyphPoint(pScalerContext,
0N/A glyphCode, ptNumber);
0N/A } catch(FontScalerException fe) {
0N/A return null;
0N/A }
0N/A }
0N/A
0N/A private char[] gaspTable;
0N/A
0N/A private char[] getGaspTable() {
0N/A
0N/A if (gaspTable != null) {
0N/A return gaspTable;
0N/A }
0N/A
0N/A ByteBuffer buffer = getTableBuffer(gaspTag);
0N/A if (buffer == null) {
0N/A return gaspTable = new char[0];
0N/A }
0N/A
0N/A CharBuffer cbuffer = buffer.asCharBuffer();
0N/A char format = cbuffer.get();
0N/A /* format "1" has appeared for some Windows Vista fonts.
0N/A * Its presently undocumented but the existing values
0N/A * seem to be still valid so we can use it.
0N/A */
0N/A if (format > 1) { // unrecognised format
0N/A return gaspTable = new char[0];
0N/A }
0N/A
0N/A char numRanges = cbuffer.get();
0N/A if (4+numRanges*4 > getTableSize(gaspTag)) { // sanity check
0N/A return gaspTable = new char[0];
0N/A }
0N/A gaspTable = new char[2*numRanges];
0N/A cbuffer.get(gaspTable);
0N/A return gaspTable;
0N/A }
0N/A
0N/A /* This is to obtain info from the TT 'gasp' (grid-fitting and
0N/A * scan-conversion procedure) table which specifies three combinations:
0N/A * Hint, Smooth (greyscale), Hint and Smooth.
0N/A * In this simplified scheme we don't distinguish the latter two. We
0N/A * hint even at small sizes, so as to preserve metrics consistency.
0N/A * If the information isn't available default values are substituted.
0N/A * The more precise defaults we'd do if we distinguished the cases are:
0N/A * Bold (no other style) fonts :
0N/A * 0-8 : Smooth ( do grey)
0N/A * 9+ : Hint + smooth (gridfit + grey)
0N/A * Plain, Italic and Bold-Italic fonts :
0N/A * 0-8 : Smooth ( do grey)
0N/A * 9-17 : Hint (gridfit)
0N/A * 18+ : Hint + smooth (gridfit + grey)
0N/A * The defaults should rarely come into play as most TT fonts provide
0N/A * better defaults.
0N/A * REMIND: consider unpacking the table into an array of booleans
0N/A * for faster use.
0N/A */
1686N/A @Override
0N/A public boolean useAAForPtSize(int ptsize) {
0N/A
0N/A char[] gasp = getGaspTable();
0N/A if (gasp.length > 0) {
0N/A for (int i=0;i<gasp.length;i+=2) {
0N/A if (ptsize <= gasp[i]) {
0N/A return ((gasp[i+1] & 0x2) != 0); // bit 2 means DO_GRAY;
0N/A }
0N/A }
0N/A return true;
0N/A }
0N/A
0N/A if (style == Font.BOLD) {
0N/A return true;
0N/A } else {
0N/A return ptsize <= 8 || ptsize >= 18;
0N/A }
0N/A }
0N/A
1686N/A @Override
0N/A public boolean hasSupplementaryChars() {
0N/A return ((TrueTypeGlyphMapper)getMapper()).hasSupplementaryChars();
0N/A }
0N/A
1686N/A @Override
0N/A public String toString() {
0N/A return "** TrueType Font: Family="+familyName+ " Name="+fullName+
3784N/A " style="+style+" fileName="+getPublicFileName();
0N/A }
0N/A}