changeset 14040:5ddf42bdf7ca

8055463: Need public API allowing full access to font collections in Font.createFont() Reviewed-by: serb, vadim
author prr
date Tue, 22 Mar 2016 14:46:48 -0700
parents 8b4ca0ad69a4
children 818f74ccccf9
files src/java.desktop/macosx/classes/sun/font/CFontManager.java src/java.desktop/share/classes/java/awt/Font.java src/java.desktop/share/classes/sun/font/FileFont.java src/java.desktop/share/classes/sun/font/FontManager.java src/java.desktop/share/classes/sun/font/SunFontManager.java test/java/awt/FontClass/CreateFont/BigFont.java test/java/awt/FontClass/CreateFont/CreateFontArrayTest.java test/java/awt/font/FontNames/GetLCIDFromLocale.java
diffstat 8 files changed, 379 insertions(+), 120 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.desktop/macosx/classes/sun/font/CFontManager.java	Tue Mar 22 13:10:07 2016 -0700
+++ b/src/java.desktop/macosx/classes/sun/font/CFontManager.java	Tue Mar 22 14:46:48 2016 -0700
@@ -142,94 +142,6 @@
         }
     }
 
-    @Override
-    public Font2D createFont2D(File fontFile, int fontFormat, boolean isCopy, CreatedFontTracker tracker) throws FontFormatException {
-
-    String fontFilePath = fontFile.getPath();
-    Font2D font2D = null;
-    final File fFile = fontFile;
-    final CreatedFontTracker _tracker = tracker;
-    try {
-        switch (fontFormat) {
-            case Font.TRUETYPE_FONT:
-                        font2D = new TrueTypeFont(fontFilePath, null, 0, true);
-                break;
-            case Font.TYPE1_FONT:
-                        font2D = new Type1Font(fontFilePath, null, isCopy);
-                break;
-            default:
-                throw new FontFormatException("Unrecognised Font Format");
-        }
-    } catch (FontFormatException e) {
-        if (isCopy) {
-            java.security.AccessController.doPrivileged(
-                    new java.security.PrivilegedAction<Object>() {
-                        public Object run() {
-                            if (_tracker != null) {
-                                _tracker.subBytes((int)fFile.length());
-                            }
-                            fFile.delete();
-                            return null;
-                        }
-                    });
-        }
-        throw(e);
-    }
-    if (isCopy) {
-        FileFont.setFileToRemove(font2D, fontFile, tracker);
-        synchronized (FontManager.class) {
-
-            if (tmpFontFiles == null) {
-                tmpFontFiles = new Vector<File>();
-            }
-            tmpFontFiles.add(fontFile);
-
-            if (fileCloser == null) {
-                final Runnable fileCloserRunnable = new Runnable() {
-                    public void run() {
-                        java.security.AccessController.doPrivileged(
-                                new java.security.PrivilegedAction<Object>() {
-                                    public Object run() {
-
-                                        for (int i=0;i<CHANNELPOOLSIZE;i++) {
-                                            if (fontFileCache[i] != null) {
-                                                try {
-                                                    fontFileCache[i].close();
-                                                } catch (Exception e) {}
-                                            }
-                                        }
-                                        if (tmpFontFiles != null) {
-                                            File[] files = new File[tmpFontFiles.size()];
-                                            files = tmpFontFiles.toArray(files);
-                                            for (int f=0; f<files.length;f++) {
-                                                try {
-                                                    files[f].delete();
-                                                } catch (Exception e) {}
-                                            }
-                                        }
-                                        return null;
-                                    }
-                                });
-                    }
-                };
-                AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
-                            /* The thread must be a member of a thread group
-                             * which will not get GCed before VM exit.
-                             * Make its parent the top-level thread group.
-                             */
-                            ThreadGroup rootTG = ThreadGroupUtils.getRootThreadGroup();
-                            fileCloser = new ManagedLocalsThread(rootTG, fileCloserRunnable);
-                            fileCloser.setContextClassLoader(null);
-                            Runtime.getRuntime().addShutdownHook(fileCloser);
-                            return null;
-                        }
-                );
-                }
-            }
-        }
-        return font2D;
-    }
-
     protected void registerFontsInDir(String dirName, boolean useJavaRasterizer, int fontRank, boolean defer, boolean resolveSymLinks) {
         loadNativeDirFonts(dirName);
         super.registerFontsInDir(dirName, useJavaRasterizer, fontRank, defer, resolveSymLinks);
--- a/src/java.desktop/share/classes/java/awt/Font.java	Tue Mar 22 13:10:07 2016 -0700
+++ b/src/java.desktop/share/classes/java/awt/Font.java	Tue Mar 22 14:46:48 2016 -0700
@@ -611,8 +611,9 @@
          * so that when the Font2D is GC'd it can also remove the file.
          */
         FontManager fm = FontManagerFactory.getInstance();
-        this.font2DHandle = fm.createFont2D(fontFile, fontFormat, isCopy,
-                                            tracker).handle;
+        Font2D[] fonts =
+            fm.createFont2D(fontFile, fontFormat, false, isCopy, tracker);
+        this.font2DHandle = fonts[0].handle;
         this.name = this.font2DHandle.font2D.getFontName(Locale.getDefault());
         this.style = Font.PLAIN;
         this.size = 1;
@@ -841,6 +842,132 @@
         return hasPerm;
     }
 
