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) {
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) {
373 /* NIO I/O is interruptible, recurse to retry operation.
374 * Clear interrupts before recursing in case NIO didn't.
375 */
376 Thread.interrupted();
377 close();
378 return readBlock(buffer, offset, length);
379 } catch (IOException e) {
380 /* If we did not read any bytes at all and the exception is
381 * not a recoverable one (ie is not ClosedChannelException) then
382 * we should indicate that there is no point in re-trying.
383 * Other than an attempt to read past the end of the file it
384 * seems unlikely this would occur as problems opening the
385 * file are handled as a FontFormatException.
386 */
387 if (FontUtilities.isLogging()) {
388 FontUtilities.getLogger().severe(
389 "While reading " + platName, e);
390 }
391 if (bread == 0) {
392 bread = -1; // signal EOF
393 deregisterFontAndClearStrikeCache();
394 }
395 }
396 return bread;
397 }
398
399 ByteBuffer readBlock(int offset, int length) {
400
401 ByteBuffer buffer = ByteBuffer.allocate(length);
402 try {
403 synchronized (this) {
404 if (disposerRecord.channel == null) {
405 open();
406 }
407 if (offset + length > fileSize) {
408 if (offset > fileSize) {
409 return null; // assert?
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
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
|
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.logInfo("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) {
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.logSevere(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.logSevere(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 + " bytes instead of " + length;
350 FontUtilities.logSevere(msg);
351 }
352 } else {
353 bread = -1;
354 }
355 throw new IOException(msg);
356 }
357 bread += cnt;
358 }
359 buffer.flip();
360 if (bread > length) { // possible if buffer.size() > length
361 bread = length;
362 }
363 }
364 } catch (FontFormatException e) {
365 if (FontUtilities.isLogging()) {
366 FontUtilities.getLogger().severe("While reading " + platName, e);
367 }
368 bread = -1; // signal EOF
369 deregisterFontAndClearStrikeCache();
370 } catch (ClosedChannelException e) {
371 /* NIO I/O is interruptible, recurse to retry operation.
372 * Clear interrupts before recursing in case NIO didn't.
373 */
374 Thread.interrupted();
375 close();
376 return readBlock(buffer, offset, length);
377 } catch (IOException e) {
378 /* If we did not read any bytes at all and the exception is
379 * not a recoverable one (ie is not ClosedChannelException) then
380 * we should indicate that there is no point in re-trying.
381 * Other than an attempt to read past the end of the file it
382 * seems unlikely this would occur as problems opening the
383 * file are handled as a FontFormatException.
384 */
385 if (FontUtilities.isLogging()) {
386 FontUtilities.getLogger().severe("While reading " + platName, e);
387 }
388 if (bread == 0) {
389 bread = -1; // signal EOF
390 deregisterFontAndClearStrikeCache();
391 }
392 }
393 return bread;
394 }
395
396 ByteBuffer readBlock(int offset, int length) {
397
398 ByteBuffer buffer = ByteBuffer.allocate(length);
399 try {
400 synchronized (this) {
401 if (disposerRecord.channel == null) {
402 open();
403 }
404 if (offset + length > fileSize) {
405 if (offset > fileSize) {
406 return null; // assert?
503 table.offset = ibuffer.get();
504 table.length = ibuffer.get();
505 if (table.offset + table.length > fileSize) {
506 throw new FontFormatException("bad table, tag="+table.tag);
507 }
508 }
509
510 if (getDirectoryEntry(headTag) == null) {
511 throw new FontFormatException("missing head table");
512 }
513 if (getDirectoryEntry(maxpTag) == null) {
514 throw new FontFormatException("missing maxp table");
515 }
516 if (getDirectoryEntry(hmtxTag) != null
517 && getDirectoryEntry(hheaTag) == null) {
518 throw new FontFormatException("missing hhea table");
519 }
520 initNames();
521 } catch (Exception e) {
522 if (FontUtilities.isLogging()) {
523 FontUtilities.logSevere(e.toString());
524 }
525 if (e instanceof FontFormatException) {
526 throw (FontFormatException)e;
527 } else {
528 throw new FontFormatException(e.toString());
529 }
530 }
531 if (familyName == null || fullName == null) {
532 throw new FontFormatException("Font name not found");
533 }
534 /* The os2_Table is needed to gather some info, but we don't
535 * want to keep it around (as a field) so obtain it once and
536 * pass it to the code that needs it.
537 */
538 ByteBuffer os2_Table = getTableBuffer(os_2Tag);
539 setStyle(os2_Table);
540 setCJKSupport(os2_Table);
541 }
542
543 /* The array index corresponds to a bit offset in the TrueType
1053 }
1054 }
1055
1056 String charset;
1057 switch (encoding) {
1058 case -1: charset = "US-ASCII";break;
1059 case 1: charset = "UTF-16"; break; // most common case first.
1060 case 0: charset = "UTF-16"; break; // symbol uses this
1061 case 2: charset = "SJIS"; break;
1062 case 3: charset = "GBK"; break;
1063 case 4: charset = "MS950"; break;
1064 case 5: charset = "EUC_KR"; break;
1065 case 6: charset = "Johab"; break;
1066 default: charset = "UTF-16"; break;
1067 }
1068
1069 try {
1070 return new String(bytes, 0, len, charset);
1071 } catch (UnsupportedEncodingException e) {
1072 if (FontUtilities.isLogging()) {
1073 FontUtilities.logWarning(e + " EncodingID=" + encoding);
1074 }
1075 return new String(bytes, 0, len);
1076 } catch (Throwable t) {
1077 return null;
1078 }
1079 }
1080
1081 protected void initNames() {
1082
1083 byte[] name = new byte[256];
1084 ByteBuffer buffer = getTableBuffer(nameTag);
1085
1086 if (buffer != null) {
1087 ShortBuffer sbuffer = buffer.asShortBuffer();
1088 sbuffer.get(); // format - not needed.
1089 short numRecords = sbuffer.get();
1090 /* The name table uses unsigned shorts. Many of these
1091 * are known small values that fit in a short.
1092 * The values that are sizes or offsets into the table could be
1093 * greater than 32767, so read and store those as ints
|