| /* |
| * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package sun.font; |
| |
| import java.awt.Font; |
| import java.awt.FontFormatException; |
| import java.awt.GraphicsEnvironment; |
| import java.awt.font.FontRenderContext; |
| import java.awt.geom.GeneralPath; |
| import java.awt.geom.Point2D; |
| import java.awt.geom.Rectangle2D; |
| import java.io.UnsupportedEncodingException; |
| import java.lang.ref.WeakReference; |
| import java.util.Locale; |
| |
| /* |
| * Ideally there would be no native fonts used, and this class would be |
| * unneeded and removed. Presently it is still needed until such time |
| * as font configuration files (or the implementation equivalent) can have |
| * all references to fonts that are not handled via Java 2D removed. |
| * Currently there are two cases where this class is needed, both on |
| * Unix, primarily Solaris, but useful on Linux too if fonts have moved. |
| * 1. Some legacy F3 fonts are still referenced so that AWT "X/Motif" |
| * can get dingbats and symbols from them. This can be dispensed with when |
| * either AWT is based on 2D, or when the X font path is known to always |
| * contain a Type1 or TrueType font that can be used in font configuration |
| * files to replace the F3 fonts. |
| * 2. When location of font files by 2D fails, because of some system |
| * configuration problem, it is desirable to have a fall back to some |
| * functionality that lessens the immediate impact on users. Being able |
| * to perform limited operations by using bitmaps from X11 helps here. |
| */ |
| |
| public class NativeFont extends PhysicalFont { |
| |
| String encoding; |
| |
| private int numGlyphs = -1; |
| boolean isBitmapDelegate; |
| PhysicalFont delegateFont; |
| |
| /** |
| * Verifies native font is accessible. |
| * @throws FontFormatException if the font can't be located. |
| */ |
| public NativeFont(String platName, boolean bitmapDelegate) |
| throws FontFormatException { |
| super(platName, null); |
| |
| /* This is set true if this is an instance of a NativeFont |
| * created by some other font, to get native bitmaps. |
| * The delegating font will call this font only for "basic" |
| * cases - ie non-rotated, uniform scale, monochrome bitmaps. |
| * If this is false, then this instance may need to itself |
| * delegate to another font for non-basic cases. Since |
| * NativeFonts are used in that way only for symbol and dingbats |
| * we know its safe to delegate these to the JRE's default |
| * physical font (Lucida Sans Regular). |
| */ |
| isBitmapDelegate = bitmapDelegate; |
| |
| if (GraphicsEnvironment.isHeadless()) { |
| throw new FontFormatException("Native font in headless toolkit"); |
| } |
| fontRank = Font2D.NATIVE_RANK; |
| initNames(); |
| if (getNumGlyphs() == 0) { |
| throw new FontFormatException("Couldn't locate font" + platName); |
| } |
| } |
| |
| private void initNames() throws FontFormatException { |
| /* Valid XLFD has exactly 14 "-" chars. |
| * First run over the string to verify have at least this many |
| * At the same time record the locations of the hyphens |
| * so we can just pick the right substring later on |
| */ |
| int[] hPos = new int[14]; |
| int hyphenCnt = 1; |
| int pos = 1; |
| |
| String xlfd = platName.toLowerCase(Locale.ENGLISH); |
| if (xlfd.startsWith("-")) { |
| while (pos != -1 && hyphenCnt < 14) { |
| pos = xlfd.indexOf('-', pos); |
| if (pos != -1) { |
| hPos[hyphenCnt++] = pos; |
| pos++; |
| } |
| } |
| } |
| |
| if (hyphenCnt == 14 && pos != -1) { |
| |
| /* Capitalise words in the Family name */ |
| String tmpFamily = xlfd.substring(hPos[1]+1, hPos[2]); |
| StringBuilder sBuffer = new StringBuilder(tmpFamily); |
| char ch = Character.toUpperCase(sBuffer.charAt(0)); |
| sBuffer.replace(0, 1, String.valueOf(ch)); |
| for (int i=1;i<sBuffer.length()-1; i++) { |
| if (sBuffer.charAt(i) == ' ') { |
| ch = Character.toUpperCase(sBuffer.charAt(i+1)); |
| sBuffer.replace(i+1, i+2, String.valueOf(ch)); |
| } |
| } |
| familyName = sBuffer.toString(); |
| |
| String tmpWeight = xlfd.substring(hPos[2]+1, hPos[3]); |
| String tmpSlant = xlfd.substring(hPos[3]+1, hPos[4]); |
| |
| String styleStr = null; |
| |
| if (tmpWeight.indexOf("bold") >= 0 || |
| tmpWeight.indexOf("demi") >= 0) { |
| style |= Font.BOLD; |
| styleStr = "Bold"; |
| } |
| |
| if (tmpSlant.equals("i") || |
| tmpSlant.indexOf("italic") >= 0) { |
| style |= Font.ITALIC; |
| |
| if (styleStr == null) { |
| styleStr = "Italic"; |
| } else { |
| styleStr = styleStr + " Italic"; |
| } |
| } |
| else if (tmpSlant.equals("o") || |
| tmpSlant.indexOf("oblique") >= 0) { |
| style |= Font.ITALIC; |
| if (styleStr == null) { |
| styleStr = "Oblique"; |
| } else { |
| styleStr = styleStr + " Oblique"; |
| } |
| } |
| |
| if (styleStr == null) { |
| fullName = familyName; |
| } else { |
| fullName = familyName + " " + styleStr; |
| } |
| |
| encoding = xlfd.substring(hPos[12]+1); |
| if (encoding.startsWith("-")) { |
| encoding = xlfd.substring(hPos[13]+1); |
| } |
| if (encoding.indexOf("fontspecific") >= 0) { |
| if (tmpFamily.indexOf("dingbats") >= 0) { |
| encoding = "dingbats"; |
| } else if (tmpFamily.indexOf("symbol") >= 0) { |
| encoding = "symbol"; |
| } else { |
| encoding = "iso8859-1"; |
| } |
| } |
| } else { |
| throw new FontFormatException("Bad native name " + platName); |
| // familyName = "Unknown"; |
| // fullName = "Unknown"; |
| // style = Font.PLAIN; |
| // encoding = "iso8859-1"; |
| } |
| } |
| |
| /* Wildcard all the size fields in the XLFD and retrieve a list of |
| * XLFD's that match. |
| * We only look for scaleable fonts, so we can just replace the 0's |
| * with *'s and see what we get back |
| * No matches means even the scaleable version wasn't found. This is |
| * means the X font path isn't set up for this font at all. |
| * One match means only the scaleable version we started with was found |
| * -monotype-arial-bold-i-normal--0-0-0-0-p-0-iso8859-1 |
| * Two matches apparently means as well as the above, a scaleable |
| * specified for 72 dpi is found, not that there are bitmaps : eg |
| * -monotype-arial-bold-i-normal--0-0-72-72-p-0-iso8859-1 |
| * So require at least 3 matches (no need to parse) to determine that |
| * there are external bitmaps. |
| */ |
| static boolean hasExternalBitmaps(String platName) { |
| /* Turn -monotype-arial-bold-i-normal--0-0-0-0-p-0-iso8859-1 |
| * into -monotype-arial-bold-i-normal--*-*-*-*-p-*-iso8859-1 |
| * by replacing all -0- substrings with -*- |
| */ |
| StringBuilder sb = new StringBuilder(platName); |
| int pos = sb.indexOf("-0-"); |
| while (pos >=0) { |
| sb.replace(pos+1, pos+2, "*"); |
| pos = sb.indexOf("-0-", pos); |
| }; |
| String xlfd = sb.toString(); |
| byte[] bytes = null; |
| try { |
| bytes = xlfd.getBytes("UTF-8"); |
| } catch (UnsupportedEncodingException e) { |
| bytes = xlfd.getBytes(); |
| } |
| return haveBitmapFonts(bytes); |
| } |
| |
| public static boolean fontExists(String xlfd) { |
| byte[] bytes = null; |
| try { |
| bytes = xlfd.getBytes("UTF-8"); |
| } catch (UnsupportedEncodingException e) { |
| bytes = xlfd.getBytes(); |
| } |
| return fontExists(bytes); |
| } |
| |
| private static native boolean haveBitmapFonts(byte[] xlfd); |
| private static native boolean fontExists(byte[] xlfd); |
| |
| public CharToGlyphMapper getMapper() { |
| if (mapper == null) { |
| if (isBitmapDelegate) { |
| /* we are a delegate */ |
| mapper = new NativeGlyphMapper(this); |
| } else { |
| /* we need to delegate */ |
| SunFontManager fm = SunFontManager.getInstance(); |
| delegateFont = fm.getDefaultPhysicalFont(); |
| mapper = delegateFont.getMapper(); |
| } |
| } |
| return mapper; |
| } |
| |
| FontStrike createStrike(FontStrikeDesc desc) { |
| if (isBitmapDelegate) { |
| return new NativeStrike(this, desc); |
| } else { |
| if (delegateFont == null) { |
| SunFontManager fm = SunFontManager.getInstance(); |
| delegateFont = fm.getDefaultPhysicalFont(); |
| } |
| /* If no FileFont's are found, delegate font may be |
| * a NativeFont, so we need to avoid recursing here. |
| */ |
| if (delegateFont instanceof NativeFont) { |
| return new NativeStrike((NativeFont)delegateFont, desc); |
| } |
| FontStrike delegate = delegateFont.createStrike(desc); |
| return new DelegateStrike(this, desc, delegate); |
| } |
| } |
| |
| public Rectangle2D getMaxCharBounds(FontRenderContext frc) { |
| return null; |
| } |
| |
| native StrikeMetrics getFontMetrics(long pScalerContext); |
| |
| native float getGlyphAdvance(long pContext, int glyphCode); |
| |
| Rectangle2D.Float getGlyphOutlineBounds(long pScalerContext, |
| int glyphCode) { |
| return new Rectangle2D.Float(0f, 0f, 0f, 0f); |
| } |
| |
| public GeneralPath getGlyphOutline(long pScalerContext, |
| int glyphCode, |
| float x, |
| float y) { |
| return null; |
| } |
| |
| native long getGlyphImage(long pScalerContext, int glyphCode); |
| |
| native long getGlyphImageNoDefault(long pScalerContext, int glyphCode); |
| |
| void getGlyphMetrics(long pScalerContext, int glyphCode, |
| Point2D.Float metrics) { |
| throw new RuntimeException("this should be called on the strike"); |
| } |
| |
| public GeneralPath getGlyphVectorOutline(long pScalerContext, |
| int[] glyphs, int numGlyphs, |
| float x, float y) { |
| return null; |
| } |
| |
| private native int countGlyphs(byte[] platformNameBytes, int ptSize); |
| |
| public int getNumGlyphs() { |
| if (numGlyphs == -1) { |
| byte[] bytes = getPlatformNameBytes(8); |
| numGlyphs = countGlyphs(bytes, 8); |
| } |
| return numGlyphs; |
| } |
| |
| PhysicalFont getDelegateFont() { |
| if (delegateFont == null) { |
| SunFontManager fm = SunFontManager.getInstance(); |
| delegateFont = fm.getDefaultPhysicalFont(); |
| } |
| return delegateFont; |
| } |
| |
| /* Specify that the dpi is 72x72, as this corresponds to JDK's |
| * default user space. These are the 10th and 11th fields in the XLFD. |
| * ptSize in XLFD is in 10th's of a point so multiply by 10, |
| * Replace the 9th field in the XLFD (ie after the 8th hyphen) |
| * with this pt size (this corresponds to the field that's "%d" in the |
| * font configuration files). Wild card the other numeric fields. |
| * ie to request 12 pt Times New Roman italic font, use an XLFD like : |
| * -monotype-times new roman-regular-i---*-120-72-72-p-*-iso8859-1 |
| */ |
| @SuppressWarnings("cast") |
| byte[] getPlatformNameBytes(int ptSize) { |
| int[] hPos = new int[14]; |
| int hyphenCnt = 1; |
| int pos = 1; |
| |
| while (pos != -1 && hyphenCnt < 14) { |
| pos = platName.indexOf('-', pos); |
| if (pos != -1) { |
| hPos[hyphenCnt++] = pos; |
| pos++; |
| } |
| } |
| String sizeStr = Integer.toString((int)Math.abs(ptSize)*10); |
| StringBuilder sb = new StringBuilder(platName); |
| /* work backwards so as to not invalidate the positions. */ |
| sb.replace(hPos[11]+1, hPos[12], "*"); |
| |
| sb.replace(hPos[9]+1, hPos[10], "72"); |
| |
| sb.replace(hPos[8]+1, hPos[9], "72"); |
| |
| /* replace the 3 lines above with the next 3 lines to get the 1.4.2 |
| * behaviour |
| */ |
| // sb.replace(hPos[11]+1, hPos[12], "0"); |
| // sb.replace(hPos[9]+1, hPos[10], "0"); |
| // sb.replace(hPos[8]+1, hPos[9], "0"); |
| |
| sb.replace(hPos[7]+1, hPos[8], sizeStr); |
| |
| sb.replace(hPos[6]+1, hPos[7], "*"); |
| |
| /* replace the 1 line above with the next line to get the 1.4.2 |
| * behaviour |
| */ |
| // sb.replace(hPos[6]+1, hPos[7], "0"); |
| |
| /* comment out this block to the 1.4.2 behaviour */ |
| if (hPos[0] == 0 && hPos[1] == 1) { |
| /* null foundry name : some linux font configuration files have |
| * symbol font entries like this and its just plain wrong. |
| * Replace with a wild card. (Although those fonts should be |
| * located via disk access rather than X11). |
| */ |
| sb.replace(hPos[0]+1, hPos[1], "*"); |
| } |
| |
| String xlfd = sb.toString(); |
| byte[] bytes = null; |
| try { |
| bytes = xlfd.getBytes("UTF-8"); |
| } catch (UnsupportedEncodingException e) { |
| bytes = xlfd.getBytes(); |
| } |
| return bytes; |
| } |
| |
| public String toString() { |
| return " ** Native Font: Family="+familyName+ " Name="+fullName+ |
| " style="+style+" nativeName="+platName; |
| } |
| } |