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 144 static CMap initialize(TrueTypeFont font) { 145 146 CMap cmap = null; 147 148 int offset, platformID, encodingID=-1; 149 150 int three0=0, three1=0, three2=0, three3=0, three4=0, three5=0, 151 three6=0, three10=0; 152 boolean threeStar = false; 153 154 ByteBuffer cmapBuffer = font.getTableBuffer(TrueTypeFont.cmapTag); 155 int cmapTableOffset = font.getTableSize(TrueTypeFont.cmapTag); 156 short numberSubTables = cmapBuffer.getShort(2); 157 158 /* locate the offsets of all 3,* (ie Microsoft platform) encodings */ 159 for (int i=0; i<numberSubTables; i++) { 160 cmapBuffer.position(i * 8 + 4); 161 platformID = cmapBuffer.getShort(); 162 if (platformID == 3) { 163 threeStar = true; 164 encodingID = cmapBuffer.getShort(); 165 offset = cmapBuffer.getInt(); 166 switch (encodingID) { 167 case 0: three0 = offset; break; // MS Symbol encoding 168 case 1: three1 = offset; break; // MS Unicode cmap 169 case 2: three2 = offset; break; // ShiftJIS cmap. 170 case 3: three3 = offset; break; // GBK cmap 171 case 4: three4 = offset; break; // Big 5 cmap 172 case 5: three5 = offset; break; // Wansung 173 case 6: three6 = offset; break; // Johab 174 case 10: three10 = offset; break; // MS Unicode surrogates 175 } 176 } 177 } 178 179 /* This defines the preference order for cmap subtables */ 180 if (threeStar) { 181 if (three10 != 0) { 182 cmap = createCMap(cmapBuffer, three10, null); 183 } 184 else if (three0 != 0) { 185 /* The special case treatment of these fonts leads to 186 * anomalies where a user can view "wingdings" and "wingdings2" 187 * and the latter shows all its code points in the unicode 188 * private use area at 0xF000->0XF0FF and the former shows 189 * a scattered subset of its glyphs that are known mappings to 190 * unicode code points. 191 * The primary purpose of these mappings was to facilitate 192 * display of symbol chars etc in composite fonts, however 193 * this is not needed as all these code points are covered 194 * by Lucida Sans Regular. 195 * Commenting this out reduces the role of these two files 245 else { 246 cmap = createCMap(cmapBuffer, three4, 247 getConverterMap(Big5Encoding)); 248 } 249 } 250 else if (three5 != 0) { 251 cmap = createCMap(cmapBuffer, three5, 252 getConverterMap(WansungEncoding)); 253 } 254 else if (three6 != 0) { 255 cmap = createCMap(cmapBuffer, three6, 256 getConverterMap(JohabEncoding)); 257 } 258 } else { 259 /* No 3,* subtable was found. Just use whatever is the first 260 * table listed. Not very useful but maybe better than 261 * rejecting the font entirely? 262 */ 263 cmap = createCMap(cmapBuffer, cmapBuffer.getInt(8), null); 264 } 265 return cmap; 266 } 267 268 /* speed up the converting by setting the range for double 269 * byte characters; 270 */ 271 static char[] getConverter(short encodingID) { 272 int dBegin = 0x8000; 273 int dEnd = 0xffff; 274 String encoding; 275 276 switch (encodingID) { 277 case ShiftJISEncoding: 278 dBegin = 0x8140; 279 dEnd = 0xfcfc; 280 encoding = "SJIS"; 281 break; 282 case GBKEncoding: 283 dBegin = 0x8140; 284 dEnd = 0xfea0; 407 subtableLength = buffer.getInt(offset+4) & INTMASK; 408 } 409 if (offset+subtableLength > buffer.capacity()) { 410 if (FontUtilities.isLogging()) { 411 FontUtilities.getLogger().warning("Cmap subtable overflows buffer."); 412 } 413 } 414 switch (subtableFormat) { 415 case 0: return new CMapFormat0(buffer, offset); 416 case 2: return new CMapFormat2(buffer, offset, xlat); 417 case 4: return new CMapFormat4(buffer, offset, xlat); 418 case 6: return new CMapFormat6(buffer, offset, xlat); 419 case 8: return new CMapFormat8(buffer, offset, xlat); 420 case 10: return new CMapFormat10(buffer, offset, xlat); 421 case 12: return new CMapFormat12(buffer, offset, xlat); 422 default: throw new RuntimeException("Cmap format unimplemented: " + 423 (int)buffer.getChar(offset)); 424 } 425 } 426 427 /* 428 final char charVal(byte[] cmap, int index) { 429 return (char)(((0xff & cmap[index]) << 8)+(0xff & cmap[index+1])); 430 } 431 432 final short shortVal(byte[] cmap, int index) { 433 return (short)(((0xff & cmap[index]) << 8)+(0xff & cmap[index+1])); 434 } 435 */ 436 abstract char getGlyph(int charCode); 437 438 /* Format 4 Header is 439 * ushort format (off=0) 440 * ushort length (off=2) 441 * ushort language (off=4) 442 * ushort segCountX2 (off=6) 443 * ushort searchRange (off=8) 444 * ushort entrySelector (off=10) 445 * ushort rangeShift (off=12) 446 * ushort endCount[segCount] (off=14) 1042 public static final NullCMapClass theNullCmap = new NullCMapClass(); 1043 1044 final int getControlCodeGlyph(int charCode, boolean noSurrogates) { 1045 if (charCode < 0x0010) { 1046 switch (charCode) { 1047 case 0x0009: 1048 case 0x000a: 1049 case 0x000d: return CharToGlyphMapper.INVISIBLE_GLYPH_ID; 1050 } 1051 } else if (charCode >= 0x200c) { 1052 if ((charCode <= 0x200f) || 1053 (charCode >= 0x2028 && charCode <= 0x202e) || 1054 (charCode >= 0x206a && charCode <= 0x206f)) { 1055 return CharToGlyphMapper.INVISIBLE_GLYPH_ID; 1056 } else if (noSurrogates && charCode >= 0xFFFF) { 1057 return 0; 1058 } 1059 } 1060 return -1; 1061 } 1062 } | 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 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; 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) 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 } |