+
+    /**
+     * Returns a new array of {@code Font} decoded from the specified stream.
+     * The returned {@code Font[]} will have at least one element.
+     * <p>
+     * The explicit purpose of this variation on the
+     * {@code createFont(int, InputStream)} method is to support font
+     * sources which represent a TrueType/OpenType font collection and
+     * be able to return all individual fonts in that collection.
+     * Consequently this method will throw {@code FontFormatException}
+     * if the data source does not contain at least one TrueType/OpenType
+     * font. The same exception will also be thrown if any of the fonts in
+     * the collection does not contain the required font tables.
+     * <p>
+     * The condition "at least one", allows for the stream to represent
+     * a single OpenType/TrueType font. That is, it does not have to be
+     * a collection.
+     * Each {@code Font} element of the returned array is
+     * created with a point size of 1 and style {@link #PLAIN PLAIN}.
+     * This base font can then be used with the {@code deriveFont}
+     * methods in this class to derive new {@code Font} objects with
+     * varying sizes, styles, transforms and font features.
+     * <p>This method does not close the {@link InputStream}.
+     * <p>
+     * To make each {@code Font} available to Font constructors it
+     * must be registered in the {@code GraphicsEnvironment} by calling
+     * {@link GraphicsEnvironment#registerFont(Font) registerFont(Font)}.
+     * @param fontStream an {@code InputStream} object representing the
+     * input data for the font or font collection.
+     * @return a new {@code Font[]}.
+     * @throws FontFormatException if the {@code fontStream} data does
+     *     not contain the required font tables for any of the elements of
+     *     the collection, or if it contains no fonts at all.
+     * @throws IOException if the {@code fontStream} cannot be completely read.
+     * @see GraphicsEnvironment#registerFont(Font)
+     * @since 9
+     */
+    public static Font[] createFonts(InputStream fontStream)
+        throws FontFormatException, IOException {
+
+        final int fontFormat = Font.TRUETYPE_FONT;
+        if (hasTempPermission()) {
+            return createFont0(fontFormat, fontStream, true, null);
+        }
+
+        // Otherwise, be extra conscious of pending temp file creation and
+        // resourcefully handle the temp file resources, among other things.
+        CreatedFontTracker tracker = CreatedFontTracker.getTracker();
+        boolean acquired = false;
+        try {
+            acquired = tracker.acquirePermit();
+            if (!acquired) {
+                throw new IOException("Timed out waiting for resources.");
+            }
+            return createFont0(fontFormat, fontStream, true, tracker);
+        } catch (InterruptedException e) {
+            throw new IOException("Problem reading font data.");
+        } finally {
+            if (acquired) {
+                tracker.releasePermit();
+            }
+        }
+    }
+
+    /* used to implement Font.createFont */
+    private Font(Font2D font2D) {
+
+        this.createdFont = true;
+        this.font2DHandle = font2D.handle;
+        this.name = font2D.getFontName(Locale.getDefault());
+        this.style = Font.PLAIN;
+        this.size = 1;
+        this.pointSize = 1f;
+    }
+
+    /**
+     * Returns a new array of {@code Font} decoded from the specified file.
+     * The returned {@code Font[]} will have at least one element.
+     * <p>
+     * The explicit purpose of this variation on the
+     * {@code createFont(int, File)} method is to support font
+     * sources which represent a TrueType/OpenType font collection and
+     * be able to return all individual fonts in that collection.
+     * Consequently this method will throw {@code FontFormatException}
+     * if the data source does not contain at least one TrueType/OpenType
+     * font. The same exception will also be thrown if any of the fonts in
+     * the collection does not contain the required font tables.
+     * <p>
+     * The condition "at least one", allows for the stream to represent
+     * a single OpenType/TrueType font. That is, it does not have to be
+     * a collection.
+     * Each {@code Font} element of the returned array is
+     * created with a point size of 1 and style {@link #PLAIN PLAIN}.
+     * This base font can then be used with the {@code deriveFont}
+     * methods in this class to derive new {@code Font} objects with
+     * varying sizes, styles, transforms and font features.
+     * <p>
+     * To make each {@code Font} available to Font constructors it
+     * must be registered in the {@code GraphicsEnvironment} by calling
+     * {@link GraphicsEnvironment#registerFont(Font) registerFont(Font)}.
+     * @param fontFile a {@code File} object containing the
+     * input data for the font or font collection.
+     * @return a new {@code Font[]}.
+     * @throws FontFormatException if the {@code File} does
+     *     not contain the required font tables for any of the elements of
+     *     the collection, or if it contains no fonts at all.
+     * @throws IOException if the {@code fontFile} cannot be read.
+     * @see GraphicsEnvironment#registerFont(Font)
+     * @since 9
+     */
+    public static Font[] createFonts(File fontFile)
+            throws FontFormatException, IOException
+    {
+        int fontFormat = Font.TRUETYPE_FONT;
+        fontFile = checkFontFile(fontFormat, fontFile);
+        FontManager fm = FontManagerFactory.getInstance();
+        Font2D[] font2DArr =
+            fm.createFont2D(fontFile, fontFormat, true, false, null);
+        int num = font2DArr.length;
+        Font[] fonts = new Font[num];
+        for (int i = 0; i < num; i++) {
+           fonts[i] = new Font(font2DArr[i]);
+        }
+        return fonts;
+    }
+
     /**
      * Returns a new {@code Font} using the specified font type
      * and input data.  The new {@code Font} is
@@ -873,7 +1000,7 @@
         throws java.awt.FontFormatException, java.io.IOException {
 
         if (hasTempPermission()) {
-            return createFont0(fontFormat, fontStream, null);
+            return createFont0(fontFormat, fontStream, false, null)[0];
         }
 
         // Otherwise, be extra conscious of pending temp file creation and
@@ -885,7 +1012,7 @@
             if (!acquired) {
                 throw new IOException("Timed out waiting for resources.");
             }
-            return createFont0(fontFormat, fontStream, tracker);
+            return createFont0(fontFormat, fontStream, false, tracker)[0];
         } catch (InterruptedException e) {
             throw new IOException("Problem reading font data.");
         } finally {
@@ -895,8 +1022,9 @@
         }
     }
 
-    private static Font createFont0(int fontFormat, InputStream fontStream,
-                                    CreatedFontTracker tracker)
+    private static Font[] createFont0(int fontFormat, InputStream fontStream,
+                                      boolean allFonts,
+                                      CreatedFontTracker tracker)
         throws java.awt.FontFormatException, java.io.IOException {
 
         if (fontFormat != Font.TRUETYPE_FONT &&
@@ -965,8 +1093,15 @@
                  * without waiting for the results of that constructor.
                  */
                 copiedFontData = true;
