1 /* 2 * Copyright (c) 2003, 2020, 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 42 TrueTypeFont font; 43 CMap cmap; 44 int numGlyphs; 45 46 public TrueTypeGlyphMapper(TrueTypeFont font) { 47 this.font = font; 48 try { 49 cmap = CMap.initialize(font); 50 } catch (Exception e) { 51 cmap = null; 52 } 53 if (cmap == null) { 54 handleBadCMAP(); 55 } 56 missingGlyph = 0; /* standard for TrueType fonts */ 57 ByteBuffer buffer = font.getTableBuffer(TrueTypeFont.maxpTag); 58 if (buffer != null && buffer.capacity() >= 6) { 59 numGlyphs = buffer.getChar(4); // offset 4 bytes in MAXP table. 60 } else { 61 handleBadCMAP(); 62 } 63 } 64 65 public int getNumGlyphs() { 66 return numGlyphs; 67 } 68 69 private char getGlyphFromCMAP(int charCode) { 70 try { 71 char glyphCode = cmap.getGlyph(charCode); 72 if (glyphCode < numGlyphs || 73 glyphCode >= FileFontStrike.INVISIBLE_GLYPHS) { 74 return glyphCode; 75 } else { 76 if (FontUtilities.isLogging()) { 77 FontUtilities.getLogger().warning 78 (font + " out of range glyph id=" + 79 Integer.toHexString((int)glyphCode) + 80 " for char " + Integer.toHexString(charCode)); 81 } 82 return (char)missingGlyph; 83 } 84 } catch(Exception e) { 85 handleBadCMAP(); 86 return (char) missingGlyph; 87 } 88 } 89 90 private char getGlyphFromCMAP(int charCode, int variationSelector) { 91 if (variationSelector == 0) { 92 return getGlyphFromCMAP(charCode); 93 } 94 try { 95 char glyphCode = cmap.getVariationGlyph(charCode, 96 variationSelector); 97 if (glyphCode < numGlyphs || 98 glyphCode >= FileFontStrike.INVISIBLE_GLYPHS) { 99 return glyphCode; 100 } else { 101 if (FontUtilities.isLogging()) { 102 FontUtilities.getLogger().warning 103 (font + " out of range glyph id=" + 104 Integer.toHexString((int)glyphCode) + 105 " for char " + Integer.toHexString(charCode) + 106 " for vs " + Integer.toHexString(variationSelector)); 107 } 108 return (char)missingGlyph; 109 } 110 } catch (Exception e) { 111 handleBadCMAP(); 112 return (char) missingGlyph; 113 } 114 } 115 116 private void handleBadCMAP() { 117 if (FontUtilities.isLogging()) { 118 FontUtilities.getLogger().severe("Null Cmap for " + font + 119 "substituting for this font"); 120 } 121 SunFontManager.getInstance().deRegisterBadFont(font); 122 /* The next line is not really a solution, but might 123 * reduce the exceptions until references to this font2D 124 * are gone. 125 */ 126 cmap = CMap.theNullCmap; 127 } 128 129 private char remapJAChar(char unicode) { 130 return (unicode == REVERSE_SOLIDUS) ? JA_YEN : unicode; 131 } 132 133 private int remapJAIntChar(int unicode) { 134 return (unicode == REVERSE_SOLIDUS) ? JA_YEN : unicode; 135 } 136 137 public int charToGlyph(char unicode) { 138 int glyph = getGlyphFromCMAP(unicode); 139 return glyph; 140 } 141 142 public int charToGlyph(int unicode) { 143 int glyph = getGlyphFromCMAP(unicode); 144 return glyph; 145 } 146 147 @Override 148 public int charToVariationGlyph(int unicode, int variationSelector) { 149 int glyph = getGlyphFromCMAP(unicode, variationSelector); 150 return glyph; 151 } 152 153 public void charsToGlyphs(int count, int[] unicodes, int[] glyphs) { 154 for (int i=0;i<count;i++) { 155 glyphs[i] = getGlyphFromCMAP(unicodes[i]); 156 } 157 } 158 159 public void charsToGlyphs(int count, char[] unicodes, int[] glyphs) { 160 161 for (int i=0; i<count; i++) { 162 int code = unicodes[i]; // char is unsigned. 163 164 if (code >= HI_SURROGATE_START && 165 code <= HI_SURROGATE_END && i < count - 1) { 166 char low = unicodes[i + 1]; 167 168 if (low >= LO_SURROGATE_START && 169 low <= LO_SURROGATE_END) { 170 code = (code - HI_SURROGATE_START) * 171 0x400 + low - LO_SURROGATE_START + 0x10000; 172 173 glyphs[i] = getGlyphFromCMAP(code); 174 i += 1; // Empty glyph slot after surrogate 175 glyphs[i] = INVISIBLE_GLYPH_ID; 176 continue; 177 } 178 } 179 glyphs[i] = getGlyphFromCMAP(code); 180 181 } 182 } 183 184 /* This variant checks if shaping is needed and immediately 185 * returns true if it does. A caller of this method should be expecting 186 * to check the return type because it needs to know how to handle 187 * the character data for display. 188 */ 189 public boolean charsToGlyphsNS(int count, char[] unicodes, int[] glyphs) { 190 191 for (int i=0; i<count; i++) { 192 int code = unicodes[i]; // char is unsigned. 193 194 if (code >= HI_SURROGATE_START && 195 code <= HI_SURROGATE_END && i < count - 1) { 196 char low = unicodes[i + 1]; 197 198 if (low >= LO_SURROGATE_START && 199 low <= LO_SURROGATE_END) { 200 code = (code - HI_SURROGATE_START) * 201 0x400 + low - LO_SURROGATE_START + 0x10000; 202 glyphs[i + 1] = INVISIBLE_GLYPH_ID; 203 } 204 } 205 206 glyphs[i] = getGlyphFromCMAP(code); 207 208 if (code < FontUtilities.MIN_LAYOUT_CHARCODE) { 209 continue; 210 } 211 else if (FontUtilities.isComplexCharCode(code) || 212 CharToGlyphMapper.isVariationSelector(code)) { 213 return true; 214 } 215 else if (code >= 0x10000) { 216 i += 1; // Empty glyph slot after surrogate 217 continue; 218 } 219 } 220 221 return false; 222 } 223 224 /* A pretty good heuristic is that the cmap we are using 225 * supports 32 bit character codes. 226 */ 227 boolean hasSupplementaryChars() { 228 return 229 cmap instanceof CMap.CMapFormat8 || 230 cmap instanceof CMap.CMapFormat10 || 231 cmap instanceof CMap.CMapFormat12; 232 } 233 }