diff --git a/src/share/classes/java/awt/Font.java b/src/share/classes/java/awt/Font.java
index 8dc5938d4118bf86f6e677b4565503d3f88c56cc..3ed40325b04ec126dd14afe049557be76c2bb738 100644
--- a/src/share/classes/java/awt/Font.java
+++ b/src/share/classes/java/awt/Font.java
@@ -37,6 +37,8 @@ import java.awt.geom.Rectangle2D;
import java.awt.peer.FontPeer;
import java.io.*;
import java.lang.ref.SoftReference;
+import java.security.AccessController;
+import java.security.PrivilegedExceptionAction;
import java.text.AttributedCharacterIterator.Attribute;
import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
@@ -51,6 +53,7 @@ import sun.font.AttributeMap;
import sun.font.AttributeValues;
import sun.font.EAttribute;
import sun.font.CompositeFont;
+import sun.font.CreatedFontTracker;
import sun.font.Font2D;
import sun.font.Font2DHandle;
import sun.font.FontManager;
@@ -575,14 +578,16 @@ public class Font implements java.io.Serializable
}
/* used to implement Font.createFont */
- private Font(File fontFile, int fontFormat, boolean isCopy)
+ private Font(File fontFile, int fontFormat,
+ boolean isCopy, CreatedFontTracker tracker)
throws FontFormatException {
this.createdFont = true;
/* Font2D instances created by this method track their font file
* so that when the Font2D is GC'd it can also remove the file.
*/
this.font2DHandle =
- FontManager.createFont2D(fontFile, fontFormat, isCopy).handle;
+ FontManager.createFont2D(fontFile, fontFormat,
+ isCopy, tracker).handle;
this.name = this.font2DHandle.font2D.getFontName(Locale.getDefault());
this.style = Font.PLAIN;
this.size = 1;
@@ -787,6 +792,29 @@ public class Font implements java.io.Serializable
return new Font(attributes);
}
+ /**
+ * Used with the byte count tracker for fonts created from streams.
+ * If a thread can create temp files anyway, no point in counting
+ * font bytes.
+ */
+ private static boolean hasTempPermission() {
+
+ if (System.getSecurityManager() == null) {
+ return true;
+ }
+ File f = null;
+ boolean hasPerm = false;
+ try {
+ f = File.createTempFile("+~JT", ".tmp", null);
+ f.delete();
+ f = null;
+ hasPerm = true;
+ } catch (Throwable t) {
+ /* inc. any kind of SecurityException */
+ }
+ return hasPerm;
+ }
+
/**
* Returns a new Font
using the specified font type
* and input data. The new Font
is
@@ -822,58 +850,96 @@ public class Font implements java.io.Serializable
fontFormat != Font.TYPE1_FONT) {
throw new IllegalArgumentException ("font format not recognized");
}
- final InputStream fStream = fontStream;
- Object ret = java.security.AccessController.doPrivileged(
- new java.security.PrivilegedAction() {
- public Object run() {
- File tFile = null;
- FileOutputStream outStream = null;
- try {
- tFile = File.createTempFile("+~JF", ".tmp", null);
- /* Temp file deleted by font shutdown hook */
- BufferedInputStream inStream =
- new BufferedInputStream(fStream);
- outStream = new FileOutputStream(tFile);
- int bytesRead = 0;
- int bufSize = 8192;
- byte [] buf = new byte[bufSize];
- while (bytesRead != -1) {
- try {
- bytesRead = inStream.read(buf, 0, bufSize);
- } catch (Throwable t) {
- throw new IOException();
- }
- if (bytesRead != -1) {
- outStream.write(buf, 0, bytesRead);
- }
- }
- /* don't close the input stream */
- outStream.close();
- } catch (IOException e) {
- if (outStream != null) {
- try {
- outStream.close();
- } catch (Exception e1) {
- }
- }
- if (tFile != null) {
- try {
- tFile.delete();
- } catch (Exception e2) {
- }
- }
- return e;
- }
- return tFile;
- }
- });
-
- if (ret instanceof File) {
- return new Font((File)ret, fontFormat, true);
- } else if (ret instanceof IOException) {
- throw (IOException)ret;
- } else {
- throw new FontFormatException("Couldn't access font stream");
+ boolean copiedFontData = false;
+
+ try {
+ final File tFile = AccessController.doPrivileged(
+ new PrivilegedExceptionAction() {
+ public File run() throws IOException {
+ return File.createTempFile("+~JF", ".tmp", null);
+ }
+ }
+ );
+
+ int totalSize = 0;
+ CreatedFontTracker tracker = null;
+ try {
+ final OutputStream outStream =
+ AccessController.doPrivileged(
+ new PrivilegedExceptionAction() {
+ public OutputStream run() throws IOException {
+ return new FileOutputStream(tFile);
+ }
+ }
+ );
+ if (!hasTempPermission()) {
+ tracker = CreatedFontTracker.getTracker();
+ }
+ try {
+ byte[] buf = new byte[8192];
+ for (;;) {
+ int bytesRead = fontStream.read(buf);
+ if (bytesRead < 0) {
+ break;
+ }
+ if (tracker != null) {
+ if (totalSize+bytesRead > tracker.MAX_FILE_SIZE) {
+ throw new IOException("File too big.");
+ }
+ if (totalSize+tracker.getNumBytes() >
+ tracker.MAX_TOTAL_BYTES)
+ {
+ throw new IOException("Total files too big.");
+ }
+ totalSize += bytesRead;
+ tracker.addBytes(bytesRead);
+ }
+ outStream.write(buf, 0, bytesRead);
+ }
+ /* don't close the input stream */
+ } finally {
+ outStream.close();
+ }
+ /* After all references to a Font2D are dropped, the file
+ * will be removed. To support long-lived AppContexts,
+ * we need to then decrement the byte count by the size
+ * of the file.
+ * If the data isn't a valid font, the implementation will
+ * delete the tmp file and decrement the byte count
+ * in the tracker object before returning from the
+ * constructor, so we can set 'copiedFontData' to true here
+ * without waiting for the results of that constructor.
+ */
+ copiedFontData = true;
+ Font font = new Font(tFile, fontFormat, true, tracker);
+ return font;
+ } finally {
+ if (!copiedFontData) {
+ if (tracker != null) {
+ tracker.subBytes(totalSize);
+ }
+ AccessController.doPrivileged(
+ new PrivilegedExceptionAction() {
+ public Void run() {
+ tFile.delete();
+ return null;
+ }
+ }
+ );
+ }
+ }
+ } catch (Throwable t) {
+ if (t instanceof FontFormatException) {
+ throw (FontFormatException)t;
+ }
+ if (t instanceof IOException) {
+ throw (IOException)t;
+ }
+ Throwable cause = t.getCause();
+ if (cause instanceof FontFormatException) {
+ throw (FontFormatException)cause;
+ }
+ throw new IOException("Problem reading font data.");
}
}
@@ -913,6 +979,9 @@ public class Font implements java.io.Serializable
*/
public static Font createFont(int fontFormat, File fontFile)
throws java.awt.FontFormatException, java.io.IOException {
+
+ fontFile = new File(fontFile.getPath());
+
if (fontFormat != Font.TRUETYPE_FONT &&
fontFormat != Font.TYPE1_FONT) {
throw new IllegalArgumentException ("font format not recognized");
@@ -926,7 +995,7 @@ public class Font implements java.io.Serializable
if (!fontFile.canRead()) {
throw new IOException("Can't read " + fontFile);
}
- return new Font(fontFile, fontFormat, false);
+ return new Font(fontFile, fontFormat, false, null);
}
/**
diff --git a/src/share/classes/sun/font/CreatedFontTracker.java b/src/share/classes/sun/font/CreatedFontTracker.java
new file mode 100644
index 0000000000000000000000000000000000000000..741337d5b1902d174b8a6d9996c92c32280bd806
--- /dev/null
+++ b/src/share/classes/sun/font/CreatedFontTracker.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.font;
+
+public class CreatedFontTracker {
+
+ public static final int MAX_FILE_SIZE = 32 * 1024 * 1024;
+ public static final int MAX_TOTAL_BYTES = 10 * MAX_FILE_SIZE;
+
+ static int numBytes;
+ static CreatedFontTracker tracker;
+
+ public static synchronized CreatedFontTracker getTracker() {
+ if (tracker == null) {
+ tracker = new CreatedFontTracker();
+ }
+ return tracker;
+ }
+
+ public synchronized int getNumBytes() {
+ return numBytes;
+ }
+
+ public synchronized void addBytes(int sz) {
+ numBytes += sz;
+ }
+
+ public synchronized void subBytes(int sz) {
+ numBytes -= sz;
+ }
+}
diff --git a/src/share/classes/sun/font/FileFont.java b/src/share/classes/sun/font/FileFont.java
index b6a2099d2a41ff1ac70c6e37d7697ff848c79e03..5aad11b2acd9ff35e0929bb1fbad6551237e942e 100644
--- a/src/share/classes/sun/font/FileFont.java
+++ b/src/share/classes/sun/font/FileFont.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2003-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -125,9 +125,9 @@ public abstract class FileFont extends PhysicalFont {
return true;
}
- void setFileToRemove(File file) {
+ void setFileToRemove(File file, CreatedFontTracker tracker) {
Disposer.addObjectRecord(this,
- new CreatedFontFileDisposerRecord(file));
+ new CreatedFontFileDisposerRecord(file, tracker));
}
/* This is called when a font scaler is determined to
@@ -246,12 +246,16 @@ public abstract class FileFont extends PhysicalFont {
return getScaler().getUnitsPerEm();
}
- private static class CreatedFontFileDisposerRecord implements DisposerRecord {
+ private static class CreatedFontFileDisposerRecord
+ implements DisposerRecord {
File fontFile = null;
+ CreatedFontTracker tracker;
- private CreatedFontFileDisposerRecord(File file) {
+ private CreatedFontFileDisposerRecord(File file,
+ CreatedFontTracker tracker) {
fontFile = file;
+ this.tracker = tracker;
}
public void dispose() {
@@ -260,6 +264,9 @@ public abstract class FileFont extends PhysicalFont {
public Object run() {
if (fontFile != null) {
try {
+ if (tracker != null) {
+ tracker.subBytes((int)fontFile.length());
+ }
/* REMIND: is it possible that the file is
* still open? It will be closed when the
* font2D is disposed but could this code
diff --git a/src/share/classes/sun/font/FontManager.java b/src/share/classes/sun/font/FontManager.java
index 09f181f12c2288da97b3026b13524971f7ee8804..29d14047fbd4d0b2500292d63873fcbdbf8ecba4 100644
--- a/src/share/classes/sun/font/FontManager.java
+++ b/src/share/classes/sun/font/FontManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2003-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -2347,12 +2347,14 @@ public final class FontManager {
static Vector tmpFontFiles = null;
public static Font2D createFont2D(File fontFile, int fontFormat,
- boolean isCopy)
+ boolean isCopy,
+ CreatedFontTracker tracker)
throws FontFormatException {
String fontFilePath = fontFile.getPath();
FileFont font2D = null;
final File fFile = fontFile;
+ final CreatedFontTracker _tracker = tracker;
try {
switch (fontFormat) {
case Font.TRUETYPE_FONT:
@@ -2369,6 +2371,9 @@ public final class FontManager {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
public Object run() {
+ if (_tracker != null) {
+ _tracker.subBytes((int)fFile.length());
+ }
fFile.delete();
return null;
}
@@ -2377,7 +2382,7 @@ public final class FontManager {
throw(e);
}
if (isCopy) {
- font2D.setFileToRemove(fontFile);
+ font2D.setFileToRemove(fontFile, tracker);
synchronized (FontManager.class) {
if (tmpFontFiles == null) {
diff --git a/test/java/awt/FontClass/CreateFont/A.ttf b/test/java/awt/FontClass/CreateFont/A.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..f80f5c3d569c810493a9fbaf7141a63831733764
Binary files /dev/null and b/test/java/awt/FontClass/CreateFont/A.ttf differ
diff --git a/test/java/awt/FontClass/CreateFont/BigFont.java b/test/java/awt/FontClass/CreateFont/BigFont.java
new file mode 100644
index 0000000000000000000000000000000000000000..c14802485bcbe091473e2a5a5a81e72b10770140
--- /dev/null
+++ b/test/java/awt/FontClass/CreateFont/BigFont.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+import java.applet.*;
+import java.awt.*;
+import java.io.*;
+import java.net.*;
+
+public class BigFont extends Applet {
+
+ static private class SizedInputStream extends InputStream {
+
+ int size;
+ int cnt = 0;
+
+ SizedInputStream(int size) {
+ this.size = size;
+ }
+
+ public int read() {
+ if (cnt < size) {
+ cnt++;
+ return 0;
+ } else {
+ return -1;
+ }
+ }
+
+ public int getCurrentSize() {
+ return cnt;
+ }
+ }
+
+ String id;
+ String fileName;
+
+ public void init() {
+ id = getParameter("number");
+ fileName = getParameter("font");
+
+ System.out.println("Applet " + id + " "+
+ Thread.currentThread().getThreadGroup());
+ // Larger than size for a single font.
+ int fontSize = 64 * 1000 * 1000;
+ SizedInputStream sis = new SizedInputStream(fontSize);
+ try {
+ Font font = Font.createFont(Font.TRUETYPE_FONT, sis);
+ } catch (Throwable t) {
+ if (t instanceof FontFormatException ||
+ fontSize <= sis.getCurrentSize())
+ {
+ System.out.println(sis.getCurrentSize());
+ System.out.println(t);
+ throw new RuntimeException("Allowed file to be too large.");
+ }
+ }
+ // The following part of the test was verified manually but
+ // is impractical to enable because it requires a fairly large
+ // valid font to be part of the test, and we can't easily include
+ // that, nor dependably reference one from the applet environment.
+ /*
+ if (fileName == null) {
+ return;
+ }
+ int size = getFileSize(fileName);
+ if (size == 0) {
+ return;
+ }
+ int fontCnt = 1000 * 1000 * 1000 / size;
+ loadMany(size, fontCnt, fileName);
+ System.gc(); System.gc();
+ fontCnt = fontCnt / 2;
+ System.out.println("Applet " + id + " load more.");
+ loadMany(size, fontCnt, fileName);
+ */
+ System.out.println("Applet " + id + " finished.");
+ }
+
+ int getFileSize(String fileName) {
+ try {
+ URL url = new URL(getCodeBase(), fileName);
+ InputStream inStream = url.openStream();
+ BufferedInputStream fontStream = new BufferedInputStream(inStream);
+ int size = 0;
+ while (fontStream.read() != -1) {
+ size++;
+ }
+ fontStream.close();
+ return size;
+ } catch (IOException e) {
+ return 0;
+ }
+
+ }
+ void loadMany(int oneFont, int fontCnt, String fileName) {
+ System.out.println("fontcnt= " + fontCnt);
+ Font[] fonts = new Font[fontCnt];
+ int totalSize = 0;
+ boolean gotException = false;
+ for (int i=0; i
+
+
+
+ Test Font Creation Limits
+
+
+
+
+
+
+
+
+
diff --git a/test/java/awt/FontClass/CreateFont/fileaccess/FontFile.java b/test/java/awt/FontClass/CreateFont/fileaccess/FontFile.java
new file mode 100644
index 0000000000000000000000000000000000000000..44419748903c9a6f10dad6511ed47b7dc3d089d3
--- /dev/null
+++ b/test/java/awt/FontClass/CreateFont/fileaccess/FontFile.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/*
+ * @test
+ * @bug 6652929
+ * @summary verify handling of File.getPath()
+ */
+
+import java.awt.*;
+import java.io.*;
+
+public class FontFile {
+ public static void main(String[] args) throws Exception {
+ String sep = System.getProperty("file.separator");
+ String fname = ".." + sep + "A.ttf";
+ String dir = System.getProperty("test.src");
+ if (dir != null) {
+ fname = dir + sep + fname;
+ }
+ final String name = fname;
+ System.out.println("Will try to access " + name);
+ if (!(new File(name)).canRead()) {
+ System.out.println("File not available : can't run test");
+ return;
+ }
+ System.out.println("File is available. Verify no access under SM");
+
+ System.setSecurityManager(new SecurityManager());
+
+
+ // Check cannot read file.
+ try {
+ new FileInputStream(name);
+ throw new Error("Something wrong with test environment");
+ } catch (SecurityException exc) {
+ // Good.
+ }
+
+ try {
+ Font font = Font.createFont(Font.TRUETYPE_FONT,
+ new File("nosuchfile") {
+ private boolean read;
+ @Override public String getPath() {
+ if (read) {
+ return name;
+ } else {
+ read = true;
+ return "somefile";
+ }
+ }
+ @Override public boolean canRead() {
+ return true;
+ }
+ }
+ );
+ System.err.println(font.getFontName());
+ throw new RuntimeException("No expected exception");
+ } catch (IOException e) {
+ System.err.println("Test passed.");
+ }
+ }
+}