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