jdk/src/share/demo/jfc/Font2DTest/Font2DTest.java
changeset 2 90ce3da70b43
child 5506 202f599c92aa
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/demo/jfc/Font2DTest/Font2DTest.java	Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,1184 @@
+/*
+ * Copyright 1999-2007 Sun Microsystems, Inc.  All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   - Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *
+ *   - Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ *   - Neither the name of Sun Microsystems nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.BorderLayout;
+import java.awt.CheckboxGroup;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.GraphicsEnvironment;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.GridLayout;
+import java.awt.Insets;
+import java.awt.RenderingHints;
+import java.awt.Toolkit;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.awt.image.BufferedImage;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.util.EnumSet;
+import java.util.StringTokenizer;
+import java.util.BitSet;
+import javax.swing.*;
+import javax.swing.event.*;
+
+/**
+ * Font2DTest.java
+ *
+ * @author Shinsuke Fukuda
+ * @author Ankit Patel [Conversion to Swing - 01/07/30]
+ */
+
+/// Main Font2DTest Class
+
+public final class Font2DTest extends JPanel
+    implements ActionListener, ItemListener, ChangeListener {
+
+    /// JFrame that will contain Font2DTest
+    private final JFrame parent;
+    /// FontPanel class that will contain all graphical output
+    private final FontPanel fp;
+    /// RangeMenu class that contains info about the unicode ranges
+    private final RangeMenu rm;
+
+    /// Other menus to set parameters for text drawing
+    private final ChoiceV2 fontMenu;
+    private final JTextField sizeField;
+    private final ChoiceV2 styleMenu;
+    private final ChoiceV2 textMenu;
+    private int currentTextChoice = 0;
+    private final ChoiceV2 transformMenu;
+    private final ChoiceV2 transformMenuG2;
+    private final ChoiceV2 methodsMenu;
+    private final JComboBox antiAliasMenu;
+    private final JComboBox fracMetricsMenu;
+
+    private final JSlider contrastSlider;
+
+    /// CheckboxMenuItems
+    private CheckboxMenuItemV2 displayGridCBMI;
+    private CheckboxMenuItemV2 force16ColsCBMI;
+    private CheckboxMenuItemV2 showFontInfoCBMI;
+
+    /// JDialog boxes
+    private JDialog userTextDialog;
+    private JTextArea userTextArea;
+    private JDialog printDialog;
+    private JDialog fontInfoDialog;
+    private LabelV2 fontInfos[] = new LabelV2[2];
+    private JFileChooser filePromptDialog = null;
+
+    private ButtonGroup printCBGroup;
+    private JRadioButton printModeCBs[] = new JRadioButton[3];
+
+    /// Status bar
+    private final LabelV2 statusBar;
+
+    private int fontStyles [] = {Font.PLAIN, Font.BOLD, Font.ITALIC, Font.BOLD | Font.ITALIC};
+
+    /// Text filename
+    private String tFileName;
+
+    // Enabled or disabled status of canDisplay check
+    private static boolean canDisplayCheck = true;
+
+    /// Initialize GUI variables and its layouts
+    public Font2DTest( JFrame f, boolean isApplet ) {
+        parent = f;
+
+        rm = new RangeMenu( this, parent );
+        fp = new FontPanel( this, parent );
+        statusBar = new LabelV2("");
+
+        fontMenu = new ChoiceV2( this, canDisplayCheck );
+        sizeField = new JTextField( "12", 3 );
+        sizeField.addActionListener( this );
+        styleMenu = new ChoiceV2( this );
+        textMenu = new ChoiceV2( ); // listener added later
+        transformMenu = new ChoiceV2( this );
+        transformMenuG2 = new ChoiceV2( this );
+        methodsMenu = new ChoiceV2( this );
+
+        antiAliasMenu =
+            new JComboBox(EnumSet.allOf(FontPanel.AAValues.class).toArray());
+        antiAliasMenu.addActionListener(this);
+        fracMetricsMenu =
+            new JComboBox(EnumSet.allOf(FontPanel.FMValues.class).toArray());
+        fracMetricsMenu.addActionListener(this);
+
+        contrastSlider = new JSlider(JSlider.HORIZONTAL, 100, 250,
+                                 FontPanel.getDefaultLCDContrast().intValue());
+        contrastSlider.setEnabled(false);
+        contrastSlider.setMajorTickSpacing(20);
+        contrastSlider.setMinorTickSpacing(10);
+        contrastSlider.setPaintTicks(true);
+        contrastSlider.setPaintLabels(true);
+        contrastSlider.addChangeListener(this);
+        setupPanel();
+        setupMenu( isApplet );
+        setupDialog( isApplet );
+
+        if(canDisplayCheck) {
+            fireRangeChanged();
+        }
+    }
+
+    /// Set up the main interface panel
+    private void setupPanel() {
+        GridBagLayout gbl = new GridBagLayout();
+        GridBagConstraints gbc = new GridBagConstraints();
+        gbc.fill = GridBagConstraints.HORIZONTAL;
+        gbc.weightx = 1;
+        gbc.insets = new Insets( 2, 0, 2, 2 );
+        this.setLayout( gbl );
+
+        addLabeledComponentToGBL( "Font: ", fontMenu, gbl, gbc, this );
+        addLabeledComponentToGBL( "Size: ", sizeField, gbl, gbc, this );
+        gbc.gridwidth = GridBagConstraints.REMAINDER;
+        addLabeledComponentToGBL( "Font Transform:",
+                                  transformMenu, gbl, gbc, this );
+        gbc.gridwidth = 1;
+
+        addLabeledComponentToGBL( "Range: ", rm, gbl, gbc, this );
+        addLabeledComponentToGBL( "Style: ", styleMenu, gbl, gbc, this );
+        gbc.gridwidth = GridBagConstraints.REMAINDER;
+        addLabeledComponentToGBL( "Graphics Transform: ",
+                                  transformMenuG2, gbl, gbc, this );
+        gbc.gridwidth = 1;
+
+        gbc.anchor = GridBagConstraints.WEST;
+        addLabeledComponentToGBL( "Method: ", methodsMenu, gbl, gbc, this );
+        addLabeledComponentToGBL("", null, gbl, gbc, this);
+        gbc.anchor = GridBagConstraints.EAST;
+        gbc.gridwidth = GridBagConstraints.REMAINDER;
+        addLabeledComponentToGBL( "Text to use:", textMenu, gbl, gbc, this );
+
+        gbc.weightx=1;
+        gbc.gridwidth = 1;
+        gbc.fill = GridBagConstraints.HORIZONTAL;
+        gbc.anchor = GridBagConstraints.WEST;
+        addLabeledComponentToGBL("LCD contrast: ",
+                                  contrastSlider, gbl, gbc, this);
+
+        gbc.gridwidth = 1;
+        gbc.fill = GridBagConstraints.NONE;
+        addLabeledComponentToGBL("Antialiasing: ",
+                                  antiAliasMenu, gbl, gbc, this);
+
+        gbc.anchor = GridBagConstraints.EAST;
+        gbc.gridwidth = GridBagConstraints.REMAINDER;
+        addLabeledComponentToGBL("Fractional metrics: ",
+                                  fracMetricsMenu, gbl, gbc, this);
+
+        gbc.weightx = 1;
+        gbc.weighty = 1;
+        gbc.anchor = GridBagConstraints.WEST;
+        gbc.insets = new Insets( 2, 0, 0, 2 );
+        gbc.fill = GridBagConstraints.BOTH;
+        gbl.setConstraints( fp, gbc );
+        this.add( fp );
+
+        gbc.weighty = 0;
+        gbc.insets = new Insets( 0, 2, 0, 0 );
+        gbl.setConstraints( statusBar, gbc );
+        this.add( statusBar );
+    }
+
+    /// Adds a component to a container with a label to its left in GridBagLayout
+    private void addLabeledComponentToGBL( String name,
+                                           JComponent c,
+                                           GridBagLayout gbl,
+                                           GridBagConstraints gbc,
+                                           Container target ) {
+        LabelV2 l = new LabelV2( name );
+        GridBagConstraints gbcLabel = (GridBagConstraints) gbc.clone();
+        gbcLabel.insets = new Insets( 2, 2, 2, 0 );
+        gbcLabel.gridwidth = 1;
+        gbcLabel.weightx = 0;
+
+        if ( c == null )
+          c = new JLabel( "" );
+
+        gbl.setConstraints( l, gbcLabel );
+        target.add( l );
+        gbl.setConstraints( c, gbc );
+        target.add( c );
+    }
+
+    /// Sets up menu entries
+    private void setupMenu( boolean isApplet ) {
+        JMenu fileMenu = new JMenu( "File" );
+        JMenu optionMenu = new JMenu( "Option" );
+
+        fileMenu.add( new MenuItemV2( "Save Selected Options...", this ));
+        fileMenu.add( new MenuItemV2( "Load Options...", this ));
+        fileMenu.addSeparator();
+        fileMenu.add( new MenuItemV2( "Save as PNG...", this ));
+        fileMenu.add( new MenuItemV2( "Load PNG File to Compare...", this ));
+        fileMenu.add( new MenuItemV2( "Page Setup...", this ));
+        fileMenu.add( new MenuItemV2( "Print...", this ));
+        fileMenu.addSeparator();
+        if ( !isApplet )
+          fileMenu.add( new MenuItemV2( "Exit", this ));
+        else
+          fileMenu.add( new MenuItemV2( "Close", this ));
+
+        displayGridCBMI = new CheckboxMenuItemV2( "Display Grid", true, this );
+        force16ColsCBMI = new CheckboxMenuItemV2( "Force 16 Columns", false, this );
+        showFontInfoCBMI = new CheckboxMenuItemV2( "Display Font Info", false, this );
+        optionMenu.add( displayGridCBMI );
+        optionMenu.add( force16ColsCBMI );
+        optionMenu.add( showFontInfoCBMI );
+
+        JMenuBar mb = parent.getJMenuBar();
+        if ( mb == null )
+          mb = new JMenuBar();
+        mb.add( fileMenu );
+        mb.add( optionMenu );
+
+        parent.setJMenuBar( mb );
+
+        String fontList[] =
+          GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
+
+        for ( int i = 0; i < fontList.length; i++ )
+          fontMenu.addItem( fontList[i] );
+        fontMenu.setSelectedItem( "Dialog" );
+
+        styleMenu.addItem( "Plain" );
+        styleMenu.addItem( "Bold" );
+        styleMenu.addItem( "Italic" );
+        styleMenu.addItem( "Bold Italic" );
+
+        transformMenu.addItem( "None" );
+        transformMenu.addItem( "Scale" );
+        transformMenu.addItem( "Shear" );
+        transformMenu.addItem( "Rotate" );
+
+        transformMenuG2.addItem( "None" );
+        transformMenuG2.addItem( "Scale" );
+        transformMenuG2.addItem( "Shear" );
+        transformMenuG2.addItem( "Rotate" );
+
+        methodsMenu.addItem( "drawString" );
+        methodsMenu.addItem( "drawChars" );
+        methodsMenu.addItem( "drawBytes" );
+        methodsMenu.addItem( "drawGlyphVector" );
+        methodsMenu.addItem( "TextLayout.draw" );
+        methodsMenu.addItem( "GlyphVector.getOutline + draw" );
+        methodsMenu.addItem( "TextLayout.getOutline + draw" );
+
+        textMenu.addItem( "Unicode Range" );
+        textMenu.addItem( "All Glyphs" );
+        textMenu.addItem( "User Text" );
+        textMenu.addItem( "Text File" );
+        textMenu.addActionListener ( this ); // listener added later so unneeded events not thrown
+    }
+
+    /// Sets up the all dialogs used in Font2DTest...
+    private void setupDialog( boolean isApplet ) {
+        if (!isApplet)
+                filePromptDialog = new JFileChooser( );
+        else
+                filePromptDialog = null;
+
+        /// Prepare user text dialog...
+        userTextDialog = new JDialog( parent, "User Text", false );
+        JPanel dialogTopPanel = new JPanel();
+        JPanel dialogBottomPanel = new JPanel();
+        LabelV2 message1 = new LabelV2( "Enter text below and then press update" );
+        LabelV2 message2 = new LabelV2( "(Unicode char can be denoted by \\uXXXX)" );
+        LabelV2 message3 = new LabelV2( "(Supplementary chars can be denoted by \\UXXXXXX)" );
+        userTextArea = new JTextArea( "Font2DTest!" );
+        ButtonV2 bUpdate = new ButtonV2( "Update", this );
+        userTextArea.setFont( new Font( "dialog", Font.PLAIN, 12 ));
+        dialogTopPanel.setLayout( new GridLayout( 3, 1 ));
+        dialogTopPanel.add( message1 );
+        dialogTopPanel.add( message2 );
+        dialogTopPanel.add( message3 );
+        dialogBottomPanel.add( bUpdate );
+        //ABP
+        JScrollPane userTextAreaSP = new JScrollPane(userTextArea);
+        userTextAreaSP.setPreferredSize(new Dimension(300, 100));
+
+        userTextDialog.getContentPane().setLayout( new BorderLayout() );
+        userTextDialog.getContentPane().add( "North", dialogTopPanel );
+        userTextDialog.getContentPane().add( "Center", userTextAreaSP );
+        userTextDialog.getContentPane().add( "South", dialogBottomPanel );
+        userTextDialog.pack();
+        userTextDialog.addWindowListener( new WindowAdapter() {
+            public void windowClosing( WindowEvent e ) {
+                userTextDialog.hide();
+            }
+        });
+
+        /// Prepare printing dialog...
+        printCBGroup = new ButtonGroup();
+        printModeCBs[ fp.ONE_PAGE ] =
+          new JRadioButton( "Print one page from currently displayed character/line",
+                         true );
+        printModeCBs[ fp.CUR_RANGE ] =
+          new JRadioButton( "Print all characters in currently selected range",
+                         false );
+        printModeCBs[ fp.ALL_TEXT ] =
+          new JRadioButton( "Print all lines of text",
+                         false );
+        LabelV2 l =
+          new LabelV2( "Note: Page range in native \"Print\" dialog will not affect the result" );
+        JPanel buttonPanel = new JPanel();
+        printModeCBs[ fp.ALL_TEXT ].setEnabled( false );
+        buttonPanel.add( new ButtonV2( "Print", this ));
+        buttonPanel.add( new ButtonV2( "Cancel", this ));
+
+        printDialog = new JDialog( parent, "Print...", true );
+        printDialog.setResizable( false );
+        printDialog.addWindowListener( new WindowAdapter() {
+            public void windowClosing( WindowEvent e ) {
+                printDialog.hide();
+            }
+        });
+        printDialog.getContentPane().setLayout( new GridLayout( printModeCBs.length + 2, 1 ));
+        printDialog.getContentPane().add( l );
+        for ( int i = 0; i < printModeCBs.length; i++ ) {
+            printCBGroup.add( printModeCBs[i] );
+            printDialog.getContentPane().add( printModeCBs[i] );
+        }
+        printDialog.getContentPane().add( buttonPanel );
+        printDialog.pack();
+
+        /// Prepare font information dialog...
+        fontInfoDialog = new JDialog( parent, "Font info", false );
+        fontInfoDialog.setResizable( false );
+        fontInfoDialog.addWindowListener( new WindowAdapter() {
+            public void windowClosing( WindowEvent e ) {
+                fontInfoDialog.hide();
+                showFontInfoCBMI.setState( false );
+            }
+        });
+        JPanel fontInfoPanel = new JPanel();
+        fontInfoPanel.setLayout( new GridLayout( fontInfos.length, 1 ));
+        for ( int i = 0; i < fontInfos.length; i++ ) {
+            fontInfos[i] = new LabelV2("");
+            fontInfoPanel.add( fontInfos[i] );
+        }
+        fontInfoDialog.getContentPane().add( fontInfoPanel );
+
+        /// Move the location of the dialog...
+        userTextDialog.setLocation( 200, 300 );
+        fontInfoDialog.setLocation( 0, 400 );
+    }
+
+    /// RangeMenu object signals using this function
+    /// when Unicode range has been changed and text needs to be redrawn
+    public void fireRangeChanged() {
+        int range[] = rm.getSelectedRange();
+        fp.setTextToDraw( fp.RANGE_TEXT, range, null, null );
+        if(canDisplayCheck) {
+            setupFontList(range[0], range[1]);
+        }
+        if ( showFontInfoCBMI.getState() )
+          fireUpdateFontInfo();
+    }
+
+    /// Changes the message on the status bar
+    public void fireChangeStatus( String message, boolean error ) {
+        /// If this is not ran as an applet, use own status bar,
+        /// Otherwise, use the appletviewer/browser's status bar
+        statusBar.setText( message );
+        if ( error )
+          fp.showingError = true;
+        else
+          fp.showingError = false;
+    }
+
+    /// Updates the information about the selected font
+    public void fireUpdateFontInfo() {
+        if ( showFontInfoCBMI.getState() ) {
+            String infos[] = fp.getFontInfo();
+            for ( int i = 0; i < fontInfos.length; i++ )
+              fontInfos[i].setText( infos[i] );
+            fontInfoDialog.pack();
+        }
+    }
+
+    private void setupFontList(int rangeStart, int rangeEnd) {
+
+        int listCount = fontMenu.getItemCount();
+        int size = 16;
+
+        try {
+            size =  Float.valueOf(sizeField.getText()).intValue();
+        }
+        catch ( Exception e ) {
+            System.out.println("Invalid font size in the size textField. Using default value of 16");
+        }
+
+        int style = fontStyles[styleMenu.getSelectedIndex()];
+        Font f;
+        for (int i = 0; i < listCount; i++) {
+            String fontName = (String)fontMenu.getItemAt(i);
+            f = new Font(fontName, style, size);
+            if ((rm.getSelectedIndex() != RangeMenu.SURROGATES_AREA_INDEX) &&
+                canDisplayRange(f, rangeStart, rangeEnd)) {
+                fontMenu.setBit(i, true);
+            }
+            else {
+                fontMenu.setBit(i, false);
+            }
+        }
+
+        fontMenu.repaint();
+    }
+
+    protected boolean canDisplayRange(Font font, int rangeStart, int rangeEnd) {
+        for (int i = rangeStart; i < rangeEnd; i++) {
+            if (font.canDisplay(i)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /// Displays a file load/save dialog and returns the specified file
+    private String promptFile( boolean isSave, String initFileName ) {
+        int retVal;
+        String str;
+
+        /// ABP
+        if ( filePromptDialog == null)
+                return null;
+
+        if ( isSave ) {
+            filePromptDialog.setDialogType( JFileChooser.SAVE_DIALOG );
+            filePromptDialog.setDialogTitle( "Save..." );
+            str = "Save";
+
+
+        }
+        else {
+            filePromptDialog.setDialogType( JFileChooser.OPEN_DIALOG );
+            filePromptDialog.setDialogTitle( "Load..." );
+            str = "Load";
+        }
+
+        if (initFileName != null)
+                filePromptDialog.setSelectedFile( new File( initFileName ) );
+        retVal = filePromptDialog.showDialog( this, str );
+
+        if ( retVal == JFileChooser.APPROVE_OPTION ) {
+                File file = filePromptDialog.getSelectedFile();
+                String fileName = file.getAbsolutePath();
+                if ( fileName != null ) {
+                        return fileName;
+                }
+        }
+
+        return null;
+    }
+
+    /// Converts user text into arrays of String, delimited at newline character
+    /// Also replaces any valid escape sequence with appropriate unicode character
+    /// Support \\UXXXXXX notation for surrogates
+    private String[] parseUserText( String orig ) {
+        int length = orig.length();
+        StringTokenizer perLine = new StringTokenizer( orig, "\n" );
+        String textLines[] = new String[ perLine.countTokens() ];
+        int lineNumber = 0;
+
+        while ( perLine.hasMoreElements() ) {
+            StringBuffer converted = new StringBuffer();
+            String oneLine = perLine.nextToken();
+            int lineLength = oneLine.length();
+            int prevEscapeEnd = 0;
+            int nextEscape = -1;
+            do {
+                int nextBMPEscape = oneLine.indexOf( "\\u", prevEscapeEnd );
+                int nextSupEscape = oneLine.indexOf( "\\U", prevEscapeEnd );
+                nextEscape = (nextBMPEscape < 0)
+                    ? ((nextSupEscape < 0)
+                       ? -1
+                       : nextSupEscape)
+                    : ((nextSupEscape < 0)
+                       ? nextBMPEscape
+                       : Math.min(nextBMPEscape, nextSupEscape));
+
+                if ( nextEscape != -1 ) {
+                    if ( prevEscapeEnd < nextEscape )
+                        converted.append( oneLine.substring( prevEscapeEnd, nextEscape ));
+
+                    prevEscapeEnd = nextEscape + (nextEscape == nextBMPEscape ? 6 : 8);
+                    try {
+                        String hex = oneLine.substring( nextEscape + 2, prevEscapeEnd );
+                        if (nextEscape == nextBMPEscape) {
+                            converted.append( (char) Integer.parseInt( hex, 16 ));
+                        } else {
+                            converted.append( new String( Character.toChars( Integer.parseInt( hex, 16 ))));
+                        }
+                    }
+                    catch ( Exception e ) {
+                        int copyLimit = Math.min(lineLength, prevEscapeEnd);
+                        converted.append( oneLine.substring( nextEscape, copyLimit ));
+                    }
+                }
+            } while (nextEscape != -1);
+            if ( prevEscapeEnd < lineLength )
+              converted.append( oneLine.substring( prevEscapeEnd, lineLength ));
+            textLines[ lineNumber++ ] = converted.toString();
+        }
+        return textLines;
+    }
+
+    /// Reads the text from specified file, detecting UTF-16 encoding
+    /// Then breaks the text into String array, delimited at every line break
+    private void readTextFile( String fileName ) {
+        try {
+            String fileText, textLines[];
+            BufferedInputStream bis =
+              new BufferedInputStream( new FileInputStream( fileName ));
+            int numBytes = bis.available();
+            if (numBytes == 0) {
+                throw new Exception("Text file " + fileName + " is empty");
+            }
+            byte byteData[] = new byte[ numBytes ];
+            bis.read( byteData, 0, numBytes );
+            bis.close();
+
+            /// If byte mark is found, then use UTF-16 encoding to convert bytes...
+            if (numBytes >= 2 &&
+                (( byteData[0] == (byte) 0xFF && byteData[1] == (byte) 0xFE ) ||
+                 ( byteData[0] == (byte) 0xFE && byteData[1] == (byte) 0xFF )))
+              fileText = new String( byteData, "UTF-16" );
+            /// Otherwise, use system default encoding
+            else
+              fileText = new String( byteData );
+
+            int length = fileText.length();
+            StringTokenizer perLine = new StringTokenizer( fileText, "\n" );
+            /// Determine "Return Char" used in this file
+            /// This simply finds first occurrence of CR, CR+LF or LF...
+            for ( int i = 0; i < length; i++ ) {
+                char iTh = fileText.charAt( i );
+                if ( iTh == '\r' ) {
+                    if ( i < length - 1 && fileText.charAt( i + 1 ) == '\n' )
+                      perLine = new StringTokenizer( fileText, "\r\n" );
+                    else
+                      perLine = new StringTokenizer( fileText, "\r" );
+                    break;
+                }
+                else if ( iTh == '\n' )
+                  /// Use the one already created
+                  break;
+            }
+            int lineNumber = 0, numLines = perLine.countTokens();
+            textLines = new String[ numLines ];
+
+            while ( perLine.hasMoreElements() ) {
+                String oneLine = perLine.nextToken();
+                if ( oneLine == null )
+                  /// To make LineBreakMeasurer to return a valid TextLayout
+                  /// on an empty line, simply feed it a space char...
+                  oneLine = " ";
+                textLines[ lineNumber++ ] = oneLine;
+            }
+            fp.setTextToDraw( fp.FILE_TEXT, null, null, textLines );
+            rm.setEnabled( false );
+            methodsMenu.setEnabled( false );
+        }
+        catch ( Exception ex ) {
+            fireChangeStatus( "ERROR: Failed to Read Text File; See Stack Trace", true );
+            ex.printStackTrace();
+        }
+    }
+
+    /// Returns a String storing current configuration
+    private void writeCurrentOptions( String fileName ) {
+        try {
+            String curOptions = fp.getCurrentOptions();
+            BufferedOutputStream bos =
+              new BufferedOutputStream( new FileOutputStream( fileName ));
+            /// Prepend title and the option that is only obtainable here
+            int range[] = rm.getSelectedRange();
+            String completeOptions =
+              ( "Font2DTest Option File\n" +
+                displayGridCBMI.getState() + "\n" +
+                force16ColsCBMI.getState() + "\n" +
+                showFontInfoCBMI.getState() + "\n" +
+                rm.getSelectedItem() + "\n" +
+                range[0] + "\n" + range[1] + "\n" + curOptions + tFileName);
+            byte toBeWritten[] = completeOptions.getBytes( "UTF-16" );
+            bos.write( toBeWritten, 0, toBeWritten.length );
+            bos.close();
+        }
+        catch ( Exception ex ) {
+            fireChangeStatus( "ERROR: Failed to Save Options File; See Stack Trace", true );
+            ex.printStackTrace();
+        }
+    }
+
+    /// Updates GUI visibility/status after some parameters have changed
+    private void updateGUI() {
+        int selectedText = textMenu.getSelectedIndex();
+
+        /// Set the visibility of User Text dialog
+        if ( selectedText == fp.USER_TEXT )
+          userTextDialog.show();
+        else
+          userTextDialog.hide();
+        /// Change the visibility/status/availability of Print JDialog buttons
+        printModeCBs[ fp.ONE_PAGE ].setSelected( true );
+        if ( selectedText == fp.FILE_TEXT || selectedText == fp.USER_TEXT ) {
+            /// ABP
+            /// update methodsMenu to show that TextLayout.draw is being used
+            /// when we are in FILE_TEXT mode
+            if ( selectedText == fp.FILE_TEXT )
+                methodsMenu.setSelectedItem("TextLayout.draw");
+            methodsMenu.setEnabled( selectedText == fp.USER_TEXT );
+            printModeCBs[ fp.CUR_RANGE ].setEnabled( false );
+            printModeCBs[ fp.ALL_TEXT ].setEnabled( true );
+        }
+        else {
+            /// ABP
+            /// update methodsMenu to show that drawGlyph is being used
+            /// when we are in ALL_GLYPHS mode
+            if ( selectedText == fp.ALL_GLYPHS )
+                methodsMenu.setSelectedItem("drawGlyphVector");
+            methodsMenu.setEnabled( selectedText == fp.RANGE_TEXT );
+            printModeCBs[ fp.CUR_RANGE ].setEnabled( true );
+            printModeCBs[ fp.ALL_TEXT ].setEnabled( false );
+        }
+        /// Modify RangeMenu and fontInfo label availabilty
+        if ( selectedText == fp.RANGE_TEXT ) {
+            fontInfos[1].setVisible( true );
+            rm.setEnabled( true );
+        }
+        else {
+            fontInfos[1].setVisible( false );
+            rm.setEnabled( false );
+        }
+    }
+
+    /// Loads saved options and applies them
+    private void loadOptions( String fileName ) {
+        try {
+            BufferedInputStream bis =
+              new BufferedInputStream( new FileInputStream( fileName ));
+            int numBytes = bis.available();
+            byte byteData[] = new byte[ numBytes ];
+            bis.read( byteData, 0, numBytes );
+            bis.close();
+            if ( numBytes < 2 ||
+                (byteData[0] != (byte) 0xFE || byteData[1] != (byte) 0xFF) )
+              throw new Exception( "Not a Font2DTest options file" );
+
+            String options = new String( byteData, "UTF-16" );
+            StringTokenizer perLine = new StringTokenizer( options, "\n" );
+            String title = perLine.nextToken();
+            if ( !title.equals( "Font2DTest Option File" ))
+              throw new Exception( "Not a Font2DTest options file" );
+
+            /// Parse all options
+            boolean displayGridOpt = Boolean.parseBoolean( perLine.nextToken() );
+            boolean force16ColsOpt = Boolean.parseBoolean( perLine.nextToken() );
+            boolean showFontInfoOpt = Boolean.parseBoolean( perLine.nextToken() );
+            String rangeNameOpt = perLine.nextToken();
+            int rangeStartOpt = Integer.parseInt( perLine.nextToken() );
+            int rangeEndOpt = Integer.parseInt( perLine.nextToken() );
+            String fontNameOpt = perLine.nextToken();
+            float fontSizeOpt = Float.parseFloat( perLine.nextToken() );
+            int fontStyleOpt = Integer.parseInt( perLine.nextToken() );
+            int fontTransformOpt = Integer.parseInt( perLine.nextToken() );
+            int g2TransformOpt = Integer.parseInt( perLine.nextToken() );
+            int textToUseOpt = Integer.parseInt( perLine.nextToken() );
+            int drawMethodOpt = Integer.parseInt( perLine.nextToken() );
+            int antialiasOpt = Integer.parseInt(perLine.nextToken());
+            int fractionalOpt = Integer.parseInt(perLine.nextToken());
+            int lcdContrast = Integer.parseInt(perLine.nextToken());
+            String userTextOpt[] = { "Font2DTest!" };
+            String dialogEntry = "Font2DTest!";
+            if (textToUseOpt == fp.USER_TEXT )  {
+                int numLines = perLine.countTokens(), lineNumber = 0;
+                if ( numLines != 0 ) {
+                    userTextOpt = new String[ numLines ];
+                    dialogEntry = "";
+                    for ( ; perLine.hasMoreElements(); lineNumber++ ) {
+                        userTextOpt[ lineNumber ] = perLine.nextToken();
+                        dialogEntry += userTextOpt[ lineNumber ] + "\n";
+                    }
+                }
+            }
+
+            /// Reset GUIs
+            displayGridCBMI.setState( displayGridOpt );
+            force16ColsCBMI.setState( force16ColsOpt );
+            showFontInfoCBMI.setState( showFontInfoOpt );
+            rm.setSelectedRange( rangeNameOpt, rangeStartOpt, rangeEndOpt );
+            fontMenu.setSelectedItem( fontNameOpt );
+            sizeField.setText( String.valueOf( fontSizeOpt ));
+            styleMenu.setSelectedIndex( fontStyleOpt );
+            transformMenu.setSelectedIndex( fontTransformOpt );
+            transformMenuG2.setSelectedIndex( g2TransformOpt );
+            textMenu.setSelectedIndex( textToUseOpt );
+            methodsMenu.setSelectedIndex( drawMethodOpt );
+            antiAliasMenu.setSelectedIndex( antialiasOpt );
+            fracMetricsMenu.setSelectedIndex( fractionalOpt );
+            contrastSlider.setValue(lcdContrast);
+
+            userTextArea.setText( dialogEntry );
+            updateGUI();
+
+            if ( textToUseOpt == fp.FILE_TEXT ) {
+              tFileName = perLine.nextToken();
+              readTextFile(tFileName );
+            }
+
+            /// Reset option variables and repaint
+            fp.loadOptions( displayGridOpt, force16ColsOpt,
+                            rangeStartOpt, rangeEndOpt,
+                            fontNameOpt, fontSizeOpt,
+                            fontStyleOpt, fontTransformOpt, g2TransformOpt,
+                            textToUseOpt, drawMethodOpt,
+                            antialiasOpt, fractionalOpt,
+                            lcdContrast, userTextOpt );
+            if ( showFontInfoOpt ) {
+                fireUpdateFontInfo();
+                fontInfoDialog.show();
+            }
+            else
+              fontInfoDialog.hide();
+        }
+        catch ( Exception ex ) {
+            fireChangeStatus( "ERROR: Failed to Load Options File; See Stack Trace", true );
+            ex.printStackTrace();
+        }
+    }
+
+    /// Loads a previously saved image
+    private void loadComparisonPNG( String fileName ) {
+        try {
+            BufferedImage image =
+                javax.imageio.ImageIO.read(new File(fileName));
+            JFrame f = new JFrame( "Comparison PNG" );
+            ImagePanel ip = new ImagePanel( image );
+            f.setResizable( false );
+            f.getContentPane().add( ip );
+            f.addWindowListener( new WindowAdapter() {
+                public void windowClosing( WindowEvent e ) {
+                    ( (JFrame) e.getSource() ).dispose();
+                }
+            });
+            f.pack();
+            f.show();
+        }
+        catch ( Exception ex ) {
+            fireChangeStatus( "ERROR: Failed to Load PNG File; See Stack Trace", true );
+            ex.printStackTrace();
+        }
+    }
+
+    /// Interface functions...
+
+    /// ActionListener interface function
+    /// Responds to JMenuItem, JTextField and JButton actions
+    public void actionPerformed( ActionEvent e ) {
+        Object source = e.getSource();
+
+        if ( source instanceof JMenuItem ) {
+            JMenuItem mi = (JMenuItem) source;
+            String itemName = mi.getText();
+
+            if ( itemName.equals( "Save Selected Options..." )) {
+                String fileName = promptFile( true, "options.txt" );
+                if ( fileName != null )
+                  writeCurrentOptions( fileName );
+            }
+            else if ( itemName.equals( "Load Options..." )) {
+                String fileName = promptFile( false, "options.txt" );
+                if ( fileName != null )
+                  loadOptions( fileName );
+            }
+            else if ( itemName.equals( "Save as PNG..." )) {
+                String fileName = promptFile( true, fontMenu.getSelectedItem() + ".png" );
+                if ( fileName != null )
+                  fp.doSavePNG( fileName );
+            }
+            else if ( itemName.equals( "Load PNG File to Compare..." )) {
+                String fileName = promptFile( false, null );
+                if ( fileName != null )
+                  loadComparisonPNG( fileName );
+            }
+            else if ( itemName.equals( "Page Setup..." ))
+              fp.doPageSetup();
+            else if ( itemName.equals( "Print..." ))
+              printDialog.show();
+            else if ( itemName.equals( "Close" ))
+              parent.dispose();
+            else if ( itemName.equals( "Exit" ))
+              System.exit(0);
+        }
+
+        else if ( source instanceof JTextField ) {
+            JTextField tf = (JTextField) source;
+            float sz = 12f;
+            try {
+                 sz = Float.parseFloat(sizeField.getText());
+                 if (sz < 1f || sz > 120f) {
+                      sz = 12f;
+                      sizeField.setText("12");
+                 }
+            } catch (Exception se) {
+                 sizeField.setText("12");
+            }
+            if ( tf == sizeField )
+              fp.setFontParams( fontMenu.getSelectedItem(),
+                                sz,
+                                styleMenu.getSelectedIndex(),
+                                transformMenu.getSelectedIndex() );
+        }
+
+        else if ( source instanceof JButton ) {
+            String itemName = ( (JButton) source ).getText();
+            /// Print dialog buttons...
+            if ( itemName.equals( "Print" )) {
+                for ( int i = 0; i < printModeCBs.length; i++ )
+                  if ( printModeCBs[i].isSelected() ) {
+                      printDialog.hide();
+                      fp.doPrint( i );
+                  }
+            }
+            else if ( itemName.equals( "Cancel" ))
+              printDialog.hide();
+            /// Update button from Usert Text JDialog...
+            else if ( itemName.equals( "Update" ))
+              fp.setTextToDraw( fp.USER_TEXT, null,
+                                parseUserText( userTextArea.getText() ), null );
+        }
+        else if ( source instanceof JComboBox ) {
+            JComboBox c = (JComboBox) source;
+
+            /// RangeMenu handles actions by itself and then calls fireRangeChanged,
+            /// so it is not listed or handled here
+            if ( c == fontMenu || c == styleMenu || c == transformMenu ) {
+                float sz = 12f;
+                try {
+                    sz = Float.parseFloat(sizeField.getText());
+                    if (sz < 1f || sz > 120f) {
+                        sz = 12f;
+                        sizeField.setText("12");
+                    }
+                } catch (Exception se) {
+                    sizeField.setText("12");
+                }
+                fp.setFontParams(fontMenu.getSelectedItem(),
+                                 sz,
+                                 styleMenu.getSelectedIndex(),
+                                 transformMenu.getSelectedIndex());
+            } else if ( c == methodsMenu )
+              fp.setDrawMethod( methodsMenu.getSelectedIndex() );
+            else if ( c == textMenu ) {
+
+                if(canDisplayCheck) {
+                    fireRangeChanged();
+                }
+
+                int selected = textMenu.getSelectedIndex();
+
+                if ( selected == fp.RANGE_TEXT )
+                  fp.setTextToDraw( fp.RANGE_TEXT, rm.getSelectedRange(),
+                                    null, null );
+                else if ( selected == fp.USER_TEXT )
+                  fp.setTextToDraw( fp.USER_TEXT, null,
+                                    parseUserText( userTextArea.getText() ), null );
+                else if ( selected == fp.FILE_TEXT ) {
+                    String fileName = promptFile( false, null );
+                    if ( fileName != null ) {
+                      tFileName = fileName;
+                      readTextFile( fileName );
+                    } else {
+                        /// User cancelled selection; reset to previous choice
+                        c.setSelectedIndex( currentTextChoice );
+                        return;
+                    }
+                }
+                else if ( selected == fp.ALL_GLYPHS )
+                  fp.setTextToDraw( fp.ALL_GLYPHS, null, null, null );
+
+                updateGUI();
+                currentTextChoice = selected;
+            }
+            else if ( c == transformMenuG2 ) {
+                fp.setTransformG2( transformMenuG2.getSelectedIndex() );
+            }
+            else if (c == antiAliasMenu || c == fracMetricsMenu) {
+                if (c == antiAliasMenu) {
+                    boolean enabled = FontPanel.AAValues.
+                        isLCDMode(antiAliasMenu.getSelectedItem());
+                        contrastSlider.setEnabled(enabled);
+                }
+                fp.setRenderingHints(antiAliasMenu.getSelectedItem(),
+                                     fracMetricsMenu.getSelectedItem(),
+                                     contrastSlider.getValue());
+            }
+        }
+    }
+
+    public void stateChanged(ChangeEvent e) {
+         Object source = e.getSource();
+         if (source instanceof JSlider) {
+             fp.setRenderingHints(antiAliasMenu.getSelectedItem(),
+                                  fracMetricsMenu.getSelectedItem(),
+                                  contrastSlider.getValue());
+         }
+    }
+
+    /// ItemListener interface function
+    /// Responds to JCheckBoxMenuItem, JComboBox and JCheckBox actions
+    public void itemStateChanged( ItemEvent e ) {
+        Object source = e.getSource();
+
+        if ( source instanceof JCheckBoxMenuItem ) {
+            JCheckBoxMenuItem cbmi = (JCheckBoxMenuItem) source;
+            if ( cbmi == displayGridCBMI )
+              fp.setGridDisplay( displayGridCBMI.getState() );
+            else if ( cbmi == force16ColsCBMI )
+              fp.setForce16Columns( force16ColsCBMI.getState() );
+            else if ( cbmi == showFontInfoCBMI ) {
+                if ( showFontInfoCBMI.getState() ) {
+                    fireUpdateFontInfo();
+                    fontInfoDialog.show();
+                }
+                else
+                  fontInfoDialog.hide();
+            }
+        }
+    }
+
+    private static void printUsage() {
+        String usage = "Usage: java -jar Font2DTest.jar [options]\n" +
+            "\nwhere options include:\n" +
+            "    -dcdc | -disablecandisplaycheck disable canDisplay check for font\n" +
+            "    -?    | -help                   print this help message\n" +
+            "\nExample :\n" +
+            "     To disable canDisplay check on font for ranges\n" +
+            "     java -jar Font2DTest.jar -dcdc";
+        System.out.println(usage);
+        System.exit(0);
+    }
+
+    /// Main function
+    public static void main(String argv[]) {
+
+        if(argv.length > 0) {
+            if(argv[0].equalsIgnoreCase("-disablecandisplaycheck") ||
+               argv[0].equalsIgnoreCase("-dcdc")) {
+                canDisplayCheck = false;
+            }
+            else {
+                printUsage();
+            }
+        }
+
+        UIManager.put("swing.boldMetal", Boolean.FALSE);
+        final JFrame f = new JFrame( "Font2DTest" );
+        final Font2DTest f2dt = new Font2DTest( f, false );
+        f.addWindowListener( new WindowAdapter() {
+            public void windowOpening( WindowEvent e ) { f2dt.repaint(); }
+            public void windowClosing( WindowEvent e ) { System.exit(0); }
+        });
+
+        f.getContentPane().add( f2dt );
+        f.pack();
+        f.show();
+    }
+
+    /// Inner class definitions...
+
+    /// Class to display just an image file
+    /// Used to show the comparison PNG image
+    private final class ImagePanel extends JPanel {
+        private final BufferedImage bi;
+
+        public ImagePanel( BufferedImage image ) {
+            bi = image;
+        }
+
+        public Dimension getPreferredSize() {
+            return new Dimension( bi.getWidth(), bi.getHeight() );
+        }
+
+        public void paintComponent( Graphics g ) {
+            g.drawImage( bi, 0, 0, this );
+        }
+    }
+
+    /// Classes made to avoid repetitive calls... (being lazy)
+    private final class ButtonV2 extends JButton {
+        public ButtonV2( String name, ActionListener al ) {
+            super( name );
+            this.addActionListener( al );
+        }
+    }
+
+    private final class ChoiceV2 extends JComboBox {
+
+        private BitSet bitSet = null;
+
+        public ChoiceV2() {;}
+
+        public ChoiceV2( ActionListener al ) {
+            super();
+            this.addActionListener( al );
+        }
+
+        public ChoiceV2( ActionListener al, boolean fontChoice) {
+            this(al);
+            if(fontChoice) {
+                //Register this component in ToolTipManager
+                setToolTipText("");
+                bitSet = new BitSet();
+                setRenderer(new ChoiceV2Renderer(this));
+            }
+        }
+
+        public String getToolTipText() {
+            int index = this.getSelectedIndex();
+            String fontName = (String) this.getSelectedItem();
+            if(fontName != null &&
+               (textMenu.getSelectedIndex() == fp.RANGE_TEXT)) {
+                if (getBit(index)) {
+                    return "Font \"" + fontName + "\" can display some characters in \"" +
+                        rm.getSelectedItem() + "\" range";
+                }
+                else {
+                    return "Font \"" + fontName + "\" cannot display any characters in \"" +
+                        rm.getSelectedItem() + "\" range";
+                }
+            }
+            return super.getToolTipText();
+        }
+
+        public void setBit(int bitIndex, boolean value) {
+            bitSet.set(bitIndex, value);
+        }
+
+        public boolean getBit(int bitIndex) {
+            return bitSet.get(bitIndex);
+        }
+    }
+
+    private final class ChoiceV2Renderer extends DefaultListCellRenderer {
+
+        private ImageIcon yesImage, blankImage;
+        private ChoiceV2 choice = null;
+
+        public ChoiceV2Renderer(ChoiceV2 choice) {
+            BufferedImage yes =
+                new BufferedImage(10, 10, BufferedImage.TYPE_INT_ARGB);
+            Graphics2D g = yes.createGraphics();
+            g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+                               RenderingHints.VALUE_ANTIALIAS_ON);
+            g.setColor(Color.BLUE);
+            g.drawLine(0, 5, 3, 10);
+            g.drawLine(1, 5, 4, 10);
+            g.drawLine(3, 10, 10, 0);
+            g.drawLine(4, 9, 9, 0);
+            g.dispose();
+            BufferedImage blank =
+                new BufferedImage(10, 10, BufferedImage.TYPE_INT_ARGB);
+            yesImage = new ImageIcon(yes);
+            blankImage = new ImageIcon(blank);
+            this.choice = choice;
+        }
+
+        public Component getListCellRendererComponent(JList list,
+                                                      Object value,
+                                                      int index,
+                                                      boolean isSelected,
+                                                      boolean cellHasFocus) {
+
+            if(textMenu.getSelectedIndex() == fp.RANGE_TEXT) {
+
+                super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
+
+                //For JComboBox if index is -1, its rendering the selected index.
+                if(index == -1) {
+                    index = choice.getSelectedIndex();
+                }
+
+                if(choice.getBit(index)) {
+                    setIcon(yesImage);
+                }
+                else {
+                    setIcon(blankImage);
+                }
+
+            } else {
+                super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
+                setIcon(blankImage);
+            }
+
+            return this;
+        }
+    }
+
+    private final class LabelV2 extends JLabel {
+        public LabelV2( String name ) {
+            super( name );
+        }
+    }
+
+    private final class MenuItemV2 extends JMenuItem {
+        public MenuItemV2( String name, ActionListener al ) {
+            super( name );
+            this.addActionListener( al );
+        }
+    }
+
+    private final class CheckboxMenuItemV2 extends JCheckBoxMenuItem {
+        public CheckboxMenuItemV2( String name, boolean b, ItemListener il ) {
+            super( name, b );
+            this.addItemListener( il );
+        }
+    }
+}