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 getGlyphFromCMAPVS(int charCode, int variationSelector) { 97 if (variationSelector == 0) { 98 return getGlyphFromCMAP(charCode); 99 } 100 try { 101 char glyphCode = cmap.getGlyph(charCode, variationSelector, true); 102 if (glyphCode < numGlyphs || 103 glyphCode >= FileFontStrike.INVISIBLE_GLYPHS) { 104 return glyphCode; 105 } else { 106 if (FontUtilities.isLogging()) { 107 FontUtilities.getLogger().warning 108 (font + " out of range glyph id=" + 109 Integer.toHexString((int)glyphCode) + 110 " for char " + Integer.toHexString(charCode) + 111 " for vs " + Integer.toHexString(variationSelector)); 112 } 113 return (char)missingGlyph; 114 } 115 } catch (Exception e) { 116 handleBadCMAP(); 117 return (char) missingGlyph; 118 } 119 } 120 121 private void handleBadCMAP() { 122 if (FontUtilities.isLogging()) { 123 FontUtilities.getLogger().severe("Null Cmap for " + font + 124 "substituting for this font"); 125 } 126 SunFontManager.getInstance().deRegisterBadFont(font); 127 /* The next line is not really a solution, but might 128 * reduce the exceptions until references to this font2D 129 * are gone. 130 */ 131 cmap = CMap.theNullCmap; 132 } 133 134 private char remapJAChar(char unicode) { 135 return (unicode == REVERSE_SOLIDUS) ? JA_YEN : unicode; 136 } 137 138 private int remapJAIntChar(int unicode) { 139 return (unicode == REVERSE_SOLIDUS) ? JA_YEN : unicode; 140 } 141 142 public int charToGlyph(char unicode) { 143 if (needsJAremapping) { 144 unicode = remapJAChar(unicode); 145 } 146 int glyph = getGlyphFromCMAP(unicode); 147 if (font.checkUseNatives() && glyph < font.glyphToCharMap.length) { 148 font.glyphToCharMap[glyph] = unicode; 149 } 150 return glyph; 151 } 152 153 public int charToGlyph(int unicode) { 154 if (needsJAremapping) { 155 unicode = remapJAIntChar(unicode); 156 } 157 int glyph = getGlyphFromCMAP(unicode); 158 if (font.checkUseNatives() && glyph < font.glyphToCharMap.length) { 159 font.glyphToCharMap[glyph] = (char)unicode; 160 } 161 return glyph; 162 } 163 164 public void charsToGlyphs(int count, int[] unicodes, int[] glyphs) { 165 for (int i = 0; i < count; i++) { 166 if (isVariationSelector(unicodes[i])) { 167 charsToGlyphsVS(count, unicodes, glyphs); 168 return; 169 } 170 if (needsJAremapping) { 171 glyphs[i] = getGlyphFromCMAP(remapJAIntChar(unicodes[i])); 172 } else { 173 glyphs[i] = getGlyphFromCMAP(unicodes[i]); 174 } 175 if (font.checkUseNatives() && 176 glyphs[i] < font.glyphToCharMap.length) { 177 font.glyphToCharMap[glyphs[i]] = (char)unicodes[i]; 178 } 179 } 180 } 181 182 private void charsToGlyphsVS(int count, int[] unicodes, int[] glyphs) { 183 for (int i = 0; i < count; i++) { 184 if (i < count - 1 && 185 isVariationSelector(unicodes[i + 1]) && 186 isVSBaseChar(unicodes[i])) { 187 if (needsJAremapping) { 188 glyphs[i] = getGlyphFromCMAPVS(remapJAIntChar(unicodes[i]), 189 unicodes[i + 1]); 190 } else { 191 glyphs[i] = getGlyphFromCMAPVS(unicodes[i], 192 unicodes[i + 1]); 193 } 194 i++; 195 glyphs[i] = INVISIBLE_GLYPH_ID; 196 } else { 197 if (needsJAremapping) { 198 glyphs[i] = getGlyphFromCMAP(remapJAIntChar(unicodes[i])); 199 } else { 200 glyphs[i] = getGlyphFromCMAP(unicodes[i]); 201 } 202 if (font.checkUseNatives() && 203 glyphs[i] < font.glyphToCharMap.length) { 204 font.glyphToCharMap[glyphs[i]] = (char)unicodes[i]; 205 } 206 } 207 } 208 } 209 210 public void charsToGlyphs(int count, char[] unicodes, int[] glyphs) { 211 212 for (int i = 0; i < count; i++) { 213 int code; 214 if (needsJAremapping) { 215 code = remapJAChar(unicodes[i]); 216 } else { 217 code = unicodes[i]; // char is unsigned. 218 } 219 220 if (code >= HI_SURROGATE_START && 221 code <= HI_SURROGATE_END && i < count - 1) { 222 char low = unicodes[i + 1]; 223 224 if (low >= LO_SURROGATE_START && 225 low <= LO_SURROGATE_END) { 226 code = (code - HI_SURROGATE_START) * 227 0x400 + low - LO_SURROGATE_START + 0x10000; 228 229 if (isVariationSelector(code)) { 230 charsToGlyphsVS(count, unicodes, glyphs); 231 return; 232 } 233 glyphs[i] = getGlyphFromCMAP(code); 234 i += 1; // Empty glyph slot after surrogate 235 glyphs[i] = INVISIBLE_GLYPH_ID; 236 continue; 237 } 238 } else if (isVariationSelectorBMP(unicodes[i])) { 239 charsToGlyphsVS(count, unicodes, glyphs); 240 return; 241 } 242 glyphs[i] = getGlyphFromCMAP(code); 243 244 if (font.checkUseNatives() && 245 glyphs[i] < font.glyphToCharMap.length) { 246 font.glyphToCharMap[glyphs[i]] = (char)code; 247 } 248 249 } 250 } 251 252 private void charsToGlyphsVS(int count, char[] unicodes, int[] glyphs) { 253 for (int i = 0; i < count; i++) { 254 int code; 255 int variationSelector = 0; 256 int step = 1; 257 if (needsJAremapping) { 258 code = remapJAChar(unicodes[i]); 259 } else { 260 code = unicodes[i]; // char is unsigned. 261 } 262 263 if (code >= HI_SURROGATE_START && 264 code <= HI_SURROGATE_END && i < count - 1) { 265 char low = unicodes[i + 1]; 266 267 if (low >= LO_SURROGATE_START && 268 low <= LO_SURROGATE_END) { 269 code = (code - HI_SURROGATE_START) * 270 0x400 + low - LO_SURROGATE_START + 0x10000; 271 272 glyphs[i + 1] = INVISIBLE_GLYPH_ID; 273 step = 2; 274 } 275 } 276 if (i < count - step && 277 isVariationSelectorBMP(unicodes[i + step]) && 278 isVSBaseChar(code)) { 279 variationSelector = unicodes[i + step]; 280 glyphs[i] = getGlyphFromCMAPVS(code, variationSelector); 281 glyphs[i+step] = INVISIBLE_GLYPH_ID; 282 i += 1; 283 } else if (i < count - step -1 && 284 isVariationSelectorExt(unicodes[i + step], 285 unicodes[i + step + 1]) && 286 isVSBaseChar(code)) { 287 variationSelector = (unicodes[i + step] - HI_SURROGATE_START) 288 * 0x400 + unicodes[i + step + 1] 289 - LO_SURROGATE_START + 0x10000; 290 glyphs[i] = getGlyphFromCMAPVS(code, variationSelector); 291 glyphs[i + step] = INVISIBLE_GLYPH_ID; 292 glyphs[i + step + 1] = INVISIBLE_GLYPH_ID; 293 i += 2; 294 } 295 if (variationSelector == 0) { 296 glyphs[i] = getGlyphFromCMAP(code); 297 298 if (font.checkUseNatives() && 299 glyphs[i] < font.glyphToCharMap.length) { 300 font.glyphToCharMap[glyphs[i]] = (char)code; 301 } 302 } 303 if (code >= 0x10000) { 304 i++; 305 } 306 307 } 308 } 309 310 /* This variant checks if shaping is needed and immediately 311 * returns true if it does. A caller of this method should be expecting 312 * to check the return type because it needs to know how to handle 313 * the character data for display. 314 */ 315 public boolean charsToGlyphsNS(int count, char[] unicodes, int[] glyphs) { 316 317 for (int i = 0; i < count; i++) { 318 int code; 319 if (needsJAremapping) { 320 code = remapJAChar(unicodes[i]); 321 } else { 322 code = unicodes[i]; // char is unsigned. 323 } 324 325 if (code >= HI_SURROGATE_START && 326 code <= HI_SURROGATE_END && i < count - 1) { 327 char low = unicodes[i + 1]; 328 329 if (low >= LO_SURROGATE_START && 330 low <= LO_SURROGATE_END) { 331 code = (code - HI_SURROGATE_START) * 332 0x400 + low - LO_SURROGATE_START + 0x10000; 333 glyphs[i + 1] = INVISIBLE_GLYPH_ID; 334 } 335 } 336 if (isVariationSelector(code)) { 337 return charsToGlyphsNSVS(count, unicodes, glyphs); 338 } 339 340 glyphs[i] = getGlyphFromCMAP(code); 341 if (font.checkUseNatives() && 342 glyphs[i] < font.glyphToCharMap.length) { 343 font.glyphToCharMap[glyphs[i]] = (char)code; 344 } 345 346 if (code < FontUtilities.MIN_LAYOUT_CHARCODE) { 347 continue; 348 } 349 else if (FontUtilities.isComplexCharCode(code)) { 350 return true; 351 } 352 else if (code >= 0x10000) { 353 i += 1; // Empty glyph slot after surrogate 354 continue; 355 } 356 } 357 358 return false; 359 } 360 361 private boolean charsToGlyphsNSVS(int count, char[] unicodes, 362 int[] glyphs) { 363 for (int i = 0; i < count; i++) { 364 int code; 365 int step = 1; 366 int variationSelector = 0; 367 if (needsJAremapping) { 368 code = remapJAChar(unicodes[i]); 369 } else { 370 code = unicodes[i]; // char is unsigned. 371 } 372 373 if (code >= HI_SURROGATE_START && 374 code <= HI_SURROGATE_END && i < count - 1) { 375 char low = unicodes[i + 1]; 376 377 if (low >= LO_SURROGATE_START && 378 low <= LO_SURROGATE_END) { 379 code = (code - HI_SURROGATE_START) * 380 0x400 + low - LO_SURROGATE_START + 0x10000; 381 glyphs[i + 1] = INVISIBLE_GLYPH_ID; 382 step = 2; 383 } 384 } 385 386 if (i < count - step && 387 isVariationSelectorBMP(unicodes[i + step]) && 388 isVSBaseChar(code)) { 389 variationSelector = unicodes[i + step]; 390 glyphs[i] = getGlyphFromCMAPVS(code, variationSelector); 391 glyphs[i+step] = INVISIBLE_GLYPH_ID; 392 i += 1; 393 } else if (i < count - step - 1 && 394 isVariationSelectorExt(unicodes[i + step], 395 unicodes[i + step + 1]) && 396 isVSBaseChar(code)) { 397 variationSelector = (unicodes[i + step] - HI_SURROGATE_START) 398 * 0x400 + unicodes[i + step + 1] 399 - LO_SURROGATE_START + 0x10000; 400 glyphs[i] = getGlyphFromCMAPVS(code, variationSelector); 401 glyphs[i + step] = INVISIBLE_GLYPH_ID; 402 glyphs[i + step + 1] = INVISIBLE_GLYPH_ID; 403 i += 2; 404 } 405 if (variationSelector == 0) { 406 glyphs[i] = getGlyphFromCMAP(code); 407 if (font.checkUseNatives() && 408 glyphs[i] < font.glyphToCharMap.length) { 409 font.glyphToCharMap[glyphs[i]] = (char)code; 410 } 411 } 412 413 if (code < FontUtilities.MIN_LAYOUT_CHARCODE) { 414 continue; 415 } 416 else if (FontUtilities.isComplexCharCode(code)) { 417 return true; 418 } 419 else if (code >= 0x10000) { 420 i += 1; // Empty glyph slot after surrogate 421 continue; 422 } 423 } 424 425 return false; 426 } 427 428 /* A pretty good heuristic is that the cmap we are using 429 * supports 32 bit character codes. 430 */ 431 boolean hasSupplementaryChars() { 432 return 433 cmap instanceof CMap.CMapFormat8 || 434 cmap instanceof CMap.CMapFormat10 || 435 cmap instanceof CMap.CMapFormat12; 436 } 437 438 int getGlyphOfVS(int charCode, int variationSelector) { 439 return cmap.getGlyph(charCode, variationSelector, false); 440 } 441 }