idDelta[i] = (short)buffer.get(); } for (int i=0; i<segCount; i++) { char ctmp = buffer.get(); idRangeOffset[i] = (char)((ctmp>>1)&0xffff); } /* Can calculate the number of glyph IDs by subtracting * "pos" from the length of the cmap */ int pos = (segCount*8+16)/2; buffer.position(pos); int numGlyphIds = (subtableLength/2 - pos); glyphIds = new char[numGlyphIds]; for (int i=0;i<numGlyphIds;i++) { glyphIds[i] = buffer.get(); }/* System.err.println("segcount="+segCount); System.err.println("entrySelector="+entrySelector); System.err.println("rangeShift="+rangeShift); for (int j=0;j<segCount;j++) { System.err.println("j="+j+ " sc="+(int)(startCount[j]&0xffff)+ " ec="+(int)(endCount[j]&0xffff)+ " delta="+idDelta[j] + " ro="+(int)idRangeOffset[j]); } //System.err.println("numglyphs="+glyphIds.length); for (int i=0;i<numGlyphIds;i++) { System.err.println("gid["+i+"]="+(int)glyphIds[i]); }*/ } char getGlyph(int charCode) { int index = 0; char glyphCode = 0; int controlGlyph = getControlCodeGlyph(charCode, true); if (controlGlyph >= 0) { return (char)controlGlyph; } /* presence of translation array indicates that this * cmap is in some other (non-unicode encoding). * In order to look-up a char->glyph mapping we need to * translate the unicode code point to the encoding of * the cmap. * REMIND: VALID CHARCODES?? */ if (xlat != null) { charCode = xlat[charCode]; } /* * Citation from the TrueType (and OpenType) spec: * The segments are sorted in order of increasing endCode * values, and the segment values are specified in four parallel * arrays. You search for the first endCode that is greater than * or equal to the character code you want to map. If the * corresponding startCode is less than or equal to the * character code, then you use the corresponding idDelta and * idRangeOffset to map the character code to a glyph index * (otherwise, the missingGlyph is returned). */ /* * CMAP format4 defines several fields for optimized search of * the segment list (entrySelector, searchRange, rangeShift). * However, benefits are neglible and some fonts have incorrect * data - so we use straightforward binary search (see bug 6247425) */ int left = 0, right = startCount.length; index = startCount.length >> 1; while (left < right) { if (endCount[index] < charCode) { left = index + 1; } else { right = index; } index = (left + right) >> 1; } if (charCode >= startCount[index] && charCode <= endCount[index]) { int rangeOffset = idRangeOffset[index]; if (rangeOffset == 0) { glyphCode = (char)(charCode + idDelta[index]); } else { /* Calculate an index into the glyphIds array */ /* System.err.println("rangeoffset="+rangeOffset+ " charCode=" + charCode + " scnt["+index+"]="+(int)startCount[index] + " segCnt="+segCount);*/ int glyphIDIndex = rangeOffset - segCount + index + (charCode - startCount[index]); glyphCode = glyphIds[glyphIDIndex]; if (glyphCode != 0) { glyphCode = (char)(glyphCode + idDelta[index]); } } } if (glyphCode != 0) { //System.err.println("cc="+Integer.toHexString((int)charCode) + " gc="+(int)glyphCode); } return glyphCode; } } // Format 0: Byte Encoding table static class CMapFormat0 extends CMap { byte [] cmap; CMapFormat0(ByteBuffer buffer, int offset) { /* skip 6 bytes of format, length, and version */ int len = buffer.getChar(offset+2); cmap = new byte[len-6]; buffer.position(offset+6); buffer.get(cmap); } char getGlyph(int charCode) { if (charCode < 256) { if (charCode < 0x0010) { switch (charCode) { case 0x0009: case 0x000a: case 0x000d: return CharToGlyphMapper.INVISIBLE_GLYPH_ID; } } return (char)(0xff & cmap[charCode]); } else { return 0; } } } // static CMap createSymbolCMap(ByteBuffer buffer, int offset, char[] syms) { // CMap cmap = createCMap(buffer, offset, null);// if (cmap == null) {// return null;// } else {// return new CMapFormatSymbol(cmap, syms);// }// } // static class CMapFormatSymbol extends CMap { // CMap cmap;// static final int NUM_BUCKETS = 128;// Bucket[] buckets = new Bucket[NUM_BUCKETS]; // class Bucket {// char unicode;// char glyph;// Bucket next; // Bucket(char u, char g) {// unicode = u;// glyph = g;// }// } // CMapFormatSymbol(CMap cmap, char[] syms) { // this.cmap = cmap; // for (int i=0;i<syms.length;i++) {// char unicode = syms[i];// if (unicode != noSuchChar) {// char glyph = cmap.getGlyph(i + 0xf000);// int hash = unicode % NUM_BUCKETS;// Bucket bucket = new Bucket(unicode, glyph);// if (buckets[hash] == null) {// buckets[hash] = bucket;// } else {// Bucket b = buckets[hash];// while (b.next != null) {// b = b.next;// }// b.next = bucket;// }// }// }// } // char getGlyph(int unicode) {// if (unicode >= 0x1000) {// return 0;// }// else if (unicode >=0xf000 && unicode < 0xf100) {// return cmap.getGlyph(unicode);// } else {// Bucket b = buckets[unicode % NUM_BUCKETS];// while (b != null) {// if (b.unicode == unicode) {// return b.glyph;// } else {// b = b.next;// }// }// return 0;// }// }// } // Format 2: High-byte mapping through table static class CMapFormat2 extends CMap { char[] subHeaderKey = new char[256]; /* Store subheaders in individual arrays * A SubHeader entry theortically looks like { * char firstCode; * char entryCount; * short idDelta; * char idRangeOffset; * } */ char[] firstCodeArray; char[] entryCountArray; short[] idDeltaArray; char[] idRangeOffSetArray; char[] glyphIndexArray; CMapFormat2(ByteBuffer buffer, int offset, char[] xlat) { this.xlat = xlat; int tableLen = buffer.getChar(offset+2); buffer.position(offset+6); CharBuffer cBuffer = buffer.asCharBuffer(); char maxSubHeader = 0; for (int i=0;i<256;i++) { subHeaderKey[i] = cBuffer.get(); if (subHeaderKey[i] > maxSubHeader) { maxSubHeader = subHeaderKey[i]; } } /* The value of the subHeaderKey is 8 * the subHeader index, * so the number of subHeaders can be obtained by dividing * this value bv 8 and adding 1. */ int numSubHeaders = (maxSubHeader >> 3) +1; firstCodeArray = new char[numSubHeaders]; entryCountArray = new char[numSubHeaders]; idDeltaArray = new short[numSubHeaders]; idRangeOffSetArray = new char[numSubHeaders]; for (int i=0; i<numSubHeaders; i++) { firstCodeArray[i] = cBuffer.get(); entryCountArray[i] = cBuffer.get(); idDeltaArray[i] = (short)cBuffer.get(); idRangeOffSetArray[i] = cBuffer.get();// System.out.println("sh["+i+"]:fc="+(int)firstCodeArray[i]+// " ec="+(int)entryCountArray[i]+// " delta="+(int)idDeltaArray[i]+// " offset="+(int)idRangeOffSetArray[i]); } int glyphIndexArrSize = (tableLen-518-numSubHeaders*8)/2; glyphIndexArray = new char[glyphIndexArrSize]; for (int i=0; i<glyphIndexArrSize;i++) { glyphIndexArray[i] = cBuffer.get(); } } char getGlyph(int charCode) { int controlGlyph = getControlCodeGlyph(charCode, true); if (controlGlyph >= 0) { return (char)controlGlyph; } if (xlat != null) { charCode = xlat[charCode]; } char highByte = (char)(charCode >> 8); char lowByte = (char)(charCode & 0xff); int key = subHeaderKey[highByte]>>3; // index into subHeaders char mapMe; if (key != 0) { mapMe = lowByte; } else { mapMe = highByte; if (mapMe == 0) { mapMe = lowByte; } } // System.err.println("charCode="+Integer.toHexString(charCode)+// " key="+key+ " mapMe="+Integer.toHexString(mapMe)); char firstCode = firstCodeArray[key]; if (mapMe < firstCode) { return 0; } else { mapMe -= firstCode; } if (mapMe < entryCountArray[key]) { /* "address" arithmetic is needed to calculate the offset * into glyphIndexArray. "idRangeOffSetArray[key]" specifies * the number of bytes from that location in the table where * the subarray of glyphIndexes starting at "firstCode" begins. * Each entry in the subHeader table is 8 bytes, and the * idRangeOffSetArray field is at offset 6 in the entry. * The glyphIndexArray immediately follows the subHeaders. * So if there are "N" entries then the number of bytes to the * start of glyphIndexArray is (N-key)*8-6. * Subtract this from the idRangeOffSetArray value to get * the number of bytes into glyphIndexArray and divide by 2 to * get the (char) array index. */ int glyphArrayOffset = ((idRangeOffSetArray.length-key)*8)-6; int glyphSubArrayStart = (idRangeOffSetArray[key] - glyphArrayOffset)/2; char glyphCode = glyphIndexArray[glyphSubArrayStart+mapMe]; if (glyphCode != 0) { glyphCode += idDeltaArray[key]; //idDelta return glyphCode; } } return 0; } } // Format 6: Trimmed table mapping static class CMapFormat6 extends CMap { char firstCode; char entryCount; char[] glyphIdArray; CMapFormat6(ByteBuffer bbuffer, int offset, char[] xlat) { System.err.println("WARNING: CMapFormat8 is untested."); bbuffer.position(offset+6); CharBuffer buffer = bbuffer.asCharBuffer(); firstCode = buffer.get(); entryCount = buffer.get(); glyphIdArray = new char[entryCount]; for (int i=0; i< entryCount; i++) { glyphIdArray[i] = buffer.get(); } } char getGlyph(int charCode) { int controlGlyph = getControlCodeGlyph(charCode, true); if (controlGlyph >= 0) { return (char)controlGlyph; } if (xlat != null) { charCode = xlat[charCode]; } charCode -= firstCode; if (charCode < 0 || charCode >= entryCount) { return 0; } else { return glyphIdArray[charCode]; } } } // Format 8: mixed 16-bit and 32-bit coverage // Seems unlikely this code will ever get tested as we look for // MS platform Cmaps and MS states (in the Opentype spec on their website) // that MS doesn't support this format static class CMapFormat8 extends CMap { byte[] is32 = new byte[8192]; int nGroups; int[] startCharCode; int[] endCharCode; int[] startGlyphID; CMapFormat8(ByteBuffer bbuffer, int offset, char[] xlat) { System.err.println("WARNING: CMapFormat8 is untested."); bbuffer.position(12); bbuffer.get(is32); nGroups = bbuffer.getInt(); startCharCode = new int[nGroups]; endCharCode = new int[nGroups]; startGlyphID = new int[nGroups]; } char getGlyph(int charCode) { if (xlat != null) { throw new RuntimeException("xlat array for cmap fmt=8"); } return 0; } } // Format 4-byte 10: Trimmed table mapping // Seems unlikely this code will ever get tested as we look for // MS platform Cmaps and MS states (in the Opentype spec on their website) // that MS doesn't support this format static class CMapFormat10 extends CMap { long firstCode; int entryCount; char[] glyphIdArray; CMapFormat10(ByteBuffer bbuffer, int offset, char[] xlat) { System.err.println("WARNING: CMapFormat10 is untested."); firstCode = bbuffer.getInt() & INTMASK; entryCount = bbuffer.getInt() & INTMASK; bbuffer.position(offset+20); CharBuffer buffer = bbuffer.asCharBuffer(); glyphIdArray = new char[entryCount]; for (int i=0; i< entryCount; i++) { glyphIdArray[i] = buffer.get(); } } char getGlyph(int charCode) { if (xlat != null) { throw new RuntimeException("xlat array for cmap fmt=10"); } int code = (int)(charCode - firstCode); if (code < 0 || code >= entryCount) { return 0; } else { return glyphIdArray[code]; } } } // Format 12: Segmented coverage for UCS-4 (fonts supporting // surrogate pairs) static class CMapFormat12 extends CMap { int numGroups; int highBit =0; int power; int extra; long[] startCharCode; long[] endCharCode; int[] startGlyphID; CMapFormat12(ByteBuffer buffer, int offset, char[] xlat) { if (xlat != null) { throw new RuntimeException("xlat array for cmap fmt=12"); } numGroups = buffer.getInt(offset+12); startCharCode = new long[numGroups]; endCharCode = new long[numGroups]; startGlyphID = new int[numGroups]; buffer.position(offset+16); buffer = buffer.slice(); IntBuffer ibuffer = buffer.asIntBuffer(); for (int i=0; i<numGroups; i++) { startCharCode[i] = ibuffer.get() & INTMASK; endCharCode[i] = ibuffer.get() & INTMASK; startGlyphID[i] = ibuffer.get() & INTMASK; } /* Finds the high bit by binary searching through the bits */ int value = numGroups; if (value >= 1 << 16) { value >>= 16; highBit += 16; } if (value >= 1 << 8) { value >>= 8; highBit += 8; } if (value >= 1 << 4) { value >>= 4; highBit += 4; } if (value >= 1 << 2) { value >>= 2; highBit += 2; } if (value >= 1 << 1) { value >>= 1; highBit += 1; }Lines 501–1000 of 1065. Previous lines Load more
idDelta[i] = (short)buffer.get(); } for (int i=0; i<segCount; i++) { char ctmp = buffer.get(); idRangeOffset[i] = (char)((ctmp>>1)&0xffff); } /* Can calculate the number of glyph IDs by subtracting * "pos" from the length of the cmap */ int pos = (segCount*8+16)/2; buffer.position(pos); int numGlyphIds = (subtableLength/2 - pos); glyphIds = new char[numGlyphIds]; for (int i=0;i<numGlyphIds;i++) { glyphIds[i] = buffer.get(); } /* System.err.println("segcount="+segCount); System.err.println("entrySelector="+entrySelector); System.err.println("rangeShift="+rangeShift); for (int j=0;j<segCount;j++) { System.err.println("j="+j+ " sc="+(int)(startCount[j]&0xffff)+ " ec="+(int)(endCount[j]&0xffff)+ " delta="+idDelta[j] + " ro="+(int)idRangeOffset[j]); } //System.err.println("numglyphs="+glyphIds.length); for (int i=0;i<numGlyphIds;i++) { System.err.println("gid["+i+"]="+(int)glyphIds[i]); } */ } char getGlyph(int charCode) { int index = 0; char glyphCode = 0; int controlGlyph = getControlCodeGlyph(charCode, true); if (controlGlyph >= 0) { return (char)controlGlyph; } /* presence of translation array indicates that this * cmap is in some other (non-unicode encoding). * In order to look-up a char->glyph mapping we need to * translate the unicode code point to the encoding of * the cmap. * REMIND: VALID CHARCODES?? */ if (xlat != null) { charCode = xlat[charCode]; } /* * Citation from the TrueType (and OpenType) spec: * The segments are sorted in order of increasing endCode * values, and the segment values are specified in four parallel * arrays. You search for the first endCode that is greater than * or equal to the character code you want to map. If the * corresponding startCode is less than or equal to the * character code, then you use the corresponding idDelta and * idRangeOffset to map the character code to a glyph index * (otherwise, the missingGlyph is returned). */ /* * CMAP format4 defines several fields for optimized search of * the segment list (entrySelector, searchRange, rangeShift). * However, benefits are neglible and some fonts have incorrect * data - so we use straightforward binary search (see bug 6247425) */ int left = 0, right = startCount.length; index = startCount.length >> 1; while (left < right) { if (endCount[index] < charCode) { left = index + 1; } else { right = index; } index = (left + right) >> 1; } if (charCode >= startCount[index] && charCode <= endCount[index]) { int rangeOffset = idRangeOffset[index]; if (rangeOffset == 0) { glyphCode = (char)(charCode + idDelta[index]); } else { /* Calculate an index into the glyphIds array */ /* System.err.println("rangeoffset="+rangeOffset+ " charCode=" + charCode + " scnt["+index+"]="+(int)startCount[index] + " segCnt="+segCount); */ int glyphIDIndex = rangeOffset - segCount + index + (charCode - startCount[index]); glyphCode = glyphIds[glyphIDIndex]; if (glyphCode != 0) { glyphCode = (char)(glyphCode + idDelta[index]); } } } if (glyphCode != 0) { //System.err.println("cc="+Integer.toHexString((int)charCode) + " gc="+(int)glyphCode); } return glyphCode; } } // Format 0: Byte Encoding table static class CMapFormat0 extends CMap { byte [] cmap; CMapFormat0(ByteBuffer buffer, int offset) { /* skip 6 bytes of format, length, and version */ int len = buffer.getChar(offset+2); cmap = new byte[len-6]; buffer.position(offset+6); buffer.get(cmap); } char getGlyph(int charCode) { if (charCode < 256) { if (charCode < 0x0010) { switch (charCode) { case 0x0009: case 0x000a: case 0x000d: return CharToGlyphMapper.INVISIBLE_GLYPH_ID; } } return (char)(0xff & cmap[charCode]); } else { return 0; } } } // static CMap createSymbolCMap(ByteBuffer buffer, int offset, char[] syms) { // CMap cmap = createCMap(buffer, offset, null); // if (cmap == null) { // return null; // } else { // return new CMapFormatSymbol(cmap, syms); // } // } // static class CMapFormatSymbol extends CMap { // CMap cmap; // static final int NUM_BUCKETS = 128; // Bucket[] buckets = new Bucket[NUM_BUCKETS]; // class Bucket { // char unicode; // char glyph; // Bucket next; // Bucket(char u, char g) { // unicode = u; // glyph = g; // } // } // CMapFormatSymbol(CMap cmap, char[] syms) { // this.cmap = cmap; // for (int i=0;i<syms.length;i++) { // char unicode = syms[i]; // if (unicode != noSuchChar) { // char glyph = cmap.getGlyph(i + 0xf000); // int hash = unicode % NUM_BUCKETS; // Bucket bucket = new Bucket(unicode, glyph); // if (buckets[hash] == null) { // buckets[hash] = bucket; // } else { // Bucket b = buckets[hash]; // while (b.next != null) { // b = b.next; // } // b.next = bucket; // } // } // } // } // char getGlyph(int unicode) { // if (unicode >= 0x1000) { // return 0; // } // else if (unicode >=0xf000 && unicode < 0xf100) { // return cmap.getGlyph(unicode); // } else { // Bucket b = buckets[unicode % NUM_BUCKETS]; // while (b != null) { // if (b.unicode == unicode) { // return b.glyph; // } else { // b = b.next; // } // } // return 0; // } // } // } // Format 2: High-byte mapping through table static class CMapFormat2 extends CMap { char[] subHeaderKey = new char[256]; /* Store subheaders in individual arrays * A SubHeader entry theortically looks like { * char firstCode; * char entryCount; * short idDelta; * char idRangeOffset; * } */ char[] firstCodeArray; char[] entryCountArray; short[] idDeltaArray; char[] idRangeOffSetArray; char[] glyphIndexArray; CMapFormat2(ByteBuffer buffer, int offset, char[] xlat) { this.xlat = xlat; int tableLen = buffer.getChar(offset+2); buffer.position(offset+6); CharBuffer cBuffer = buffer.asCharBuffer(); char maxSubHeader = 0; for (int i=0;i<256;i++) { subHeaderKey[i] = cBuffer.get(); if (subHeaderKey[i] > maxSubHeader) { maxSubHeader = subHeaderKey[i]; } } /* The value of the subHeaderKey is 8 * the subHeader index, * so the number of subHeaders can be obtained by dividing * this value bv 8 and adding 1. */ int numSubHeaders = (maxSubHeader >> 3) +1; firstCodeArray = new char[numSubHeaders]; entryCountArray = new char[numSubHeaders]; idDeltaArray = new short[numSubHeaders]; idRangeOffSetArray = new char[numSubHeaders]; for (int i=0; i<numSubHeaders; i++) { firstCodeArray[i] = cBuffer.get(); entryCountArray[i] = cBuffer.get(); idDeltaArray[i] = (short)cBuffer.get(); idRangeOffSetArray[i] = cBuffer.get(); // System.out.println("sh["+i+"]:fc="+(int)firstCodeArray[i]+ // " ec="+(int)entryCountArray[i]+ // " delta="+(int)idDeltaArray[i]+ // " offset="+(int)idRangeOffSetArray[i]); } int glyphIndexArrSize = (tableLen-518-numSubHeaders*8)/2; glyphIndexArray = new char[glyphIndexArrSize]; for (int i=0; i<glyphIndexArrSize;i++) { glyphIndexArray[i] = cBuffer.get(); } } char getGlyph(int charCode) { int controlGlyph = getControlCodeGlyph(charCode, true); if (controlGlyph >= 0) { return (char)controlGlyph; } if (xlat != null) { charCode = xlat[charCode]; } char highByte = (char)(charCode >> 8); char lowByte = (char)(charCode & 0xff); int key = subHeaderKey[highByte]>>3; // index into subHeaders char mapMe; if (key != 0) { mapMe = lowByte; } else { mapMe = highByte; if (mapMe == 0) { mapMe = lowByte; } } // System.err.println("charCode="+Integer.toHexString(charCode)+ // " key="+key+ " mapMe="+Integer.toHexString(mapMe)); char firstCode = firstCodeArray[key]; if (mapMe < firstCode) { return 0; } else { mapMe -= firstCode; } if (mapMe < entryCountArray[key]) { /* "address" arithmetic is needed to calculate the offset * into glyphIndexArray. "idRangeOffSetArray[key]" specifies * the number of bytes from that location in the table where * the subarray of glyphIndexes starting at "firstCode" begins. * Each entry in the subHeader table is 8 bytes, and the * idRangeOffSetArray field is at offset 6 in the entry. * The glyphIndexArray immediately follows the subHeaders. * So if there are "N" entries then the number of bytes to the * start of glyphIndexArray is (N-key)*8-6. * Subtract this from the idRangeOffSetArray value to get * the number of bytes into glyphIndexArray and divide by 2 to * get the (char) array index. */ int glyphArrayOffset = ((idRangeOffSetArray.length-key)*8)-6; int glyphSubArrayStart = (idRangeOffSetArray[key] - glyphArrayOffset)/2; char glyphCode = glyphIndexArray[glyphSubArrayStart+mapMe]; if (glyphCode != 0) { glyphCode += idDeltaArray[key]; //idDelta return glyphCode; } } return 0; } } // Format 6: Trimmed table mapping static class CMapFormat6 extends CMap { char firstCode; char entryCount; char[] glyphIdArray; CMapFormat6(ByteBuffer bbuffer, int offset, char[] xlat) { System.err.println("WARNING: CMapFormat8 is untested."); bbuffer.position(offset+6); CharBuffer buffer = bbuffer.asCharBuffer(); firstCode = buffer.get(); entryCount = buffer.get(); glyphIdArray = new char[entryCount]; for (int i=0; i< entryCount; i++) { glyphIdArray[i] = buffer.get(); } } char getGlyph(int charCode) { int controlGlyph = getControlCodeGlyph(charCode, true); if (controlGlyph >= 0) { return (char)controlGlyph; } if (xlat != null) { charCode = xlat[charCode]; } charCode -= firstCode; if (charCode < 0 || charCode >= entryCount) { return 0; } else { return glyphIdArray[charCode]; } } } // Format 8: mixed 16-bit and 32-bit coverage // Seems unlikely this code will ever get tested as we look for // MS platform Cmaps and MS states (in the Opentype spec on their website) // that MS doesn't support this format static class CMapFormat8 extends CMap { byte[] is32 = new byte[8192]; int nGroups; int[] startCharCode; int[] endCharCode; int[] startGlyphID; CMapFormat8(ByteBuffer bbuffer, int offset, char[] xlat) { System.err.println("WARNING: CMapFormat8 is untested."); bbuffer.position(12); bbuffer.get(is32); nGroups = bbuffer.getInt(); startCharCode = new int[nGroups]; endCharCode = new int[nGroups]; startGlyphID = new int[nGroups]; } char getGlyph(int charCode) { if (xlat != null) { throw new RuntimeException("xlat array for cmap fmt=8"); } return 0; } } // Format 4-byte 10: Trimmed table mapping // Seems unlikely this code will ever get tested as we look for // MS platform Cmaps and MS states (in the Opentype spec on their website) // that MS doesn't support this format static class CMapFormat10 extends CMap { long firstCode; int entryCount; char[] glyphIdArray; CMapFormat10(ByteBuffer bbuffer, int offset, char[] xlat) { System.err.println("WARNING: CMapFormat10 is untested."); firstCode = bbuffer.getInt() & INTMASK; entryCount = bbuffer.getInt() & INTMASK; bbuffer.position(offset+20); CharBuffer buffer = bbuffer.asCharBuffer(); glyphIdArray = new char[entryCount]; for (int i=0; i< entryCount; i++) { glyphIdArray[i] = buffer.get(); } } char getGlyph(int charCode) { if (xlat != null) { throw new RuntimeException("xlat array for cmap fmt=10"); } int code = (int)(charCode - firstCode); if (code < 0 || code >= entryCount) { return 0; } else { return glyphIdArray[code]; } } } // Format 12: Segmented coverage for UCS-4 (fonts supporting // surrogate pairs) static class CMapFormat12 extends CMap { int numGroups; int highBit =0; int power; int extra; long[] startCharCode; long[] endCharCode; int[] startGlyphID; CMapFormat12(ByteBuffer buffer, int offset, char[] xlat) { if (xlat != null) { throw new RuntimeException("xlat array for cmap fmt=12"); } numGroups = buffer.getInt(offset+12); startCharCode = new long[numGroups]; endCharCode = new long[numGroups]; startGlyphID = new int[numGroups]; buffer.position(offset+16); buffer = buffer.slice(); IntBuffer ibuffer = buffer.asIntBuffer(); for (int i=0; i<numGroups; i++) { startCharCode[i] = ibuffer.get() & INTMASK; endCharCode[i] = ibuffer.get() & INTMASK; startGlyphID[i] = ibuffer.get() & INTMASK; } /* Finds the high bit by binary searching through the bits */ int value = numGroups; if (value >= 1 << 16) { value >>= 16; highBit += 16; } if (value >= 1 << 8) { value >>= 8; highBit += 8; } if (value >= 1 << 4) { value >>= 4; highBit += 4; } if (value >= 1 << 2) { value >>= 2; highBit += 2; } if (value >= 1 << 1) { value >>= 1; highBit += 1; }