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()
 446                             .warning("Cmap UVS subtable overflows buffer.");
 447                 }
 448             }
 449             this.uvs = new UVS(buffer, offset);
 450         }
 451         return;
 452     }
 453 
 454 /*
 455     final char charVal(byte[] cmap, int index) {
 456         return (char)(((0xff & cmap[index]) << 8)+(0xff & cmap[index+1]));
 457     }
 458 
 459     final short shortVal(byte[] cmap, int index) {
 460         return (short)(((0xff & cmap[index]) << 8)+(0xff & cmap[index+1]));
 461     }
 462 */
 463     abstract char getGlyph(int charCode);
 464 
 465     /* Format 4 Header is
 466      * ushort format (off=0)
 467      * ushort length (off=2)
 468      * ushort language (off=4)
 469      * ushort segCountX2 (off=6)
 470      * ushort searchRange (off=8)
 471      * ushort entrySelector (off=10)
 472      * ushort rangeShift (off=12)
 473      * ushort endCount[segCount] (off=14)
 474      * ushort reservedPad
 475      * ushort startCount[segCount]
 476      * short idDelta[segCount]
 477      * idRangeOFfset[segCount]
 478      * ushort glyphIdArray[]
 479      */
 480     static class CMapFormat4 extends CMap {
 481         int segCount;
 482         int entrySelector;
 483         int rangeShift;
 484         char[] endCount;
 485         char[] startCount;
 486         short[] idDelta;
 487         char[] idRangeOffset;
 488         char[] glyphIds;
 489 
 490         CMapFormat4(ByteBuffer bbuffer, int offset, char[] xlat) {
 491 
 492             this.xlat = xlat;
 493 
 494             bbuffer.position(offset);
 495             CharBuffer buffer = bbuffer.asCharBuffer();
 496             buffer.get(); // skip, we already know format=4
 497             int subtableLength = buffer.get();
 498             /* Try to recover from some bad fonts which specify a subtable
 499              * length that would overflow the byte buffer holding the whole
 500              * cmap table. If this isn't a recoverable situation an exception
 501              * may be thrown which is caught higher up the call stack.
 502              * Whilst this may seem lenient, in practice, unless the "bad"
 503              * subtable we are using is the last one in the cmap table we
 504              * would have no way of knowing about this problem anyway.
 505              */
 506             if (offset+subtableLength > bbuffer.capacity()) {
 507                 subtableLength = bbuffer.capacity() - offset;
 508             }
 509             buffer.get(); // skip language
 510             segCount = buffer.get()/2;
 511             int searchRange = buffer.get();
 512             entrySelector = buffer.get();
 513             rangeShift    = buffer.get()/2;
 514             startCount = new char[segCount];
 515             endCount = new char[segCount];
 516             idDelta = new short[segCount];
 517             idRangeOffset = new char[segCount];
 518 
 519             for (int i=0; i<segCount; i++) {
 520                 endCount[i] = buffer.get();
 521             }
 522             buffer.get(); // 2 bytes for reserved pad
 523             for (int i=0; i<segCount; i++) {
 524                 startCount[i] = buffer.get();
 525             }
 526 
 527             for (int i=0; i<segCount; i++) {
 528                 idDelta[i] = (short)buffer.get();
 529             }
 530 
 531             for (int i=0; i<segCount; i++) {
 532                 char ctmp = buffer.get();
 533                 idRangeOffset[i] = (char)((ctmp>>1)&0xffff);
 534             }
 535             /* Can calculate the number of glyph IDs by subtracting
 536              * "pos" from the length of the cmap
 537              */
 538             int pos = (segCount*8+16)/2;
 539             buffer.position(pos);
 540             int numGlyphIds = (subtableLength/2 - pos);
 541             glyphIds = new char[numGlyphIds];
 542             for (int i=0;i<numGlyphIds;i++) {
 543                 glyphIds[i] = buffer.get();
 544             }
 545 /*
 546             System.err.println("segcount="+segCount);
 547             System.err.println("entrySelector="+entrySelector);
 548             System.err.println("rangeShift="+rangeShift);
 549             for (int j=0;j<segCount;j++) {
 550               System.err.println("j="+j+ " sc="+(int)(startCount[j]&0xffff)+
 551                                  " ec="+(int)(endCount[j]&0xffff)+
 552                                  " delta="+idDelta[j] +
 553                                  " ro="+(int)idRangeOffset[j]);
 554             }
 555 
 556             //System.err.println("numglyphs="+glyphIds.length);
 557             for (int i=0;i<numGlyphIds;i++) {
 558                   System.err.println("gid["+i+"]="+(int)glyphIds[i]);
 559             }
 560 */
 561         }
 562 
 563         char getGlyph(int charCode) {
 564 
 565             int index = 0;
 566             char glyphCode = 0;
 567 
 568             int controlGlyph = getControlCodeGlyph(charCode, true);
 569             if (controlGlyph >= 0) {
 570                 return (char)controlGlyph;
 571             }
 572 
 573             /* presence of translation array indicates that this
 574              * cmap is in some other (non-unicode encoding).
 575              * In order to look-up a char->glyph mapping we need to
 576              * translate the unicode code point to the encoding of
 577              * the cmap.
 578              * REMIND: VALID CHARCODES??
 579              */
 580             if (xlat != null) {
 581                 charCode = xlat[charCode];
 582             }
 583 
 584             /*
 585              * Citation from the TrueType (and OpenType) spec:
 586              *   The segments are sorted in order of increasing endCode
 587              *   values, and the segment values are specified in four parallel
 588              *   arrays. You search for the first endCode that is greater than
 589              *   or equal to the character code you want to map. If the
 590              *   corresponding startCode is less than or equal to the
 591              *   character code, then you use the corresponding idDelta and
 592              *   idRangeOffset to map the character code to a glyph index
 593              *   (otherwise, the missingGlyph is returned).
 594              */
 595 
 596             /*
 597              * CMAP format4 defines several fields for optimized search of
 598              * the segment list (entrySelector, searchRange, rangeShift).
 599              * However, benefits are neglible and some fonts have incorrect
 600              * data - so we use straightforward binary search (see bug 6247425)
 601              */
 602             int left = 0, right = startCount.length;
 603             index = startCount.length >> 1;
 604             while (left < right) {
 605                 if (endCount[index] < charCode) {
 606                     left = index + 1;
 607                 } else {
 608                     right = index;
 609                 }
 610                 index = (left + right) >> 1;
 611             }
 612 
 613             if (charCode >= startCount[index] && charCode <= endCount[index]) {
 614                 int rangeOffset = idRangeOffset[index];
 615 
 616                 if (rangeOffset == 0) {
 617                     glyphCode = (char)(charCode + idDelta[index]);
 618                 } else {
 619                     /* Calculate an index into the glyphIds array */
 620 
 621 /*
 622                     System.err.println("rangeoffset="+rangeOffset+
 623                                        " charCode=" + charCode +
 624                                        " scnt["+index+"]="+(int)startCount[index] +
 625                                        " segCnt="+segCount);
 626 */
 627 
 628                     int glyphIDIndex = rangeOffset - segCount + index
 629                                          + (charCode - startCount[index]);
 630                     glyphCode = glyphIds[glyphIDIndex];
 631                     if (glyphCode != 0) {
 632                         glyphCode = (char)(glyphCode + idDelta[index]);
 633                     }
 634                 }
 635             }
 636             if (glyphCode != 0) {
 637             //System.err.println("cc="+Integer.toHexString((int)charCode) + " gc="+(int)glyphCode);
 638             }
 639             return glyphCode;
 640         }
 641     }
 642 
 643     // Format 0: Byte Encoding table
 644     static class CMapFormat0 extends CMap {
 645         byte [] cmap;
 646 
 647         CMapFormat0(ByteBuffer buffer, int offset) {
 648 
 649             /* skip 6 bytes of format, length, and version */
 650             int len = buffer.getChar(offset+2);
 651             cmap = new byte[len-6];
 652             buffer.position(offset+6);
 653             buffer.get(cmap);
 654         }
 655 
 656         char getGlyph(int charCode) {
 657             if (charCode < 256) {
 658                 if (charCode < 0x0010) {
 659                     switch (charCode) {
 660                     case 0x0009:
 661                     case 0x000a:
 662                     case 0x000d: return CharToGlyphMapper.INVISIBLE_GLYPH_ID;
 663                     }
 664                 }
 665                 return (char)(0xff & cmap[charCode]);
 666             } else {
 667                 return 0;
 668             }
 669         }
 670     }
 671 
 672 //     static CMap createSymbolCMap(ByteBuffer buffer, int offset, char[] syms) {
 673 
 674 //      CMap cmap = createCMap(buffer, offset, null);
 675 //      if (cmap == null) {
 676 //          return null;
 677 //      } else {
 678 //          return new CMapFormatSymbol(cmap, syms);
 679 //      }
 680 //     }
 681 
 682 //     static class CMapFormatSymbol extends CMap {
 683 
 684 //      CMap cmap;
 685 //      static final int NUM_BUCKETS = 128;
 686 //      Bucket[] buckets = new Bucket[NUM_BUCKETS];
 687 
 688 //      class Bucket {
 689 //          char unicode;
 690 //          char glyph;
 691 //          Bucket next;
 692 
 693 //          Bucket(char u, char g) {
 694 //              unicode = u;
 695 //              glyph = g;
 696 //          }
 697 //      }
 698 
 699 //      CMapFormatSymbol(CMap cmap, char[] syms) {
 700 
 701 //          this.cmap = cmap;
 702 
 703 //          for (int i=0;i<syms.length;i++) {
 704 //              char unicode = syms[i];
 705 //              if (unicode != noSuchChar) {
 706 //                  char glyph = cmap.getGlyph(i + 0xf000);
 707 //                  int hash = unicode % NUM_BUCKETS;
 708 //                  Bucket bucket = new Bucket(unicode, glyph);
 709 //                  if (buckets[hash] == null) {
 710 //                      buckets[hash] = bucket;
 711 //                  } else {
 712 //                      Bucket b = buckets[hash];
 713 //                      while (b.next != null) {
 714 //                          b = b.next;
 715 //                      }
 716 //                      b.next = bucket;
 717 //                  }
 718 //              }
 719 //          }
 720 //      }
 721 
 722 //      char getGlyph(int unicode) {
 723 //          if (unicode >= 0x1000) {
 724 //              return 0;
 725 //          }
 726 //          else if (unicode >=0xf000 && unicode < 0xf100) {
 727 //              return cmap.getGlyph(unicode);
 728 //          } else {
 729 //              Bucket b = buckets[unicode % NUM_BUCKETS];
 730 //              while (b != null) {
 731 //                  if (b.unicode == unicode) {
 732 //                      return b.glyph;
 733 //                  } else {
 734 //                      b = b.next;
 735 //                  }
 736 //              }
 737 //              return 0;
 738 //          }
 739 //      }
 740 //     }
 741 
 742     // Format 2: High-byte mapping through table
 743     static class CMapFormat2 extends CMap {
 744 
 745         char[] subHeaderKey = new char[256];
 746          /* Store subheaders in individual arrays
 747           * A SubHeader entry theortically looks like {
 748           *   char firstCode;
 749           *   char entryCount;
 750           *   short idDelta;
 751           *   char idRangeOffset;
 752           * }
 753           */
 754         char[] firstCodeArray;
 755         char[] entryCountArray;
 756         short[] idDeltaArray;
 757         char[] idRangeOffSetArray;
 758 
 759         char[] glyphIndexArray;
 760 
 761         CMapFormat2(ByteBuffer buffer, int offset, char[] xlat) {
 762 
 763             this.xlat = xlat;
 764 
 765             int tableLen = buffer.getChar(offset+2);
 766             buffer.position(offset+6);
 767             CharBuffer cBuffer = buffer.asCharBuffer();
 768             char maxSubHeader = 0;
 769             for (int i=0;i<256;i++) {
 770                 subHeaderKey[i] = cBuffer.get();
 771                 if (subHeaderKey[i] > maxSubHeader) {
 772                     maxSubHeader = subHeaderKey[i];
 773                 }
 774             }
 775             /* The value of the subHeaderKey is 8 * the subHeader index,
 776              * so the number of subHeaders can be obtained by dividing
 777              * this value bv 8 and adding 1.
 778              */
 779             int numSubHeaders = (maxSubHeader >> 3) +1;
 780             firstCodeArray = new char[numSubHeaders];
 781             entryCountArray = new char[numSubHeaders];
 782             idDeltaArray  = new short[numSubHeaders];
 783             idRangeOffSetArray  = new char[numSubHeaders];
 784             for (int i=0; i<numSubHeaders; i++) {
 785                 firstCodeArray[i] = cBuffer.get();
 786                 entryCountArray[i] = cBuffer.get();
 787                 idDeltaArray[i] = (short)cBuffer.get();
 788                 idRangeOffSetArray[i] = cBuffer.get();
 789 //              System.out.println("sh["+i+"]:fc="+(int)firstCodeArray[i]+
 790 //                                 " ec="+(int)entryCountArray[i]+
 791 //                                 " delta="+(int)idDeltaArray[i]+
 792 //                                 " offset="+(int)idRangeOffSetArray[i]);
 793             }
 794 
 795             int glyphIndexArrSize = (tableLen-518-numSubHeaders*8)/2;
 796             glyphIndexArray = new char[glyphIndexArrSize];
 797             for (int i=0; i<glyphIndexArrSize;i++) {
 798                 glyphIndexArray[i] = cBuffer.get();
 799             }
 800         }
 801 
 802         char getGlyph(int charCode) {
 803             int controlGlyph = getControlCodeGlyph(charCode, true);
 804             if (controlGlyph >= 0) {
 805                 return (char)controlGlyph;
 806             }
 807 
 808             if (xlat != null) {
 809                 charCode = xlat[charCode];
 810             }
 811 
 812             char highByte = (char)(charCode >> 8);
 813             char lowByte = (char)(charCode & 0xff);
 814             int key = subHeaderKey[highByte]>>3; // index into subHeaders
 815             char mapMe;
 816 
 817             if (key != 0) {
 818                 mapMe = lowByte;
 819             } else {
 820                 mapMe = highByte;
 821                 if (mapMe == 0) {
 822                     mapMe = lowByte;
 823                 }
 824             }
 825 
 826 //          System.err.println("charCode="+Integer.toHexString(charCode)+
 827 //                             " key="+key+ " mapMe="+Integer.toHexString(mapMe));
 828             char firstCode = firstCodeArray[key];
 829             if (mapMe < firstCode) {
 830                 return 0;
 831             } else {
 832                 mapMe -= firstCode;
 833             }
 834 
 835             if (mapMe < entryCountArray[key]) {
 836                 /* "address" arithmetic is needed to calculate the offset
 837                  * into glyphIndexArray. "idRangeOffSetArray[key]" specifies
 838                  * the number of bytes from that location in the table where
 839                  * the subarray of glyphIndexes starting at "firstCode" begins.
 840                  * Each entry in the subHeader table is 8 bytes, and the
 841                  * idRangeOffSetArray field is at offset 6 in the entry.
 842                  * The glyphIndexArray immediately follows the subHeaders.
 843                  * So if there are "N" entries then the number of bytes to the
 844                  * start of glyphIndexArray is (N-key)*8-6.
 845                  * Subtract this from the idRangeOffSetArray value to get
 846                  * the number of bytes into glyphIndexArray and divide by 2 to
 847                  * get the (char) array index.
 848                  */
 849                 int glyphArrayOffset = ((idRangeOffSetArray.length-key)*8)-6;
 850                 int glyphSubArrayStart =
 851                         (idRangeOffSetArray[key] - glyphArrayOffset)/2;
 852                 char glyphCode = glyphIndexArray[glyphSubArrayStart+mapMe];
 853                 if (glyphCode != 0) {
 854                     glyphCode += idDeltaArray[key]; //idDelta
 855                     return glyphCode;
 856                 }
 857             }
 858             return 0;
 859         }
 860     }
 861 
 862     // Format 6: Trimmed table mapping
 863     static class CMapFormat6 extends CMap {
 864 
 865         char firstCode;
 866         char entryCount;
 867         char[] glyphIdArray;
 868 
 869         CMapFormat6(ByteBuffer bbuffer, int offset, char[] xlat) {
 870 
 871              bbuffer.position(offset+6);
 872              CharBuffer buffer = bbuffer.asCharBuffer();
 873              firstCode = buffer.get();
 874              entryCount = buffer.get();
 875              glyphIdArray = new char[entryCount];
 876              for (int i=0; i< entryCount; i++) {
 877                  glyphIdArray[i] = buffer.get();
 878              }
 879          }
 880 
 881          char getGlyph(int charCode) {
 882             int controlGlyph = getControlCodeGlyph(charCode, true);
 883             if (controlGlyph >= 0) {
 884                 return (char)controlGlyph;
 885             }
 886 
 887              if (xlat != null) {
 888                  charCode = xlat[charCode];
 889              }
 890 
 891              charCode -= firstCode;
 892              if (charCode < 0 || charCode >= entryCount) {
 893                   return 0;
 894              } else {
 895                   return glyphIdArray[charCode];
 896              }
 897          }
 898     }
 899 
 900     // Format 8: mixed 16-bit and 32-bit coverage
 901     // Seems unlikely this code will ever get tested as we look for
 902     // MS platform Cmaps and MS states (in the Opentype spec on their website)
 903     // that MS doesn't support this format
 904     static class CMapFormat8 extends CMap {
 905          byte[] is32 = new byte[8192];
 906          int nGroups;
 907          int[] startCharCode;
 908          int[] endCharCode;
 909          int[] startGlyphID;
 910 
 911          CMapFormat8(ByteBuffer bbuffer, int offset, char[] xlat) {
 912 
 913              bbuffer.position(12);
 914              bbuffer.get(is32);
 915              nGroups = bbuffer.getInt();
 916              startCharCode = new int[nGroups];
 917              endCharCode   = new int[nGroups];
 918              startGlyphID  = new int[nGroups];
 919          }
 920 
 921         char getGlyph(int charCode) {
 922             if (xlat != null) {
 923                 throw new RuntimeException("xlat array for cmap fmt=8");
 924             }
 925             return 0;
 926         }
 927 
 928     }
 929 
 930 
 931     // Format 4-byte 10: Trimmed table mapping
 932     // Seems unlikely this code will ever get tested as we look for
 933     // MS platform Cmaps and MS states (in the Opentype spec on their website)
 934     // that MS doesn't support this format
 935     static class CMapFormat10 extends CMap {
 936 
 937          long firstCode;
 938          int entryCount;
 939          char[] glyphIdArray;
 940 
 941          CMapFormat10(ByteBuffer bbuffer, int offset, char[] xlat) {
 942 
 943              firstCode = bbuffer.getInt() & INTMASK;
 944              entryCount = bbuffer.getInt() & INTMASK;
 945              bbuffer.position(offset+20);
 946              CharBuffer buffer = bbuffer.asCharBuffer();
 947              glyphIdArray = new char[entryCount];
 948              for (int i=0; i< entryCount; i++) {
 949                  glyphIdArray[i] = buffer.get();
 950              }
 951          }
 952 
 953          char getGlyph(int charCode) {
 954 
 955              if (xlat != null) {
 956                  throw new RuntimeException("xlat array for cmap fmt=10");
 957              }
 958 
 959              int code = (int)(charCode - firstCode);
 960              if (code < 0 || code >= entryCount) {
 961                  return 0;
 962              } else {
 963                  return glyphIdArray[code];
 964              }
 965          }
 966     }
 967 
 968     // Format 12: Segmented coverage for UCS-4 (fonts supporting
 969     // surrogate pairs)
 970     static class CMapFormat12 extends CMap {
 971 
 972         int numGroups;
 973         int highBit =0;
 974         int power;
 975         int extra;
 976         long[] startCharCode;
 977         long[] endCharCode;
 978         int[] startGlyphID;
 979 
 980         CMapFormat12(ByteBuffer buffer, int offset, char[] xlat) {
 981             if (xlat != null) {
 982                 throw new RuntimeException("xlat array for cmap fmt=12");
 983             }
 984 
 985             numGroups = buffer.getInt(offset+12);
 986             startCharCode = new long[numGroups];
 987             endCharCode = new long[numGroups];
 988             startGlyphID = new int[numGroups];
 989             buffer.position(offset+16);
 990             buffer = buffer.slice();
 991             IntBuffer ibuffer = buffer.asIntBuffer();
 992             for (int i=0; i<numGroups; i++) {
 993                 startCharCode[i] = ibuffer.get() & INTMASK;
 994                 endCharCode[i] = ibuffer.get() & INTMASK;
 995                 startGlyphID[i] = ibuffer.get() & INTMASK;
 996             }
 997 
 998             /* Finds the high bit by binary searching through the bits */
 999             int value = numGroups;
1000 
1001             if (value >= 1 << 16) {
1002                 value >>= 16;
1003                 highBit += 16;
1004             }
1005 
1006             if (value >= 1 << 8) {
1007                 value >>= 8;
1008                 highBit += 8;
1009             }
1010 
1011             if (value >= 1 << 4) {
1012                 value >>= 4;
1013                 highBit += 4;
1014             }
1015 
1016             if (value >= 1 << 2) {
1017                 value >>= 2;
1018                 highBit += 2;
1019             }
1020 
1021             if (value >= 1 << 1) {
1022                 value >>= 1;
1023                 highBit += 1;
1024             }
1025 
1026             power = 1 << highBit;
1027             extra = numGroups - power;
1028         }
1029 
1030         char getGlyph(int charCode) {
1031             int controlGlyph = getControlCodeGlyph(charCode, false);
1032             if (controlGlyph >= 0) {
1033                 return (char)controlGlyph;
1034             }
1035             int probe = power;
1036             int range = 0;
1037 
1038             if (startCharCode[extra] <= charCode) {
1039                 range = extra;
1040             }
1041 
1042             while (probe > 1) {
1043                 probe >>= 1;
1044 
1045                 if (startCharCode[range+probe] <= charCode) {
1046                     range += probe;
1047                 }
1048             }
1049 
1050             if (startCharCode[range] <= charCode &&
1051                   endCharCode[range] >= charCode) {
1052                 return (char)
1053                     (startGlyphID[range] + (charCode - startCharCode[range]));
1054             }
1055 
1056             return 0;
1057         }
1058 
1059     }
1060 
1061     /* Used to substitute for bad Cmaps. */
1062     static class NullCMapClass extends CMap {
1063 
1064         char getGlyph(int charCode) {
1065             return 0;
1066         }
1067     }
1068 
1069     public static final NullCMapClass theNullCmap = new NullCMapClass();
1070 
1071     final int getControlCodeGlyph(int charCode, boolean noSurrogates) {
1072         if (charCode < 0x0010) {
1073             switch (charCode) {
1074             case 0x0009:
1075             case 0x000a:
1076             case 0x000d: return CharToGlyphMapper.INVISIBLE_GLYPH_ID;
1077             }
1078         } else if (charCode >= 0x200c) {
1079             if ((charCode <= 0x200f) ||
1080                 (charCode >= 0x2028 && charCode <= 0x202e) ||
1081                 (charCode >= 0x206a && charCode <= 0x206f)) {
1082                 return CharToGlyphMapper.INVISIBLE_GLYPH_ID;
1083             } else if (noSurrogates && charCode >= 0xFFFF) {
1084                 return 0;
1085             }
1086         }
1087         return -1;
1088     }
1089 
1090     static class UVS {
1091         int numSelectors;
1092         int[] selector;
1093 
1094         //for Default UVS Table
1095         int[] numUnicodeValueRanges;
1096         int[][] startUnicodeValue;
1097         byte[][] additionalCount;
1098         //for Non-Default UVS Table
1099         int[] numUVSMapping;
1100         int[][] unicodeValue;
1101         char[][] glyphID;
1102 
1103         UVS(ByteBuffer buffer, int offset) {
1104             numSelectors = buffer.getInt(offset+6);
1105             selector = new int[numSelectors];
1106             numUnicodeValueRanges = new int[numSelectors];
1107             startUnicodeValue = new int[numSelectors][];
1108             additionalCount = new byte[numSelectors][];
1109             numUVSMapping = new int[numSelectors];
1110             unicodeValue = new int[numSelectors][];
1111             glyphID = new char[numSelectors][];
1112 
1113             for (int i = 0; i < numSelectors; i++) {
1114                 buffer.position(offset + 10 + i * 11);
1115                 selector[i] = (buffer.get() & 0xff) << 16; //UINT24
1116                 selector[i] += (buffer.get() & 0xff) << 8;
1117                 selector[i] += buffer.get() & 0xff;
1118 
1119                 //for Default UVS Table
1120                 int tableOffset = buffer.getInt(offset + 10 + i * 11 + 3);
1121                 if (tableOffset == 0) {
1122                     numUnicodeValueRanges[i] = 0;
1123                 } else {
1124                     buffer.position(offset+tableOffset);
1125                     numUnicodeValueRanges[i] = buffer.getInt() & INTMASK;
1126 
1127                     startUnicodeValue[i] = new int[numUnicodeValueRanges[i]];
1128                     additionalCount[i] = new byte[numUnicodeValueRanges[i]];
1129 
1130                     for (int j = 0; j < numUnicodeValueRanges[i]; j++) {
1131                         int temp = (buffer.get() & 0xff) << 16; //UINT24
1132                         temp += (buffer.get() & 0xff) << 8;
1133                         temp += buffer.get() & 0xff;
1134                         startUnicodeValue[i][j] = temp;
1135                         additionalCount[i][j] =  buffer.get();
1136                     }
1137                 }
1138 
1139                 //for Non-Default UVS Table
1140                 tableOffset = buffer.getInt(offset + 10 + i * 11 + 7);
1141                 if (tableOffset == 0) {
1142                     numUVSMapping[i] = 0;
1143                 } else {
1144                     buffer.position(offset+tableOffset);
1145                     numUVSMapping[i] = buffer.getInt() & INTMASK;
1146                     unicodeValue[i] = new int[numUVSMapping[i]];
1147                     glyphID[i] = new char[numUVSMapping[i]];
1148 
1149                     for (int j = 0; j < numUVSMapping[i]; j++) {
1150                         int temp = (buffer.get() & 0xff) << 16; //UINT24
1151                         temp += (buffer.get() & 0xff) << 8;
1152                         temp += buffer.get() & 0xff;
1153                         unicodeValue[i][j] = temp;
1154                         glyphID[i][j] = buffer.getChar();
1155                     }
1156                 }
1157             }
1158         }
1159 
1160         private int cachedCode;
1161         private int targetCachedCode;
1162         private int targetCachedSelector = -1;
1163 
1164         /* getGlyph for Variation selector
1165            return value:
1166             0: A special glyph for the variation selector is Not found
1167            -1: Default glyph should be used
1168            0>: A special glyph is found
1169         */
1170         int getGlyph(int charCode, int variationSelector) {
1171             synchronized(this) {
1172                 if (charCode == targetCachedCode && 
1173                     variationSelector == targetCachedSelector) {
1174                     return cachedCode;
1175                 }
1176             }
1177 
1178             int targetSelector = -1;
1179             int result;
1180             for (int i = 0; i < numSelectors; i++) {
1181                 if (selector[i] == variationSelector) {
1182                     targetSelector = i;
1183                     break;
1184                 }
1185             }
1186             if (targetSelector == -1) {
1187                 result = 0;
1188                 storeCache(charCode, variationSelector, result);
1189                 return result;
1190             }
1191             if (numUnicodeValueRanges[targetSelector] > 0) {
1192                 int index = java.util.Arrays.binarySearch(
1193                                 startUnicodeValue[targetSelector], charCode);
1194                 if (index >= 0) {
1195                     result = -1; //pass through default table in actual CMAP
1196                     storeCache(charCode, variationSelector, result);
1197                     return result;
1198                 } else {
1199                     index = -index - 2;
1200                     if (index >=0 &&
1201                         charCode >= startUnicodeValue[targetSelector][index] &&
1202                         charCode <= startUnicodeValue[targetSelector][index]
1203                                     +additionalCount[targetSelector][index]) {
1204                         result = -1; //pass through default table in actual CMAP
1205                         storeCache(charCode, variationSelector, result);
1206                         return result;
1207                     }
1208                 }
1209             }
1210             if (numUVSMapping[targetSelector] > 0) {
1211                 int index = java.util.Arrays.binarySearch(
1212                                 unicodeValue[targetSelector], charCode);
1213                 if (index >= 0) {
1214                     result = glyphID[targetSelector][index];
1215                     storeCache(charCode, variationSelector, result);
1216                     return result;
1217                 }
1218             }
1219             result = 0;
1220             storeCache(charCode, variationSelector, result);
1221             return result;
1222         }
1223 
1224         private synchronized void storeCache(int charCode, 
1225                                              int variationSelector, 
1226                                              int glyph) {
1227             cachedCode = glyph;
1228             targetCachedCode = charCode;
1229             targetCachedSelector = variationSelector;
1230         }
1231 
1232         boolean hasVariationSelectorGlyph(int charCode,
1233                                           int variationSelector) {
1234             int result = getGlyph(charCode, variationSelector);
1235             if (result == 0) {
1236                 return false;
1237             } else {
1238                 return true;
1239             }
1240         }
1241     }
1242 
1243     public char getGlyph(int charCode, int variationSelector) {
1244         if (uvs == null) {
1245             return 0;
1246         }
1247         int result = uvs.getGlyph(charCode, variationSelector);
1248         if (result == -1) {
1249             result = this.getGlyph(charCode);
1250         }
1251         return (char)(result & 0xFFFF);
1252     }
1253 
1254     public boolean hasVariationSelectorGlyph(int charCode, 
1255                                              int variationSelector) {
1256         if (uvs == null) {
1257             return false;
1258         }
1259         return uvs.hasVariationSelectorGlyph(charCode, variationSelector);
1260     }
1261 }