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 return charToGlyph((char)unicode); 134 } 135 136 public synchronized void charsToGlyphs(int count, char[] unicodes, int[] glyphs) { 137 cache.get(count, unicodes, glyphs); 138 } 139 140 public synchronized void charsToGlyphs(int count, int[] unicodes, int[] glyphs) { 141 final char[] unicodeChars = new char[count]; 142 for (int i = 0; i < count; i++) unicodeChars[i] = (char)unicodes[i]; 143 cache.get(count, unicodeChars, glyphs); 144 } 145 146 // This mapper returns either the glyph code, or if the character can be 147 // replaced on-the-fly using CoreText substitution; the negative unicode 148 // value. If this "glyph code int" is treated as an opaque code, it will 149 // strike and measure exactly as a real glyph code - whether the character 150 // is present or not. Missing characters for any font on the system will 151 // be returned as 0, as the getMissingGlyphCode() function above indicates. 152 private static native void nativeCharsToGlyphs(final long nativeFontPtr, 153 int count, char[] unicodes, 154 int[] glyphs); 155 156 private class Cache { 157 private static final int FIRST_LAYER_SIZE = 256; 158 private static final int SECOND_LAYER_SIZE = 16384; // 16384 = 128x128 159 160 private final int[] firstLayerCache = new int[FIRST_LAYER_SIZE]; 161 private SparseBitShiftingTwoLayerArray secondLayerCache; 162 private HashMap<Integer, Integer> generalCache; 163 164 Cache() { 165 // <rdar://problem/5331678> need to prevent getting '-1' stuck in the cache 166 firstLayerCache[1] = 1; 167 } 168 169 public int get(final char index) { 170 if (index < FIRST_LAYER_SIZE) { 171 // catch common glyphcodes 172 return firstLayerCache[index]; 173 } 174 175 if (index < SECOND_LAYER_SIZE) { 176 // catch common unicodes 177 if (secondLayerCache == null) return 0; 178 return secondLayerCache.get(index); 179 } 180 181 if (generalCache == null) return 0; 182 final Integer value = generalCache.get(new Integer(index)); 183 if (value == null) return 0; 184 return value.intValue(); 185 } 186 187 public void put(final char index, final int value) { 188 if (index < FIRST_LAYER_SIZE) { 189 // catch common glyphcodes 190 firstLayerCache[index] = value; 191 return; 192 } 193 194 if (index < SECOND_LAYER_SIZE) { 195 // catch common unicodes 196 if (secondLayerCache == null) { 197 secondLayerCache = new SparseBitShiftingTwoLayerArray(SECOND_LAYER_SIZE, 7); // 128x128 198 } 199 secondLayerCache.put(index, value); 200 return; 201 } 202 203 if (generalCache == null) { 204 generalCache = new HashMap<Integer, Integer>(); 205 } 206 207 generalCache.put(new Integer(index), new Integer(value)); 208 } 209 210 private class SparseBitShiftingTwoLayerArray { 211 final int[][] cache; 212 final int shift; 213 final int secondLayerLength; 214 215 public SparseBitShiftingTwoLayerArray(final int size, 216 final int shift) 217 { 218 this.shift = shift; 219 this.cache = new int[1 << shift][]; 220 this.secondLayerLength = size >> shift; 221 } 222 223 public int get(final char index) { 224 final int firstIndex = index >> shift; 225 final int[] firstLayerRow = cache[firstIndex]; 226 if (firstLayerRow == null) return 0; 227 return firstLayerRow[index - (firstIndex * (1 << shift))]; 228 } 229 230 public void put(final char index, final int value) { 231 final int firstIndex = index >> shift; 232 int[] firstLayerRow = cache[firstIndex]; 233 if (firstLayerRow == null) { 234 cache[firstIndex] = firstLayerRow = new int[secondLayerLength]; 235 } 236 firstLayerRow[index - (firstIndex * (1 << shift))] = value; 237 } 238 } 239 240 public void get(int count, char[] indicies, int[] values){ 241 int missed = 0; 242 for(int i = 0; i < count; i++){ 243 char code = indicies[i]; 244 245 final int value = get(code); 246 if(value != 0){ 247 values[i] = value; 248 }else{ 249 // zero this element out, because the caller does not 250 // promise to keep it clean 251 values[i] = 0; 252 missed++; 253 } 254 } 255 256 if (missed == 0) return; // horray! everything is already cached! 257 258 final char[] filteredCodes = new char[missed]; // all index codes requested (partially filled) 259 final int[] filteredIndicies = new int[missed]; // local indicies into filteredCodes array (totally filled) 260 261 // scan, mark, and store the index codes again to send into native 262 int j = 0; 263 int dupes = 0; 264 for (int i = 0; i < count; i++){ 265 if (values[i] != 0L) continue; // already filled 266 267 final char code = indicies[i]; 268 269 // we have already promised to fill this code - this is a dupe 270 if (get(code) == -1){ 271 filteredIndicies[j] = -1; 272 dupes++; 273 j++; 274 continue; 275 } 276 277 // this is a code we have not obtained before 278 // mark this one as "promise to get" in the global cache with a -1 279 final int k = j - dupes; 280 filteredCodes[k] = code; 281 put(code, -1); 282 filteredIndicies[j] = k; 283 j++; 284 } 285 286 final int filteredRunLen = j - dupes; 287 final int[] filteredValues = new int[filteredRunLen]; 288 289 // bulk call to fill in the distinct values 290 nativeCharsToGlyphs(fFont.getNativeFontPtr(), filteredRunLen, filteredCodes, filteredValues); 291 292 // scan the requested list, and fill in values from our 293 // distinct code list which has been filled from "getDistinct" 294 j = 0; 295 for (int i = 0; i < count; i++){ 296 if (values[i] != 0L && values[i] != -1L) continue; // already placed 297 298 final int k = filteredIndicies[j]; // index into filteredImages array 299 final char code = indicies[i]; 300 if(k == -1L){ 301 // we should have already filled the cache with this value 302 values[i] = get(code); 303 }else{ 304 // fill the particular code request, and store in the cache 305 final int ptr = filteredValues[k]; 306 values[i] = ptr; 307 put(code, ptr); 308 } 309 310 j++; 311 } 312 } 313 } 314 }