| /* | |
| * Copyright (c) 2003, 2021, 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.util.Locale; | |
| import static java.nio.charset.StandardCharsets.UTF_8; | |
| /* | |
| * 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 final 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.contains("bold") || | |
| tmpWeight.contains("demi")) { | |
| style |= Font.BOLD; | |
| styleStr = "Bold"; | |
| } | |
| if (tmpSlant.equals("i") || | |
| tmpSlant.contains("italic")) { | |
| style |= Font.ITALIC; | |
| if (styleStr == null) { | |
| styleStr = "Italic"; | |
| } else { | |
| styleStr = styleStr + " Italic"; | |
| } | |
| } | |
| else if (tmpSlant.equals("o") || | |
| tmpSlant.contains("oblique")) { | |
| 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.contains("fontspecific")) { | |
| if (tmpFamily.contains("dingbats")) { | |
| encoding = "dingbats"; | |
| } else if (tmpFamily.contains("symbol")) { | |
| 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 scalable fonts, so we can just replace the 0's | |
| * with *'s and see what we get back | |
| * No matches means even the scalable 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 scalable 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 scalable | |
| * 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(); | |
| return haveBitmapFonts(xlfd.getBytes(UTF_8)); | |
| } | |
| public static boolean fontExists(String xlfd) { | |
| return fontExists(xlfd.getBytes(UTF_8)); | |
| } | |
| private static native boolean haveBitmapFonts(byte[] xlfd); | |
| private static native boolean fontExists(byte[] xlfd); | |
| @Override | |
| 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; | |
| } | |
| @Override | |
| 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; | |
| } | |
| @Override | |
| native StrikeMetrics getFontMetrics(long pScalerContext); | |
| @Override | |
| native float getGlyphAdvance(long pContext, int glyphCode); | |
| @Override | |
| Rectangle2D.Float getGlyphOutlineBounds(long pScalerContext, | |
| int glyphCode) { | |
| return new Rectangle2D.Float(0f, 0f, 0f, 0f); | |
| } | |
| @Override | |
| public GeneralPath getGlyphOutline(long pScalerContext, | |
| int glyphCode, | |
| float x, | |
| float y) { | |
| return null; | |
| } | |
| @Override | |
| native long getGlyphImage(long pScalerContext, int glyphCode); | |
| native long getGlyphImageNoDefault(long pScalerContext, int glyphCode); | |
| @Override | |
| void getGlyphMetrics(long pScalerContext, int glyphCode, | |
| Point2D.Float metrics) { | |
| throw new RuntimeException("this should be called on the strike"); | |
| } | |
| @Override | |
| public GeneralPath getGlyphVectorOutline(long pScalerContext, | |
| int[] glyphs, int numGlyphs, | |
| float x, float y) { | |
| return null; | |
| } | |
| private native int countGlyphs(byte[] platformNameBytes, int ptSize); | |
| @Override | |
| 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(); | |
| return xlfd.getBytes(UTF_8); | |
| } | |
| @Override | |
| public String toString() { | |
| return " ** Native Font: Family="+familyName+ " Name="+fullName+ | |
| " style="+style+" nativeName="+platName; | |
| } | |
| } |