Path: blob/master/src/java.desktop/share/classes/sun/awt/PlatformFont.java
41152 views
/*1* Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.awt;2627import java.awt.peer.FontPeer;28import java.util.Locale;29import java.util.Vector;30import sun.font.SunFontManager;31import sun.java2d.FontSupport;32import java.nio.CharBuffer;33import java.nio.ByteBuffer;3435public abstract class PlatformFont implements FontPeer {3637static {38NativeLibLoader.loadLibraries();39initIDs();40}4142protected FontDescriptor[] componentFonts;43protected char defaultChar;44protected FontConfiguration fontConfig;4546protected FontDescriptor defaultFont;4748protected String familyName;4950private Object[] fontCache;5152// Maybe this should be a property that is set based53// on the locale?54protected static int FONTCACHESIZE = 256;55protected static int FONTCACHEMASK = PlatformFont.FONTCACHESIZE - 1;56protected static String osVersion;5758public PlatformFont(String name, int style){59SunFontManager sfm = SunFontManager.getInstance();60if (sfm instanceof FontSupport) {61fontConfig = ((FontSupport)sfm).getFontConfiguration();62}63if (fontConfig == null) {64return;65}6667// map given font name to a valid logical font family name68familyName = name.toLowerCase(Locale.ENGLISH);69if (!FontConfiguration.isLogicalFontFamilyName(familyName)) {70familyName = fontConfig.getFallbackFamilyName(familyName, "sansserif");71}7273componentFonts = fontConfig.getFontDescriptors(familyName, style);7475// search default character76//77char missingGlyphCharacter = getMissingGlyphCharacter();7879defaultChar = '?';80if (componentFonts.length > 0)81defaultFont = componentFonts[0];8283for (int i = 0; i < componentFonts.length; i++){84if (componentFonts[i].isExcluded(missingGlyphCharacter)) {85continue;86}8788if (componentFonts[i].encoder.canEncode(missingGlyphCharacter)) {89defaultFont = componentFonts[i];90defaultChar = missingGlyphCharacter;91break;92}93}94}9596/**97* Returns the character that should be rendered when a glyph98* is missing.99*/100protected abstract char getMissingGlyphCharacter();101102/**103* make a array of CharsetString with given String.104*/105public CharsetString[] makeMultiCharsetString(String str){106return makeMultiCharsetString(str.toCharArray(), 0, str.length(), true);107}108109/**110* make a array of CharsetString with given String.111*/112public CharsetString[] makeMultiCharsetString(String str, boolean allowdefault){113return makeMultiCharsetString(str.toCharArray(), 0, str.length(), allowdefault);114}115116/**117* make a array of CharsetString with given char array.118* @param str The char array to convert.119* @param offset offset of first character of interest120* @param len number of characters to convert121*/122public CharsetString[] makeMultiCharsetString(char[] str, int offset, int len) {123return makeMultiCharsetString(str, offset, len, true);124}125126/**127* make a array of CharsetString with given char array.128* @param str The char array to convert.129* @param offset offset of first character of interest130* @param len number of characters to convert131* @param allowDefault whether to allow the default char.132* Setting this to true overloads the meaning of this method to133* return non-null only if all chars can be converted.134* @return array of CharsetString or if allowDefault is false and any135* of the returned chars would have been converted to a default char,136* then return null.137* This is used to choose alternative means of displaying the text.138*/139public CharsetString[] makeMultiCharsetString(char[] str, int offset, int len,140boolean allowDefault) {141142if (len < 1) {143return new CharsetString[0];144}145Vector<CharsetString> mcs = null;146char[] tmpStr = new char[len];147char tmpChar = defaultChar;148boolean encoded = false;149150FontDescriptor currentFont = defaultFont;151152153for (int i = 0; i < componentFonts.length; i++) {154if (componentFonts[i].isExcluded(str[offset])){155continue;156}157158/* Need "encoded" variable to distinguish the case when159* the default char is the same as the encoded char.160* The defaultChar on Linux is '?' so it is needed there.161*/162if (componentFonts[i].encoder.canEncode(str[offset])){163currentFont = componentFonts[i];164tmpChar = str[offset];165encoded = true;166break;167}168}169if (!allowDefault && !encoded) {170return null;171} else {172tmpStr[0] = tmpChar;173}174175int lastIndex = 0;176for (int i = 1; i < len; i++){177char ch = str[offset + i];178FontDescriptor fd = defaultFont;179tmpChar = defaultChar;180encoded = false;181for (int j = 0; j < componentFonts.length; j++){182if (componentFonts[j].isExcluded(ch)){183continue;184}185186if (componentFonts[j].encoder.canEncode(ch)){187fd = componentFonts[j];188tmpChar = ch;189encoded = true;190break;191}192}193if (!allowDefault && !encoded) {194return null;195} else {196tmpStr[i] = tmpChar;197}198if (currentFont != fd){199if (mcs == null) {200mcs = new Vector<>(3);201}202mcs.addElement(new CharsetString(tmpStr, lastIndex,203i-lastIndex, currentFont));204currentFont = fd;205fd = defaultFont;206lastIndex = i;207}208}209CharsetString[] result;210CharsetString cs = new CharsetString(tmpStr, lastIndex,211len-lastIndex, currentFont);212if (mcs == null) {213result = new CharsetString[1];214result[0] = cs;215} else {216mcs.addElement(cs);217result = mcs.toArray(new CharsetString[mcs.size()]);218}219return result;220}221222/**223* Is it possible that this font's metrics require the multi-font calls?224* This might be true, for example, if the font supports kerning.225**/226public boolean mightHaveMultiFontMetrics() {227return fontConfig != null;228}229230/**231* Specialized fast path string conversion for AWT.232*/233public Object[] makeConvertedMultiFontString(String str)234{235return makeConvertedMultiFontChars(str.toCharArray(),0,str.length());236}237238public Object[] makeConvertedMultiFontChars(char[] data,239int start, int len)240{241Object[] result = new Object[2];242Object[] workingCache;243byte[] convertedData = null;244int stringIndex = start;245int convertedDataIndex = 0;246int resultIndex = 0;247int cacheIndex;248FontDescriptor currentFontDescriptor = null;249FontDescriptor lastFontDescriptor = null;250char currentDefaultChar;251PlatformFontCache theChar;252253// Simple bounds check254int end = start + len;255if (start < 0 || end > data.length) {256throw new ArrayIndexOutOfBoundsException();257}258259if(stringIndex >= end) {260return null;261}262263// coversion loop264while(stringIndex < end)265{266currentDefaultChar = data[stringIndex];267268// Note that cache sizes must be a power of two!269cacheIndex = (currentDefaultChar & PlatformFont.FONTCACHEMASK);270271theChar = (PlatformFontCache)getFontCache()[cacheIndex];272273// Is the unicode char we want cached?274if(theChar == null || theChar.uniChar != currentDefaultChar)275{276/* find a converter that can convert the current character */277currentFontDescriptor = defaultFont;278currentDefaultChar = defaultChar;279char ch = data[stringIndex];280int componentCount = componentFonts.length;281282for (int j = 0; j < componentCount; j++) {283FontDescriptor fontDescriptor = componentFonts[j];284285fontDescriptor.encoder.reset();286//fontDescriptor.encoder.onUnmappleCharacterAction(...);287288if (fontDescriptor.isExcluded(ch)) {289continue;290}291if (fontDescriptor.encoder.canEncode(ch)) {292currentFontDescriptor = fontDescriptor;293currentDefaultChar = ch;294break;295}296}297try {298char[] input = new char[1];299input[0] = currentDefaultChar;300301theChar = new PlatformFontCache();302if (currentFontDescriptor.useUnicode()) {303/*304currentFontDescriptor.unicodeEncoder.encode(CharBuffer.wrap(input),305theChar.bb,306true);307*/308if (FontDescriptor.isLE) {309theChar.bb.put((byte)(input[0] & 0xff));310theChar.bb.put((byte)(input[0] >>8));311} else {312theChar.bb.put((byte)(input[0] >> 8));313theChar.bb.put((byte)(input[0] & 0xff));314}315}316else {317currentFontDescriptor.encoder.encode(CharBuffer.wrap(input),318theChar.bb,319true);320}321theChar.fontDescriptor = currentFontDescriptor;322theChar.uniChar = data[stringIndex];323getFontCache()[cacheIndex] = theChar;324} catch(Exception e){325// Should never happen!326System.err.println(e);327e.printStackTrace();328return null;329}330}331332// Check to see if we've changed fonts.333if(lastFontDescriptor != theChar.fontDescriptor) {334if(lastFontDescriptor != null) {335result[resultIndex++] = lastFontDescriptor;336result[resultIndex++] = convertedData;337// Add the size to the converted data field.338if(convertedData != null) {339convertedDataIndex -= 4;340convertedData[0] = (byte)(convertedDataIndex >> 24);341convertedData[1] = (byte)(convertedDataIndex >> 16);342convertedData[2] = (byte)(convertedDataIndex >> 8);343convertedData[3] = (byte)convertedDataIndex;344}345346if(resultIndex >= result.length) {347Object[] newResult = new Object[result.length * 2];348349System.arraycopy(result, 0, newResult, 0,350result.length);351result = newResult;352}353}354355if (theChar.fontDescriptor.useUnicode()) {356convertedData = new byte[(end - stringIndex + 1) *357(int)theChar.fontDescriptor.unicodeEncoder.maxBytesPerChar()358+ 4];359}360else {361convertedData = new byte[(end - stringIndex + 1) *362(int)theChar.fontDescriptor.encoder.maxBytesPerChar()363+ 4];364}365366convertedDataIndex = 4;367368lastFontDescriptor = theChar.fontDescriptor;369}370371byte[] ba = theChar.bb.array();372int size = theChar.bb.position();373if(size == 1) {374convertedData[convertedDataIndex++] = ba[0];375}376else if(size == 2) {377convertedData[convertedDataIndex++] = ba[0];378convertedData[convertedDataIndex++] = ba[1];379} else if(size == 3) {380convertedData[convertedDataIndex++] = ba[0];381convertedData[convertedDataIndex++] = ba[1];382convertedData[convertedDataIndex++] = ba[2];383} else if(size == 4) {384convertedData[convertedDataIndex++] = ba[0];385convertedData[convertedDataIndex++] = ba[1];386convertedData[convertedDataIndex++] = ba[2];387convertedData[convertedDataIndex++] = ba[3];388}389stringIndex++;390}391392result[resultIndex++] = lastFontDescriptor;393result[resultIndex] = convertedData;394395// Add the size to the converted data field.396if(convertedData != null) {397convertedDataIndex -= 4;398convertedData[0] = (byte)(convertedDataIndex >> 24);399convertedData[1] = (byte)(convertedDataIndex >> 16);400convertedData[2] = (byte)(convertedDataIndex >> 8);401convertedData[3] = (byte)convertedDataIndex;402}403return result;404}405406/*407* Create fontCache on demand instead of during construction to408* reduce overall memory consumption.409*410* This method is declared final so that its code can be inlined411* by the compiler.412*/413protected final Object[] getFontCache() {414// This method is not MT-safe by design. Since this is just a415// cache anyways, it's okay if we occasionally allocate the array416// twice or return an array which will be dereferenced and gced417// right away.418if (fontCache == null) {419fontCache = new Object[PlatformFont.FONTCACHESIZE];420}421422return fontCache;423}424425/**426* Initialize JNI field and method IDs427*/428private static native void initIDs();429430class PlatformFontCache431{432char uniChar;433FontDescriptor fontDescriptor;434ByteBuffer bb = ByteBuffer.allocate(4);435}436}437438439