blob: ce2c12d936543d25afec2ad97c3b09f023f2aa12 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
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
26package sun.font;
27
28import java.nio.ByteBuffer;
29import java.util.Locale;
30
31public 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 (FontManager.isSolaris && isJAlocale && font.supportsJA()) {
64 needsJAremapping = true;
65 if (FontManager.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 (FontManager.logging) {
86 FontManager.logger.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 (FontManager.logging) {
101 FontManager.logger.severe("Null Cmap for " + font +
102 "substituting for this font");
103 }
104 FontManager.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 < FontManager.MIN_LAYOUT_CHARCODE) {
246 continue;
247 }
248 else if (FontManager.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}