1 /* 2 * Copyright 2003-2006 Sun Microsystems, Inc. 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. Sun designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 22 * CA 95054 USA or visit www.sun.com if you need additional information or 23 * have any 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 private final char remapJAChar(char unicode) { 113 switch (unicode) { 114 case REVERSE_SOLIDUS: 115 return JA_YEN; 116 /* This is a workaround for bug 4533422. 117 * Japanese wave dash missing from Solaris JA TrueType fonts. 118 */ 119 case JA_WAVE_DASH_CHAR: 120 if (remapJAWaveDash) { 121 return JA_FULLWIDTH_TILDE_CHAR; 122 } 123 default: return unicode; 124 } 125 } 126 private final int remapJAIntChar(int unicode) { 127 switch (unicode) { 128 case REVERSE_SOLIDUS: 129 return JA_YEN; 130 /* This is a workaround for bug 4533422. 131 * Japanese wave dash missing from Solaris JA TrueType fonts. 132 */ 133 case JA_WAVE_DASH_CHAR: 134 if (remapJAWaveDash) { 135 return JA_FULLWIDTH_TILDE_CHAR; 136 } 137 default: return unicode; 138 } 139 } 140 141 public int charToGlyph(char unicode) { 142 if (needsJAremapping) { 143 unicode = remapJAChar(unicode); 144 } 145 int glyph = getGlyphFromCMAP(unicode); 146 if (font.checkUseNatives() && glyph < font.glyphToCharMap.length) { 147 font.glyphToCharMap[glyph] = unicode; 148 } 149 return glyph; 150 } 151 152 public int charToGlyph(int unicode) { 153 if (needsJAremapping) { 154 unicode = remapJAIntChar(unicode); 155 } 156 int glyph = getGlyphFromCMAP(unicode); 157 if (font.checkUseNatives() && glyph < font.glyphToCharMap.length) { 158 font.glyphToCharMap[glyph] = (char)unicode; 159 } 160 return glyph; 161 } 162 163 public void charsToGlyphs(int count, int[] unicodes, int[] glyphs) { 164 for (int i=0;i<count;i++) { 165 if (needsJAremapping) { 166 glyphs[i] = getGlyphFromCMAP(remapJAIntChar(unicodes[i])); 167 } else { 168 glyphs[i] = getGlyphFromCMAP(unicodes[i]); 169 } 170 if (font.checkUseNatives() && 171 glyphs[i] < font.glyphToCharMap.length) { 172 font.glyphToCharMap[glyphs[i]] = (char)unicodes[i]; 173 } 174 } 175 } 176 177 public void charsToGlyphs(int count, char[] unicodes, int[] glyphs) { 178 179 for (int i=0; i<count; i++) { 180 int code; 181 if (needsJAremapping) { 182 code = remapJAChar(unicodes[i]); 183 } else { 184 code = unicodes[i]; // char is unsigned. 185 } 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 196 glyphs[i] = getGlyphFromCMAP(code); 197 i += 1; // Empty glyph slot after surrogate 198 glyphs[i] = INVISIBLE_GLYPH_ID; 199 continue; 200 } 201 } 202 glyphs[i] = getGlyphFromCMAP(code); 203 204 if (font.checkUseNatives() && 205 glyphs[i] < font.glyphToCharMap.length) { 206 font.glyphToCharMap[glyphs[i]] = (char)code; 207 } 208 209 } 210 } 211 212 /* This variant checks if shaping is needed and immediately 213 * returns true if it does. A caller of this method should be expecting 214 * to check the return type because it needs to know how to handle 215 * the character data for display. 216 */ 217 public boolean charsToGlyphsNS(int count, char[] unicodes, int[] glyphs) { 218 219 for (int i=0; i<count; i++) { 220 int code; 221 if (needsJAremapping) { 222 code = remapJAChar(unicodes[i]); 223 } else { 224 code = unicodes[i]; // char is unsigned. 225 } 226 227 if (code >= HI_SURROGATE_START && 228 code <= HI_SURROGATE_END && i < count - 1) { 229 char low = unicodes[i + 1]; 230 231 if (low >= LO_SURROGATE_START && 232 low <= LO_SURROGATE_END) { 233 code = (code - HI_SURROGATE_START) * 234 0x400 + low - LO_SURROGATE_START + 0x10000; 235 glyphs[i + 1] = INVISIBLE_GLYPH_ID; 236 } 237 } 238 239 glyphs[i] = getGlyphFromCMAP(code); 240 if (font.checkUseNatives() && 241 glyphs[i] < font.glyphToCharMap.length) { 242 font.glyphToCharMap[glyphs[i]] = (char)code; 243 } 244 245 if (code < FontUtilities.MIN_LAYOUT_CHARCODE) { 246 continue; 247 } 248 else if (FontUtilities.isComplexCharCode(code)) { 249 return true; 250 } 251 else if (code >= 0x10000) { 252 i += 1; // Empty glyph slot after surrogate 253 continue; 254 } 255 } 256 257 return false; 258 } 259 260 /* A pretty good heuristic is that the cmap we are using 261 * supports 32 bit character codes. 262 */ 263 boolean hasSupplementaryChars() { 264 return 265 cmap instanceof CMap.CMapFormat8 || 266 cmap instanceof CMap.CMapFormat10 || 267 cmap instanceof CMap.CMapFormat12; 268 } 269 }