1 /* 2 * Copyright (c) 2003, 2017, 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 private final boolean needsJAremapping; 42 43 TrueTypeFont font; 44 CMap cmap; 45 int numGlyphs; 46 47 public TrueTypeGlyphMapper(TrueTypeFont font) { 48 this.font = font; 49 try { 50 cmap = CMap.initialize(font); 51 } catch (Exception e) { 52 cmap = null; 53 } 54 if (cmap == null) { 55 handleBadCMAP(); 56 } 57 missingGlyph = 0; /* standard for TrueType fonts */ 58 ByteBuffer buffer = font.getTableBuffer(TrueTypeFont.maxpTag); 59 if (buffer != null && buffer.capacity() >= 6) { 60 numGlyphs = buffer.getChar(4); // offset 4 bytes in MAXP table. 61 } else { 62 handleBadCMAP(); 63 } 64 if (FontUtilities.isSolaris && isJAlocale && font.supportsJA()) { 65 needsJAremapping = true; 66 } else { 67 needsJAremapping = false; 68 } 69 } 70 71 public int getNumGlyphs() { 72 return numGlyphs; 73 } 74 75 private char getGlyphFromCMAP(int charCode) { 76 try { 77 char glyphCode = cmap.getGlyph(charCode); 78 if (glyphCode < numGlyphs || 79 glyphCode >= FileFontStrike.INVISIBLE_GLYPHS) { 80 return glyphCode; 81 } else { 82 if (FontUtilities.isLogging()) { 83 FontUtilities.getLogger().warning 84 (font + " out of range glyph id=" + 85 Integer.toHexString((int)glyphCode) + 86 " for char " + Integer.toHexString(charCode)); 87 } 88 return (char)missingGlyph; 89 } 90 } catch(Exception e) { 91 handleBadCMAP(); 92 return (char) missingGlyph; 93 } 94 } 95 96 private char getGlyphFromCMAP(int charCode, int variationSelector) { 97 if (variationSelector == 0) { 98 return getGlyphFromCMAP(charCode); 99 } 100 try { 101 char glyphCode = cmap.getVariationGlyph(charCode, 102 variationSelector); 103 if (glyphCode < numGlyphs || 104 glyphCode >= FileFontStrike.INVISIBLE_GLYPHS) { 105 return glyphCode; 106 } else { 107 if (FontUtilities.isLogging()) { 108 FontUtilities.getLogger().warning 109 (font + " out of range glyph id=" + 110 Integer.toHexString((int)glyphCode) + 111 " for char " + Integer.toHexString(charCode) + 112 " for vs " + Integer.toHexString(variationSelector)); 113 } 114 return (char)missingGlyph; 115 } 116 } catch (Exception e) { 117 handleBadCMAP(); 118 return (char) missingGlyph; 119 } 120 } 121 122 private void handleBadCMAP() { 123 if (FontUtilities.isLogging()) { 124 FontUtilities.getLogger().severe("Null Cmap for " + font + 125 "substituting for this font"); 126 } 127 SunFontManager.getInstance().deRegisterBadFont(font); 128 /* The next line is not really a solution, but might 129 * reduce the exceptions until references to this font2D 130 * are gone. 131 */ 132 cmap = CMap.theNullCmap; 133 } 134 135 private char remapJAChar(char unicode) { 136 return (unicode == REVERSE_SOLIDUS) ? JA_YEN : unicode; 137 } 138 139 private int remapJAIntChar(int unicode) { 140 return (unicode == REVERSE_SOLIDUS) ? JA_YEN : unicode; 141 } 142 143 public int charToGlyph(char unicode) { 144 if (needsJAremapping) { 145 unicode = remapJAChar(unicode); 146 } 147 int glyph = getGlyphFromCMAP(unicode); 148 if (font.checkUseNatives() && glyph < font.glyphToCharMap.length) { 149 font.glyphToCharMap[glyph] = unicode; 150 } 151 return glyph; 152 } 153 154 public int charToGlyph(int unicode) { 155 if (needsJAremapping) { 156 unicode = remapJAIntChar(unicode); 157 } 158 int glyph = getGlyphFromCMAP(unicode); 159 if (font.checkUseNatives() && glyph < font.glyphToCharMap.length) { 160 font.glyphToCharMap[glyph] = (char)unicode; 161 } 162 return glyph; 163 } 164 165 @Override 166 public int charToVariationGlyph(int unicode, int variationSelector) { 167 if (needsJAremapping) { 168 unicode = remapJAIntChar(unicode); 169 } 170 int glyph = getGlyphFromCMAP(unicode, variationSelector); 171 if (font.checkUseNatives() && glyph < font.glyphToCharMap.length) { 172 font.glyphToCharMap[glyph] = (char)unicode; 173 } 174 return glyph; 175 } 176 177 public void charsToGlyphs(int count, int[] unicodes, int[] glyphs) { 178 for (int i=0;i<count;i++) { 179 if (needsJAremapping) { 180 glyphs[i] = getGlyphFromCMAP(remapJAIntChar(unicodes[i])); 181 } else { 182 glyphs[i] = getGlyphFromCMAP(unicodes[i]); 183 } 184 if (font.checkUseNatives() && 185 glyphs[i] < font.glyphToCharMap.length) { 186 font.glyphToCharMap[glyphs[i]] = (char)unicodes[i]; 187 } 188 } 189 } 190 191 public void charsToGlyphs(int count, char[] unicodes, int[] glyphs) { 192 193 for (int i=0; i<count; i++) { 194 int code; 195 if (needsJAremapping) { 196 code = remapJAChar(unicodes[i]); 197 } else { 198 code = unicodes[i]; // char is unsigned. 199 } 200 201 if (code >= HI_SURROGATE_START && 202 code <= HI_SURROGATE_END && i < count - 1) { 203 char low = unicodes[i + 1]; 204 205 if (low >= LO_SURROGATE_START && 206 low <= LO_SURROGATE_END) { 207 code = (code - HI_SURROGATE_START) * 208 0x400 + low - LO_SURROGATE_START + 0x10000; 209 210 glyphs[i] = getGlyphFromCMAP(code); 211 i += 1; // Empty glyph slot after surrogate 212 glyphs[i] = INVISIBLE_GLYPH_ID; 213 continue; 214 } 215 } 216 glyphs[i] = getGlyphFromCMAP(code); 217 218 if (font.checkUseNatives() && 219 glyphs[i] < font.glyphToCharMap.length) { 220 font.glyphToCharMap[glyphs[i]] = (char)code; 221 } 222 223 } 224 } 225 226 /* This variant checks if shaping is needed and immediately 227 * returns true if it does. A caller of this method should be expecting 228 * to check the return type because it needs to know how to handle 229 * the character data for display. 230 */ 231 public boolean charsToGlyphsNS(int count, char[] unicodes, int[] glyphs) { 232 233 for (int i=0; i<count; i++) { 234 int code; 235 if (needsJAremapping) { 236 code = remapJAChar(unicodes[i]); 237 } else { 238 code = unicodes[i]; // char is unsigned. 239 } 240 241 if (code >= HI_SURROGATE_START && 242 code <= HI_SURROGATE_END && i < count - 1) { 243 char low = unicodes[i + 1]; 244 245 if (low >= LO_SURROGATE_START && 246 low <= LO_SURROGATE_END) { 247 code = (code - HI_SURROGATE_START) * 248 0x400 + low - LO_SURROGATE_START + 0x10000; 249 glyphs[i + 1] = INVISIBLE_GLYPH_ID; 250 } 251 } 252 253 glyphs[i] = getGlyphFromCMAP(code); 254 if (font.checkUseNatives() && 255 glyphs[i] < font.glyphToCharMap.length) { 256 font.glyphToCharMap[glyphs[i]] = (char)code; 257 } 258 259 if (code < FontUtilities.MIN_LAYOUT_CHARCODE) { 260 continue; 261 } 262 else if (FontUtilities.isComplexCharCode(code) || 263 CharToGlyphMapper.isVariationSelector(code)) { 264 return true; 265 } 266 else if (code >= 0x10000) { 267 i += 1; // Empty glyph slot after surrogate 268 continue; 269 } 270 } 271 272 return false; 273 } 274 275 /* A pretty good heuristic is that the cmap we are using 276 * supports 32 bit character codes. 277 */ 278 boolean hasSupplementaryChars() { 279 return 280 cmap instanceof CMap.CMapFormat8 || 281 cmap instanceof CMap.CMapFormat10 || 282 cmap instanceof CMap.CMapFormat12; 283 } 284 }