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, variationSelector)) { 145 int glyphCodes[] = { 0, 0}; 146 int codes[] = {unicode, variationSelector}; 147 mapper.charsToGlyphs(2, codes, glyphCodes); 148 glyphCode = glyphCodes[0]; 149 if (glyphCode != mapper.getMissingGlyphCode()) { 150 glyphCode = compositeGlyphCode(slot, glyphCode); 151 return glyphCode; 152 } 153 } 154 } 155 } 156 return convertToGlyph(unicode); //retry without Variation Selector 157 } 158 159 public int getNumGlyphs() { 160 int numGlyphs = 0; 161 /* The number of glyphs in a composite is affected by 162 * exclusion ranges and duplicates (ie the same code point is 163 * mapped by two different fonts) and also whether or not to 164 * count fallback fonts. A nearly correct answer would be very 165 * expensive to generate. A rough ballpark answer would 166 * just count the glyphs in all the slots. However this would 167 * initialize mappers for all slots when they aren't necessarily 168 * needed. For now just use the first slot as JDK 1.4 did. 169 */ 170 for (int slot=0; slot<1 /*font.numSlots*/; slot++) { 171 CharToGlyphMapper mapper = slotMappers[slot]; 172 if (mapper == null) { 173 mapper = font.getSlotFont(slot).getMapper(); 174 slotMappers[slot] = mapper; 175 } 176 numGlyphs += mapper.getNumGlyphs(); 177 } 178 return numGlyphs; 179 } 180 181 public int charToGlyph(int unicode) { 182 183 int glyphCode = getCachedGlyphCode(unicode); 184 if (glyphCode == UNINITIALIZED_GLYPH) { 185 glyphCode = convertToGlyph(unicode); 186 } 187 return glyphCode; 188 } 189 190 public int charToGlyph(int unicode, int prefSlot) { 191 if (prefSlot >= 0) { 192 CharToGlyphMapper mapper = getSlotMapper(prefSlot); 193 int glyphCode = mapper.charToGlyph(unicode); 194 if (glyphCode != mapper.getMissingGlyphCode()) { 195 return compositeGlyphCode(prefSlot, glyphCode); 196 } 197 } 198 return charToGlyph(unicode); 199 } 200 201 public int charToGlyph(char unicode) { 202 203 int glyphCode = getCachedGlyphCode(unicode); 204 if (glyphCode == UNINITIALIZED_GLYPH) { 205 glyphCode = convertToGlyph(unicode); 206 } 207 return glyphCode; 208 } 209 210 /* This variant checks if shaping is needed and immediately 211 * returns true if it does. A caller of this method should be expecting 212 * to check the return type because it needs to know how to handle 213 * the character data for display. 214 */ 215 public boolean charsToGlyphsNS(int count, char[] unicodes, int[] glyphs) { 216 217 for (int i=0; i<count; i++) { 218 int code = unicodes[i]; // char is unsigned. 219 int step = 1; 220 int variationSelector = 0; 221 222 if (code >= HI_SURROGATE_START && 223 code <= HI_SURROGATE_END && i < count - 1) { 224 char low = unicodes[i + 1]; 225 226 if (low >= LO_SURROGATE_START && 227 low <= LO_SURROGATE_END) { 228 code = (code - HI_SURROGATE_START) * 229 0x400 + low - LO_SURROGATE_START + 0x10000; 230 glyphs[i + 1] = INVISIBLE_GLYPH_ID; 231 step = 2; 232 } 233 } 234 235 if (i < count - step && 236 isVariationSelector(unicodes[i+step]) && 237 isBaseChar(code)) { 238 variationSelector = unicodes[i+step]; 239 glyphs[i] = convertToGlyph(code, variationSelector); 240 glyphs[i+step] = INVISIBLE_GLYPH_ID; 241 i += 1; 242 } else if (i < count - step -1 && 243 isVariationSelector(unicodes[i+step], unicodes[i+step+1]) && 244 isBaseChar(code)) { 245 variationSelector = (unicodes[i+step] - HI_SURROGATE_START) * 246 0x400 + unicodes[i+step+1] - LO_SURROGATE_START + 0x10000; 247 glyphs[i] = convertToGlyph(code, variationSelector); 248 glyphs[i+step] = INVISIBLE_GLYPH_ID; 249 glyphs[i+step+1] = INVISIBLE_GLYPH_ID; 250 i += 2; 251 } 252 if (variationSelector == 0) { 253 int gc = glyphs[i] = getCachedGlyphCode(code); 254 if (gc == UNINITIALIZED_GLYPH) { 255 glyphs[i] = convertToGlyph(code); 256 } 257 } 258 259 if (code < FontUtilities.MIN_LAYOUT_CHARCODE) { 260 continue; 261 } 262 else if (FontUtilities.isComplexCharCode(code)) { 263 return true; 264 } 265 else if (code >= 0x10000) { 266 i += 1; // Empty glyph slot after surrogate 267 continue; 268 } 269 } 270 271 return false; 272 } 273 274 /* The conversion is not very efficient - looping as it does, converting 275 * one char at a time. However the cache should fill very rapidly. 276 */ 277 public void charsToGlyphs(int count, char[] unicodes, int[] glyphs) { 278 for (int i=0; i<count; i++) { 279 int code = unicodes[i]; // char is unsigned. 280 int variationSelector = 0; 281 int step = 1; 282 283 if (code >= HI_SURROGATE_START && 284 code <= HI_SURROGATE_END && i < count - 1) { 285 char low = unicodes[i + 1]; 286 287 if (low >= LO_SURROGATE_START && 288 low <= LO_SURROGATE_END) { 289 code = (code - HI_SURROGATE_START) * 290 0x400 + low - LO_SURROGATE_START + 0x10000; 291 292 glyphs[i+1] = INVISIBLE_GLYPH_ID; 293 step = 2; 294 } 295 } 296 297 if (i < count - step && 298 isVariationSelector(unicodes[i+step]) && 299 isBaseChar(code)) { 300 variationSelector = unicodes[i+step]; 301 glyphs[i] = convertToGlyph(code, variationSelector); 302 glyphs[i+step] = INVISIBLE_GLYPH_ID; 303 i += 1; 304 } else if (i < count - step -1 && 305 isVariationSelector(unicodes[i+step], unicodes[i+step+1]) && 306 isBaseChar(code)) { 307 variationSelector = (unicodes[i+step] - HI_SURROGATE_START) * 308 0x400 + unicodes[i+step+1] - LO_SURROGATE_START + 0x10000; 309 glyphs[i] = convertToGlyph(code, variationSelector); 310 glyphs[i+step] = INVISIBLE_GLYPH_ID; 311 glyphs[i+step+1] = INVISIBLE_GLYPH_ID; 312 i += 2; 313 } 314 if (variationSelector == 0) { 315 int gc = glyphs[i] = getCachedGlyphCode(code); 316 if (gc == UNINITIALIZED_GLYPH) { 317 glyphs[i] = convertToGlyph(code); 318 } 319 } 320 if (code >= 0x10000) { 321 i++; 322 } 323 } 324 } 325 326 public void charsToGlyphs(int count, int[] unicodes, int[] glyphs) { 327 for (int i=0; i<count; i++) { 328 int code = unicodes[i]; 329 330 if (i < count-1 331 && isVariationSelector(unicodes[i+1]) 332 && isBaseChar(code) ) { 333 glyphs[i] = convertToGlyph(code, unicodes[i+1]); 334 glyphs[i+1] = INVISIBLE_GLYPH_ID; 335 i++; 336 }else{ 337 glyphs[i] = getCachedGlyphCode(code); 338 if (glyphs[i] == UNINITIALIZED_GLYPH) { 339 glyphs[i] = convertToGlyph(code); 340 } 341 } 342 } 343 } 344 345 }