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