226 throw new FontFormatException("Unexpected runtime exception.");
227 }
228 }
229 Disposer.addObjectRecord(this, disposerRecord);
230 }
231
232 private synchronized FileChannel open() throws FontFormatException {
233 return open(true);
234 }
235
236 /* This is intended to be called, and the returned value used,
237 * from within a block synchronized on this font object.
238 * ie the channel returned may be nulled out at any time by "close()"
239 * unless the caller holds a lock.
240 * Deadlock warning: FontManager.addToPool(..) acquires a global lock,
241 * which means nested locks may be in effect.
242 */
243 private synchronized FileChannel open(boolean usePool)
244 throws FontFormatException {
245 if (disposerRecord.channel == null) {
246 if (FontUtilities.isLogging()) {
247 FontUtilities.getLogger().info("open TTF: " + platName);
248 }
249 try {
250 RandomAccessFile raf = AccessController.doPrivileged(
251 new PrivilegedExceptionAction<RandomAccessFile>() {
252 public RandomAccessFile run() throws FileNotFoundException {
253 return new RandomAccessFile(platName, "r");
254 }
255 });
256 disposerRecord.channel = raf.getChannel();
257 fileSize = (int)disposerRecord.channel.size();
258 if (usePool) {
259 FontManager fm = FontManagerFactory.getInstance();
260 if (fm instanceof SunFontManager) {
261 ((SunFontManager) fm).addToPool(this);
262 }
263 }
264 } catch (PrivilegedActionException e) {
265 close();
266 Throwable reason = e.getCause();
267 if (reason == null) {
268 reason = e;
291
292
293 int readBlock(ByteBuffer buffer, int offset, int length) {
294 int bread = 0;
295 try {
296 synchronized (this) {
297 if (disposerRecord.channel == null) {
298 open();
299 }
300 if (offset + length > fileSize) {
301 if (offset >= fileSize) {
302 /* Since the caller ensures that offset is < fileSize
303 * this condition suggests that fileSize is now
304 * different than the value we originally provided
305 * to native when the scaler was created.
306 * Also fileSize is updated every time we
307 * open() the file here, but in native the value
308 * isn't updated. If the file has changed whilst we
309 * are executing we want to bail, not spin.
310 */
311 if (FontUtilities.isLogging()) {
312 String msg = "Read offset is " + offset +
313 " file size is " + fileSize+
314 " file is " + platName;
315 FontUtilities.getLogger().severe(msg);
316 }
317 return -1;
318 } else {
319 length = fileSize - offset;
320 }
321 }
322 buffer.clear();
323 disposerRecord.channel.position(offset);
324 while (bread < length) {
325 int cnt = disposerRecord.channel.read(buffer);
326 if (cnt == -1) {
327 String msg = "Unexpected EOF " + this;
328 int currSize = (int)disposerRecord.channel.size();
329 if (currSize != fileSize) {
330 msg += " File size was " + fileSize +
331 " and now is " + currSize;
332 }
333 if (FontUtilities.isLogging()) {
334 FontUtilities.getLogger().severe(msg);
335 }
336 // We could still flip() the buffer here because
337 // it's possible that we did read some data in
338 // an earlier loop, and we probably should
339 // return that to the caller. Although if
340 // the caller expected 8K of data and we return
341 // only a few bytes then maybe it's better instead to
342 // set bread = -1 to indicate failure.
343 // The following is therefore using arbitrary values
344 // but is meant to allow cases where enough
345 // data was read to probably continue.
346 if (bread > length/2 || bread > 16384) {
347 buffer.flip();
348 if (FontUtilities.isLogging()) {
349 msg = "Returning " + bread +
350 " bytes instead of " + length;
351 FontUtilities.getLogger().severe(msg);
352 }
353 } else {
354 bread = -1;
355 }
356 throw new IOException(msg);
357 }
358 bread += cnt;
359 }
360 buffer.flip();
361 if (bread > length) { // possible if buffer.size() > length
362 bread = length;
363 }
364 }
365 } catch (FontFormatException e) {
366 if (FontUtilities.isLogging()) {
367 FontUtilities.getLogger().severe(
368 "While reading " + platName, e);
369 }
370 bread = -1; // signal EOF
371 deregisterFontAndClearStrikeCache();
372 } catch (ClosedChannelException e) {
505 /* checksum */ ibuffer.get();
506 table.offset = ibuffer.get();
507 table.length = ibuffer.get();
508 if (table.offset + table.length > fileSize) {
509 throw new FontFormatException("bad table, tag="+table.tag);
510 }
511 }
512
513 if (getDirectoryEntry(headTag) == null) {
514 throw new FontFormatException("missing head table");
515 }
516 if (getDirectoryEntry(maxpTag) == null) {
517 throw new FontFormatException("missing maxp table");
518 }
519 if (getDirectoryEntry(hmtxTag) != null
520 && getDirectoryEntry(hheaTag) == null) {
521 throw new FontFormatException("missing hhea table");
522 }
523 initNames();
524 } catch (Exception e) {
525 if (FontUtilities.isLogging()) {
526 FontUtilities.getLogger().severe(e.toString());
527 }
528 if (e instanceof FontFormatException) {
529 throw (FontFormatException)e;
530 } else {
531 throw new FontFormatException(e.toString());
532 }
533 }
534 if (familyName == null || fullName == null) {
535 throw new FontFormatException("Font name not found");
536 }
537 /* The os2_Table is needed to gather some info, but we don't
538 * want to keep it around (as a field) so obtain it once and
539 * pass it to the code that needs it.
540 */
541 ByteBuffer os2_Table = getTableBuffer(os_2Tag);
542 setStyle(os2_Table);
543 setCJKSupport(os2_Table);
544 }
545
546 /* The array index corresponds to a bit offset in the TrueType
547 * font's OS/2 compatibility table's code page ranges fields.
1055 }
1056 }
1057 }
1058
1059 String charset;
1060 switch (encoding) {
1061 case -1: charset = "US-ASCII";break;
1062 case 1: charset = "UTF-16"; break; // most common case first.
1063 case 0: charset = "UTF-16"; break; // symbol uses this
1064 case 2: charset = "SJIS"; break;
1065 case 3: charset = "GBK"; break;
1066 case 4: charset = "MS950"; break;
1067 case 5: charset = "EUC_KR"; break;
1068 case 6: charset = "Johab"; break;
1069 default: charset = "UTF-16"; break;
1070 }
1071
1072 try {
1073 return new String(bytes, 0, len, charset);
1074 } catch (UnsupportedEncodingException e) {
1075 if (FontUtilities.isLogging()) {
1076 FontUtilities.getLogger().warning(e + " EncodingID=" + encoding);
1077 }
1078 return new String(bytes, 0, len);
1079 } catch (Throwable t) {
1080 return null;
1081 }
1082 }
1083
1084 protected void initNames() {
1085
1086 byte[] name = new byte[256];
1087 ByteBuffer buffer = getTableBuffer(nameTag);
1088
1089 if (buffer != null) {
1090 ShortBuffer sbuffer = buffer.asShortBuffer();
1091 sbuffer.get(); // format - not needed.
1092 short numRecords = sbuffer.get();
1093 /* The name table uses unsigned shorts. Many of these
1094 * are known small values that fit in a short.
1095 * The values that are sizes or offsets into the table could be
1096 * greater than 32767, so read and store those as ints
1097 */
|
226 throw new FontFormatException("Unexpected runtime exception.");
227 }
228 }
229 Disposer.addObjectRecord(this, disposerRecord);
230 }
231
232 private synchronized FileChannel open() throws FontFormatException {
233 return open(true);
234 }
235
236 /* This is intended to be called, and the returned value used,
237 * from within a block synchronized on this font object.
238 * ie the channel returned may be nulled out at any time by "close()"
239 * unless the caller holds a lock.
240 * Deadlock warning: FontManager.addToPool(..) acquires a global lock,
241 * which means nested locks may be in effect.
242 */
243 private synchronized FileChannel open(boolean usePool)
244 throws FontFormatException {
245 if (disposerRecord.channel == null) {
246 FontUtilities.logInfo("open TTF: " + platName);
247 try {
248 RandomAccessFile raf = AccessController.doPrivileged(
249 new PrivilegedExceptionAction<RandomAccessFile>() {
250 public RandomAccessFile run() throws FileNotFoundException {
251 return new RandomAccessFile(platName, "r");
252 }
253 });
254 disposerRecord.channel = raf.getChannel();
255 fileSize = (int)disposerRecord.channel.size();
256 if (usePool) {
257 FontManager fm = FontManagerFactory.getInstance();
258 if (fm instanceof SunFontManager) {
259 ((SunFontManager) fm).addToPool(this);
260 }
261 }
262 } catch (PrivilegedActionException e) {
263 close();
264 Throwable reason = e.getCause();
265 if (reason == null) {
266 reason = e;
289
290
291 int readBlock(ByteBuffer buffer, int offset, int length) {
292 int bread = 0;
293 try {
294 synchronized (this) {
295 if (disposerRecord.channel == null) {
296 open();
297 }
298 if (offset + length > fileSize) {
299 if (offset >= fileSize) {
300 /* Since the caller ensures that offset is < fileSize
301 * this condition suggests that fileSize is now
302 * different than the value we originally provided
303 * to native when the scaler was created.
304 * Also fileSize is updated every time we
305 * open() the file here, but in native the value
306 * isn't updated. If the file has changed whilst we
307 * are executing we want to bail, not spin.
308 */
309 String msg = "Read offset is " + offset +
310 " file size is " + fileSize+
311 " file is " + platName;
312 FontUtilities.logSevere(msg);
313 return -1;
314 } else {
315 length = fileSize - offset;
316 }
317 }
318 buffer.clear();
319 disposerRecord.channel.position(offset);
320 while (bread < length) {
321 int cnt = disposerRecord.channel.read(buffer);
322 if (cnt == -1) {
323 String msg = "Unexpected EOF " + this;
324 int currSize = (int)disposerRecord.channel.size();
325 if (currSize != fileSize) {
326 msg += " File size was " + fileSize +
327 " and now is " + currSize;
328 }
329 FontUtilities.logSevere(msg);
330 // We could still flip() the buffer here because
331 // it's possible that we did read some data in
332 // an earlier loop, and we probably should
333 // return that to the caller. Although if
334 // the caller expected 8K of data and we return
335 // only a few bytes then maybe it's better instead to
336 // set bread = -1 to indicate failure.
337 // The following is therefore using arbitrary values
338 // but is meant to allow cases where enough
339 // data was read to probably continue.
340 if (bread > length/2 || bread > 16384) {
341 buffer.flip();
342 msg = "Returning " + bread + " bytes instead of " + length;
343 FontUtilities.logSevere(msg);
344 } else {
345 bread = -1;
346 }
347 throw new IOException(msg);
348 }
349 bread += cnt;
350 }
351 buffer.flip();
352 if (bread > length) { // possible if buffer.size() > length
353 bread = length;
354 }
355 }
356 } catch (FontFormatException e) {
357 if (FontUtilities.isLogging()) {
358 FontUtilities.getLogger().severe(
359 "While reading " + platName, e);
360 }
361 bread = -1; // signal EOF
362 deregisterFontAndClearStrikeCache();
363 } catch (ClosedChannelException e) {
496 /* checksum */ ibuffer.get();
497 table.offset = ibuffer.get();
498 table.length = ibuffer.get();
499 if (table.offset + table.length > fileSize) {
500 throw new FontFormatException("bad table, tag="+table.tag);
501 }
502 }
503
504 if (getDirectoryEntry(headTag) == null) {
505 throw new FontFormatException("missing head table");
506 }
507 if (getDirectoryEntry(maxpTag) == null) {
508 throw new FontFormatException("missing maxp table");
509 }
510 if (getDirectoryEntry(hmtxTag) != null
511 && getDirectoryEntry(hheaTag) == null) {
512 throw new FontFormatException("missing hhea table");
513 }
514 initNames();
515 } catch (Exception e) {
516 FontUtilities.logSevere(e.toString());
517 if (e instanceof FontFormatException) {
518 throw (FontFormatException)e;
519 } else {
520 throw new FontFormatException(e.toString());
521 }
522 }
523 if (familyName == null || fullName == null) {
524 throw new FontFormatException("Font name not found");
525 }
526 /* The os2_Table is needed to gather some info, but we don't
527 * want to keep it around (as a field) so obtain it once and
528 * pass it to the code that needs it.
529 */
530 ByteBuffer os2_Table = getTableBuffer(os_2Tag);
531 setStyle(os2_Table);
532 setCJKSupport(os2_Table);
533 }
534
535 /* The array index corresponds to a bit offset in the TrueType
536 * font's OS/2 compatibility table's code page ranges fields.
1044 }
1045 }
1046 }
1047
1048 String charset;
1049 switch (encoding) {
1050 case -1: charset = "US-ASCII";break;
1051 case 1: charset = "UTF-16"; break; // most common case first.
1052 case 0: charset = "UTF-16"; break; // symbol uses this
1053 case 2: charset = "SJIS"; break;
1054 case 3: charset = "GBK"; break;
1055 case 4: charset = "MS950"; break;
1056 case 5: charset = "EUC_KR"; break;
1057 case 6: charset = "Johab"; break;
1058 default: charset = "UTF-16"; break;
1059 }
1060
1061 try {
1062 return new String(bytes, 0, len, charset);
1063 } catch (UnsupportedEncodingException e) {
1064 FontUtilities.logWarning(e + " EncodingID=" + encoding);
1065 return new String(bytes, 0, len);
1066 } catch (Throwable t) {
1067 return null;
1068 }
1069 }
1070
1071 protected void initNames() {
1072
1073 byte[] name = new byte[256];
1074 ByteBuffer buffer = getTableBuffer(nameTag);
1075
1076 if (buffer != null) {
1077 ShortBuffer sbuffer = buffer.asShortBuffer();
1078 sbuffer.get(); // format - not needed.
1079 short numRecords = sbuffer.get();
1080 /* The name table uses unsigned shorts. Many of these
1081 * are known small values that fit in a short.
1082 * The values that are sizes or offsets into the table could be
1083 * greater than 32767, so read and store those as ints
1084 */
|