1 /* 2 * Copyright (c) 2003, 2006, 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 static final char JA_FULLWIDTH_TILDE_CHAR = 0xff5e; 36 static final char JA_WAVE_DASH_CHAR = 0x301c; 37 38 /* if running on Solaris and default Locale is ja_JP then 39 * we map need to remap reverse solidus (backslash) to Yen as 40 * apparently expected there. 41 */ 42 static final boolean isJAlocale = Locale.JAPAN.equals(Locale.getDefault()); 43 private final boolean needsJAremapping; 44 private boolean remapJAWaveDash; 45 46 TrueTypeFont font; 47 CMap cmap; 48 int numGlyphs; 49 50 public TrueTypeGlyphMapper(TrueTypeFont font) { 51 this.font = font; 52 try { 53 cmap = CMap.initialize(font); 54 } catch (Exception e) { 55 cmap = null; 56 } 57 if (cmap == null) { 58 handleBadCMAP(); 59 } 60 missingGlyph = 0; /* standard for TrueType fonts */ 61 ByteBuffer buffer = font.getTableBuffer(TrueTypeFont.maxpTag); 62 numGlyphs = buffer.getChar(4); // offset 4 bytes in MAXP table. 63 if (FontUtilities.isSolaris && isJAlocale && font.supportsJA()) { 64 needsJAremapping = true; 65 if (FontUtilities.isSolaris8 && 66 getGlyphFromCMAP(JA_WAVE_DASH_CHAR) == missingGlyph) { 67 remapJAWaveDash = true; 68 } 69 } else { 70 needsJAremapping = false; 71 } 72 } 73 74 public int getNumGlyphs() { 75 return numGlyphs; 76 } 77 78 private char getGlyphFromCMAP(int charCode) { 79 try { 80 char glyphCode = cmap.getGlyph(charCode); 81 if (glyphCode < numGlyphs || 82 glyphCode >= FileFontStrike.INVISIBLE_GLYPHS) { 83 return glyphCode; 84 } else { 85 if (FontUtilities.isLogging()) { 86 FontUtilities.getLogger().warning 87 (font + " out of range glyph id=" + 88 Integer.toHexString((int)glyphCode) + 89 " for char " + Integer.toHexString(charCode)); 90 } 91 return (char)missingGlyph; 92 } 93 } catch(Exception e) { 94 handleBadCMAP(); 95 return (char) missingGlyph; 96 } 97 } 98 99 private void handleBadCMAP() { 100 if (FontUtilities.isLogging()) { 101 FontUtilities.getLogger().severe("Null Cmap for " + font + 102 "substituting for this font"); 103 } 104 SunFontManager.getInstance().deRegisterBadFont(font); 105 /* The next line is not really a solution, but might 106 * reduce the exceptions until references to this font2D 107 * are gone. 108 */ 109 cmap = CMap.theNullCmap; 110 } 111 112 @SuppressWarnings("fallthrough") 113 private final char remapJAChar(char unicode) { 114 switch (unicode) { 115 case REVERSE_SOLIDUS: 116 return JA_YEN; 117 /* This is a workaround for bug 4533422. 118 * Japanese wave dash missing from Solaris JA TrueType fonts. 119 */ 120 case JA_WAVE_DASH_CHAR: 121 if (remapJAWaveDash) { 122 return JA_FULLWIDTH_TILDE_CHAR; 123 } 124 // Fall through okay? 125 default: return unicode; 126 } 127 } 128 @SuppressWarnings("fallthrough") 129 private final int remapJAIntChar(int unicode) { 130 switch (unicode) { 131 case REVERSE_SOLIDUS: 132 return JA_YEN; 133 /* This is a workaround for bug 4533422. 134 * Japanese wave dash missing from Solaris JA TrueType fonts. 135 */ 136 case JA_WAVE_DASH_CHAR: 137 if (remapJAWaveDash) { 138 return JA_FULLWIDTH_TILDE_CHAR; 139 } 140 // Fall through okay? 141 default: return unicode; 142 } 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 (needsJAremapping) { 170 glyphs[i] = getGlyphFromCMAP(remapJAIntChar(unicodes[i])); 171 } else { 172 glyphs[i] = getGlyphFromCMAP(unicodes[i]); 173 } 174 if (font.checkUseNatives() && 175 glyphs[i] < font.glyphToCharMap.length) { 176 font.glyphToCharMap[glyphs[i]] = (char)unicodes[i]; 177 } 178 } 179 } 180 181 public void charsToGlyphs(int count, char[] unicodes, int[] glyphs) { 182 183 for (int i=0; i<count; i++) { 184 int code; 185 if (needsJAremapping) { 186 code = remapJAChar(unicodes[i]); 187 } else { 188 code = unicodes[i]; // char is unsigned. 189 } 190 191 if (code >= HI_SURROGATE_START && 192 code <= HI_SURROGATE_END && i < count - 1) { 193 char low = unicodes[i + 1]; 194 195 if (low >= LO_SURROGATE_START && 196 low <= LO_SURROGATE_END) { 197 code = (code - HI_SURROGATE_START) * 198 0x400 + low - LO_SURROGATE_START + 0x10000; 199 200 glyphs[i] = getGlyphFromCMAP(code); 201 i += 1; // Empty glyph slot after surrogate 202 glyphs[i] = INVISIBLE_GLYPH_ID; 203 continue; 204 } 205 } 206 glyphs[i] = getGlyphFromCMAP(code); 207 208 if (font.checkUseNatives() && 209 glyphs[i] < font.glyphToCharMap.length) { 210 font.glyphToCharMap[glyphs[i]] = (char)code; 211 } 212 213 } 214 } 215 216 /* This variant checks if shaping is needed and immediately 217 * returns true if it does. A caller of this method should be expecting 218 * to check the return type because it needs to know how to handle 219 * the character data for display. 220 */ 221 public boolean charsToGlyphsNS(int count, char[] unicodes, int[] glyphs) { 222 223 for (int i=0; i<count; i++) { 224 int code; 225 if (needsJAremapping) { 226 code = remapJAChar(unicodes[i]); 227 } else { 228 code = unicodes[i]; // char is unsigned. 229 } 230 231 if (code >= HI_SURROGATE_START && 232 code <= HI_SURROGATE_END && i < count - 1) { 233 char low = unicodes[i + 1]; 234 235 if (low >= LO_SURROGATE_START && 236 low <= LO_SURROGATE_END) { 237 code = (code - HI_SURROGATE_START) * 238 0x400 + low - LO_SURROGATE_START + 0x10000; 239 glyphs[i + 1] = INVISIBLE_GLYPH_ID; 240 } 241 } 242 243 glyphs[i] = getGlyphFromCMAP(code); 244 if (font.checkUseNatives() && 245 glyphs[i] < font.glyphToCharMap.length) { 246 font.glyphToCharMap[glyphs[i]] = (char)code; 247 } 248 249 if (code < FontUtilities.MIN_LAYOUT_CHARCODE) { 250 continue; 251 } 252 else if (FontUtilities.isComplexCharCode(code)) { 253 return true; 254 } 255 else if (code >= 0x10000) { 256 i += 1; // Empty glyph slot after surrogate 257 continue; 258 } 259 } 260 261 return false; 262 } 263 264 /* A pretty good heuristic is that the cmap we are using 265 * supports 32 bit character codes. 266 */ 267 boolean hasSupplementaryChars() { 268 return 269 cmap instanceof CMap.CMapFormat8 || 270 cmap instanceof CMap.CMapFormat10 || 271 cmap instanceof CMap.CMapFormat12; 272 } 273 }