1 /* 2 * Copyright (c) 2011, 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.util.HashMap; 29 30 public class CCharToGlyphMapper extends CharToGlyphMapper { 31 private static native int countGlyphs(final long nativeFontPtr); 32 33 private Cache cache = new Cache(); 34 CFont fFont; 35 int numGlyphs = -1; 36 37 public CCharToGlyphMapper(CFont font) { 38 fFont = font; 39 missingGlyph = 0; // for getMissingGlyphCode() 40 } 41 42 public int getNumGlyphs() { 43 if (numGlyphs == -1) { 44 numGlyphs = countGlyphs(fFont.getNativeFontPtr()); 45 } 46 return numGlyphs; 47 } 48 49 public boolean canDisplay(char ch) { 50 int glyph = charToGlyph(ch); 51 return glyph != missingGlyph; 52 } 53 54 public boolean canDisplay(int cp) { 55 int glyph = charToGlyph(cp); 56 return glyph != missingGlyph; 57 } 58 59 public synchronized boolean charsToGlyphsNS(int count, 60 char[] unicodes, int[] glyphs) 61 { 62 charsToGlyphs(count, unicodes, glyphs); 63 64 // The following shaping checks are from either 65 // TrueTypeGlyphMapper or Type1GlyphMapper 66 for (int i = 0; i < count; i++) { 67 int code = unicodes[i]; 68 69 if (code >= HI_SURROGATE_START && code <= HI_SURROGATE_END && i < count - 1) { 70 char low = unicodes[i + 1]; 71 72 if (low >= LO_SURROGATE_START && low <= LO_SURROGATE_END) { 73 code = (code - HI_SURROGATE_START) * 0x400 + low - LO_SURROGATE_START + 0x10000; 74 glyphs[i + 1] = INVISIBLE_GLYPH_ID; 75 } 76 } 77 78 if (code < 0x0590) { 79 continue; 80 } else if (code <= 0x05ff) { 81 // Hebrew 0x0590->0x05ff 82 return true; 83 } else if (code >= 0x0600 && code <= 0x06ff) { 84 // Arabic 85 return true; 86 } else if (code >= 0x0900 && code <= 0x0d7f) { 87 // if Indic, assume shaping for conjuncts, reordering: 88 // 0900 - 097F Devanagari 89 // 0980 - 09FF Bengali 90 // 0A00 - 0A7F Gurmukhi 91 // 0A80 - 0AFF Gujarati 92 // 0B00 - 0B7F Oriya 93 // 0B80 - 0BFF Tamil 94 // 0C00 - 0C7F Telugu 95 // 0C80 - 0CFF Kannada 96 // 0D00 - 0D7F Malayalam 97 return true; 98 } else if (code >= 0x0e00 && code <= 0x0e7f) { 99 // if Thai, assume shaping for vowel, tone marks 100 return true; 101 } else if (code >= 0x200c && code <= 0x200d) { 102 // zwj or zwnj 103 return true; 104 } else if (code >= 0x202a && code <= 0x202e) { 105 // directional control 106 return true; 107 } else if (code >= 0x206a && code <= 0x206f) { 108 // directional control 109 return true; 110 } else if (code >= 0x10000) { 111 i += 1; // Empty glyph slot after surrogate 112 continue; 113 } 114 } 115 116 return false; 117 } 118 119 public synchronized int charToGlyph(char unicode) { 120 final int glyph = cache.get(unicode); 121 if (glyph != 0) return glyph; 122 123 final char[] unicodeArray = new char[] { unicode }; 124 final int[] glyphArray = new int[1]; 125 126 nativeCharsToGlyphs(fFont.getNativeFontPtr(), 1, unicodeArray, glyphArray); 127 cache.put(unicode, glyphArray[0]); 128 129 return glyphArray[0]; 130 } 131 132 public synchronized int charToGlyph(int unicode) { 133 if (unicode >= 0x10000) { 134 int[] glyphs = new int[2]; 135 char[] surrogates = new char[2]; 136 int base = unicode - 0x10000; 137 surrogates[0] = (char)((base >>> 10) + HI_SURROGATE_START); 138 surrogates[1] = (char)((base % 0x400) + LO_SURROGATE_START); 139 charsToGlyphs(2, surrogates, glyphs); 140 return glyphs[0]; 141 } else 142 return charToGlyph((char)unicode); 143 } 144 145 public synchronized void charsToGlyphs(int count, char[] unicodes, int[] glyphs) { 146 cache.get(count, unicodes, glyphs); 147 } 148 149 public synchronized void charsToGlyphs(int count, int[] unicodes, int[] glyphs) { 150 for (int i = 0; i < count; i++) { 151 glyphs[i] = charToGlyph(unicodes[i]); 152 }; 153 } 154 155 // This mapper returns either the glyph code, or if the character can be 156 // replaced on-the-fly using CoreText substitution; the negative unicode 157 // value. If this "glyph code int" is treated as an opaque code, it will 158 // strike and measure exactly as a real glyph code - whether the character 159 // is present or not. Missing characters for any font on the system will 160 // be returned as 0, as the getMissingGlyphCode() function above indicates. 161 private static native void nativeCharsToGlyphs(final long nativeFontPtr, 162 int count, char[] unicodes, 163 int[] glyphs); 164 165 private class Cache { 166 private static final int FIRST_LAYER_SIZE = 256; 167 private static final int SECOND_LAYER_SIZE = 16384; // 16384 = 128x128 168 169 private final int[] firstLayerCache = new int[FIRST_LAYER_SIZE]; 170 private SparseBitShiftingTwoLayerArray secondLayerCache; 171 private HashMap<Integer, Integer> generalCache; 172 173 Cache() { 174 // <rdar://problem/5331678> need to prevent getting '-1' stuck in the cache 175 firstLayerCache[1] = 1; 176 } 177 178 public synchronized int get(final int index) { 179 if (index < FIRST_LAYER_SIZE) { 180 // catch common glyphcodes 181 return firstLayerCache[index]; 182 } 183 184 if (index < SECOND_LAYER_SIZE) { 185 // catch common unicodes 186 if (secondLayerCache == null) return 0; 187 return secondLayerCache.get(index); 188 } 189 190 if (generalCache == null) return 0; 191 final Integer value = generalCache.get(index); 192 if (value == null) return 0; 193 return value.intValue(); 194 } 195 196 public synchronized void put(final int index, final int value) { 197 if (index < FIRST_LAYER_SIZE) { 198 // catch common glyphcodes 199 firstLayerCache[index] = value; 200 return; 201 } 202 203 if (index < SECOND_LAYER_SIZE) { 204 // catch common unicodes 205 if (secondLayerCache == null) { 206 secondLayerCache = new SparseBitShiftingTwoLayerArray(SECOND_LAYER_SIZE, 7); // 128x128 207 } 208 secondLayerCache.put(index, value); 209 return; 210 } 211 212 if (generalCache == null) { 213 generalCache = new HashMap<Integer, Integer>(); 214 } 215 216 generalCache.put(index, value); 217 } 218 219 private class SparseBitShiftingTwoLayerArray { 220 final int[][] cache; 221 final int shift; 222 final int secondLayerLength; 223 224 public SparseBitShiftingTwoLayerArray(final int size, 225 final int shift) 226 { 227 this.shift = shift; 228 this.cache = new int[1 << shift][]; 229 this.secondLayerLength = size >> shift; 230 } 231 232 public int get(final int index) { 233 final int firstIndex = index >> shift; 234 final int[] firstLayerRow = cache[firstIndex]; 235 if (firstLayerRow == null) return 0; 236 return firstLayerRow[index - (firstIndex * (1 << shift))]; 237 } 238 239 public void put(final int index, final int value) { 240 final int firstIndex = index >> shift; 241 int[] firstLayerRow = cache[firstIndex]; 242 if (firstLayerRow == null) { 243 cache[firstIndex] = firstLayerRow = new int[secondLayerLength]; 244 } 245 firstLayerRow[index - (firstIndex * (1 << shift))] = value; 246 } 247 } 248 249 public synchronized void get(int count, char[] indicies, int[] values) 250 { 251 // "missed" is the count of 'char' that are not mapped. 252 // Surrogates count for 2. 253 // unmappedChars is the unique list of these chars. 254 // unmappedCharIndices is the location in the original array 255 int missed = 0; 256 char[] unmappedChars = null; 257 int [] unmappedCharIndices = null; 258 259 for (int i = 0; i < count; i++){ 260 int code = indicies[i]; 261 if (code >= HI_SURROGATE_START && 262 code <= HI_SURROGATE_END && i < count - 1) 263 { 264 char low = indicies[i + 1]; 265 if (low >= LO_SURROGATE_START && low <= LO_SURROGATE_END) { 266 code = (code - HI_SURROGATE_START) * 0x400 + 267 low - LO_SURROGATE_START + 0x10000; 268 } 269 } 270 271 final int value = get(code); 272 if (value != 0 && value != -1) { 273 values[i] = value; 274 if (code >= 0x10000) { 275 values[i+1] = INVISIBLE_GLYPH_ID; 276 i++; 277 } 278 } else { 279 values[i] = 0; 280 put(code, -1); 281 if (unmappedChars == null) { 282 // This is likely to be longer than we need, 283 // but is the simplest and cheapest option. 284 unmappedChars = new char[indicies.length]; 285 unmappedCharIndices = new int[indicies.length]; 286 } 287 unmappedChars[missed] = indicies[i]; 288 unmappedCharIndices[missed] = i; 289 if (code >= 0x10000) { // was a surrogate pair 290 unmappedChars[++missed] = indicies[++i]; 291 } 292 missed++; 293 } 294 } 295 296 if (missed == 0) { 297 return; 298 } 299 300 final int[] glyphCodes = new int[missed]; 301 302 // bulk call to fill in the unmapped code points. 303 nativeCharsToGlyphs(fFont.getNativeFontPtr(), 304 missed, unmappedChars, glyphCodes); 305 306 for (int m = 0; m < missed; m++){ 307 int i = unmappedCharIndices[m]; 308 int code = unmappedChars[m]; 309 if (code >= HI_SURROGATE_START && 310 code <= HI_SURROGATE_END && m < missed - 1) 311 { 312 char low = indicies[m + 1]; 313 if (low >= LO_SURROGATE_START && low <= LO_SURROGATE_END) { 314 code = (code - HI_SURROGATE_START) * 0x400 + 315 low - LO_SURROGATE_START + 0x10000; 316 } 317 } 318 values[i] = glyphCodes[m]; 319 put(code, values[i]); 320 if (code >= 0x10000) { 321 m++; 322 values[i + 1] = INVISIBLE_GLYPH_ID; 323 } 324 } 325 } 326 } 327 } --- EOF ---