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 if (mapper instanceof TrueTypeGlyphMapper) { 144 int glyphCode = ((TrueTypeGlyphMapper)mapper). 145 getGlyphOfVS(unicode, variationSelector); 146 if (glyphCode != mapper.getMissingGlyphCode()) { 147 glyphCode = compositeGlyphCode(slot, glyphCode); 148 return glyphCode; 149 } 150 } 151 } 152 } 153 return convertToGlyph(unicode); //retry without Variation Selector 154 } 155 156 public int getNumGlyphs() { 157 int numGlyphs = 0; 158 /* The number of glyphs in a composite is affected by 159 * exclusion ranges and duplicates (ie the same code point is 160 * mapped by two different fonts) and also whether or not to 161 * count fallback fonts. A nearly correct answer would be very 162 * expensive to generate. A rough ballpark answer would 163 * just count the glyphs in all the slots. However this would 164 * initialize mappers for all slots when they aren't necessarily 165 * needed. For now just use the first slot as JDK 1.4 did. 166 */ 167 for (int slot=0; slot<1 /*font.numSlots*/; slot++) { 168 CharToGlyphMapper mapper = slotMappers[slot]; 169 if (mapper == null) { 170 mapper = font.getSlotFont(slot).getMapper(); 171 slotMappers[slot] = mapper; 172 } 173 numGlyphs += mapper.getNumGlyphs(); 174 } 175 return numGlyphs; 176 } 177 178 public int charToGlyph(int unicode) { 179 180 int glyphCode = getCachedGlyphCode(unicode); 181 if (glyphCode == UNINITIALIZED_GLYPH) { 182 glyphCode = convertToGlyph(unicode); 183 } 184 return glyphCode; 185 } 186 187 public int charToGlyph(int unicode, int prefSlot) { 188 if (prefSlot >= 0) { 189 CharToGlyphMapper mapper = getSlotMapper(prefSlot); 190 int glyphCode = mapper.charToGlyph(unicode); 191 if (glyphCode != mapper.getMissingGlyphCode()) { 192 return compositeGlyphCode(prefSlot, glyphCode); 193 } 194 } 195 return charToGlyph(unicode); 196 } 197 198 public int charToGlyph(char unicode) { 199 200 int glyphCode = getCachedGlyphCode(unicode); 201 if (glyphCode == UNINITIALIZED_GLYPH) { 202 glyphCode = convertToGlyph(unicode); 203 } 204 return glyphCode; 205 } 206 207 /* This variant checks if shaping is needed and immediately 208 * returns true if it does. A caller of this method should be expecting 209 * to check the return type because it needs to know how to handle 210 * the character data for display. 211 */ 212 public boolean charsToGlyphsNS(int count, char[] unicodes, int[] glyphs) { 213 214 for (int i=0; i<count; i++) { 215 int code = unicodes[i]; // char is unsigned. 216 217 if (code >= HI_SURROGATE_START && 218 code <= HI_SURROGATE_END && i < count - 1) { 219 char low = unicodes[i + 1]; 220 221 if (low >= LO_SURROGATE_START && 222 low <= LO_SURROGATE_END) { 223 code = (code - HI_SURROGATE_START) * 224 0x400 + low - LO_SURROGATE_START + 0x10000; 225 glyphs[i + 1] = INVISIBLE_GLYPH_ID; 226 } 227 } 228 if (isVariationSelector(code)) { 229 return charsToGlyphsNSVS(count, unicodes, glyphs); 230 } 231 232 int gc = glyphs[i] = getCachedGlyphCode(code); 233 if (gc == UNINITIALIZED_GLYPH) { 234 glyphs[i] = convertToGlyph(code); 235 } 236 237 if (code < FontUtilities.MIN_LAYOUT_CHARCODE) { 238 continue; 239 } 240 else if (FontUtilities.isComplexCharCode(code)) { 241 return true; 242 } 243 else if (code >= 0x10000) { 244 i += 1; // Empty glyph slot after surrogate 245 continue; 246 } 247 } 248 249 return false; 250 } 251 252 private boolean charsToGlyphsNSVS(int count, char[] unicodes, 253 int[] glyphs) { 254 255 for (int i = 0; i < count; i++) { 256 int code = unicodes[i]; // char is unsigned. 257 int step = 1; 258 int variationSelector = 0; 259 260 if (code >= HI_SURROGATE_START && 261 code <= HI_SURROGATE_END && i < count - 1) { 262 char low = unicodes[i + 1]; 263 264 if (low >= LO_SURROGATE_START && 265 low <= LO_SURROGATE_END) { 266 code = (code - HI_SURROGATE_START) * 267 0x400 + low - LO_SURROGATE_START + 0x10000; 268 glyphs[i + 1] = INVISIBLE_GLYPH_ID; 269 step = 2; 270 } 271 } 272 273 if (i < count - step && 274 isVariationSelectorBMP(unicodes[i+step]) && 275 isVSBaseChar(code)) { 276 variationSelector = unicodes[i+step]; 277 glyphs[i] = convertToGlyph(code, variationSelector); 278 glyphs[i+step] = INVISIBLE_GLYPH_ID; 279 i += 1; 280 } else if (i < count - step -1 && 281 isVariationSelectorExt(unicodes[i+step], 282 unicodes[i+step+1]) && 283 isVSBaseChar(code)) { 284 variationSelector = (unicodes[i+step] 285 - HI_SURROGATE_START) * 0x400 286 + unicodes[i+step+1] - LO_SURROGATE_START 287 + 0x10000; 288 glyphs[i] = convertToGlyph(code, variationSelector); 289 glyphs[i+step] = INVISIBLE_GLYPH_ID; 290 glyphs[i+step+1] = INVISIBLE_GLYPH_ID; 291 i += 2; 292 } 293 if (variationSelector == 0) { 294 int gc = glyphs[i] = getCachedGlyphCode(code); 295 if (gc == UNINITIALIZED_GLYPH) { 296 glyphs[i] = convertToGlyph(code); 297 } 298 } 299 300 if (code < FontUtilities.MIN_LAYOUT_CHARCODE) { 301 continue; 302 } 303 else if (FontUtilities.isComplexCharCode(code)) { 304 return true; 305 } 306 else if (code >= 0x10000) { 307 i += 1; // Empty glyph slot after surrogate 308 continue; 309 } 310 } 311 312 return false; 313 } 314 315 /* The conversion is not very efficient - looping as it does, converting 316 * one char at a time. However the cache should fill very rapidly. 317 */ 318 public void charsToGlyphs(int count, char[] unicodes, int[] glyphs) { 319 for (int i=0; i<count; i++) { 320 int code = unicodes[i]; // char is unsigned. 321 322 if (code >= HI_SURROGATE_START && 323 code <= HI_SURROGATE_END && i < count - 1) { 324 char low = unicodes[i + 1]; 325 326 if (low >= LO_SURROGATE_START && 327 low <= LO_SURROGATE_END) { 328 code = (code - HI_SURROGATE_START) * 329 0x400 + low - LO_SURROGATE_START + 0x10000; 330 if (isVariationSelector(code)) { 331 charsToGlyphsVS(count, unicodes, glyphs); 332 return; 333 } 334 335 int gc = glyphs[i] = getCachedGlyphCode(code); 336 if (gc == UNINITIALIZED_GLYPH) { 337 glyphs[i] = convertToGlyph(code); 338 } 339 i += 1; // Empty glyph slot after surrogate 340 glyphs[i] = INVISIBLE_GLYPH_ID; 341 continue; 342 } 343 } else if (isVariationSelectorBMP(unicodes[i])) { 344 charsToGlyphsVS(count, unicodes, glyphs); 345 return; 346 } 347 348 int gc = glyphs[i] = getCachedGlyphCode(code); 349 if (gc == UNINITIALIZED_GLYPH) { 350 glyphs[i] = convertToGlyph(code); 351 } 352 } 353 } 354 355 private void charsToGlyphsVS(int count, char[] unicodes, int[] glyphs) { 356 for (int i = 0; i < count; i++) { 357 int code = unicodes[i]; // char is unsigned. 358 int variationSelector = 0; 359 int step = 1; 360 361 if (code >= HI_SURROGATE_START && 362 code <= HI_SURROGATE_END && i < count - 1) { 363 char low = unicodes[i + 1]; 364 365 if (low >= LO_SURROGATE_START && 366 low <= LO_SURROGATE_END) { 367 code = (code - HI_SURROGATE_START) * 368 0x400 + low - LO_SURROGATE_START + 0x10000; 369 370 glyphs[i+1] = INVISIBLE_GLYPH_ID; 371 step = 2; 372 } 373 } 374 375 if (i < count - step && 376 isVariationSelectorBMP(unicodes[i+step]) && 377 isVSBaseChar(code)) { 378 variationSelector = unicodes[i+step]; 379 glyphs[i] = convertToGlyph(code, variationSelector); 380 glyphs[i+step] = INVISIBLE_GLYPH_ID; 381 i += 1; 382 } else if (i < count - step -1 && 383 isVariationSelectorExt(unicodes[i+step], 384 unicodes[i+step+1]) && 385 isVSBaseChar(code)) { 386 variationSelector = (unicodes[i+step] 387 - HI_SURROGATE_START) * 0x400 388 + unicodes[i+step+1] - LO_SURROGATE_START 389 + 0x10000; 390 glyphs[i] = convertToGlyph(code, variationSelector); 391 glyphs[i+step] = INVISIBLE_GLYPH_ID; 392 glyphs[i+step+1] = INVISIBLE_GLYPH_ID; 393 i += 2; 394 } 395 if (variationSelector == 0) { 396 int gc = glyphs[i] = getCachedGlyphCode(code); 397 if (gc == UNINITIALIZED_GLYPH) { 398 glyphs[i] = convertToGlyph(code); 399 } 400 } 401 if (code >= 0x10000) { 402 i++; 403 } 404 } 405 } 406 407 public void charsToGlyphs(int count, int[] unicodes, int[] glyphs) { 408 for (int i=0; i<count; i++) { 409 int code = unicodes[i]; 410 if (isVariationSelector(code)) { 411 charsToGlyphsVS(count, unicodes, glyphs); 412 return; 413 } 414 415 glyphs[i] = getCachedGlyphCode(code); 416 if (glyphs[i] == UNINITIALIZED_GLYPH) { 417 glyphs[i] = convertToGlyph(code); 418 } 419 } 420 } 421 422 private void charsToGlyphsVS(int count, int[] unicodes, int[] glyphs) { 423 for (int i = 0; i < count; i++) { 424 int code = unicodes[i]; 425 426 if (i < count-1 && 427 isVariationSelector(unicodes[i+1]) && 428 isVSBaseChar(code) ) { 429 glyphs[i] = convertToGlyph(code, unicodes[i+1]); 430 glyphs[i+1] = INVISIBLE_GLYPH_ID; 431 i++; 432 } else { 433 glyphs[i] = getCachedGlyphCode(code); 434 if (glyphs[i] == UNINITIALIZED_GLYPH) { 435 glyphs[i] = convertToGlyph(code); 436 } 437 } 438 } 439 } 440 441 }