1 /* 2 * Copyright (c) 2003, 2006, 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 /* remember that the API requires a Font use a 29 * consistent glyph id. for a code point, and this is a 30 * problem if a particular strike uses native scaler sometimes 31 * and T2K others. That needs to be dealt with somewhere, but 32 * here we can just always get the same glyph code without 33 * needing a strike. 34 * 35 * The C implementation would cache the results of anything up 36 * to the maximum surrogate pair code point. 37 * This implementation will not cache as much, since the storage 38 * requirements are not justifiable. Even so it still can use up 39 * to 216*256*4 bytes of storage per composite font. If an app 40 * calls canDisplay on this range for all 20 composite fonts that's 41 * over 1Mb of cached data. May need to employ WeakReferences if 42 * this appears to cause problems. 43 */ 44 45 public class CompositeGlyphMapper extends CharToGlyphMapper { 46 47 public static final int SLOTMASK = 0xff000000; 48 public static final int GLYPHMASK = 0x00ffffff; 49 50 public static final int NBLOCKS = 216; 51 public static final int BLOCKSZ = 256; 52 public static final int MAXUNICODE = NBLOCKS*BLOCKSZ; 53 54 55 CompositeFont font; 56 CharToGlyphMapper slotMappers[]; 57 int[][] glyphMaps; 58 private boolean hasExcludes; 59 60 public CompositeGlyphMapper(CompositeFont compFont) { 61 font = compFont; 62 initMapper(); 63 /* This is often false which saves the overhead of a 64 * per-mapped char method call. 65 */ 66 hasExcludes = compFont.exclusionRanges != null && 67 compFont.maxIndices != null; 68 } 69 70 public int compositeGlyphCode(int slot, int glyphCode) { 71 return (slot << 24 | (glyphCode & GLYPHMASK)); 72 } 73 74 private void initMapper() { 75 if (missingGlyph == CharToGlyphMapper.UNINITIALIZED_GLYPH) { 76 if (glyphMaps == null) { 77 glyphMaps = new int[NBLOCKS][]; 78 } 79 slotMappers = new CharToGlyphMapper[font.numSlots]; 80 /* This requires that slot 0 is never empty. */ 81 missingGlyph = font.getSlotFont(0).getMissingGlyphCode(); 82 missingGlyph = compositeGlyphCode(0, missingGlyph); 83 } 84 } 85 86 private int getCachedGlyphCode(int unicode) { 87 if (unicode >= MAXUNICODE) { 88 return UNINITIALIZED_GLYPH; // don't cache surrogates 89 } 90 int[] gmap; 91 if ((gmap = glyphMaps[unicode >> 8]) == null) { 92 return UNINITIALIZED_GLYPH; 93 } 94 return gmap[unicode & 0xff]; 95 } 96 97 private void setCachedGlyphCode(int unicode, int glyphCode) { 98 if (unicode >= MAXUNICODE) { 99 return; // don't cache surrogates 100 } 101 int index0 = unicode >> 8; 102 if (glyphMaps[index0] == null) { 103 glyphMaps[index0] = new int[BLOCKSZ]; 104 for (int i=0;i<BLOCKSZ;i++) { 105 glyphMaps[index0][i] = UNINITIALIZED_GLYPH; 106 } 107 } 108 glyphMaps[index0][unicode & 0xff] = glyphCode; 109 } 110 111 private CharToGlyphMapper getSlotMapper(int slot) { 112 CharToGlyphMapper mapper = slotMappers[slot]; 113 if (mapper == null) { 114 mapper = font.getSlotFont(slot).getMapper(); 115 slotMappers[slot] = mapper; 116 } 117 return mapper; 118 } 119 120 private int convertToGlyph(int unicode) { 121 122 for (int slot = 0; slot < font.numSlots; slot++) { 123 if (!hasExcludes || !font.isExcludedChar(slot, unicode)) { 124 CharToGlyphMapper mapper = getSlotMapper(slot); 125 int glyphCode = mapper.charToGlyph(unicode); 126 if (glyphCode != mapper.getMissingGlyphCode()) { 127 glyphCode = compositeGlyphCode(slot, glyphCode); 128 setCachedGlyphCode(unicode, glyphCode); 129 return glyphCode; 130 } 131 } 132 } 133 return missingGlyph; 134 } 135 136 private int convertToGlyph(int unicode, int variationSelector) { 137 if (variationSelector == 0) { 138 return convertToGlyph(unicode); 139 } 140 for (int slot = 0; slot < font.numSlots; slot++) { 141 if (!hasExcludes || !font.isExcludedChar(slot, unicode)) { 142 CharToGlyphMapper mapper = getSlotMapper(slot); 143 int glyphCode = missingGlyph; 144 if (mapper.hasVariationSelectorGlyph(unicode, 145 variationSelector)) { 146 int glyphCodes[] = { 0, 0}; 147 int codes[] = {unicode, variationSelector}; 148 mapper.charsToGlyphs(2, codes, glyphCodes); 149 glyphCode = glyphCodes[0]; 150 if (glyphCode != mapper.getMissingGlyphCode()) { 151 glyphCode = compositeGlyphCode(slot, glyphCode); 152 return glyphCode; 153 } 154 } 155 } 156 } 157 return convertToGlyph(unicode); //retry without Variation Selector 158 } 159 160 public int getNumGlyphs() { 161 int numGlyphs = 0; 162 /* The number of glyphs in a composite is affected by 163 * exclusion ranges and duplicates (ie the same code point is 164 * mapped by two different fonts) and also whether or not to 165 * count fallback fonts. A nearly correct answer would be very 166 * expensive to generate. A rough ballpark answer would 167 * just count the glyphs in all the slots. However this would 168 * initialize mappers for all slots when they aren't necessarily 169 * needed. For now just use the first slot as JDK 1.4 did. 170 */ 171 for (int slot=0; slot<1 /*font.numSlots*/; slot++) { 172 CharToGlyphMapper mapper = slotMappers[slot]; 173 if (mapper == null) { 174 mapper = font.getSlotFont(slot).getMapper(); 175 slotMappers[slot] = mapper; 176 } 177 numGlyphs += mapper.getNumGlyphs(); 178 } 179 return numGlyphs; 180 } 181 182 public int charToGlyph(int unicode) { 183 184 int glyphCode = getCachedGlyphCode(unicode); 185 if (glyphCode == UNINITIALIZED_GLYPH) { 186 glyphCode = convertToGlyph(unicode); 187 } 188 return glyphCode; 189 } 190 191 public int charToGlyph(int unicode, int prefSlot) { 192 if (prefSlot >= 0) { 193 CharToGlyphMapper mapper = getSlotMapper(prefSlot); 194 int glyphCode = mapper.charToGlyph(unicode); 195 if (glyphCode != mapper.getMissingGlyphCode()) { 196 return compositeGlyphCode(prefSlot, glyphCode); 197 } 198 } 199 return charToGlyph(unicode); 200 } 201 202 public int charToGlyph(char unicode) { 203 204 int glyphCode = getCachedGlyphCode(unicode); 205 if (glyphCode == UNINITIALIZED_GLYPH) { 206 glyphCode = convertToGlyph(unicode); 207 } 208 return glyphCode; 209 } 210 211 /* This variant checks if shaping is needed and immediately 212 * returns true if it does. A caller of this method should be expecting 213 * to check the return type because it needs to know how to handle 214 * the character data for display. 215 */ 216 public boolean charsToGlyphsNS(int count, char[] unicodes, int[] glyphs) { 217 218 for (int i=0; i<count; i++) { 219 int code = unicodes[i]; // char is unsigned. 220 int step = 1; 221 int variationSelector = 0; 222 223 if (code >= HI_SURROGATE_START && 224 code <= HI_SURROGATE_END && i < count - 1) { 225 char low = unicodes[i + 1]; 226 227 if (low >= LO_SURROGATE_START && 228 low <= LO_SURROGATE_END) { 229 code = (code - HI_SURROGATE_START) * 230 0x400 + low - LO_SURROGATE_START + 0x10000; 231 glyphs[i + 1] = INVISIBLE_GLYPH_ID; 232 step = 2; 233 } 234 } 235 236 if (i < count - step && 237 isVariationSelector(unicodes[i+step]) && 238 isBaseChar(code)) { 239 variationSelector = unicodes[i+step]; 240 glyphs[i] = convertToGlyph(code, variationSelector); 241 glyphs[i+step] = INVISIBLE_GLYPH_ID; 242 i += 1; 243 } else if (i < count - step -1 && 244 isVariationSelector(unicodes[i+step], 245 unicodes[i+step+1]) && 246 isBaseChar(code)) { 247 variationSelector = (unicodes[i+step] 248 - HI_SURROGATE_START) * 0x400 249 + unicodes[i+step+1] - LO_SURROGATE_START 250 + 0x10000; 251 glyphs[i] = convertToGlyph(code, variationSelector); 252 glyphs[i+step] = INVISIBLE_GLYPH_ID; 253 glyphs[i+step+1] = INVISIBLE_GLYPH_ID; 254 i += 2; 255 } 256 if (variationSelector == 0) { 257 int gc = glyphs[i] = getCachedGlyphCode(code); 258 if (gc == UNINITIALIZED_GLYPH) { 259 glyphs[i] = convertToGlyph(code); 260 } 261 } 262 263 if (code < FontUtilities.MIN_LAYOUT_CHARCODE) { 264 continue; 265 } 266 else if (FontUtilities.isComplexCharCode(code)) { 267 return true; 268 } 269 else if (code >= 0x10000) { 270 i += 1; // Empty glyph slot after surrogate 271 continue; 272 } 273 } 274 275 return false; 276 } 277 278 /* The conversion is not very efficient - looping as it does, converting 279 * one char at a time. However the cache should fill very rapidly. 280 */ 281 public void charsToGlyphs(int count, char[] unicodes, int[] glyphs) { 282 for (int i=0; i<count; i++) { 283 int code = unicodes[i]; // char is unsigned. 284 int variationSelector = 0; 285 int step = 1; 286 287 if (code >= HI_SURROGATE_START && 288 code <= HI_SURROGATE_END && i < count - 1) { 289 char low = unicodes[i + 1]; 290 291 if (low >= LO_SURROGATE_START && 292 low <= LO_SURROGATE_END) { 293 code = (code - HI_SURROGATE_START) * 294 0x400 + low - LO_SURROGATE_START + 0x10000; 295 296 glyphs[i+1] = INVISIBLE_GLYPH_ID; 297 step = 2; 298 } 299 } 300 301 if (i < count - step && 302 isVariationSelector(unicodes[i+step]) && 303 isBaseChar(code)) { 304 variationSelector = unicodes[i+step]; 305 glyphs[i] = convertToGlyph(code, variationSelector); 306 glyphs[i+step] = INVISIBLE_GLYPH_ID; 307 i += 1; 308 } else if (i < count - step -1 && 309 isVariationSelector(unicodes[i+step], 310 unicodes[i+step+1]) && 311 isBaseChar(code)) { 312 variationSelector = (unicodes[i+step] 313 - HI_SURROGATE_START) * 0x400 314 + unicodes[i+step+1] - LO_SURROGATE_START 315 + 0x10000; 316 glyphs[i] = convertToGlyph(code, variationSelector); 317 glyphs[i+step] = INVISIBLE_GLYPH_ID; 318 glyphs[i+step+1] = INVISIBLE_GLYPH_ID; 319 i += 2; 320 } 321 if (variationSelector == 0) { 322 int gc = glyphs[i] = getCachedGlyphCode(code); 323 if (gc == UNINITIALIZED_GLYPH) { 324 glyphs[i] = convertToGlyph(code); 325 } 326 } 327 if (code >= 0x10000) { 328 i++; 329 } 330 } 331 } 332 333 public void charsToGlyphs(int count, int[] unicodes, int[] glyphs) { 334 for (int i=0; i<count; i++) { 335 int code = unicodes[i]; 336 337 if (i < count-1 338 && isVariationSelector(unicodes[i+1]) 339 && isBaseChar(code) ) { 340 glyphs[i] = convertToGlyph(code, unicodes[i+1]); 341 glyphs[i+1] = INVISIBLE_GLYPH_ID; 342 i++; 343 } else { 344 glyphs[i] = getCachedGlyphCode(code); 345 if (glyphs[i] == UNINITIALIZED_GLYPH) { 346 glyphs[i] = convertToGlyph(code); 347 } 348 } 349 } 350 } 351 352 }