1 /* 2 * Copyright (c) 2003, 2013, 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.nio.ByteBuffer; 29 import java.nio.CharBuffer; 30 import java.nio.IntBuffer; 31 import java.util.Locale; 32 import java.nio.charset.*; 33 34 /* 35 * A tt font has a CMAP table which is in turn made up of sub-tables which 36 * describe the char to glyph mapping in (possibly) multiple ways. 37 * CMAP subtables are described by 3 values. 38 * 1. Platform ID (eg 3=Microsoft, which is the id we look for in JDK) 39 * 2. Encoding (eg 0=symbol, 1=unicode) 40 * 3. TrueType subtable format (how the char->glyph mapping for the encoding 41 * is stored in the subtable). See the TrueType spec. Format 4 is required 42 * by MS in fonts for windows. Its uses segmented mapping to delta values. 43 * Most typically we see are (3,1,4) : 44 * CMAP Platform ID=3 is what we use. 45 * Encodings that are used in practice by JDK on Solaris are 46 * symbol (3,0) 47 * unicode (3,1) 48 * GBK (3,5) (note that solaris zh fonts report 3,4 but are really 3,5) 49 * The format for almost all subtables is 4. However the solaris (3,5) 50 * encodings are typically in format 2. 51 */ 52 abstract class CMap { 53 54 // static char WingDings_b2c[] = { 55 // 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 56 // 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 57 // 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 58 // 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 59 // 0xfffd, 0xfffd, 0x2702, 0x2701, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 60 // 0xfffd, 0x2706, 0x2709, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 61 // 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 62 // 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x2707, 0x270d, 63 // 0xfffd, 0x270c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 64 // 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 65 // 0xfffd, 0x2708, 0xfffd, 0xfffd, 0x2744, 0xfffd, 0x271e, 0xfffd, 66 // 0x2720, 0x2721, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 67 // 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 68 // 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 69 // 0xfffd, 0x2751, 0x2752, 0xfffd, 0xfffd, 0x2756, 0xfffd, 0xfffd, 70 // 0xfffd, 0xfffd, 0xfffd, 0x2740, 0x273f, 0x275d, 0x275e, 0xfffd, 71 // 0xfffd, 0x2780, 0x2781, 0x2782, 0x2783, 0x2784, 0x2785, 0x2786, 72 // 0x2787, 0x2788, 0x2789, 0xfffd, 0x278a, 0x278b, 0x278c, 0x278d, 73 // 0x278e, 0x278f, 0x2790, 0x2791, 0x2792, 0x2793, 0xfffd, 0xfffd, 74 // 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 75 // 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x274d, 0xfffd, 76 // 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x2736, 0x2734, 0xfffd, 0x2735, 77 // 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x272a, 0x2730, 0xfffd, 78 // 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 79 // 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x27a5, 0xfffd, 0x27a6, 0xfffd, 80 // 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 81 // 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 82 // 0x27a2, 0xfffd, 0xfffd, 0xfffd, 0x27b3, 0xfffd, 0xfffd, 0xfffd, 83 // 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 84 // 0x27a1, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 85 // 0x27a9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 86 // 0xfffd, 0xfffd, 0xfffd, 0x2717, 0x2713, 0xfffd, 0xfffd, 0xfffd, 87 // }; 88 89 // static char Symbols_b2c[] = { 90 // 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 91 // 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 92 // 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 93 // 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 94 // 0xfffd, 0xfffd, 0x2200, 0xfffd, 0x2203, 0xfffd, 0xfffd, 0x220d, 95 // 0xfffd, 0xfffd, 0x2217, 0xfffd, 0xfffd, 0x2212, 0xfffd, 0xfffd, 96 // 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 97 // 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 98 // 0x2245, 0x0391, 0x0392, 0x03a7, 0x0394, 0x0395, 0x03a6, 0x0393, 99 // 0x0397, 0x0399, 0x03d1, 0x039a, 0x039b, 0x039c, 0x039d, 0x039f, 100 // 0x03a0, 0x0398, 0x03a1, 0x03a3, 0x03a4, 0x03a5, 0x03c2, 0x03a9, 101 // 0x039e, 0x03a8, 0x0396, 0xfffd, 0x2234, 0xfffd, 0x22a5, 0xfffd, 102 // 0xfffd, 0x03b1, 0x03b2, 0x03c7, 0x03b4, 0x03b5, 0x03c6, 0x03b3, 103 // 0x03b7, 0x03b9, 0x03d5, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03bf, 104 // 0x03c0, 0x03b8, 0x03c1, 0x03c3, 0x03c4, 0x03c5, 0x03d6, 0x03c9, 105 // 0x03be, 0x03c8, 0x03b6, 0xfffd, 0xfffd, 0xfffd, 0x223c, 0xfffd, 106 // 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 107 // 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 108 // 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 109 // 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 110 // 0xfffd, 0x03d2, 0xfffd, 0x2264, 0x2215, 0x221e, 0xfffd, 0xfffd, 111 // 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 112 // 0x2218, 0xfffd, 0xfffd, 0x2265, 0xfffd, 0x221d, 0xfffd, 0x2219, 113 // 0xfffd, 0x2260, 0x2261, 0x2248, 0x22ef, 0x2223, 0xfffd, 0xfffd, 114 // 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x2297, 0x2295, 0x2205, 0x2229, 115 // 0x222a, 0x2283, 0x2287, 0x2284, 0x2282, 0x2286, 0x2208, 0x2209, 116 // 0xfffd, 0x2207, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x221a, 0x22c5, 117 // 0xfffd, 0x2227, 0x2228, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 118 // 0x22c4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x2211, 0xfffd, 0xfffd, 119 // 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 120 // 0xfffd, 0xfffd, 0x222b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 121 // 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 122 // }; 123 124 static final short ShiftJISEncoding = 2; 125 static final short GBKEncoding = 3; 126 static final short Big5Encoding = 4; 127 static final short WansungEncoding = 5; 128 static final short JohabEncoding = 6; 129 static final short MSUnicodeSurrogateEncoding = 10; 130 131 static final char noSuchChar = (char)0xfffd; 132 static final int SHORTMASK = 0x0000ffff; 133 static final int INTMASK = 0xffffffff; 134 135 static final char[][] converterMaps = new char[7][]; 136 137 /* 138 * Unicode->other encoding translation array. A pre-computed look up 139 * which can be shared across all fonts using that encoding. 140 * Using this saves running character coverters repeatedly. 141 */ 142 char[] xlat; 143 UVS uvs = null; 144 145 static CMap initialize(TrueTypeFont font) { 146 147 CMap cmap = null; 148 149 int offset, platformID, encodingID=-1; 150 151 int three0=0, three1=0, three2=0, three3=0, three4=0, three5=0, 152 three6=0, three10=0; 153 int zero5=0; // for Unicode Variation Sequences 154 boolean threeStar = false; 155 156 ByteBuffer cmapBuffer = font.getTableBuffer(TrueTypeFont.cmapTag); 157 int cmapTableOffset = font.getTableSize(TrueTypeFont.cmapTag); 158 short numberSubTables = cmapBuffer.getShort(2); 159 160 /* locate the offsets of all 3,* (ie Microsoft platform) encodings */ 161 for (int i=0; i<numberSubTables; i++) { 162 cmapBuffer.position(i * 8 + 4); 163 platformID = cmapBuffer.getShort(); 164 if (platformID == 3) { 165 threeStar = true; 166 encodingID = cmapBuffer.getShort(); 167 offset = cmapBuffer.getInt(); 168 switch (encodingID) { 169 case 0: three0 = offset; break; // MS Symbol encoding 170 case 1: three1 = offset; break; // MS Unicode cmap 171 case 2: three2 = offset; break; // ShiftJIS cmap. 172 case 3: three3 = offset; break; // GBK cmap 173 case 4: three4 = offset; break; // Big 5 cmap 174 case 5: three5 = offset; break; // Wansung 175 case 6: three6 = offset; break; // Johab 176 case 10: three10 = offset; break; // MS Unicode surrogates 177 } 178 } else if (platformID == 0) { 179 encodingID = cmapBuffer.getShort(); 180 offset = cmapBuffer.getInt(); 181 if (encodingID == 5) { 182 zero5 = offset; 183 } 184 } 185 } 186 187 /* This defines the preference order for cmap subtables */ 188 if (threeStar) { 189 if (three10 != 0) { 190 cmap = createCMap(cmapBuffer, three10, null); 191 } 192 else if (three0 != 0) { 193 /* The special case treatment of these fonts leads to 194 * anomalies where a user can view "wingdings" and "wingdings2" 195 * and the latter shows all its code points in the unicode 196 * private use area at 0xF000->0XF0FF and the former shows 197 * a scattered subset of its glyphs that are known mappings to 198 * unicode code points. 199 * The primary purpose of these mappings was to facilitate 200 * display of symbol chars etc in composite fonts, however 201 * this is not needed as all these code points are covered 202 * by Lucida Sans Regular. 203 * Commenting this out reduces the role of these two files 204 * (assuming that they continue to be used in font.properties) 205 * to just one of contributing to the overall composite 206 * font metrics, and also AWT can still access the fonts. 207 * Clients which explicitly accessed these fonts as names 208 * "Symbol" and "Wingdings" (ie as physical fonts) and 209 * expected to see a scattering of these characters will 210 * see them now as missing. How much of a problem is this? 211 * Perhaps we could still support this mapping just for 212 * "Symbol.ttf" but I suspect some users would prefer it 213 * to be mapped in to the Latin range as that is how 214 * the "symbol" font is used in native apps. 215 */ 216 // String name = font.platName.toLowerCase(Locale.ENGLISH); 217 // if (name.endsWith("symbol.ttf")) { 218 // cmap = createSymbolCMap(cmapBuffer, three0, Symbols_b2c); 219 // } else if (name.endsWith("wingding.ttf")) { 220 // cmap = createSymbolCMap(cmapBuffer, three0, WingDings_b2c); 221 // } else { 222 cmap = createCMap(cmapBuffer, three0, null); 223 // } 224 } 225 else if (three1 != 0) { 226 cmap = createCMap(cmapBuffer, three1, null); 227 } 228 else if (three2 != 0) { 229 cmap = createCMap(cmapBuffer, three2, 230 getConverterMap(ShiftJISEncoding)); 231 } 232 else if (three3 != 0) { 233 cmap = createCMap(cmapBuffer, three3, 234 getConverterMap(GBKEncoding)); 235 } 236 else if (three4 != 0) { 237 /* GB2312 TrueType fonts on Solaris have wrong encoding ID for 238 * cmap table, these fonts have EncodingID 4 which is Big5 239 * encoding according the TrueType spec, but actually the 240 * fonts are using gb2312 encoding, have to use this 241 * workaround to make Solaris zh_CN locale work. -sherman 242 */ 243 if (FontUtilities.isSolaris && font.platName != null && 244 (font.platName.startsWith( 245 "/usr/openwin/lib/locale/zh_CN.EUC/X11/fonts/TrueType") || 246 font.platName.startsWith( 247 "/usr/openwin/lib/locale/zh_CN/X11/fonts/TrueType") || 248 font.platName.startsWith( 249 "/usr/openwin/lib/locale/zh/X11/fonts/TrueType"))) { 250 cmap = createCMap(cmapBuffer, three4, 251 getConverterMap(GBKEncoding)); 252 } 253 else { 254 cmap = createCMap(cmapBuffer, three4, 255 getConverterMap(Big5Encoding)); 256 } 257 } 258 else if (three5 != 0) { 259 cmap = createCMap(cmapBuffer, three5, 260 getConverterMap(WansungEncoding)); 261 } 262 else if (three6 != 0) { 263 cmap = createCMap(cmapBuffer, three6, 264 getConverterMap(JohabEncoding)); 265 } 266 } else { 267 /* No 3,* subtable was found. Just use whatever is the first 268 * table listed. Not very useful but maybe better than 269 * rejecting the font entirely? 270 */ 271 cmap = createCMap(cmapBuffer, cmapBuffer.getInt(8), null); 272 } 273 // For Unicode Variation Sequences 274 if (cmap != null && zero5 != 0){ 275 cmap.createUVS(cmapBuffer, zero5); 276 } 277 return cmap; 278 } 279 280 /* speed up the converting by setting the range for double 281 * byte characters; 282 */ 283 static char[] getConverter(short encodingID) { 284 int dBegin = 0x8000; 285 int dEnd = 0xffff; 286 String encoding; 287 288 switch (encodingID) { 289 case ShiftJISEncoding: 290 dBegin = 0x8140; 291 dEnd = 0xfcfc; 292 encoding = "SJIS"; 293 break; 294 case GBKEncoding: 295 dBegin = 0x8140; 296 dEnd = 0xfea0; 297 encoding = "GBK"; 298 break; 299 case Big5Encoding: 300 dBegin = 0xa140; 301 dEnd = 0xfefe; 302 encoding = "Big5"; 303 break; 304 case WansungEncoding: 305 dBegin = 0xa1a1; 306 dEnd = 0xfede; 307 encoding = "EUC_KR"; 308 break; 309 case JohabEncoding: 310 dBegin = 0x8141; 311 dEnd = 0xfdfe; 312 encoding = "Johab"; 313 break; 314 default: 315 return null; 316 } 317 318 try { 319 char[] convertedChars = new char[65536]; 320 for (int i=0; i<65536; i++) { 321 convertedChars[i] = noSuchChar; 322 } 323 324 byte[] inputBytes = new byte[(dEnd-dBegin+1)*2]; 325 char[] outputChars = new char[(dEnd-dBegin+1)]; 326 327 int j = 0; 328 int firstByte; 329 if (encodingID == ShiftJISEncoding) { 330 for (int i = dBegin; i <= dEnd; i++) { 331 firstByte = (i >> 8 & 0xff); 332 if (firstByte >= 0xa1 && firstByte <= 0xdf) { 333 //sjis halfwidth katakana 334 inputBytes[j++] = (byte)0xff; 335 inputBytes[j++] = (byte)0xff; 336 } else { 337 inputBytes[j++] = (byte)firstByte; 338 inputBytes[j++] = (byte)(i & 0xff); 339 } 340 } 341 } else { 342 for (int i = dBegin; i <= dEnd; i++) { 343 inputBytes[j++] = (byte)(i>>8 & 0xff); 344 inputBytes[j++] = (byte)(i & 0xff); 345 } 346 } 347 348 Charset.forName(encoding).newDecoder() 349 .onMalformedInput(CodingErrorAction.REPLACE) 350 .onUnmappableCharacter(CodingErrorAction.REPLACE) 351 .replaceWith("\u0000") 352 .decode(ByteBuffer.wrap(inputBytes, 0, inputBytes.length), 353 CharBuffer.wrap(outputChars, 0, outputChars.length), 354 true); 355 356 // ensure single byte ascii 357 for (int i = 0x20; i <= 0x7e; i++) { 358 convertedChars[i] = (char)i; 359 } 360 361 //sjis halfwidth katakana 362 if (encodingID == ShiftJISEncoding) { 363 for (int i = 0xa1; i <= 0xdf; i++) { 364 convertedChars[i] = (char)(i - 0xa1 + 0xff61); 365 } 366 } 367 368 /* It would save heap space (approx 60Kbytes for each of these 369 * converters) if stored only valid ranges (ie returned 370 * outputChars directly. But this is tricky since want to 371 * include the ASCII range too. 372 */ 373 // System.err.println("oc.len="+outputChars.length); 374 // System.err.println("cc.len="+convertedChars.length); 375 // System.err.println("dbegin="+dBegin); 376 System.arraycopy(outputChars, 0, convertedChars, dBegin, 377 outputChars.length); 378 379 //return convertedChars; 380 /* invert this map as now want it to map from Unicode 381 * to other encoding. 382 */ 383 char [] invertedChars = new char[65536]; 384 for (int i=0;i<65536;i++) { 385 if (convertedChars[i] != noSuchChar) { 386 invertedChars[convertedChars[i]] = (char)i; 387 } 388 } 389 return invertedChars; 390 391 } catch (Exception e) { 392 e.printStackTrace(); 393 } 394 return null; 395 } 396 397 /* 398 * The returned array maps to unicode from some other 2 byte encoding 399 * eg for a 2byte index which represents a SJIS char, the indexed 400 * value is the corresponding unicode char. 401 */ 402 static char[] getConverterMap(short encodingID) { 403 if (converterMaps[encodingID] == null) { 404 converterMaps[encodingID] = getConverter(encodingID); 405 } 406 return converterMaps[encodingID]; 407 } 408 409 410 static CMap createCMap(ByteBuffer buffer, int offset, char[] xlat) { 411 /* First do a sanity check that this cmap subtable is contained 412 * within the cmap table. 413 */ 414 int subtableFormat = buffer.getChar(offset); 415 long subtableLength; 416 if (subtableFormat < 8) { 417 subtableLength = buffer.getChar(offset+2); 418 } else { 419 subtableLength = buffer.getInt(offset+4) & INTMASK; 420 } 421 if (offset+subtableLength > buffer.capacity()) { 422 if (FontUtilities.isLogging()) { 423 FontUtilities.getLogger().warning("Cmap subtable overflows buffer."); 424 } 425 } 426 switch (subtableFormat) { 427 case 0: return new CMapFormat0(buffer, offset); 428 case 2: return new CMapFormat2(buffer, offset, xlat); 429 case 4: return new CMapFormat4(buffer, offset, xlat); 430 case 6: return new CMapFormat6(buffer, offset, xlat); 431 case 8: return new CMapFormat8(buffer, offset, xlat); 432 case 10: return new CMapFormat10(buffer, offset, xlat); 433 case 12: return new CMapFormat12(buffer, offset, xlat); 434 default: throw new RuntimeException("Cmap format unimplemented: " + 435 (int)buffer.getChar(offset)); 436 } 437 } 438 439 private void createUVS(ByteBuffer buffer, int offset) { 440 int subtableFormat = buffer.getChar(offset); 441 if (subtableFormat == 14) { 442 long subtableLength = buffer.getInt(offset + 2) & INTMASK; 443 if (offset + subtableLength > buffer.capacity()) { 444 if (FontUtilities.isLogging()) { 445 FontUtilities.getLogger().warning("Cmap UVS subtable overflows buffer."); 446 } 447 } 448 this.uvs = new UVS(buffer, offset); 449 } 450 return; 451 } 452 453 /* 454 final char charVal(byte[] cmap, int index) { 455 return (char)(((0xff & cmap[index]) << 8)+(0xff & cmap[index+1])); 456 } 457 458 final short shortVal(byte[] cmap, int index) { 459 return (short)(((0xff & cmap[index]) << 8)+(0xff & cmap[index+1])); 460 } 461 */ 462 abstract char getGlyph(int charCode); 463 464 /* Format 4 Header is 465 * ushort format (off=0) 466 * ushort length (off=2) 467 * ushort language (off=4) 468 * ushort segCountX2 (off=6) 469 * ushort searchRange (off=8) 470 * ushort entrySelector (off=10) 471 * ushort rangeShift (off=12) 472 * ushort endCount[segCount] (off=14) 473 * ushort reservedPad 474 * ushort startCount[segCount] 475 * short idDelta[segCount] 476 * idRangeOFfset[segCount] 477 * ushort glyphIdArray[] 478 */ 479 static class CMapFormat4 extends CMap { 480 int segCount; 481 int entrySelector; 482 int rangeShift; 483 char[] endCount; 484 char[] startCount; 485 short[] idDelta; 486 char[] idRangeOffset; 487 char[] glyphIds; 488 489 CMapFormat4(ByteBuffer bbuffer, int offset, char[] xlat) { 490 491 this.xlat = xlat; 492 493 bbuffer.position(offset); 494 CharBuffer buffer = bbuffer.asCharBuffer(); 495 buffer.get(); // skip, we already know format=4 496 int subtableLength = buffer.get(); 497 /* Try to recover from some bad fonts which specify a subtable 498 * length that would overflow the byte buffer holding the whole 499 * cmap table. If this isn't a recoverable situation an exception 500 * may be thrown which is caught higher up the call stack. 501 * Whilst this may seem lenient, in practice, unless the "bad" 502 * subtable we are using is the last one in the cmap table we 503 * would have no way of knowing about this problem anyway. 504 */ 505 if (offset+subtableLength > bbuffer.capacity()) { 506 subtableLength = bbuffer.capacity() - offset; 507 } 508 buffer.get(); // skip language 509 segCount = buffer.get()/2; 510 int searchRange = buffer.get(); 511 entrySelector = buffer.get(); 512 rangeShift = buffer.get()/2; 513 startCount = new char[segCount]; 514 endCount = new char[segCount]; 515 idDelta = new short[segCount]; 516 idRangeOffset = new char[segCount]; 517 518 for (int i=0; i<segCount; i++) { 519 endCount[i] = buffer.get(); 520 } 521 buffer.get(); // 2 bytes for reserved pad 522 for (int i=0; i<segCount; i++) { 523 startCount[i] = buffer.get(); 524 } 525 526 for (int i=0; i<segCount; i++) { 527 idDelta[i] = (short)buffer.get(); 528 } 529 530 for (int i=0; i<segCount; i++) { 531 char ctmp = buffer.get(); 532 idRangeOffset[i] = (char)((ctmp>>1)&0xffff); 533 } 534 /* Can calculate the number of glyph IDs by subtracting 535 * "pos" from the length of the cmap 536 */ 537 int pos = (segCount*8+16)/2; 538 buffer.position(pos); 539 int numGlyphIds = (subtableLength/2 - pos); 540 glyphIds = new char[numGlyphIds]; 541 for (int i=0;i<numGlyphIds;i++) { 542 glyphIds[i] = buffer.get(); 543 } 544 /* 545 System.err.println("segcount="+segCount); 546 System.err.println("entrySelector="+entrySelector); 547 System.err.println("rangeShift="+rangeShift); 548 for (int j=0;j<segCount;j++) { 549 System.err.println("j="+j+ " sc="+(int)(startCount[j]&0xffff)+ 550 " ec="+(int)(endCount[j]&0xffff)+ 551 " delta="+idDelta[j] + 552 " ro="+(int)idRangeOffset[j]); 553 } 554 555 //System.err.println("numglyphs="+glyphIds.length); 556 for (int i=0;i<numGlyphIds;i++) { 557 System.err.println("gid["+i+"]="+(int)glyphIds[i]); 558 } 559 */ 560 } 561 562 char getGlyph(int charCode) { 563 564 int index = 0; 565 char glyphCode = 0; 566 567 int controlGlyph = getControlCodeGlyph(charCode, true); 568 if (controlGlyph >= 0) { 569 return (char)controlGlyph; 570 } 571 572 /* presence of translation array indicates that this 573 * cmap is in some other (non-unicode encoding). 574 * In order to look-up a char->glyph mapping we need to 575 * translate the unicode code point to the encoding of 576 * the cmap. 577 * REMIND: VALID CHARCODES?? 578 */ 579 if (xlat != null) { 580 charCode = xlat[charCode]; 581 } 582 583 /* 584 * Citation from the TrueType (and OpenType) spec: 585 * The segments are sorted in order of increasing endCode 586 * values, and the segment values are specified in four parallel 587 * arrays. You search for the first endCode that is greater than 588 * or equal to the character code you want to map. If the 589 * corresponding startCode is less than or equal to the 590 * character code, then you use the corresponding idDelta and 591 * idRangeOffset to map the character code to a glyph index 592 * (otherwise, the missingGlyph is returned). 593 */ 594 595 /* 596 * CMAP format4 defines several fields for optimized search of 597 * the segment list (entrySelector, searchRange, rangeShift). 598 * However, benefits are neglible and some fonts have incorrect 599 * data - so we use straightforward binary search (see bug 6247425) 600 */ 601 int left = 0, right = startCount.length; 602 index = startCount.length >> 1; 603 while (left < right) { 604 if (endCount[index] < charCode) { 605 left = index + 1; 606 } else { 607 right = index; 608 } 609 index = (left + right) >> 1; 610 } 611 612 if (charCode >= startCount[index] && charCode <= endCount[index]) { 613 int rangeOffset = idRangeOffset[index]; 614 615 if (rangeOffset == 0) { 616 glyphCode = (char)(charCode + idDelta[index]); 617 } else { 618 /* Calculate an index into the glyphIds array */ 619 620 /* 621 System.err.println("rangeoffset="+rangeOffset+ 622 " charCode=" + charCode + 623 " scnt["+index+"]="+(int)startCount[index] + 624 " segCnt="+segCount); 625 */ 626 627 int glyphIDIndex = rangeOffset - segCount + index 628 + (charCode - startCount[index]); 629 glyphCode = glyphIds[glyphIDIndex]; 630 if (glyphCode != 0) { 631 glyphCode = (char)(glyphCode + idDelta[index]); 632 } 633 } 634 } 635 if (glyphCode != 0) { 636 //System.err.println("cc="+Integer.toHexString((int)charCode) + " gc="+(int)glyphCode); 637 } 638 return glyphCode; 639 } 640 } 641 642 // Format 0: Byte Encoding table 643 static class CMapFormat0 extends CMap { 644 byte [] cmap; 645 646 CMapFormat0(ByteBuffer buffer, int offset) { 647 648 /* skip 6 bytes of format, length, and version */ 649 int len = buffer.getChar(offset+2); 650 cmap = new byte[len-6]; 651 buffer.position(offset+6); 652 buffer.get(cmap); 653 } 654 655 char getGlyph(int charCode) { 656 if (charCode < 256) { 657 if (charCode < 0x0010) { 658 switch (charCode) { 659 case 0x0009: 660 case 0x000a: 661 case 0x000d: return CharToGlyphMapper.INVISIBLE_GLYPH_ID; 662 } 663 } 664 return (char)(0xff & cmap[charCode]); 665 } else { 666 return 0; 667 } 668 } 669 } 670 671 // static CMap createSymbolCMap(ByteBuffer buffer, int offset, char[] syms) { 672 673 // CMap cmap = createCMap(buffer, offset, null); 674 // if (cmap == null) { 675 // return null; 676 // } else { 677 // return new CMapFormatSymbol(cmap, syms); 678 // } 679 // } 680 681 // static class CMapFormatSymbol extends CMap { 682 683 // CMap cmap; 684 // static final int NUM_BUCKETS = 128; 685 // Bucket[] buckets = new Bucket[NUM_BUCKETS]; 686 687 // class Bucket { 688 // char unicode; 689 // char glyph; 690 // Bucket next; 691 692 // Bucket(char u, char g) { 693 // unicode = u; 694 // glyph = g; 695 // } 696 // } 697 698 // CMapFormatSymbol(CMap cmap, char[] syms) { 699 700 // this.cmap = cmap; 701 702 // for (int i=0;i<syms.length;i++) { 703 // char unicode = syms[i]; 704 // if (unicode != noSuchChar) { 705 // char glyph = cmap.getGlyph(i + 0xf000); 706 // int hash = unicode % NUM_BUCKETS; 707 // Bucket bucket = new Bucket(unicode, glyph); 708 // if (buckets[hash] == null) { 709 // buckets[hash] = bucket; 710 // } else { 711 // Bucket b = buckets[hash]; 712 // while (b.next != null) { 713 // b = b.next; 714 // } 715 // b.next = bucket; 716 // } 717 // } 718 // } 719 // } 720 721 // char getGlyph(int unicode) { 722 // if (unicode >= 0x1000) { 723 // return 0; 724 // } 725 // else if (unicode >=0xf000 && unicode < 0xf100) { 726 // return cmap.getGlyph(unicode); 727 // } else { 728 // Bucket b = buckets[unicode % NUM_BUCKETS]; 729 // while (b != null) { 730 // if (b.unicode == unicode) { 731 // return b.glyph; 732 // } else { 733 // b = b.next; 734 // } 735 // } 736 // return 0; 737 // } 738 // } 739 // } 740 741 // Format 2: High-byte mapping through table 742 static class CMapFormat2 extends CMap { 743 744 char[] subHeaderKey = new char[256]; 745 /* Store subheaders in individual arrays 746 * A SubHeader entry theortically looks like { 747 * char firstCode; 748 * char entryCount; 749 * short idDelta; 750 * char idRangeOffset; 751 * } 752 */ 753 char[] firstCodeArray; 754 char[] entryCountArray; 755 short[] idDeltaArray; 756 char[] idRangeOffSetArray; 757 758 char[] glyphIndexArray; 759 760 CMapFormat2(ByteBuffer buffer, int offset, char[] xlat) { 761 762 this.xlat = xlat; 763 764 int tableLen = buffer.getChar(offset+2); 765 buffer.position(offset+6); 766 CharBuffer cBuffer = buffer.asCharBuffer(); 767 char maxSubHeader = 0; 768 for (int i=0;i<256;i++) { 769 subHeaderKey[i] = cBuffer.get(); 770 if (subHeaderKey[i] > maxSubHeader) { 771 maxSubHeader = subHeaderKey[i]; 772 } 773 } 774 /* The value of the subHeaderKey is 8 * the subHeader index, 775 * so the number of subHeaders can be obtained by dividing 776 * this value bv 8 and adding 1. 777 */ 778 int numSubHeaders = (maxSubHeader >> 3) +1; 779 firstCodeArray = new char[numSubHeaders]; 780 entryCountArray = new char[numSubHeaders]; 781 idDeltaArray = new short[numSubHeaders]; 782 idRangeOffSetArray = new char[numSubHeaders]; 783 for (int i=0; i<numSubHeaders; i++) { 784 firstCodeArray[i] = cBuffer.get(); 785 entryCountArray[i] = cBuffer.get(); 786 idDeltaArray[i] = (short)cBuffer.get(); 787 idRangeOffSetArray[i] = cBuffer.get(); 788 // System.out.println("sh["+i+"]:fc="+(int)firstCodeArray[i]+ 789 // " ec="+(int)entryCountArray[i]+ 790 // " delta="+(int)idDeltaArray[i]+ 791 // " offset="+(int)idRangeOffSetArray[i]); 792 } 793 794 int glyphIndexArrSize = (tableLen-518-numSubHeaders*8)/2; 795 glyphIndexArray = new char[glyphIndexArrSize]; 796 for (int i=0; i<glyphIndexArrSize;i++) { 797 glyphIndexArray[i] = cBuffer.get(); 798 } 799 } 800 801 char getGlyph(int charCode) { 802 int controlGlyph = getControlCodeGlyph(charCode, true); 803 if (controlGlyph >= 0) { 804 return (char)controlGlyph; 805 } 806 807 if (xlat != null) { 808 charCode = xlat[charCode]; 809 } 810 811 char highByte = (char)(charCode >> 8); 812 char lowByte = (char)(charCode & 0xff); 813 int key = subHeaderKey[highByte]>>3; // index into subHeaders 814 char mapMe; 815 816 if (key != 0) { 817 mapMe = lowByte; 818 } else { 819 mapMe = highByte; 820 if (mapMe == 0) { 821 mapMe = lowByte; 822 } 823 } 824 825 // System.err.println("charCode="+Integer.toHexString(charCode)+ 826 // " key="+key+ " mapMe="+Integer.toHexString(mapMe)); 827 char firstCode = firstCodeArray[key]; 828 if (mapMe < firstCode) { 829 return 0; 830 } else { 831 mapMe -= firstCode; 832 } 833 834 if (mapMe < entryCountArray[key]) { 835 /* "address" arithmetic is needed to calculate the offset 836 * into glyphIndexArray. "idRangeOffSetArray[key]" specifies 837 * the number of bytes from that location in the table where 838 * the subarray of glyphIndexes starting at "firstCode" begins. 839 * Each entry in the subHeader table is 8 bytes, and the 840 * idRangeOffSetArray field is at offset 6 in the entry. 841 * The glyphIndexArray immediately follows the subHeaders. 842 * So if there are "N" entries then the number of bytes to the 843 * start of glyphIndexArray is (N-key)*8-6. 844 * Subtract this from the idRangeOffSetArray value to get 845 * the number of bytes into glyphIndexArray and divide by 2 to 846 * get the (char) array index. 847 */ 848 int glyphArrayOffset = ((idRangeOffSetArray.length-key)*8)-6; 849 int glyphSubArrayStart = 850 (idRangeOffSetArray[key] - glyphArrayOffset)/2; 851 char glyphCode = glyphIndexArray[glyphSubArrayStart+mapMe]; 852 if (glyphCode != 0) { 853 glyphCode += idDeltaArray[key]; //idDelta 854 return glyphCode; 855 } 856 } 857 return 0; 858 } 859 } 860 861 // Format 6: Trimmed table mapping 862 static class CMapFormat6 extends CMap { 863 864 char firstCode; 865 char entryCount; 866 char[] glyphIdArray; 867 868 CMapFormat6(ByteBuffer bbuffer, int offset, char[] xlat) { 869 870 bbuffer.position(offset+6); 871 CharBuffer buffer = bbuffer.asCharBuffer(); 872 firstCode = buffer.get(); 873 entryCount = buffer.get(); 874 glyphIdArray = new char[entryCount]; 875 for (int i=0; i< entryCount; i++) { 876 glyphIdArray[i] = buffer.get(); 877 } 878 } 879 880 char getGlyph(int charCode) { 881 int controlGlyph = getControlCodeGlyph(charCode, true); 882 if (controlGlyph >= 0) { 883 return (char)controlGlyph; 884 } 885 886 if (xlat != null) { 887 charCode = xlat[charCode]; 888 } 889 890 charCode -= firstCode; 891 if (charCode < 0 || charCode >= entryCount) { 892 return 0; 893 } else { 894 return glyphIdArray[charCode]; 895 } 896 } 897 } 898 899 // Format 8: mixed 16-bit and 32-bit coverage 900 // Seems unlikely this code will ever get tested as we look for 901 // MS platform Cmaps and MS states (in the Opentype spec on their website) 902 // that MS doesn't support this format 903 static class CMapFormat8 extends CMap { 904 byte[] is32 = new byte[8192]; 905 int nGroups; 906 int[] startCharCode; 907 int[] endCharCode; 908 int[] startGlyphID; 909 910 CMapFormat8(ByteBuffer bbuffer, int offset, char[] xlat) { 911 912 bbuffer.position(12); 913 bbuffer.get(is32); 914 nGroups = bbuffer.getInt(); 915 startCharCode = new int[nGroups]; 916 endCharCode = new int[nGroups]; 917 startGlyphID = new int[nGroups]; 918 } 919 920 char getGlyph(int charCode) { 921 if (xlat != null) { 922 throw new RuntimeException("xlat array for cmap fmt=8"); 923 } 924 return 0; 925 } 926 927 } 928 929 930 // Format 4-byte 10: Trimmed table mapping 931 // Seems unlikely this code will ever get tested as we look for 932 // MS platform Cmaps and MS states (in the Opentype spec on their website) 933 // that MS doesn't support this format 934 static class CMapFormat10 extends CMap { 935 936 long firstCode; 937 int entryCount; 938 char[] glyphIdArray; 939 940 CMapFormat10(ByteBuffer bbuffer, int offset, char[] xlat) { 941 942 firstCode = bbuffer.getInt() & INTMASK; 943 entryCount = bbuffer.getInt() & INTMASK; 944 bbuffer.position(offset+20); 945 CharBuffer buffer = bbuffer.asCharBuffer(); 946 glyphIdArray = new char[entryCount]; 947 for (int i=0; i< entryCount; i++) { 948 glyphIdArray[i] = buffer.get(); 949 } 950 } 951 952 char getGlyph(int charCode) { 953 954 if (xlat != null) { 955 throw new RuntimeException("xlat array for cmap fmt=10"); 956 } 957 958 int code = (int)(charCode - firstCode); 959 if (code < 0 || code >= entryCount) { 960 return 0; 961 } else { 962 return glyphIdArray[code]; 963 } 964 } 965 } 966 967 // Format 12: Segmented coverage for UCS-4 (fonts supporting 968 // surrogate pairs) 969 static class CMapFormat12 extends CMap { 970 971 int numGroups; 972 int highBit =0; 973 int power; 974 int extra; 975 long[] startCharCode; 976 long[] endCharCode; 977 int[] startGlyphID; 978 979 CMapFormat12(ByteBuffer buffer, int offset, char[] xlat) { 980 if (xlat != null) { 981 throw new RuntimeException("xlat array for cmap fmt=12"); 982 } 983 984 numGroups = buffer.getInt(offset+12); 985 startCharCode = new long[numGroups]; 986 endCharCode = new long[numGroups]; 987 startGlyphID = new int[numGroups]; 988 buffer.position(offset+16); 989 buffer = buffer.slice(); 990 IntBuffer ibuffer = buffer.asIntBuffer(); 991 for (int i=0; i<numGroups; i++) { 992 startCharCode[i] = ibuffer.get() & INTMASK; 993 endCharCode[i] = ibuffer.get() & INTMASK; 994 startGlyphID[i] = ibuffer.get() & INTMASK; 995 } 996 997 /* Finds the high bit by binary searching through the bits */ 998 int value = numGroups; 999 1000 if (value >= 1 << 16) { 1001 value >>= 16; 1002 highBit += 16; 1003 } 1004 1005 if (value >= 1 << 8) { 1006 value >>= 8; 1007 highBit += 8; 1008 } 1009 1010 if (value >= 1 << 4) { 1011 value >>= 4; 1012 highBit += 4; 1013 } 1014 1015 if (value >= 1 << 2) { 1016 value >>= 2; 1017 highBit += 2; 1018 } 1019 1020 if (value >= 1 << 1) { 1021 value >>= 1; 1022 highBit += 1; 1023 } 1024 1025 power = 1 << highBit; 1026 extra = numGroups - power; 1027 } 1028 1029 char getGlyph(int charCode) { 1030 int controlGlyph = getControlCodeGlyph(charCode, false); 1031 if (controlGlyph >= 0) { 1032 return (char)controlGlyph; 1033 } 1034 int probe = power; 1035 int range = 0; 1036 1037 if (startCharCode[extra] <= charCode) { 1038 range = extra; 1039 } 1040 1041 while (probe > 1) { 1042 probe >>= 1; 1043 1044 if (startCharCode[range+probe] <= charCode) { 1045 range += probe; 1046 } 1047 } 1048 1049 if (startCharCode[range] <= charCode && 1050 endCharCode[range] >= charCode) { 1051 return (char) 1052 (startGlyphID[range] + (charCode - startCharCode[range])); 1053 } 1054 1055 return 0; 1056 } 1057 1058 } 1059 1060 /* Used to substitute for bad Cmaps. */ 1061 static class NullCMapClass extends CMap { 1062 1063 char getGlyph(int charCode) { 1064 return 0; 1065 } 1066 } 1067 1068 public static final NullCMapClass theNullCmap = new NullCMapClass(); 1069 1070 final int getControlCodeGlyph(int charCode, boolean noSurrogates) { 1071 if (charCode < 0x0010) { 1072 switch (charCode) { 1073 case 0x0009: 1074 case 0x000a: 1075 case 0x000d: return CharToGlyphMapper.INVISIBLE_GLYPH_ID; 1076 } 1077 } else if (charCode >= 0x200c) { 1078 if ((charCode <= 0x200f) || 1079 (charCode >= 0x2028 && charCode <= 0x202e) || 1080 (charCode >= 0x206a && charCode <= 0x206f)) { 1081 return CharToGlyphMapper.INVISIBLE_GLYPH_ID; 1082 } else if (noSurrogates && charCode >= 0xFFFF) { 1083 return 0; 1084 } 1085 } 1086 return -1; 1087 } 1088 1089 static class UVS { 1090 int numSelectors; 1091 int[] selector; 1092 1093 //for Default UVS Table 1094 int[] numUnicodeValueRanges; 1095 int[][] startUnicodeValue; 1096 byte[][] additionalCount; 1097 //for Non-Default UVS Table 1098 int[] numUVSMapping; 1099 int[][] unicodeValue; 1100 char[][] glyphID; 1101 1102 UVS(ByteBuffer buffer, int offset) { 1103 numSelectors = buffer.getInt(offset+6); 1104 selector = new int[numSelectors]; 1105 numUnicodeValueRanges = new int[numSelectors]; 1106 startUnicodeValue = new int[numSelectors][]; 1107 additionalCount = new byte[numSelectors][]; 1108 numUVSMapping = new int[numSelectors]; 1109 unicodeValue = new int[numSelectors][]; 1110 glyphID = new char[numSelectors][]; 1111 1112 for (int i = 0; i < numSelectors; i++) { 1113 buffer.position(offset + 10 + i * 11); 1114 selector[i] = (buffer.get() & 0xff) << 16; //UINT24 1115 selector[i] += (buffer.get() & 0xff) << 8; 1116 selector[i] += buffer.get() & 0xff; 1117 1118 //for Default UVS Table 1119 int tableOffset = buffer.getInt(offset + 10 + i * 11 + 3); 1120 if (tableOffset == 0){ 1121 numUnicodeValueRanges[i] = 0; 1122 }else{ 1123 buffer.position(offset+tableOffset); 1124 numUnicodeValueRanges[i] = buffer.getInt() & INTMASK; 1125 1126 startUnicodeValue[i] = new int[numUnicodeValueRanges[i]]; 1127 additionalCount[i] = new byte[numUnicodeValueRanges[i]]; 1128 1129 for (int j = 0; j < numUnicodeValueRanges[i]; j++) { 1130 int temp = (buffer.get() & 0xff) << 16; //UINT24 1131 temp += (buffer.get() & 0xff) << 8; 1132 temp += buffer.get() & 0xff; 1133 startUnicodeValue[i][j] = temp; 1134 additionalCount[i][j] = buffer.get(); 1135 } 1136 } 1137 1138 //for Non-Default UVS Table 1139 tableOffset = buffer.getInt(offset + 10 + i * 11 + 7); 1140 if (tableOffset == 0){ 1141 numUVSMapping[i] = 0; 1142 } else { 1143 buffer.position(offset+tableOffset); 1144 numUVSMapping[i] = buffer.getInt() & INTMASK; 1145 unicodeValue[i] = new int[numUVSMapping[i]]; 1146 glyphID[i] = new char[numUVSMapping[i]]; 1147 1148 for (int j = 0; j < numUVSMapping[i]; j++) { 1149 int temp = (buffer.get() & 0xff) << 16; //UINT24 1150 temp += (buffer.get() & 0xff) << 8; 1151 temp += buffer.get() & 0xff; 1152 unicodeValue[i][j]= temp; 1153 glyphID[i][j]= buffer.getChar(); 1154 } 1155 } 1156 } 1157 } 1158 1159 private int cachedCode; 1160 private int targetCachedCode; 1161 private int targetCachedSelector = -1; 1162 1163 /* getGlyph for Variation selector 1164 return value: 1165 0: A special glyph for the variation selector is Not found 1166 -1: Default glyph should be used 1167 0>: A special glyph is found 1168 */ 1169 int getGlyph(int charCode, int variationSelector) { 1170 synchronized(this){ 1171 if (charCode == targetCachedCode && variationSelector == targetCachedSelector) { 1172 return cachedCode; 1173 } 1174 } 1175 1176 int targetSelector = -1; 1177 int result; 1178 for (int i = 0; i < numSelectors; i++) { 1179 if (selector[i] == variationSelector) { 1180 targetSelector = i; 1181 break; 1182 } 1183 } 1184 if (targetSelector == -1){ 1185 result = 0; 1186 storeCache(charCode, variationSelector, result); 1187 return result; 1188 } 1189 if (numUnicodeValueRanges[targetSelector] > 0) { 1190 int index = java.util.Arrays.binarySearch( 1191 startUnicodeValue[targetSelector], charCode); 1192 if (index >= 0){ 1193 result = -1; //pass through default table in actual CMAP 1194 storeCache(charCode, variationSelector, result); 1195 return result; 1196 } else { 1197 index = -index - 2; 1198 if (index >=0 1199 && charCode >= startUnicodeValue[targetSelector][index] 1200 && charCode <= startUnicodeValue[targetSelector][index] 1201 +additionalCount[targetSelector][index]) { 1202 result = -1; //pass through default table in actual CMAP 1203 storeCache(charCode, variationSelector, result); 1204 return result; 1205 } 1206 } 1207 } 1208 if (numUVSMapping[targetSelector] > 0){ 1209 int index = java.util.Arrays.binarySearch( 1210 unicodeValue[targetSelector], charCode); 1211 if (index >= 0){ 1212 result = glyphID[targetSelector][index]; 1213 storeCache(charCode, variationSelector, result); 1214 return result; 1215 } 1216 } 1217 result = 0; 1218 storeCache(charCode, variationSelector, result); 1219 return result; 1220 } 1221 1222 private synchronized void storeCache(int charCode, int variationSelector, int glyph) { 1223 cachedCode = glyph; 1224 targetCachedCode = charCode; 1225 targetCachedSelector = variationSelector; 1226 } 1227 1228 boolean hasVariationSelectorGlyph(int charCode, int variationSelector) { 1229 int result= getGlyph(charCode, variationSelector); 1230 if (result == 0) { 1231 return false; 1232 } else { 1233 return true; 1234 } 1235 } 1236 } 1237 1238 public char getGlyph(int charCode, int variationSelector) { 1239 if (uvs == null) { 1240 return 0; 1241 } 1242 int result = uvs.getGlyph(charCode, variationSelector); 1243 if (result == -1) { 1244 result = this.getGlyph(charCode); 1245 } 1246 return (char)(result & 0xFFFF); 1247 } 1248 1249 public boolean hasVariationSelectorGlyph(int charCode, int variationSelector) { 1250 if (uvs == null) { 1251 return false; 1252 } 1253 return uvs.hasVariationSelectorGlyph(charCode, variationSelector); 1254 } 1255 1256 }