--- old/modules/graphics/src/main/java/javafx/scene/text/Font.java 2016-05-19 14:56:15.280738920 -0700
+++ new/modules/graphics/src/main/java/javafx/scene/text/Font.java 2016-05-19 14:56:15.164738923 -0700
@@ -399,6 +399,59 @@
* @return the Font, or null if the font cannot be created.
*/
public static Font loadFont(String urlStr, double size) {
+ Font[] fonts = loadFontInternal(urlStr, size, false);
+ return (fonts == null) ? null : fonts[0];
+ }
+
+ /**
+ * Loads font resources from the specified URL. If the load is successful
+ * such that the location is readable, and it represents a supported
+ * font format then an array ofFont object will be returned.
+ *
+ * The use case for this method is for loading all fonts
+ * from a TrueType Collection (TTC).
+ *
+ * If a security manager is present, the application
+ * must have both permission to read from the specified URL location
+ * and the {@link javafx.util.FXPermission} "loadFont".
+ * If the application does not have permission to read from the specified
+ * URL location, then null is returned.
+ * If the application does not have the "loadFont" permission then this method
+ * will return an array of one element which is the default
+ * system font with the specified font size.
+ *
+ * Any failure such as a malformed URL being unable to locate or read
+ * from the resource, or if it doesn't represent a font, will result in
+ * a null return. It is the application's responsibility
+ * to check this before use.
+ *
+ * On a successful (non-null) return the fonts will be registered
+ * with the FX graphics system for creation by available constructors
+ * and factory methods, and the application should use it in this
+ * manner rather than calling this method again, which would
+ * repeat the overhead of downloading and installing the fonts.
+ *
+ * The font size parameter is a convenience so that in
+ * typical usage the application can directly use the returned (non-null)
+ * font rather than needing to create one via a constructor. Invalid sizes
+ * are those <=0 and will result in a default size.
+ *
+ * If the URL represents a local disk file, then no copying is performed
+ * and the font file is required to persist for the lifetime of the
+ * application. Updating the file in any manner will result
+ * in unspecified and likely undesired behaviours.
+ *
+ * @param urlStr from which to load the fonts, specified as a String.
+ * @param size of the returned fonts.
+ * @return array of Font, or null if the fonts cannot be created.
+ * @since 9
+ */
+ public static Font[] loadFonts(String urlStr, double size) {
+ return loadFontInternal(urlStr, size, true);
+ }
+
+ private static Font[] loadFontInternal(String urlStr, double size,
+ boolean loadAll) {
URL url = null;
try {
url = new URL(urlStr); // null string arg. is caught here.
@@ -426,15 +479,17 @@
} catch (Exception e) {
return null;
}
- return Toolkit.getToolkit().getFontLoader().loadFont(path, size);
+ return
+ Toolkit.getToolkit().getFontLoader().loadFont(path, size, loadAll);
}
- Font font = null;
+ Font[] fonts = null;
URLConnection connection = null;
InputStream in = null;
try {
connection = url.openConnection();
in = connection.getInputStream();
- font = Toolkit.getToolkit().getFontLoader().loadFont(in, size);
+ fonts =
+ Toolkit.getToolkit().getFontLoader().loadFont(in, size, loadAll);
} catch (Exception e) {
return null;
} finally {
@@ -445,7 +500,7 @@
} catch (Exception e) {
}
}
- return font;
+ return fonts;
}
/**
@@ -454,6 +509,50 @@
* fully read, and it represents a supported font format then a
* Font object will be returned.
*
+ * The use case for this method is for loading all fonts
+ * from a TrueType Collection (TTC).
+ *
+ * If a security manager is present, the application
+ * must have the {@link javafx.util.FXPermission} "loadFont".
+ * If the application does not have permission then this method
+ * will return the default system font with the specified font size.
+ *
+ * Any failure such as abbreviated input, or an unsupported font format
+ * will result in a null return. It is the application's
+ * responsibility to check this before use.
+ *
+ * On a successful (non-null) return the fonts will be registered
+ * with the FX graphics system for creation by available constructors
+ * and factory methods, and the application should use it in this
+ * manner rather than calling this method again, which would
+ * repeat the overhead of re-reading and installing the font.
+ *
+ * The font size parameter is a convenience so that in
+ * typical usage the application can directly use the returned (non-null)
+ * fonts rather than needing to create one via a constructor. Invalid sizes
+ * are those <=0 and will result in a default size.
+ *
+ * This method does not close the input stream.
+ * @param in stream from which to load the font.
+ * @param size of the returned font.
+ * @return array of Font, or null if the fonts cannot be created.
+ * @since 9
+ */
+ public static Font loadFont(InputStream in, double size) {
+ if (size <= 0) {
+ size = getDefaultSystemFontSize();
+ }
+ Font[] fonts =
+ Toolkit.getToolkit().getFontLoader().loadFont(in, size, false);
+ return (fonts == null) ? null : fonts[0];
+ }
+
+ /**
+ * Loads font resources from the specified input stream.
+ * If the load is successful such that the stream can be
+ * fully read, and it represents a supported font format then a
+ * an array of Font object will be returned.
+ *
* If a security manager is present, the application
* must have the {@link javafx.util.FXPermission} "loadFont".
* If the application does not have permission then this method
@@ -479,11 +578,13 @@
* @param size of the returned font.
* @return the Font, or null if the font cannot be created.
*/
- public static Font loadFont(InputStream in, double size) {
+ public static Font[] loadFonts(InputStream in, double size) {
if (size <= 0) {
size = getDefaultSystemFontSize();
}
- return Toolkit.getToolkit().getFontLoader().loadFont(in, size);
+ Font[] fonts =
+ Toolkit.getToolkit().getFontLoader().loadFont(in, size, true);
+ return (fonts == null) ? null : fonts;
}
/**
--- old/modules/graphics/src/main/java/com/sun/javafx/tk/FontLoader.java 2016-05-19 14:56:15.708738908 -0700
+++ new/modules/graphics/src/main/java/com/sun/javafx/tk/FontLoader.java 2016-05-19 14:56:15.596738912 -0700
@@ -39,8 +39,8 @@
public abstract List getFontNames(String family);
public abstract Font font(String family, FontWeight weight,
FontPosture posture, float size);
- public abstract Font loadFont(InputStream in, double size);
- public abstract Font loadFont(String path, double size);
+ public abstract Font[] loadFont(InputStream in, double size, boolean all);
+ public abstract Font[] loadFont(String path, double size, boolean all);
public abstract FontMetrics getFontMetrics(Font font);
public abstract float getCharWidth(char ch, Font font);
public abstract float getSystemFontSize();
--- old/modules/graphics/src/main/java/com/sun/javafx/font/PrismFontLoader.java 2016-05-19 14:56:16.136738897 -0700
+++ new/modules/graphics/src/main/java/com/sun/javafx/font/PrismFontLoader.java 2016-05-19 14:56:16.016738900 -0700
@@ -79,7 +79,7 @@
if (p.startsWith("/")) {
p = p.substring(1);
try (InputStream in = loader.getResourceAsStream(p)) {
- fontFactory.loadEmbeddedFont(n, in, 0, true);
+ fontFactory.loadEmbeddedFont(n, in, 0, true, false);
} catch (Exception e) {
}
}
@@ -88,18 +88,35 @@
}
}
- @Override public Font loadFont(InputStream in, double size) {
- FontFactory factory = getFontFactoryFromPipeline();
- PGFont font = factory.loadEmbeddedFont(null, in, (float)size, true);
- if (font != null) return createFont(font);
- return null;
+ private Font[] createFonts(PGFont[] fonts) {
+ if (fonts == null || fonts.length == 0) {
+ return null;
+ }
+ Font[] fxFonts = new Font[fonts.length];
+ for (int i=0; i
fileNameToFontResourceMap = new HashMap();
- protected abstract PrismFontFile createFontFile(String name, String filename,
- int fIndex, boolean register,
- boolean embedded,
- boolean copy, boolean tracked)
- throws Exception;
+ protected abstract PrismFontFile
+ createFontFile(String name, String filename,
+ int fIndex, boolean register,
+ boolean embedded,
+ boolean copy, boolean tracked)
+ throws Exception;
public abstract GlyphLayout createGlyphLayout();
@@ -257,10 +258,12 @@
// the instances one at a time so as to have visibility into the
// contents of the TTC. Onus is on caller to enumerate all the fonts.
private PrismFontFile createFontResource(String filename, int index) {
- return createFontResource(filename, index, true, false, false, false);
+ return createFontResource(null, filename, index,
+ true, false, false, false);
}
- private PrismFontFile createFontResource(String filename, int index,
+ private PrismFontFile createFontResource(String name,
+ String filename, int index,
boolean register, boolean embedded,
boolean copy, boolean tracked) {
String key = (filename+index).toLowerCase();
@@ -270,7 +273,7 @@
}
try {
- fr = createFontFile(null, filename, index, register,
+ fr = createFontFile(name, filename, index, register,
embedded, copy, tracked);
if (register) {
storeInMap(fr.getFullName(), fr);
@@ -286,60 +289,76 @@
}
private PrismFontFile createFontResource(String name, String filename) {
- return createFontResource(name, filename, true, false, false, false);
+ PrismFontFile[] pffArr =
+ createFontResources(name, filename,
+ true, false, false, false, false);
+ if (pffArr == null || pffArr.length == 0) {
+ return null;
+ } else {
+ return pffArr[0];
+ }
}
- private PrismFontFile createFontResource(String name, String filename,
- boolean register,
- boolean embedded,
- boolean copy,
- boolean tracked) {
+ private PrismFontFile[] createFontResources(String name, String filename,
+ boolean register,
+ boolean embedded,
+ boolean copy,
+ boolean tracked,
+ boolean loadAll) {
+
+ PrismFontFile[] fArr = null;
if (filename == null) {
return null;
- } else {
- String lcFN = filename.toLowerCase();
- if (lcFN.endsWith(".ttc")) {
- int index = 0;
- PrismFontFile fr, namedFR = null;
- do {
- String key = (filename+index).toLowerCase();
- try {
- fr = fileNameToFontResourceMap.get(key);
- if (fr != null) {
- if (name.equals(fr.getFullName())) {
- return fr; // already mapped etc.
- } else {
- // Already loaded this TTC component, but
- // its not the one we are looking for.
- continue;
- }
- } else {
- fr = createFontFile(name, filename, index,
- register, embedded,
- copy, tracked);
- }
- } catch (Exception e) {
- if (PrismFontFactory.debugFonts) {
- e.printStackTrace();
- }
+ }
+ PrismFontFile fr = createFontResource(name, filename, 0, register,
+ embedded, copy, tracked);
+ if (fr == null) {
+ return null;
+ }
+ int cnt = (!loadAll) ? 1 : fr.getFontCount();
+ fArr = new PrismFontFile[cnt];
+ fArr[0] = fr;
+ if (cnt == 1) { // Not a TTC, or only requesting one font.
+ return fArr;
+ }
+ PrismFontFile.FileRefCounter rc = null;
+ if (copy) {
+ rc = fr.createFileRefCounter();
+ }
+ int index = 1;
+ do {
+ String key = (filename+index).toLowerCase();
+ try {
+ fr = fileNameToFontResourceMap.get(key);
+ if (fr != null) {
+ fArr[index] = fr;
+ continue;
+ } else {
+ fr = createFontFile(null, filename, index,
+ register, embedded,
+ copy, tracked);
+ if (fr == null) {
return null;
}
-
+ if (rc != null) {
+ fr.setAndIncFileRefCounter(rc);
+ }
+ fArr[index] = fr;
String fontname = fr.getFullName();
if (register) {
storeInMap(fontname, fr);
fileNameToFontResourceMap.put(key, fr);
}
- if (index == 0 || name.equals(fontname)) {
- namedFR = fr;
- }
- } while (++index < fr.getFontCount());
- return namedFR;
- } else {
- return createFontResource(filename, 0, register,
- embedded, copy, tracked);
+ }
+ } catch (Exception e) {
+ if (PrismFontFactory.debugFonts) {
+ e.printStackTrace();
+ }
+ return null;
}
- }
+
+ } while (++index < cnt);
+ return fArr;
}
private String dotStyleStr(boolean bold, boolean italic) {
@@ -384,10 +403,16 @@
tmpFonts = new ArrayList>();
}
WeakReference ref;
+ /* Registered fonts are enumerable by the application and are
+ * expected to persist until VM shutdown.
+ * Other fonts - notably ones temporarily loaded in a web page via
+ * webview - should be eligible to be collected and have their
+ * temp files deleted at any time.
+ */
if (fr.isRegistered()) {
ref = new WeakReference(fr);
} else {
- ref = fr.createFileDisposer(this);
+ ref = fr.createFileDisposer(this, fr.getFileRefCounter());
}
tmpFonts.add(ref);
addFileCloserHook();
@@ -1434,13 +1459,15 @@
private HashMap embeddedFonts;
- public PGFont loadEmbeddedFont(String name, InputStream fontStream,
- float size, boolean register) {
+ public PGFont[] loadEmbeddedFont(String name, InputStream fontStream,
+ float size,
+ boolean register,
+ boolean loadAll) {
if (!hasPermission()) {
- return createFont(DEFAULT_FULLNAME, size);
+ return new PGFont[] { createFont(DEFAULT_FULLNAME, size) } ;
}
if (FontFileWriter.hasTempPermission()) {
- return loadEmbeddedFont0(name, fontStream, size, register);
+ return loadEmbeddedFont0(name, fontStream, size, register, loadAll);
}
// Otherwise, be extra conscious of pending temp file creation and
@@ -1454,7 +1481,7 @@
// Timed out waiting for resources.
return null;
}
- return loadEmbeddedFont0(name, fontStream, size, register);
+ return loadEmbeddedFont0(name, fontStream, size, register, loadAll);
} catch (InterruptedException e) {
// Interrupted while waiting to acquire a permit.
return null;
@@ -1465,9 +1492,11 @@
}
}
- private PGFont loadEmbeddedFont0(String name, InputStream fontStream,
- float size, boolean register) {
- PrismFontFile fr = null;
+ private PGFont[] loadEmbeddedFont0(String name, InputStream fontStream,
+ float size,
+ boolean register,
+ boolean loadAll) {
+ PrismFontFile[] fr = null;
FontFileWriter fontWriter = new FontFileWriter();
try {
// We use a shutdown hook to close all open tmp files
@@ -1483,13 +1512,13 @@
}
fontWriter.closeFile();
- fr = loadEmbeddedFont(name, tFile.getPath(), register, true,
- fontWriter.isTracking());
+ fr = loadEmbeddedFont1(name, tFile.getPath(), register, true,
+ fontWriter.isTracking(), loadAll);
- if (fr != null) {
+ if (fr != null && fr.length > 0) {
/* Delete the file downloaded if it was decoded
* to another file */
- if (fr.isDecoded()) {
+ if (fr[0].isDecoded()) {
fontWriter.deleteFile();
}
}
@@ -1519,9 +1548,14 @@
fontWriter.deleteFile();
}
}
- if (fr != null) {
+ if (fr != null && fr.length > 0) {
if (size <= 0) size = getSystemFontSize();
- return new PrismFont(fr, fr.getFullName(), size);
+ int num = fr.length;
+ PrismFont[] pFonts = new PrismFont[num];
+ for (int i=0; i 0) {
if (size <= 0) size = getSystemFontSize();
- return new PrismFont(fr, fr.getFullName(), size);
+ int num = frArr.length;
+ PGFont[] pgFonts = new PGFont[num];
+ for (int i=0; i();
+ }
boolean registerEmbedded = true;
- if (embeddedFonts != null) {
+ for (int i=0; i();
+ if (!register) {
+ return frArr;
}
/* If a font name is provided then we will also store that in the
@@ -1660,24 +1711,29 @@
* probably mostly futile.
*/
if (name != null && !name.isEmpty()) {
- embeddedFonts.put(name, fr);
- storeInMap(name, fr);
+ embeddedFonts.put(name, frArr[0]);
+ storeInMap(name, frArr[0]);
}
- removeEmbeddedFont(fullname);
- embeddedFonts.put(fullname, fr);
- storeInMap(fullname, fr);
- family = family + dotStyleStr(fr.isBold(), fr.isItalic());
- storeInMap(family, fr);
- /* The remove call is to assist the case where we have
- * previously mapped into the composite map a different style
- * in this family as a partial match for the application request.
- * This can occur when an application requested a bold font before
- * it called Font.loadFont to register the bold font. It won't
- * fix the cases that already happened, but will fix the future ones.
- */
- compResourceMap.remove(family.toLowerCase());
- return fr;
+ for (int i=0; i createFileDisposer(PrismFontFactory factory) {
- FileDisposer disposer = new FileDisposer(filename, isTracked);
+ WeakReference createFileDisposer(PrismFontFactory factory,
+ FileRefCounter rc) {
+ FileDisposer disposer = new FileDisposer(filename, isTracked, rc);
WeakReference ref = Disposer.addRecord(this, disposer);
disposer.setFactory(factory, ref);
return ref;
@@ -121,6 +122,15 @@
AccessController.doPrivileged(
(PrivilegedAction) () -> {
try {
+ /* Although there is likely no harm in calling
+ * delete on a file > once, we want to refrain
+ * from deleting it until the shutdown hook
+ * code in subclasses has had an opportunity
+ * to clean up native accesses on the resource.
+ */
+ if (decFileRefCount() > 0) {
+ return null;
+ }
boolean delOK = (new File(filename)).delete();
if (!delOK && PrismFontFactory.debugFonts) {
System.err.println("Temp file not deleted : "
@@ -153,15 +163,62 @@
return fontInstallationType > 0;
}
+
+ /* A TTC file resource is shared, so reference count and delete
+ * only when no longer using the file from any PrismFontFile instance
+ */
+ static class FileRefCounter {
+ private int refCnt = 1; // start with 1.
+
+ synchronized int getRefCount() {
+ return refCnt;
+ }
+
+ synchronized int increment() {
+ return ++refCnt;
+ }
+
+ synchronized int decrement() {
+ return (refCnt == 0) ? 0 : --refCnt;
+ }
+ }
+
+ private FileRefCounter refCounter = null;
+
+ FileRefCounter getFileRefCounter() {
+ return refCounter;
+ }
+
+ FileRefCounter createFileRefCounter() {
+ refCounter = new FileRefCounter();
+ return refCounter;
+ }
+
+ void setAndIncFileRefCounter(FileRefCounter rc) {
+ this.refCounter = rc;
+ this.refCounter.increment();
+ }
+
+ int decFileRefCount() {
+ if (refCounter == null) {
+ return 0;
+ } else {
+ return refCounter.decrement();
+ }
+ }
+
static class FileDisposer implements DisposerRecord {
String fileName;
boolean isTracked;
+ FileRefCounter refCounter;
PrismFontFactory factory;
WeakReference refKey;
- public FileDisposer(String fileName, boolean isTracked) {
+ public FileDisposer(String fileName, boolean isTracked,
+ FileRefCounter rc) {
this.fileName = fileName;
this.isTracked = isTracked;
+ this.refCounter = rc;
}
public void setFactory(PrismFontFactory factory,
@@ -175,6 +232,11 @@
AccessController.doPrivileged(
(PrivilegedAction) () -> {
try {
+ if (refCounter != null &&
+ refCounter.decrement() > 0)
+ {
+ return null;
+ }
File file = new File(fileName);
int size = (int)file.length();
file.delete();
--- old/modules/graphics/src/main/java/com/sun/prism/j2d/J2DFontFactory.java 2016-05-19 14:56:18.236738839 -0700
+++ new/modules/graphics/src/main/java/com/sun/prism/j2d/J2DFontFactory.java 2016-05-19 14:56:18.116738843 -0700
@@ -95,20 +95,27 @@
* input streams on it to both prism and 2D, then when they are done,
* remove it.
*/
- public PGFont loadEmbeddedFont(String name, InputStream fontStream,
- float size, boolean register) {
+ public PGFont[] loadEmbeddedFont(String name, InputStream fontStream,
+ float size,
+ boolean register,
+ boolean loadAll) {
if (!hasPermission()) {
- return createFont(DEFAULT_FULLNAME, size);
+ PGFont[] fonts = new PGFont[1];
+ fonts[0] = createFont(DEFAULT_FULLNAME, size);
+ return fonts;
}
- PGFont font = prismFontFactory.loadEmbeddedFont(name, fontStream,
- size, register);
-
- if (font == null) return null;
- final FontResource fr = font.getFontResource();
- registerFont(font.getFontResource());
- return font;
+ PGFont[] fonts =
+ prismFontFactory.loadEmbeddedFont(name, fontStream,
+ size, register, loadAll);
+
+ if (fonts == null || fonts.length == 0) return null;
+ final FontResource fr = fonts[0].getFontResource();
+ // REMIND: this needs to be upgraded to use JDK9 createFont
+ // which can handle a collection.
+ registerFont(fonts[0].getFontResource());
+ return fonts;
}
/**
@@ -139,18 +146,25 @@
});
}
- public PGFont loadEmbeddedFont(String name, String path,
- float size, boolean register) {
+ public PGFont[] loadEmbeddedFont(String name, String path,
+ float size,
+ boolean register,
+ boolean loadAll) {
if (!hasPermission()) {
- return createFont(DEFAULT_FULLNAME, size);
+ PGFont[] fonts = new PGFont[1];
+ fonts[0] = createFont(DEFAULT_FULLNAME, size);
+ return fonts;
}
- PGFont font = prismFontFactory.loadEmbeddedFont(name, path,
- size, register);
-
- if (font == null) return null;
- final FontResource fr = font.getFontResource();
+ PGFont[] fonts =
+ prismFontFactory.loadEmbeddedFont(name, path,
+ size, register, loadAll);
+
+ if (fonts == null || fonts.length == 0) return null;
+ // REMIND: this needs to be upgraded to use JDK9 createFont
+ // which can handle a collection.
+ final FontResource fr = fonts[0].getFontResource();
AccessController.doPrivileged(new PrivilegedAction