1 /* 2 * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.font; 27 28 import java.nio.ByteBuffer; 29 import java.util.Locale; 30 31 public class TrueTypeGlyphMapper extends CharToGlyphMapper { 32 33 static final char REVERSE_SOLIDUS = 0x005c; // the backslash char. 34 static final char JA_YEN = 0x00a5; 35 36 /* if running on Solaris and default Locale is ja_JP then 37 * we map need to remap reverse solidus (backslash) to Yen as 38 * apparently expected there. 39 */ 40 static final boolean isJAlocale = Locale.JAPAN.equals(Locale.getDefault()); 41 42 TrueTypeFont font; 43 CMap cmap; 44 int numGlyphs; 45 46 public TrueTypeGlyphMapper(TrueTypeFont font) { 47 this.font = font; 48 try { 49 cmap = CMap.initialize(font); 50 } catch (Exception e) { 51 cmap = null; 52 } 53 if (cmap == null) { 54 handleBadCMAP(); 55 } 56 missingGlyph = 0; /* standard for TrueType fonts */ 57 ByteBuffer buffer = font.getTableBuffer(TrueTypeFont.maxpTag); 58 if (buffer != null && buffer.capacity() >= 6) { 59 numGlyphs = buffer.getChar(4); // offset 4 bytes in MAXP table. 60 } else { 61 handleBadCMAP(); 62 } 63 } 64 65 public int getNumGlyphs() { 66 return numGlyphs; 67 } 68 69 private char getGlyphFromCMAP(int charCode) { 70 try { 71 char glyphCode = cmap.getGlyph(charCode); 72 if (glyphCode < numGlyphs || 73 glyphCode >= FileFontStrike.INVISIBLE_GLYPHS) { 74 return glyphCode; 75 } else { 76 FontUtilities.logWarning(font + " out of range glyph id=" + 77 Integer.toHexString((int)glyphCode) + 78 " for char " + Integer.toHexString(charCode)); 79 return (char)missingGlyph; 80 } 81 } catch(Exception e) { 82 handleBadCMAP(); 83 return (char) missingGlyph; 84 } 85 } 86 87 private char getGlyphFromCMAP(int charCode, int variationSelector) { 88 if (variationSelector == 0) { 89 return getGlyphFromCMAP(charCode); 90 } 91 try { 92 char glyphCode = cmap.getVariationGlyph(charCode, 93 variationSelector); 94 if (glyphCode < numGlyphs || 95 glyphCode >= FileFontStrike.INVISIBLE_GLYPHS) { 96 return glyphCode; 97 } else { 98 FontUtilities.logWarning(font + " out of range glyph id=" + 99 Integer.toHexString((int)glyphCode) + 100 " for char " + Integer.toHexString(charCode) + 101 " for vs " + Integer.toHexString(variationSelector)); 102 return (char)missingGlyph; 103 } 104 } catch (Exception e) { 105 handleBadCMAP(); 106 return (char) missingGlyph; 107 } 108 } 109 110 private void handleBadCMAP() { 111 FontUtilities.logSevere("Null Cmap for " + font + 112 "substituting for this font"); 113 114 SunFontManager.getInstance().deRegisterBadFont(font); 115 /* The next line is not really a solution, but might 116 * reduce the exceptions until references to this font2D 117 * are gone. 118 */ 119 cmap = CMap.theNullCmap; 120 } 121 122 private char remapJAChar(char unicode) { 123 return (unicode == REVERSE_SOLIDUS) ? JA_YEN : unicode; 124 } 125 126 private int remapJAIntChar(int unicode) { 127 return (unicode == REVERSE_SOLIDUS) ? JA_YEN : unicode; 128 } 129 130 public int charToGlyph(char unicode) { 131 int glyph = getGlyphFromCMAP(unicode); 132 return glyph; 133 } 134 135 public int charToGlyph(int unicode) { 136 int glyph = getGlyphFromCMAP(unicode); 137 return glyph; 138 } 139 140 @Override 141 public int charToVariationGlyph(int unicode, int variationSelector) { 142 int glyph = getGlyphFromCMAP(unicode, variationSelector); 143 return glyph; 144 } 145 146 public void charsToGlyphs(int count, int[] unicodes, int[] glyphs) { 147 for (int i=0;i<count;i++) { 148 glyphs[i] = getGlyphFromCMAP(unicodes[i]); 149 } 150 } 151 152 public void charsToGlyphs(int count, char[] unicodes, int[] glyphs) { 153 154 for (int i=0; i<count; i++) { 155 int code = unicodes[i]; // char is unsigned. 156 157 if (code >= HI_SURROGATE_START && 158 code <= HI_SURROGATE_END && i < count - 1) { 159 char low = unicodes[i + 1]; 160 161 if (low >= LO_SURROGATE_START && 162 low <= LO_SURROGATE_END) { 163 code = (code - HI_SURROGATE_START) * 164 0x400 + low - LO_SURROGATE_START + 0x10000; 165 166 glyphs[i] = getGlyphFromCMAP(code); 167 i += 1; // Empty glyph slot after surrogate 168 glyphs[i] = INVISIBLE_GLYPH_ID; 169 continue; 170 } 171 } 172 glyphs[i] = getGlyphFromCMAP(code); 173 174 } 175 } 176 177 /* This variant checks if shaping is needed and immediately 178 * returns true if it does. A caller of this method should be expecting 179 * to check the return type because it needs to know how to handle 180 * the character data for display. 181 */ 182 public boolean charsToGlyphsNS(int count, char[] unicodes, int[] glyphs) { 183 184 for (int i=0; i<count; i++) { 185 int code = unicodes[i]; // char is unsigned. 186 187 if (code >= HI_SURROGATE_START && 188 code <= HI_SURROGATE_END && i < count - 1) { 189 char low = unicodes[i + 1]; 190 191 if (low >= LO_SURROGATE_START && 192 low <= LO_SURROGATE_END) { 193 code = (code - HI_SURROGATE_START) * 194 0x400 + low - LO_SURROGATE_START + 0x10000; 195 glyphs[i + 1] = INVISIBLE_GLYPH_ID; 196 } 197 } 198 199 glyphs[i] = getGlyphFromCMAP(code); 200 201 if (code < FontUtilities.MIN_LAYOUT_CHARCODE) { 202 continue; 203 } 204 else if (FontUtilities.isComplexCharCode(code) || 205 CharToGlyphMapper.isVariationSelector(code)) { 206 return true; 207 } 208 else if (code >= 0x10000) { 209 i += 1; // Empty glyph slot after surrogate 210 continue; 211 } 212 } 213 214 return false; 215 } 216 217 /* A pretty good heuristic is that the cmap we are using 218 * supports 32 bit character codes. 219 */ 220 boolean hasSupplementaryChars() { 221 return 222 cmap instanceof CMap.CMapFormat8 || 223 cmap instanceof CMap.CMapFormat10 || 224 cmap instanceof CMap.CMapFormat12; 225 } 226 }