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 public int getNumGlyphs() { 137 int numGlyphs = 0; 138 /* The number of glyphs in a composite is affected by 139 * exclusion ranges and duplicates (ie the same code point is 140 * mapped by two different fonts) and also whether or not to 141 * count fallback fonts. A nearly correct answer would be very 142 * expensive to generate. A rough ballpark answer would 143 * just count the glyphs in all the slots. However this would 144 * initialize mappers for all slots when they aren't necessarily 145 * needed. For now just use the first slot as JDK 1.4 did. 146 */ 147 for (int slot=0; slot<1 /*font.numSlots*/; slot++) { 148 CharToGlyphMapper mapper = slotMappers[slot]; 149 if (mapper == null) { 150 mapper = font.getSlotFont(slot).getMapper(); 151 slotMappers[slot] = mapper; 152 } 153 numGlyphs += mapper.getNumGlyphs(); 154 } 155 return numGlyphs; 156 } 157 158 public int charToGlyph(int unicode) { 159 160 int glyphCode = getCachedGlyphCode(unicode); 161 if (glyphCode == UNINITIALIZED_GLYPH) { 162 glyphCode = convertToGlyph(unicode); 163 } 164 return glyphCode; 165 } 166 167 public int charToGlyph(int unicode, int prefSlot) { 168 if (prefSlot >= 0) { 169 CharToGlyphMapper mapper = getSlotMapper(prefSlot); 170 int glyphCode = mapper.charToGlyph(unicode); 171 if (glyphCode != mapper.getMissingGlyphCode()) { 172 return compositeGlyphCode(prefSlot, glyphCode); 173 } 174 } 175 return charToGlyph(unicode); 176 } 177 178 public int charToGlyph(char unicode) { 179 180 int glyphCode = getCachedGlyphCode(unicode); 181 if (glyphCode == UNINITIALIZED_GLYPH) { 182 glyphCode = convertToGlyph(unicode); 183 } 184 return glyphCode; 185 } 186 187 /* This variant checks if shaping is needed and immediately 188 * returns true if it does. A caller of this method should be expecting 189 * to check the return type because it needs to know how to handle 190 * the character data for display. 191 */ 192 public boolean charsToGlyphsNS(int count, char[] unicodes, int[] glyphs) { 193 194 for (int i=0; i<count; i++) { 195 int code = unicodes[i]; // char is unsigned. 196 197 if (code >= HI_SURROGATE_START && 198 code <= HI_SURROGATE_END && i < count - 1) { 199 char low = unicodes[i + 1]; 200 201 if (low >= LO_SURROGATE_START && 202 low <= LO_SURROGATE_END) { 203 code = (code - HI_SURROGATE_START) * 204 0x400 + low - LO_SURROGATE_START + 0x10000; 205 glyphs[i + 1] = INVISIBLE_GLYPH_ID; 206 } 207 } 208 209 int gc = glyphs[i] = getCachedGlyphCode(code); 210 if (gc == UNINITIALIZED_GLYPH) { 211 glyphs[i] = convertToGlyph(code); 212 } 213 214 if (code < FontUtilities.MIN_LAYOUT_CHARCODE) { 215 continue; 216 } 217 else if (FontUtilities.isComplexCharCode(code)) { 218 return true; 219 } 220 else if (code >= 0x10000) { 221 i += 1; // Empty glyph slot after surrogate 222 continue; 223 } 224 } 225 226 return false; 227 } 228 229 /* The conversion is not very efficient - looping as it does, converting 230 * one char at a time. However the cache should fill very rapidly. 231 */ 232 public void charsToGlyphs(int count, char[] unicodes, int[] glyphs) { 233 for (int i=0; i<count; i++) { 234 int code = unicodes[i]; // char is unsigned. 235 236 if (code >= HI_SURROGATE_START && 237 code <= HI_SURROGATE_END && i < count - 1) { 238 char low = unicodes[i + 1]; 239 240 if (low >= LO_SURROGATE_START && 241 low <= LO_SURROGATE_END) { 242 code = (code - HI_SURROGATE_START) * 243 0x400 + low - LO_SURROGATE_START + 0x10000; 244 245 int gc = glyphs[i] = getCachedGlyphCode(code); 246 if (gc == UNINITIALIZED_GLYPH) { 247 glyphs[i] = convertToGlyph(code); 248 } 249 i += 1; // Empty glyph slot after surrogate 250 glyphs[i] = INVISIBLE_GLYPH_ID; 251 continue; 252 } 253 } 254 255 int gc = glyphs[i] = getCachedGlyphCode(code); 256 if (gc == UNINITIALIZED_GLYPH) { 257 glyphs[i] = convertToGlyph(code); 258 } 259 } 260 } 261 262 public void charsToGlyphs(int count, int[] unicodes, int[] glyphs) { 263 for (int i=0; i<count; i++) { 264 int code = unicodes[i]; 265 266 glyphs[i] = getCachedGlyphCode(code); 267 if (glyphs[i] == UNINITIALIZED_GLYPH) { 268 glyphs[i] = convertToGlyph(code); 269 } 270 } 271 } 272 273 }