/
githubmirror
/
panama-vector
Обзор
Документация
Войти
/
githubmirror
/
panama-vector
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/demo/share/jfc/Font2DTest/Font2DTest.java
1 600 строк
62 KB
Phil Race
8332416: Add more font selection options to Font2DTest
26 май 2024, 02:17
26 май 2024, 02:17
97ee2ff
Код
Авторство
О чём код?
/* * 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 ); } } }