-                Font font = new Font(tFile, fontFormat, true, tracker);
-                return font;
+                FontManager fm = FontManagerFactory.getInstance();
+                 Font2D[] font2DArr =
+                    fm.createFont2D(tFile, fontFormat, allFonts, true, tracker);
+                int num = font2DArr.length;
+                Font[] fonts = new Font[num];
+                for (int i = 0; i < num; i++) {
+                   fonts[i] = new Font(font2DArr[i]);
+                }
+                return fonts;
             } finally {
                 if (tracker != null) {
                     tracker.remove(tFile);
@@ -1037,6 +1172,13 @@
     public static Font createFont(int fontFormat, File fontFile)
         throws java.awt.FontFormatException, java.io.IOException {
 
+        fontFile = checkFontFile(fontFormat, fontFile);
+        return new Font(fontFile, fontFormat, false, null);
+    }
+
+    private static File checkFontFile(int fontFormat, File fontFile)
+        throws FontFormatException, IOException {
+
         fontFile = new File(fontFile.getPath());
 
         if (fontFormat != Font.TRUETYPE_FONT &&
@@ -1052,7 +1194,7 @@
         if (!fontFile.canRead()) {
             throw new IOException("Can't read " + fontFile);
         }
-        return new Font(fontFile, fontFormat, false, null);
+        return fontFile;
     }
 
     /**
--- a/src/java.desktop/share/classes/sun/font/FileFont.java	Tue Mar 22 13:10:07 2016 -0700
+++ b/src/java.desktop/share/classes/sun/font/FileFont.java	Tue Mar 22 14:46:48 2016 -0700
@@ -36,6 +36,7 @@
 import sun.java2d.DisposerRecord;
 
 import java.io.IOException;
+import java.util.List;
 import java.security.AccessController;
 import java.security.PrivilegedActionException;
 import java.security.PrivilegedExceptionAction;
@@ -116,18 +117,18 @@
         return true;
     }
 
-    void setFileToRemove(File file, CreatedFontTracker tracker) {
-        Disposer.addObjectRecord(this,
-                         new CreatedFontFileDisposerRecord(file, tracker));
+    static void setFileToRemove(List<Font2D> fonts,
+                                File file, int cnt,
+                                CreatedFontTracker tracker)
+    {
+        CreatedFontFileDisposerRecord dr =
+            new CreatedFontFileDisposerRecord(file, cnt, tracker);
+
+        for (Font2D f : fonts) {
+            Disposer.addObjectRecord(f, dr);
+        }
     }
 
-    // MACOSX begin -- Make this static so that we can pass in CFont
-    static void setFileToRemove(Object font, File file, CreatedFontTracker tracker) {
-        Disposer.addObjectRecord(font,
-                         new CreatedFontFileDisposerRecord(file, tracker));
-    }
-    // MACOSX - end
-
     /* This is called when a font scaler is determined to
      * be unusable (ie bad).
      * We want to replace current scaler with NullFontScaler, so
@@ -251,11 +252,13 @@
         implements DisposerRecord {
 
         File fontFile = null;
+        int count = 0; // number of fonts referencing this file object.
         CreatedFontTracker tracker;
 
-        private CreatedFontFileDisposerRecord(File file,
+        private CreatedFontFileDisposerRecord(File file, int cnt,
                                               CreatedFontTracker tracker) {
             fontFile = file;
+            count = (cnt > 0) ? cnt : 1;
             this.tracker = tracker;
         }
 
@@ -263,6 +266,12 @@
             java.security.AccessController.doPrivileged(
                  new java.security.PrivilegedAction<Object>() {
                       public Object run() {
+                          synchronized (fontFile) {
+                              count--;
+                              if (count > 0) {
+                                  return null;
+                              }
+                          }
                           if (fontFile != null) {
                               try {
                                   if (tracker != null) {
--- a/src/java.desktop/share/classes/sun/font/FontManager.java	Tue Mar 22 13:10:07 2016 -0700
+++ b/src/java.desktop/share/classes/sun/font/FontManager.java	Tue Mar 22 14:46:48 2016 -0700
@@ -81,13 +81,15 @@
      *
      * @param fontFile the file holding the font data
      * @param fontFormat the expected font format
+     * @param all whether to retrieve all fonts in the resource or
+     *        just the first one.
      * @param isCopy {@code true} if the file is a copy and needs to be
      *        deleted, {@code false} otherwise
      *
      * @return the created Font2D instance
      */
-    public Font2D createFont2D(File fontFile, int fontFormat,
-                               boolean isCopy, CreatedFontTracker tracker)
+    public Font2D[] createFont2D(File fontFile, int fontFormat, boolean all,
+                                 boolean isCopy, CreatedFontTracker tracker)
         throws FontFormatException;
 
     /**
--- a/src/java.desktop/share/classes/sun/font/SunFontManager.java	Tue Mar 22 13:10:07 2016 -0700
+++ b/src/java.desktop/share/classes/sun/font/SunFontManager.java	Tue Mar 22 14:46:48 2016 -0700
@@ -40,6 +40,7 @@
 import java.util.HashSet;
 import java.util.Hashtable;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.NoSuchElementException;
@@ -2420,15 +2421,15 @@
 
     protected abstract String getFontPath(boolean noType1Fonts);
 
-    // MACOSX begin -- need to access this in subclass
-    protected Thread fileCloser = null;
-    // MACOSX end
+    Thread fileCloser = null;
     Vector<File> tmpFontFiles = null;
 
-    public Font2D createFont2D(File fontFile, int fontFormat,
-                               boolean isCopy, CreatedFontTracker tracker)
+    public Font2D[] createFont2D(File fontFile, int fontFormat, boolean all,
+                                 boolean isCopy, CreatedFontTracker tracker)
     throws FontFormatException {
 
+        List<Font2D> fList = new ArrayList<Font2D>();
+        int cnt = 1;
         String fontFilePath = fontFile.getPath();
         FileFont font2D = null;
         final File fFile = fontFile;
@@ -2437,9 +2438,19 @@
             switch (fontFormat) {
             case Font.TRUETYPE_FONT:
                 font2D = new TrueTypeFont(fontFilePath, null, 0, true);
+                fList.add(font2D);
+                if (!all) {
+                    break;
+                }
+                cnt = ((TrueTypeFont)font2D).getFontCount();
+                int index = 1;
+                while (index < cnt) {
+                    fList.add(new TrueTypeFont(fontFilePath, null, index++, true));
+                }
                 break;
             case Font.TYPE1_FONT:
                 font2D = new Type1Font(fontFilePath, null, isCopy);
+                fList.add(font2D);
                 break;
             default:
                 throw new FontFormatException("Unrecognised Font Format");
@@ -2460,7 +2471,7 @@
             throw(e);
         }
         if (isCopy) {
-            font2D.setFileToRemove(fontFile, tracker);
+            FileFont.setFileToRemove(fList, fontFile, cnt, tracker);
             synchronized (FontManager.class) {
 
                 if (tmpFontFiles == null) {
@@ -2511,7 +2522,7 @@
                 }
             }
         }
-        return font2D;
+        return fList.toArray(new Font2D[0]);
     }
 
     /* remind: used in X11GraphicsEnvironment and called often enough
--- a/test/java/awt/FontClass/CreateFont/BigFont.java	Tue Mar 22 13:10:07 2016 -0700
+++ b/test/java/awt/FontClass/CreateFont/BigFont.java	Tue Mar 22 14:46:48 2016 -0700
@@ -60,12 +60,16 @@
 
         System.out.println("Applet " + id + " "+
                            Thread.currentThread().getThreadGroup());
+        if (System.getSecurityManager() == null) {
+            System.setSecurityManager(new SecurityManager());
+        }
         // 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) {
+            t.printStackTrace();
             if (t instanceof FontFormatException ||
                 fontSize <= sis.getCurrentSize())
             {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/awt/FontClass/CreateFont/CreateFontArrayTest.java	Tue Mar 22 14:46:48 2016 -0700
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8055463
+ * @summary Test createFont APIs
+ * @run CreateFontArrayTest
+ */
+
+import java.awt.Font;
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+
+/*
+ * This test pokes around in platform folders/directories to try
+ * to find some fonts with which to test. It will do different things
+ * on different platforms and may not do anything at all if the platform
+ * directories aren't where it expects. For example if /usr/share/fonts
+ * is not used on a particular Linux distro or on Windows the fonts are
+ * not in c:\windows\fonts (which would be the right place on 99.99% of
+ * systems you will find today.
+ * It ought to be very reliable but it is not 100% guaranteed.
+ * Failure to find fonts to test is 'not a product bug'.
+ * Fonts on a system having different content than we expect based on
+ * file extension is also 'not a product bug'.
+ * The former will cause silent success, the latter may cause 'noisy' failure
+ * and the test would then need to be dialled back to be more cautious.
+ */
+
+public class CreateFontArrayTest {
+
+    public static void main(String[] args) throws Exception {
+        test(".ttc", 2, -1, true);
+        test(".ttf", 1,  1, true);
+        test(".otf", 1,  1, true);
+        test(".pfa", 0,  0, false);
+        test(".pfb", 0,  0, false);
+    }
+
+    static File getPlatformFontFolder(String ext) throws Exception {
+        boolean recurse = false;
+        String folder = null;
+        String os = System.getProperty("os.name");
+        if (os.startsWith("Win")) {
+            folder = "c:\\windows\\fonts";
+        } else if (os.startsWith("Linux")) {
+            folder = "/usr/share/fonts";
+            recurse = true; // need to dig to find fonts.
+        } else if (os.startsWith("Mac")) {
+            // Disabled until createFont can handle mac font names.
+            //folder = "/Library/Fonts";
+        }
+        if (folder == null) {
+            return null;
+        }
+        File dir = new File(folder);
+        if (!dir.exists() || !dir.isDirectory()) {
+            return null;
+        }
+        // Have a root.
+        if (!recurse) {
+            return dir;
+        }
+
+        // If "recurse" is set, try to find a sub-folder which contains
+        // fonts with the specified extension
+        return findSubFolder(dir, ext);
+    }
+
+    static File findSubFolder(File folder, String ext) {
+        File[] files =
+            folder.listFiles(f -> f.getName().toLowerCase().endsWith(ext));
+        if (files != null && files.length > 0) {
+            return folder;
+        }
+        File[] subdirs = folder.listFiles(f -> f.isDirectory());
+        for (File f : subdirs) {
+            File subfolder = findSubFolder(f, ext);
+            if (subfolder != null) {
+                return subfolder;
+            }
+        }
+        return null;
+    }
+
+    static void test(String ext, int min, int max,
+                     boolean expectSuccess ) throws Exception {
+
+        File dir = getPlatformFontFolder(ext);
+        if (dir == null) {
+            System.out.println("No folder to test for " + ext);
+            return;
+        }
+        File[] files =
+            dir.listFiles(f -> f.getName().toLowerCase().endsWith(ext));
+        if (files == null || files.length == 0) {
+            System.out.println("No files to test for " + ext);
+            return;
+        }
+        System.out.println("Create from file " + files[0]);
+        Font[] fonts = null;
+        try {
+            fonts = Font.createFonts(files[0]);
+            System.out.println("createFont from file returned " + fonts);
+        } catch (Exception e) {
+            if (expectSuccess) {
+                throw new RuntimeException("Unexpected exception", e);
+            } else {
+                System.out.println("Got expected exception " + e);
+                return;
+            }
+        }
+        for (Font f : fonts) {
+            System.out.println(ext + " component : " + f);
+        }
+        if (fonts.length < min) {
+            throw new RuntimeException("Expected at least " + min +
+                                       " but got " + fonts.length);
+        }
+        if (max > 0 && fonts.length > max) {
+            throw new RuntimeException("Expected no more than " + max +
+                                       " but got " + fonts.length);
+        }
+        FileInputStream fis = null;
+        try {
+            System.out.println("Create from stream " + files[0]);
+            fis = new FileInputStream(files[0]);
+            InputStream s = new BufferedInputStream(fis);
+            fonts = null;
+            try {
+                fonts = Font.createFonts(s);
+                System.out.println("createFont from stream returned " + fonts);
+            } catch (Exception e) {
+                if (expectSuccess) {
+                    throw new RuntimeException("Unexpected exception", e);
+                } else {
+                    System.out.println("Got expected exception " + e);
+                    return;
+                }
+            }
+            for (Font f : fonts) {
+                System.out.println(ext + " component : " + f);
+            }
+            if (fonts.length < min) {
+                throw new RuntimeException("Expected at least " + min +
+                                           " but got " + fonts.length);
+            }
+            if (max > 0 && fonts.length > max) {
+                throw new RuntimeException("Expected no more than " + max +
+                                           " but got " + fonts.length);
+            }
+        } finally {
+            if (fis != null) {
+                fis.close();
+            }
+        }
+    }
+}
--- a/test/java/awt/font/FontNames/GetLCIDFromLocale.java	Tue Mar 22 13:10:07 2016 -0700
+++ b/test/java/awt/font/FontNames/GetLCIDFromLocale.java	Tue Mar 22 14:46:48 2016 -0700
@@ -4,9 +4,7 @@
  *
  * 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
+ * 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