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.getGlyph(charCode, variationSelector); 102 if (glyphCode == 0) { // retry without variation selector 103 return getGlyphFromCMAP(charCode); 104 } 105 if (glyphCode < numGlyphs || 106 glyphCode >= FileFontStrike.INVISIBLE_GLYPHS) { 107 return glyphCode; 108 } else { 109 if (FontUtilities.isLogging()) { 110 FontUtilities.getLogger().warning 111 (font + " out of range glyph id=" + 112 Integer.toHexString((int)glyphCode) + 113 " for char " + Integer.toHexString(charCode) + 114 " for vs " + Integer.toHexString(variationSelector)); 115 } 116 return (char)missingGlyph; 117 } 118 } catch(Exception e) { 119 handleBadCMAP(); 120 return (char) missingGlyph; 121 } 122 } 123 124 private void handleBadCMAP() { 125 if (FontUtilities.isLogging()) { 126 FontUtilities.getLogger().severe("Null Cmap for " + font + 127 "substituting for this font"); 128 } 129 SunFontManager.getInstance().deRegisterBadFont(font); 130 /* The next line is not really a solution, but might 131 * reduce the exceptions until references to this font2D 132 * are gone. 133 */ 134 cmap = CMap.theNullCmap; 135 } 136 137 private char remapJAChar(char unicode) { 138 return (unicode == REVERSE_SOLIDUS) ? JA_YEN : unicode; 139 } 140 141 private int remapJAIntChar(int unicode) { 142 return (unicode == REVERSE_SOLIDUS) ? JA_YEN : unicode; 143 } 144 145 public int charToGlyph(char unicode) { 146 if (needsJAremapping) { 147 unicode = remapJAChar(unicode); 148 } 149 int glyph = getGlyphFromCMAP(unicode); 150 if (font.checkUseNatives() && glyph < font.glyphToCharMap.length) { 151 font.glyphToCharMap[glyph] = unicode; 152 } 153 return glyph; 154 } 155 156 public int charToGlyph(int unicode) { 157 if (needsJAremapping) { 158 unicode = remapJAIntChar(unicode); 159 } 160 int glyph = getGlyphFromCMAP(unicode); 161 if (font.checkUseNatives() && glyph < font.glyphToCharMap.length) { 162 font.glyphToCharMap[glyph] = (char)unicode; 163 } 164 return glyph; 165 } 166 167 public void charsToGlyphs(int count, int[] unicodes, int[] glyphs) { 168 for (int i=0;i<count;i++) { 169 if (i < count - 1 170 && isVariationSelector(unicodes[i + 1]) 171 && isBaseChar(unicodes[i]) ){ 172 if (needsJAremapping) { 173 glyphs[i] = getGlyphFromCMAP(remapJAIntChar(unicodes[i]), 174 unicodes[i + 1]); 175 } else { 176 glyphs[i] = getGlyphFromCMAP(unicodes[i], unicodes[i + 1]); 177 } 178 i++; 179 glyphs[i] = INVISIBLE_GLYPH_ID; 180 } else { 181 if (needsJAremapping) { 182 glyphs[i] = getGlyphFromCMAP(remapJAIntChar(unicodes[i])); 183 } else { 184 glyphs[i] = getGlyphFromCMAP(unicodes[i]); 185 } 186 if (font.checkUseNatives() && 187 glyphs[i] < font.glyphToCharMap.length) { 188 font.glyphToCharMap[glyphs[i]] = (char)unicodes[i]; 189 } 190 } 191 } 192 } 193 194 public void charsToGlyphs(int count, char[] unicodes, int[] glyphs) { 195 196 for (int i=0; i<count; i++) { 197 int code; 198 int variationSelector = 0; 199 int step = 1; 200 if (needsJAremapping) { 201 code = remapJAChar(unicodes[i]); 202 } else { 203 code = unicodes[i]; // char is unsigned. 204 } 205 206 if (code >= HI_SURROGATE_START && 207 code <= HI_SURROGATE_END && i < count - 1) { 208 char low = unicodes[i + 1]; 209 210 if (low >= LO_SURROGATE_START && 211 low <= LO_SURROGATE_END) { 212 code = (code - HI_SURROGATE_START) * 213 0x400 + low - LO_SURROGATE_START + 0x10000; 214 215 glyphs[i + 1] = INVISIBLE_GLYPH_ID; 216 step = 2; 217 } 218 } 219 if (i < count - step && 220 isVariationSelector(unicodes[i + step]) && 221 isBaseChar(code)) { 222 variationSelector = unicodes[i + step]; 223 glyphs[i] = getGlyphFromCMAP(code, variationSelector); 224 glyphs[i+step] = INVISIBLE_GLYPH_ID; 225 i += 1; 226 }else if (i < count - step -1 && 227 isVariationSelector(unicodes[i + step], unicodes[i + step + 1]) && 228 isBaseChar(code)) { 229 variationSelector = (unicodes[i + step] - HI_SURROGATE_START) * 230 0x400 + unicodes[i + step + 1] - LO_SURROGATE_START + 0x10000; 231 glyphs[i] = getGlyphFromCMAP(code, variationSelector); 232 glyphs[i + step] = INVISIBLE_GLYPH_ID; 233 glyphs[i + step + 1] = INVISIBLE_GLYPH_ID; 234 i += 2; 235 } 236 if (variationSelector == 0) { 237 glyphs[i] = getGlyphFromCMAP(code); 238 239 if (font.checkUseNatives() && 240 glyphs[i] < font.glyphToCharMap.length) { 241 font.glyphToCharMap[glyphs[i]] = (char)code; 242 } 243 } 244 if (code >= 0x10000) { 245 i++; 246 } 247 248 } 249 } 250 251 /* This variant checks if shaping is needed and immediately 252 * returns true if it does. A caller of this method should be expecting 253 * to check the return type because it needs to know how to handle 254 * the character data for display. 255 */ 256 public boolean charsToGlyphsNS(int count, char[] unicodes, int[] glyphs) { 257 258 for (int i=0; i<count; i++) { 259 int code; 260 int step = 1; 261 int variationSelector = 0; 262 if (needsJAremapping) { 263 code = remapJAChar(unicodes[i]); 264 } else { 265 code = unicodes[i]; // char is unsigned. 266 } 267 268 if (code >= HI_SURROGATE_START && 269 code <= HI_SURROGATE_END && i < count - 1) { 270 char low = unicodes[i + 1]; 271 272 if (low >= LO_SURROGATE_START && 273 low <= LO_SURROGATE_END) { 274 code = (code - HI_SURROGATE_START) * 275 0x400 + low - LO_SURROGATE_START + 0x10000; 276 glyphs[i + 1] = INVISIBLE_GLYPH_ID; 277 step = 2; 278 } 279 } 280 281 if (i < count - step && 282 isVariationSelector(unicodes[i + step]) && 283 isBaseChar(code)) { 284 variationSelector = unicodes[i + step]; 285 glyphs[i] = getGlyphFromCMAP(code, variationSelector); 286 if (glyphs[i] == 0) { //Retry without variation selector 287 glyphs[i] = getGlyphFromCMAP(code); 288 } 289 glyphs[i+step] = INVISIBLE_GLYPH_ID; 290 i += 1; 291 }else if (i < count - step - 1 && 292 isVariationSelector(unicodes[i + step], unicodes[i + step + 1]) && 293 isBaseChar(code)) { 294 variationSelector = (unicodes[i + step] - HI_SURROGATE_START) * 295 0x400 + unicodes[i + step + 1] - LO_SURROGATE_START + 0x10000; 296 glyphs[i] = getGlyphFromCMAP(code, variationSelector); 297 if (glyphs[i] == 0) { //Retry without variation selector 298 glyphs[i] = getGlyphFromCMAP(code); 299 } 300 glyphs[i + step] = INVISIBLE_GLYPH_ID; 301 glyphs[i + step + 1] = INVISIBLE_GLYPH_ID; 302 i += 2; 303 } 304 if (variationSelector == 0) { 305 glyphs[i] = getGlyphFromCMAP(code); 306 if (font.checkUseNatives() && 307 glyphs[i] < font.glyphToCharMap.length) { 308 font.glyphToCharMap[glyphs[i]] = (char)code; 309 } 310 } 311 312 if (code < FontUtilities.MIN_LAYOUT_CHARCODE) { 313 continue; 314 } 315 else if (FontUtilities.isComplexCharCode(code)) { 316 return true; 317 } 318 else if (code >= 0x10000) { 319 i += 1; // Empty glyph slot after surrogate 320 continue; 321 } 322 } 323 324 return false; 325 } 326 327 /* A pretty good heuristic is that the cmap we are using 328 * supports 32 bit character codes. 329 */ 330 boolean hasSupplementaryChars() { 331 return 332 cmap instanceof CMap.CMapFormat8 || 333 cmap instanceof CMap.CMapFormat10 || 334 cmap instanceof CMap.CMapFormat12; 335 } 336 337 @Override 338 protected boolean hasVariationSelectorGlyph(int charCode, int variationSelector) { 339 return cmap.hasVariationSelectorGlyph(charCode, variationSelector); 340 } 341 }