blob: e474e638105e3132e8da006ae8481415600747b2 [file]
/*
* Copyright (c) 1999, 2024, Oracle and/or its affiliates. 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 Oracle 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.
*/
/*
* This source code is provided to illustrate the usage of a given feature
* or technique and has been deliberately simplified. Additional steps
* required for a production-quality application, such as security checks,
* input validation and proper error handling, might not be present in
* this sample code.
*/
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.lang.reflect.InvocationTargetException;
import java.util.StringTokenizer;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.plaf.nimbus.NimbusLookAndFeel;
import static java.nio.charset.StandardCharsets.UTF_16;
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 JPanel fontMenuPanel;
private JPanel stylePanel;
private LabelV2 fontMenuLabel = null;
private LabelV2 styleLabel = null;
private ChoiceV2 fontNameMenu;
private ChoiceV2 fontSubFamilyMenu;
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<FontPanel.AAValues> antiAliasMenu;
private final JComboBox<FontPanel.FMValues> fracMetricsMenu;
private final JSlider contrastSlider;
/// CheckboxMenuItems
private CheckboxMenuItemV2 displayGridCBMI;
private CheckboxMenuItemV2 force16ColsCBMI;
private CheckboxMenuItemV2 showFontInfoCBMI;
private JRadioButtonMenuItem familyAndStyleRBMI;
private JRadioButtonMenuItem familyAndSubFamilyRBMI;
private JRadioButtonMenuItem fontNameRBMI;
/// 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 String currentFontName = Font.DIALOG;
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;
private static final Locale l = Locale.getDefault();
/// Initialize GUI variables and its layouts
public Font2DTest( JFrame f) {
parent = f;
rm = new RangeMenu( this, parent );
fp = new FontPanel( this, parent );
statusBar = new LabelV2("");
fontMenu = new ChoiceV2( this, canDisplayCheck );
fontNameMenu = new ChoiceV2( this, false );
fontSubFamilyMenu = new ChoiceV2( this, false );
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<>(FontPanel.AAValues.values());
antiAliasMenu.addActionListener(this);
fracMetricsMenu =
new JComboBox<>(FontPanel.FMValues.values());
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();
setupDialog();
if(canDisplayCheck) {
fireRangeChanged();
}
}
private void addFontMenuToGBL(String labelText,
JComponent menuContainer,
GridBagLayout gbl,
GridBagConstraints gbc,
int leftInset,
Container target) {
fontMenuLabel = new LabelV2(labelText);
fontMenuLabel.setLabelFor(menuContainer);
GridBagConstraints gbcLabel = (GridBagConstraints) gbc.clone();
gbcLabel.insets = new Insets(2, leftInset, 2, 0);
gbcLabel.gridwidth = 1;
gbcLabel.weightx = 0;
gbcLabel.anchor = GridBagConstraints.EAST;
gbl.setConstraints(fontMenuLabel, gbcLabel);
target.add(fontMenuLabel);
gbl.setConstraints(menuContainer, gbc);
target.add( menuContainer );
}
private void addStyleMenuToGBL(String labelText,
JComponent menuContainer,
GridBagLayout gbl,
GridBagConstraints gbc,
int leftInset,
Container target) {
styleLabel = new LabelV2(labelText);
styleLabel.setLabelFor(menuContainer);
GridBagConstraints gbcLabel = (GridBagConstraints) gbc.clone();
gbcLabel.insets = new Insets(2, leftInset, 2, 0);
gbcLabel.gridwidth = 1;
gbcLabel.weightx = 0;
gbcLabel.anchor = GridBagConstraints.EAST;
gbl.setConstraints(styleLabel, gbcLabel);
target.add(styleLabel);
gbl.setConstraints(menuContainer, gbc);
target.add(menuContainer);
}
/// 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 );
fontMenuPanel = new JPanel();
fontMenuPanel.setLayout(new GridLayout());
fontMenuPanel.add(fontMenu);
addFontMenuToGBL(FAMILY_LABEL_TEXT, fontMenuPanel, gbl, gbc, 2, this );
stylePanel = new JPanel();
stylePanel.setLayout(new GridLayout());
stylePanel.add(styleMenu);
addStyleMenuToGBL(STYLE_LABEL_TEXT, stylePanel, gbl, gbc, 40, this );
gbc.gridwidth = GridBagConstraints.REMAINDER;
addLabeledComponentToGBL( "Font Transform:",
transformMenu, gbl, gbc, 20, this );
gbc.gridwidth = 1;
addLabeledComponentToGBL( "Range: ", rm, gbl, gbc, 2, this );
addLabeledComponentToGBL( "Size: ", sizeField, gbl, gbc, 40, this );
gbc.gridwidth = GridBagConstraints.REMAINDER;
addLabeledComponentToGBL( "Graphics Transform: ",
transformMenuG2, gbl, gbc, 20, this );
gbc.gridwidth = 1;
gbc.anchor = GridBagConstraints.WEST;
addLabeledComponentToGBL( "Method: ", methodsMenu, gbl, gbc, 2, this );
addLabeledComponentToGBL("", null, gbl, gbc, 40, this);
gbc.anchor = GridBagConstraints.EAST;
gbc.gridwidth = GridBagConstraints.REMAINDER;
addLabeledComponentToGBL( "Text to use:", textMenu, gbl, gbc, 20, this );
gbc.gridwidth = 1;
gbc.anchor = GridBagConstraints.WEST;
addLabeledComponentToGBL("LCD contrast: ",
contrastSlider, gbl, gbc, 2, this);
gbc.gridwidth = 1;
addLabeledComponentToGBL("Antialiasing: ",
antiAliasMenu, gbl, gbc, 40, this);
gbc.anchor = GridBagConstraints.EAST;
gbc.gridwidth = GridBagConstraints.REMAINDER;
addLabeledComponentToGBL("Fractional metrics: ",
fracMetricsMenu, gbl, gbc, 20, 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,
int leftInset,
Container target ) {
LabelV2 l = new LabelV2( name );
l.setLabelFor(c);
GridBagConstraints gbcLabel = (GridBagConstraints) gbc.clone();
if (gbcLabel.gridwidth == GridBagConstraints.REMAINDER) {
gbcLabel.gridwidth = GridBagConstraints.RELATIVE;
}
gbcLabel.insets = new Insets( 2, leftInset, 2, 0 );
gbcLabel.gridwidth = 1;
gbcLabel.weightx = 0;
if ( c == null )
c = new JLabel( "" );
gbcLabel.anchor = GridBagConstraints.EAST;
gbl.setConstraints( l, gbcLabel );
target.add( l );
gbl.setConstraints( c, gbc );
target.add( c );
}
/// Sets up menu entries
private void setupMenu() {
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();
fileMenu.add( new MenuItemV2( "Exit", 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 );
optionMenu.addSeparator();
familyAndStyleRBMI = new JRadioButtonMenuItem("Select font using Family Name and Style");
familyAndStyleRBMI.addActionListener(this);
familyAndSubFamilyRBMI = new JRadioButtonMenuItem("Select font using Family Name and SubFamily");
familyAndSubFamilyRBMI.addActionListener(this);
fontNameRBMI = new JRadioButtonMenuItem("Select font using Full Name");
fontNameRBMI.addActionListener(this);
ButtonGroup bg = new ButtonGroup();
bg.add(familyAndStyleRBMI);
bg.add(familyAndSubFamilyRBMI);
bg.add(fontNameRBMI);
familyAndStyleRBMI.setSelected(true);
optionMenu.add(familyAndStyleRBMI);
optionMenu.add(familyAndSubFamilyRBMI);
optionMenu.add(fontNameRBMI);
JMenuBar mb = parent.getJMenuBar();
if ( mb == null )
mb = new JMenuBar();
mb.add( fileMenu );
mb.add( optionMenu );
parent.setJMenuBar( mb );
String[] fontList = getAllFamilyNames();
for (int i = 0; i < fontList.length; i++ ) {
fontMenu.addItem( fontList[i] );
}
fontMenu.setSelectedItem("Dialog");
fontList = getAllFontNames();
for (int i = 0; i < fontList.length; i++ ) {
fontNameMenu.addItem( fontList[i] );
}
fontNameMenu.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() {
filePromptDialog = new JFileChooser();
/// 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.setVisible(false);
}
});
/// 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.setVisible(false);
}
});
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.setVisible(false);
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 ) {
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 = 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;
String[] 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" +
fontSelectionType + "\n" +
(String)fontMenu.getSelectedItem() + "\n" +
(String)fontNameMenu.getSelectedItem() + "\n" +
(String)fontSubFamilyMenu.getSelectedItem() + "\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.setVisible(true);
else
userTextDialog.setVisible(false);
/// 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() );
int fontSelType = Integer.parseInt( perLine.nextToken() );
String fmItem = perLine.nextToken();
String fnmItem = perLine.nextToken();
String fsmItem = 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 );
currentFontName = fontNameOpt;
setFontSelectionType(fontSelType);
fontMenu.setSelectedItem( fmItem );
fontNameMenu.setSelectedItem( fnmItem );
fontSubFamilyMenu.setSelectedItem( fsmItem );
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.setVisible(true);
}
else
fontInfoDialog.setVisible(false);
}
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.setVisible(true);
}
catch ( Exception ex ) {
fireChangeStatus( "ERROR: Failed to Load PNG File; See Stack Trace", true );
ex.printStackTrace();
}
}
static final int FAMILY_AND_STYLE = 1;
static final int FONT_NAME = 2;
static final int FAMILY_AND_SUBFAMILY = 3;
static int fontSelectionType = FAMILY_AND_STYLE;
static final String FAMILY_LABEL_TEXT = "Font Family:";
static final String NAME_LABEL_TEXT = "Font Name:";
static final String STYLE_LABEL_TEXT = "Style:";
static final String SUBFAMILY_LABEL_TEXT = "Subfamily:";
void setUseFamilyAndStyle() {
if (fontSelectionType == FAMILY_AND_STYLE) {
return;
}
fontMenuLabel.setText(FAMILY_LABEL_TEXT);
fontMenuPanel.removeAll();
fontMenuPanel.add(fontMenu);
if (fontSelectionType == FAMILY_AND_SUBFAMILY) {
styleLabel.setText(STYLE_LABEL_TEXT);
stylePanel.removeAll();
stylePanel.add(styleMenu);
}
fontSelectionType = FAMILY_AND_STYLE;
if (!familyAndStyleRBMI.isSelected()) {
familyAndStyleRBMI.setSelected(true);
}
styleMenu.setSelectedIndex(0);
currentFontName = (String)fontMenu.getSelectedItem();
fp.setFontParams(currentFontName,
Float.parseFloat(sizeField.getText()),
0, // want to reset style to PLAIN
transformMenu.getSelectedIndex());
revalidate();
repaint();
}
void setUseFontName() {
if (fontSelectionType == FONT_NAME) {
return;
}
fontMenuLabel.setText(NAME_LABEL_TEXT);
fontMenuPanel.removeAll();
fontMenuPanel.add(fontNameMenu);
if (fontSelectionType == FAMILY_AND_SUBFAMILY) {
styleLabel.setText(STYLE_LABEL_TEXT);
stylePanel.removeAll();
stylePanel.add(styleMenu);
}
fontSelectionType = FONT_NAME;
if (!fontNameRBMI.isSelected()) {
fontNameRBMI.setSelected(true);
}
styleMenu.setSelectedIndex(0);
currentFontName = (String)fontNameMenu.getSelectedItem();
fp.setFontParams(currentFontName,
Float.parseFloat(sizeField.getText()),
0, // want to reset style to PLAIN
transformMenu.getSelectedIndex());
revalidate();
repaint();
}
void setUseFamilyAndSubFamily() {
if (fontSelectionType == FAMILY_AND_SUBFAMILY) {
return;
}
fontMenuLabel.setText(FAMILY_LABEL_TEXT);
fontMenuPanel.removeAll();
fontMenuPanel.add(fontMenu);
styleLabel.setText(SUBFAMILY_LABEL_TEXT);
stylePanel.removeAll();
styleMenu.setSelectedIndex(0);
String family = (String)fontMenu.getSelectedItem();
updateSubFamilyMenu(family);
stylePanel.add(fontSubFamilyMenu);
fontSelectionType = FAMILY_AND_SUBFAMILY;
if (!familyAndSubFamilyRBMI.isSelected()) {
familyAndSubFamilyRBMI.setSelected(true);
}
String subname = (String)fontSubFamilyMenu.getSelectedItem();
Font font = FontFamily.getFont(family, subname);
currentFontName = (font != null) ? font.getFontName(l) : family;
fp.setFontParams(currentFontName,
Float.parseFloat(sizeField.getText()),
0, // want to reset style to PLAIN
transformMenu.getSelectedIndex());
revalidate();
repaint();
}
void setFontSelectionType(int fsType) {
switch (fsType) {
case FAMILY_AND_STYLE :
setUseFamilyAndStyle();
break;
case FONT_NAME :
setUseFontName();
break;
case FAMILY_AND_SUBFAMILY :
setUseFamilyAndSubFamily();
break;
}
}
/// 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 (source == familyAndStyleRBMI) {
setUseFamilyAndStyle();
} else if (source == familyAndSubFamilyRBMI) {
setUseFamilyAndSubFamily();
} else if (source == fontNameRBMI) {
setUseFontName();
}
else 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.setVisible(true);
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(currentFontName,
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.setVisible(false);
fp.doPrint( i );
}
}
else if ( itemName.equals( "Cancel" ))
printDialog.setVisible(false);
/// 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 == fontNameMenu || c == fontSubFamilyMenu ||
c == styleMenu || c == transformMenu )
{
if (c == fontNameMenu) {
currentFontName = (String)fontNameMenu.getSelectedItem();
}
else if ((c == fontMenu) && (fontSelectionType == FAMILY_AND_STYLE)) {
currentFontName = (String)fontMenu.getSelectedItem();
}
else if ((c == fontMenu) && (fontSelectionType == FAMILY_AND_SUBFAMILY)) {
String family = (String)fontMenu.getSelectedItem();
updateSubFamilyMenu(family);
String subname = (String)fontSubFamilyMenu.getSelectedItem();
Font font = FontFamily.getFont(family, subname);
if (font == null) return;
currentFontName = font.getFontName(l);
}
else if (c == fontSubFamilyMenu) {
/*
* When switching families, all items are removed from the sub family list.
* This triggers a synchronous recursive ActionEvent on the EDT, which should
* be ignored here, the code removes them adds the new items and will then
* use the new default selected item.
* If we do not return, we'll not find a match and can get an NPE.
* This feels unsatisfactory, but it works.
*/
if (fontSubFamilyMenu.getItemCount() == 0) {
return;
}
String family = (String)fontMenu.getSelectedItem();
String subname = (String)fontSubFamilyMenu.getSelectedItem();
Font font = FontFamily.getFont(family, subname);
if (font == null) return;
currentFontName = font.getFontName(l);
}
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(currentFontName,
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.setVisible(true);
}
else
fontInfoDialog.setVisible(false);
}
}
}
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);
}
static class FontFamily {
static Map<String, FontFamily> familyMap = new HashMap<>();
private static Locale l = Locale.getDefault();
private List<Font> fonts = new ArrayList<>();
private List<String> subFamilyNames = new ArrayList<>();
private Map<String, Font> nameToFontMap = new HashMap<>();
private String familyName;
private FontFamily(String name) {
this.familyName = name;
}
String stripFamily(String family, String fullName) {
if (family.equals(fullName)) {
return "";
}
char[] familyChars = family.toCharArray();
char[] fullChars = fullName.toCharArray();
int familyIndex = 0;
int fullIndex = 0;
// there's probably a clever regexp way to do this
// iterate over the chars in the family , if they are the same
// keep going, if there's a '-' or ' ', skip it. In the font name,
// do the same. If you reach the end of the family without some
// other diff, return what's left of the fullName.
while (familyIndex < familyChars.length && fullIndex < fullChars.length) {
//while (familyIndex < familyChars.length) {
if (fullIndex == fullChars.length) {
System.err.println("WEIRD FONT " + family + " " + fullName);
break;
}
if (familyChars[familyIndex] == fullChars[fullIndex]) {
familyIndex++; fullIndex++;
}
else if (familyChars[familyIndex] == ' ' && fullChars[fullIndex] == '-') {
familyIndex++; fullIndex++;
}
else if (familyChars[familyIndex] == '-' && fullChars[fullIndex] == ' ') {
familyIndex++; fullIndex++;
}
else if (familyChars[familyIndex] == ' ' || familyChars[familyIndex] == '-') {
familyIndex++;
}
else if (fullChars[fullIndex] == ' ' || fullChars[fullIndex] == '-') {
fullIndex++;
} else {
break;
}
}
if (fullIndex == fullChars.length) {
return fullName;
} else {
return fullName.substring(fullIndex);
}
}
/*
* Getting the string to display here can be an art.
* If the family is "Arial Black", then for a regular font, the
* full name may be "Arial Black", or "Arial-Black", as reported on macOS.
* For this case for the specific font might want to display the
* full name, or synthesise "Regular". But to do that we have to
* recognise that ' ' to '-' mapping.
* For "Arial Black Italic" (Arial-Black-Italic) we want to be able to
* trim so we display just "Italic".
* Then we need to be able to map the text selection back to the
* right font.
*/
void add(Font f) {
String fontName = f.getFontName(l);
int flen = familyName.length();
int nlen = fontName.length();
String sfn;
if (fontName.equals(familyName)) {
sfn = "Regular";
}
else {
sfn = stripFamily(familyName, fontName);
sfn = sfn.replace('-', ' ');
}
fonts.add(f);
subFamilyNames.add(sfn);
nameToFontMap.put(sfn, f);
}
String[] getSubFamilyNames() {
return subFamilyNames.stream().sorted().toArray(String[]::new);
}
Font getFontForSubFamilyName(String name) {
return nameToFontMap.get(name);
}
static FontFamily getFontFamily(String name) {
return familyMap.get(name);
}
static FontFamily createFontFamily(String name) {
FontFamily f = familyMap.get(name);
if (f == null) {
f = new FontFamily(name);
familyMap.put(name, f);
}
return f;
}
/*
* familyName must be a name of an existing FontFamily
* name, must be a valid "subFamilyName" within that FontFamily
* as returned by getSubFamilyNames()
*/
static Font getFont(String familyName, String subFamilyName) {
FontFamily family = getFontFamily(familyName);
return family.getFontForSubFamilyName(subFamilyName);
}
}
static String[] familyNames;
static Font[] allFonts;
static List<String> allFontNames;
static Map<String, FontFamily> familyMap = new HashMap<>();
private static void buildFontInfo() {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
familyNames = ge.getAvailableFontFamilyNames();
allFonts = ge.getAllFonts();
allFontNames = new ArrayList<String>();
Locale l = Locale.getDefault();
for (Font f : allFonts) {
allFontNames.add(f.getFontName(l));
String family = f.getFamily(l);
FontFamily ff = FontFamily.getFontFamily(family);
if (ff == null) {
ff = FontFamily.createFontFamily(family);
}
ff.add(f);
}
}
String getFontNameFor(String family, String subFamily) {
return family + " " + subFamily;
}
void updateSubFamilyMenu(String name) {
FontFamily family = FontFamily.getFontFamily(name);
fontSubFamilyMenu.removeAllItems();
String [] sfNames = family.getSubFamilyNames();
for (int i=0; i<sfNames.length; i++) {
fontSubFamilyMenu.addItem(sfNames[i]);
}
fontSubFamilyMenu.setSelectedIndex(0); // better be at least one !
}
static String[] getAllFontNames() {
return allFontNames.stream().sorted().toArray(String[]::new);
}
static String[] getAllFamilyNames() {
return familyNames;
}
/// 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();
}
}
buildFontInfo();
try {
UIManager.setLookAndFeel(new NimbusLookAndFeel());
SwingUtilities.invokeAndWait(() -> {
final JFrame f = new JFrame( "Font2DTest" );
final Font2DTest f2dt = new Font2DTest( f);
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.setVisible(true);
});
} catch (UnsupportedLookAndFeelException|InterruptedException|InvocationTargetException e) {
}
}
/// 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<String> {
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 );
}
}
}