1 /* 2 * Copyright (c) 2011, 2013, 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 < FontUtilities.MIN_LAYOUT_CHARCODE) { 79 continue; 80 } else if (FontUtilities.isComplexCharCode(code)) { 81 return true; 82 } else if (code >= 0x10000) { 83 i += 1; // Empty glyph slot after surrogate 84 continue; 85 } 86 } 87 88 return false; 89 } 90 91 public synchronized int charToGlyph(char unicode) { 92 final int glyph = cache.get(unicode); 93 if (glyph != 0) return glyph; 94 95 final char[] unicodeArray = new char[] { unicode }; 96 final int[] glyphArray = new int[1]; 97 98 nativeCharsToGlyphs(fFont.getNativeFontPtr(), 1, unicodeArray, glyphArray); 99 cache.put(unicode, glyphArray[0]); 100 101 return glyphArray[0]; 102 } 103 104 public synchronized int charToGlyph(int unicode) { 105 if (unicode >= 0x10000) { 106 int[] glyphs = new int[2]; 107 char[] surrogates = new char[2]; 108 int base = unicode - 0x10000; 109 surrogates[0] = (char)((base >>> 10) + HI_SURROGATE_START); 110 surrogates[1] = (char)((base % 0x400) + LO_SURROGATE_START); 111 charsToGlyphs(2, surrogates, glyphs); 112 return glyphs[0]; 113 } else { 114 return charToGlyph((char)unicode); 115 } 116 } 117 118 public synchronized void charsToGlyphs(int count, char[] unicodes, int[] glyphs) { 119 cache.get(count, unicodes, glyphs); 120 } 121 122 public synchronized void charsToGlyphs(int count, int[] unicodes, int[] glyphs) { 123 for (int i = 0; i < count; i++) { 124 glyphs[i] = charToGlyph(unicodes[i]); 125 }; 126 } 127 128 // This mapper returns either the glyph code, or if the character can be 129 // replaced on-the-fly using CoreText substitution; the negative unicode 130 // value. If this "glyph code int" is treated as an opaque code, it will 131 // strike and measure exactly as a real glyph code - whether the character 132 // is present or not. Missing characters for any font on the system will 133 // be returned as 0, as the getMissingGlyphCode() function above indicates. 134 private static native void nativeCharsToGlyphs(final long nativeFontPtr, 135 int count, char[] unicodes, 136 int[] glyphs); 137 138 private class Cache { 139 private static final int FIRST_LAYER_SIZE = 256; 140 private static final int SECOND_LAYER_SIZE = 16384; // 16384 = 128x128 141 142 private final int[] firstLayerCache = new int[FIRST_LAYER_SIZE]; 143 private SparseBitShiftingTwoLayerArray secondLayerCache; 144 private HashMap<Integer, Integer> generalCache; 145 146 Cache() { 147 // <rdar://problem/5331678> need to prevent getting '-1' stuck in the cache 148 firstLayerCache[1] = 1; 149 } 150 151 public synchronized int get(final int index) { 152 if (index < FIRST_LAYER_SIZE) { 153 // catch common glyphcodes 154 return firstLayerCache[index]; 155 } 156 157 if (index < SECOND_LAYER_SIZE) { 158 // catch common unicodes 159 if (secondLayerCache == null) return 0; 160 return secondLayerCache.get(index); 161 } 162 163 if (generalCache == null) return 0; 164 final Integer value = generalCache.get(index); 165 if (value == null) return 0; 166 return value.intValue(); 167 } 168 169 public synchronized void put(final int index, final int value) { 170 if (index < FIRST_LAYER_SIZE) { 171 // catch common glyphcodes 172 firstLayerCache[index] = value; 173 return; 174 } 175 176 if (index < SECOND_LAYER_SIZE) { 177 // catch common unicodes 178 if (secondLayerCache == null) { 179 secondLayerCache = new SparseBitShiftingTwoLayerArray(SECOND_LAYER_SIZE, 7); // 128x128 180 } 181 secondLayerCache.put(index, value); 182 return; 183 } 184 185 if (generalCache == null) { 186 generalCache = new HashMap<Integer, Integer>(); 187 } 188 189 generalCache.put(index, value); 190 } 191 192 private class SparseBitShiftingTwoLayerArray { 193 final int[][] cache; 194 final int shift; 195 final int secondLayerLength; 196 197 public SparseBitShiftingTwoLayerArray(final int size, 198 final int shift) 199 { 200 this.shift = shift; 201 this.cache = new int[1 << shift][]; 202 this.secondLayerLength = size >> shift; 203 } 204 205 public int get(final int index) { 206 final int firstIndex = index >> shift; 207 final int[] firstLayerRow = cache[firstIndex]; 208 if (firstLayerRow == null) return 0; 209 return firstLayerRow[index - (firstIndex * (1 << shift))]; 210 } 211 212 public void put(final int index, final int value) { 213 final int firstIndex = index >> shift; 214 int[] firstLayerRow = cache[firstIndex]; 215 if (firstLayerRow == null) { 216 cache[firstIndex] = firstLayerRow = new int[secondLayerLength]; 217 } 218 firstLayerRow[index - (firstIndex * (1 << shift))] = value; 219 } 220 } 221 222 public synchronized void get(int count, char[] indicies, int[] values) 223 { 224 // "missed" is the count of 'char' that are not mapped. 225 // Surrogates count for 2. 226 // unmappedChars is the unique list of these chars. 227 // unmappedCharIndices is the location in the original array 228 int missed = 0; 229 char[] unmappedChars = null; 230 int [] unmappedCharIndices = null; 231 232 for (int i = 0; i < count; i++){ 233 int code = indicies[i]; 234 if (code >= HI_SURROGATE_START && 235 code <= HI_SURROGATE_END && i < count - 1) 236 { 237 char low = indicies[i + 1]; 238 if (low >= LO_SURROGATE_START && low <= LO_SURROGATE_END) { 239 code = (code - HI_SURROGATE_START) * 0x400 + 240 low - LO_SURROGATE_START + 0x10000; 241 } 242 } 243 244 final int value = get(code); 245 if (value != 0 && value != -1) { 246 values[i] = value; 247 if (code >= 0x10000) { 248 values[i+1] = INVISIBLE_GLYPH_ID; 249 i++; 250 } 251 } else { 252 values[i] = 0; 253 put(code, -1); 254 if (unmappedChars == null) { 255 // This is likely to be longer than we need, 256 // but is the simplest and cheapest option. 257 unmappedChars = new char[indicies.length]; 258 unmappedCharIndices = new int[indicies.length]; 259 } 260 unmappedChars[missed] = indicies[i]; 261 unmappedCharIndices[missed] = i; 262 if (code >= 0x10000) { // was a surrogate pair 263 unmappedChars[++missed] = indicies[++i]; 264 } 265 missed++; 266 } 267 } 268 269 if (missed == 0) { 270 return; 271 } 272 273 final int[] glyphCodes = new int[missed]; 274 275 // bulk call to fill in the unmapped code points. 276 nativeCharsToGlyphs(fFont.getNativeFontPtr(), 277 missed, unmappedChars, glyphCodes); 278 279 for (int m = 0; m < missed; m++){ 280 int i = unmappedCharIndices[m]; 281 int code = unmappedChars[m]; 282 if (code >= HI_SURROGATE_START && 283 code <= HI_SURROGATE_END && m < missed - 1) 284 { 285 char low = unmappedChars[m + 1]; 286 if (low >= LO_SURROGATE_START && low <= LO_SURROGATE_END) { 287 code = (code - HI_SURROGATE_START) * 0x400 + 288 low - LO_SURROGATE_START + 0x10000; 289 } 290 } 291 values[i] = glyphCodes[m]; 292 put(code, values[i]); 293 if (code >= 0x10000) { 294 m++; 295 values[i + 1] = INVISIBLE_GLYPH_ID; 296 } 297 } 298 } 299 } 300 }