1 /* 2 * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * - Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 11 * - Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * - Neither the name of Oracle nor the names of its 16 * contributors may be used to endorse or promote products derived 17 * from this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * This source code is provided to illustrate the usage of a given feature 34 * or technique and has been deliberately simplified. Additional steps 35 * required for a production-quality application, such as security checks, 36 * input validation and proper error handling, might not be present in 37 * this sample code. 38 */ 39 40 41 42 import java.awt.Color; 43 import java.awt.Component; 44 import java.awt.BorderLayout; 45 import java.awt.CheckboxGroup; 46 import java.awt.Container; 47 import java.awt.Dimension; 48 import java.awt.Font; 49 import java.awt.Graphics; 50 import java.awt.Graphics2D; 51 import java.awt.GraphicsEnvironment; 52 import java.awt.GridBagConstraints; 53 import java.awt.GridBagLayout; 54 import java.awt.GridLayout; 55 import java.awt.Insets; 56 import java.awt.RenderingHints; 57 import java.awt.Toolkit; 58 import java.awt.event.ActionEvent; 59 import java.awt.event.ActionListener; 60 import java.awt.event.ItemEvent; 61 import java.awt.event.ItemListener; 62 import java.awt.event.WindowAdapter; 63 import java.awt.event.WindowEvent; 64 import java.awt.image.BufferedImage; 65 import java.io.BufferedInputStream; 66 import java.io.BufferedOutputStream; 67 import java.io.File; 68 import java.io.FileInputStream; 69 import java.io.FileOutputStream; 70 import java.util.StringTokenizer; 71 import java.util.BitSet; 72 import javax.swing.*; 73 import javax.swing.event.*; 74 75 /** 76 * Font2DTest.java 77 * 78 * @author Shinsuke Fukuda 79 * @author Ankit Patel [Conversion to Swing - 01/07/30] 80 */ 81 82 /// Main Font2DTest Class 83 84 public final class Font2DTest extends JPanel 85 implements ActionListener, ItemListener, ChangeListener { 86 87 /// JFrame that will contain Font2DTest 88 private final JFrame parent; 89 /// FontPanel class that will contain all graphical output 90 private final FontPanel fp; 91 /// RangeMenu class that contains info about the unicode ranges 92 private final RangeMenu rm; 93 94 /// Other menus to set parameters for text drawing 95 private final ChoiceV2 fontMenu; 96 private final JTextField sizeField; 97 private final ChoiceV2 styleMenu; 98 private final ChoiceV2 textMenu; 99 private int currentTextChoice = 0; 100 private final ChoiceV2 transformMenu; 101 private final ChoiceV2 transformMenuG2; 102 private final ChoiceV2 methodsMenu; 103 private final JComboBox<FontPanel.AAValues> antiAliasMenu; 104 private final JComboBox<FontPanel.FMValues> fracMetricsMenu; 105 106 private final JSlider contrastSlider; 107 108 /// CheckboxMenuItems 109 private CheckboxMenuItemV2 displayGridCBMI; 110 private CheckboxMenuItemV2 force16ColsCBMI; 111 private CheckboxMenuItemV2 showFontInfoCBMI; 112 113 /// JDialog boxes 114 private JDialog userTextDialog; 115 private JTextArea userTextArea; 116 private JDialog printDialog; 117 private JDialog fontInfoDialog; 118 private LabelV2[] fontInfos = new LabelV2[2]; 119 private JFileChooser filePromptDialog = null; 120 121 private ButtonGroup printCBGroup; 122 private JRadioButton[] printModeCBs = new JRadioButton[3]; 123 124 /// Status bar 125 private final LabelV2 statusBar; 126 127 private int[] fontStyles = {Font.PLAIN, Font.BOLD, Font.ITALIC, Font.BOLD | Font.ITALIC}; 128 129 /// Text filename 130 private String tFileName; 131 132 // Enabled or disabled status of canDisplay check 133 private static boolean canDisplayCheck = true; 134 135 /// Initialize GUI variables and its layouts 136 public Font2DTest( JFrame f, boolean isApplet ) { 137 parent = f; 138 139 rm = new RangeMenu( this, parent ); 140 fp = new FontPanel( this, parent ); 141 statusBar = new LabelV2(""); 142 143 fontMenu = new ChoiceV2( this, canDisplayCheck ); 144 sizeField = new JTextField( "12", 3 ); 145 sizeField.addActionListener( this ); 146 styleMenu = new ChoiceV2( this ); 147 textMenu = new ChoiceV2( ); // listener added later 148 transformMenu = new ChoiceV2( this ); 149 transformMenuG2 = new ChoiceV2( this ); 150 methodsMenu = new ChoiceV2( this ); 151 152 antiAliasMenu = 153 new JComboBox<>(FontPanel.AAValues.values()); 154 antiAliasMenu.addActionListener(this); 155 fracMetricsMenu = 156 new JComboBox<>(FontPanel.FMValues.values()); 157 fracMetricsMenu.addActionListener(this); 158 159 contrastSlider = new JSlider(JSlider.HORIZONTAL, 100, 250, 160 FontPanel.getDefaultLCDContrast().intValue()); 161 contrastSlider.setEnabled(false); 162 contrastSlider.setMajorTickSpacing(20); 163 contrastSlider.setMinorTickSpacing(10); 164 contrastSlider.setPaintTicks(true); 165 contrastSlider.setPaintLabels(true); 166 contrastSlider.addChangeListener(this); 167 setupPanel(); 168 setupMenu( isApplet ); 169 setupDialog( isApplet ); 170 171 if(canDisplayCheck) { 172 fireRangeChanged(); 173 } 174 } 175 176 /// Set up the main interface panel 177 private void setupPanel() { 178 GridBagLayout gbl = new GridBagLayout(); 179 GridBagConstraints gbc = new GridBagConstraints(); 180 gbc.fill = GridBagConstraints.HORIZONTAL; 181 gbc.weightx = 1; 182 gbc.insets = new Insets( 2, 0, 2, 2 ); 183 this.setLayout( gbl ); 184 185 addLabeledComponentToGBL( "Font: ", fontMenu, gbl, gbc, this ); 186 addLabeledComponentToGBL( "Size: ", sizeField, gbl, gbc, this ); 187 gbc.gridwidth = GridBagConstraints.REMAINDER; 188 addLabeledComponentToGBL( "Font Transform:", 189 transformMenu, gbl, gbc, this ); 190 gbc.gridwidth = 1; 191 192 addLabeledComponentToGBL( "Range: ", rm, gbl, gbc, this ); 193 addLabeledComponentToGBL( "Style: ", styleMenu, gbl, gbc, this ); 194 gbc.gridwidth = GridBagConstraints.REMAINDER; 195 addLabeledComponentToGBL( "Graphics Transform: ", 196 transformMenuG2, gbl, gbc, this ); 197 gbc.gridwidth = 1; 198 199 gbc.anchor = GridBagConstraints.WEST; 200 addLabeledComponentToGBL( "Method: ", methodsMenu, gbl, gbc, this ); 201 addLabeledComponentToGBL("", null, gbl, gbc, this); 202 gbc.anchor = GridBagConstraints.EAST; 203 gbc.gridwidth = GridBagConstraints.REMAINDER; 204 addLabeledComponentToGBL( "Text to use:", textMenu, gbl, gbc, this ); 205 206 gbc.weightx=1; 207 gbc.gridwidth = 1; 208 gbc.fill = GridBagConstraints.HORIZONTAL; 209 gbc.anchor = GridBagConstraints.WEST; 210 addLabeledComponentToGBL("LCD contrast: ", 211 contrastSlider, gbl, gbc, this); 212 213 gbc.gridwidth = 1; 214 gbc.fill = GridBagConstraints.NONE; 215 addLabeledComponentToGBL("Antialiasing: ", 216 antiAliasMenu, gbl, gbc, this); 217 218 gbc.anchor = GridBagConstraints.EAST; 219 gbc.gridwidth = GridBagConstraints.REMAINDER; 220 addLabeledComponentToGBL("Fractional metrics: ", 221 fracMetricsMenu, gbl, gbc, this); 222 223 gbc.weightx = 1; 224 gbc.weighty = 1; 225 gbc.anchor = GridBagConstraints.WEST; 226 gbc.insets = new Insets( 2, 0, 0, 2 ); 227 gbc.fill = GridBagConstraints.BOTH; 228 gbl.setConstraints( fp, gbc ); 229 this.add( fp ); 230 231 gbc.weighty = 0; 232 gbc.insets = new Insets( 0, 2, 0, 0 ); 233 gbl.setConstraints( statusBar, gbc ); 234 this.add( statusBar ); 235 } 236 237 /// Adds a component to a container with a label to its left in GridBagLayout 238 private void addLabeledComponentToGBL( String name, 239 JComponent c, 240 GridBagLayout gbl, 241 GridBagConstraints gbc, 242 Container target ) { 243 LabelV2 l = new LabelV2( name ); 244 GridBagConstraints gbcLabel = (GridBagConstraints) gbc.clone(); 245 gbcLabel.insets = new Insets( 2, 2, 2, 0 ); 246 gbcLabel.gridwidth = 1; 247 gbcLabel.weightx = 0; 248 249 if ( c == null ) 250 c = new JLabel( "" ); 251 252 gbl.setConstraints( l, gbcLabel ); 253 target.add( l ); 254 gbl.setConstraints( c, gbc ); 255 target.add( c ); 256 } 257 258 /// Sets up menu entries 259 private void setupMenu( boolean isApplet ) { 260 JMenu fileMenu = new JMenu( "File" ); 261 JMenu optionMenu = new JMenu( "Option" ); 262 263 fileMenu.add( new MenuItemV2( "Save Selected Options...", this )); 264 fileMenu.add( new MenuItemV2( "Load Options...", this )); 265 fileMenu.addSeparator(); 266 fileMenu.add( new MenuItemV2( "Save as PNG...", this )); 267 fileMenu.add( new MenuItemV2( "Load PNG File to Compare...", this )); 268 fileMenu.add( new MenuItemV2( "Page Setup...", this )); 269 fileMenu.add( new MenuItemV2( "Print...", this )); 270 fileMenu.addSeparator(); 271 if ( !isApplet ) 272 fileMenu.add( new MenuItemV2( "Exit", this )); 273 else 274 fileMenu.add( new MenuItemV2( "Close", this )); 275 276 displayGridCBMI = new CheckboxMenuItemV2( "Display Grid", true, this ); 277 force16ColsCBMI = new CheckboxMenuItemV2( "Force 16 Columns", false, this ); 278 showFontInfoCBMI = new CheckboxMenuItemV2( "Display Font Info", false, this ); 279 optionMenu.add( displayGridCBMI ); 280 optionMenu.add( force16ColsCBMI ); 281 optionMenu.add( showFontInfoCBMI ); 282 283 JMenuBar mb = parent.getJMenuBar(); 284 if ( mb == null ) 285 mb = new JMenuBar(); 286 mb.add( fileMenu ); 287 mb.add( optionMenu ); 288 289 parent.setJMenuBar( mb ); 290 291 String[] fontList = 292 GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames(); 293 294 for ( int i = 0; i < fontList.length; i++ ) 295 fontMenu.addItem( fontList[i] ); 296 fontMenu.setSelectedItem( "Dialog" ); 297 298 styleMenu.addItem( "Plain" ); 299 styleMenu.addItem( "Bold" ); 300 styleMenu.addItem( "Italic" ); 301 styleMenu.addItem( "Bold Italic" ); 302 303 transformMenu.addItem( "None" ); 304 transformMenu.addItem( "Scale" ); 305 transformMenu.addItem( "Shear" ); 306 transformMenu.addItem( "Rotate" ); 307 308 transformMenuG2.addItem( "None" ); 309 transformMenuG2.addItem( "Scale" ); 310 transformMenuG2.addItem( "Shear" ); 311 transformMenuG2.addItem( "Rotate" ); 312 313 methodsMenu.addItem( "drawString" ); 314 methodsMenu.addItem( "drawChars" ); 315 methodsMenu.addItem( "drawBytes" ); 316 methodsMenu.addItem( "drawGlyphVector" ); 317 methodsMenu.addItem( "TextLayout.draw" ); 318 methodsMenu.addItem( "GlyphVector.getOutline + draw" ); 319 methodsMenu.addItem( "TextLayout.getOutline + draw" ); 320 321 textMenu.addItem( "Unicode Range" ); 322 textMenu.addItem( "All Glyphs" ); 323 textMenu.addItem( "User Text" ); 324 textMenu.addItem( "Text File" ); 325 textMenu.addActionListener ( this ); // listener added later so unneeded events not thrown 326 } 327 328 /// Sets up the all dialogs used in Font2DTest... 329 private void setupDialog( boolean isApplet ) { 330 if (!isApplet) 331 filePromptDialog = new JFileChooser( ); 332 else 333 filePromptDialog = null; 334 335 /// Prepare user text dialog... 336 userTextDialog = new JDialog( parent, "User Text", false ); 337 JPanel dialogTopPanel = new JPanel(); 338 JPanel dialogBottomPanel = new JPanel(); 339 LabelV2 message1 = new LabelV2( "Enter text below and then press update" ); 340 LabelV2 message2 = new LabelV2( "(Unicode char can be denoted by \\uXXXX)" ); 341 LabelV2 message3 = new LabelV2( "(Supplementary chars can be denoted by \\UXXXXXX)" ); 342 userTextArea = new JTextArea( "Font2DTest!" ); 343 ButtonV2 bUpdate = new ButtonV2( "Update", this ); 344 userTextArea.setFont( new Font( "dialog", Font.PLAIN, 12 )); 345 dialogTopPanel.setLayout( new GridLayout( 3, 1 )); 346 dialogTopPanel.add( message1 ); 347 dialogTopPanel.add( message2 ); 348 dialogTopPanel.add( message3 ); 349 dialogBottomPanel.add( bUpdate ); 350 //ABP 351 JScrollPane userTextAreaSP = new JScrollPane(userTextArea); 352 userTextAreaSP.setPreferredSize(new Dimension(300, 100)); 353 354 userTextDialog.getContentPane().setLayout( new BorderLayout() ); 355 userTextDialog.getContentPane().add( "North", dialogTopPanel ); 356 userTextDialog.getContentPane().add( "Center", userTextAreaSP ); 357 userTextDialog.getContentPane().add( "South", dialogBottomPanel ); 358 userTextDialog.pack(); 359 userTextDialog.addWindowListener( new WindowAdapter() { 360 public void windowClosing( WindowEvent e ) { 361 userTextDialog.setVisible(false); 362 } 363 }); 364 365 /// Prepare printing dialog... 366 printCBGroup = new ButtonGroup(); 367 printModeCBs[ fp.ONE_PAGE ] = 368 new JRadioButton( "Print one page from currently displayed character/line", 369 true ); 370 printModeCBs[ fp.CUR_RANGE ] = 371 new JRadioButton( "Print all characters in currently selected range", 372 false ); 373 printModeCBs[ fp.ALL_TEXT ] = 374 new JRadioButton( "Print all lines of text", 375 false ); 376 LabelV2 l = 377 new LabelV2( "Note: Page range in native \"Print\" dialog will not affect the result" ); 378 JPanel buttonPanel = new JPanel(); 379 printModeCBs[ fp.ALL_TEXT ].setEnabled( false ); 380 buttonPanel.add( new ButtonV2( "Print", this )); 381 buttonPanel.add( new ButtonV2( "Cancel", this )); 382 383 printDialog = new JDialog( parent, "Print...", true ); 384 printDialog.setResizable( false ); 385 printDialog.addWindowListener( new WindowAdapter() { 386 public void windowClosing( WindowEvent e ) { 387 printDialog.setVisible(false); 388 } 389 }); 390 printDialog.getContentPane().setLayout( new GridLayout( printModeCBs.length + 2, 1 )); 391 printDialog.getContentPane().add( l ); 392 for ( int i = 0; i < printModeCBs.length; i++ ) { 393 printCBGroup.add( printModeCBs[i] ); 394 printDialog.getContentPane().add( printModeCBs[i] ); 395 } 396 printDialog.getContentPane().add( buttonPanel ); 397 printDialog.pack(); 398 399 /// Prepare font information dialog... 400 fontInfoDialog = new JDialog( parent, "Font info", false ); 401 fontInfoDialog.setResizable( false ); 402 fontInfoDialog.addWindowListener( new WindowAdapter() { 403 public void windowClosing( WindowEvent e ) { 404 fontInfoDialog.setVisible(false); 405 showFontInfoCBMI.setState( false ); 406 } 407 }); 408 JPanel fontInfoPanel = new JPanel(); 409 fontInfoPanel.setLayout( new GridLayout( fontInfos.length, 1 )); 410 for ( int i = 0; i < fontInfos.length; i++ ) { 411 fontInfos[i] = new LabelV2(""); 412 fontInfoPanel.add( fontInfos[i] ); 413 } 414 fontInfoDialog.getContentPane().add( fontInfoPanel ); 415 416 /// Move the location of the dialog... 417 userTextDialog.setLocation( 200, 300 ); 418 fontInfoDialog.setLocation( 0, 400 ); 419 } 420 421 /// RangeMenu object signals using this function 422 /// when Unicode range has been changed and text needs to be redrawn 423 public void fireRangeChanged() { 424 int[] range = rm.getSelectedRange(); 425 fp.setTextToDraw( fp.RANGE_TEXT, range, null, null ); 426 if(canDisplayCheck) { 427 setupFontList(range[0], range[1]); 428 } 429 if ( showFontInfoCBMI.getState() ) 430 fireUpdateFontInfo(); 431 } 432 433 /// Changes the message on the status bar 434 public void fireChangeStatus( String message, boolean error ) { 435 /// If this is not ran as an applet, use own status bar, 436 /// Otherwise, use the appletviewer/browser's status bar 437 statusBar.setText( message ); 438 if ( error ) 439 fp.showingError = true; 440 else 441 fp.showingError = false; 442 } 443 444 /// Updates the information about the selected font 445 public void fireUpdateFontInfo() { 446 if ( showFontInfoCBMI.getState() ) { 447 String[] infos = fp.getFontInfo(); 448 for ( int i = 0; i < fontInfos.length; i++ ) 449 fontInfos[i].setText( infos[i] ); 450 fontInfoDialog.pack(); 451 } 452 } 453 454 private void setupFontList(int rangeStart, int rangeEnd) { 455 456 int listCount = fontMenu.getItemCount(); 457 int size = 16; 458 459 try { 460 size = Float.valueOf(sizeField.getText()).intValue(); 461 } 462 catch ( Exception e ) { 463 System.out.println("Invalid font size in the size textField. Using default value of 16"); 464 } 465 466 int style = fontStyles[styleMenu.getSelectedIndex()]; 467 Font f; 468 for (int i = 0; i < listCount; i++) { 469 String fontName = fontMenu.getItemAt(i); 470 f = new Font(fontName, style, size); 471 if ((rm.getSelectedIndex() != RangeMenu.SURROGATES_AREA_INDEX) && 472 canDisplayRange(f, rangeStart, rangeEnd)) { 473 fontMenu.setBit(i, true); 474 } 475 else { 476 fontMenu.setBit(i, false); 477 } 478 } 479 480 fontMenu.repaint(); 481 } 482 483 protected boolean canDisplayRange(Font font, int rangeStart, int rangeEnd) { 484 for (int i = rangeStart; i < rangeEnd; i++) { 485 if (font.canDisplay(i)) { 486 return true; 487 } 488 } 489 return false; 490 } 491 492 /// Displays a file load/save dialog and returns the specified file 493 private String promptFile( boolean isSave, String initFileName ) { 494 int retVal; 495 String str; 496 497 /// ABP 498 if ( filePromptDialog == null) 499 return null; 500 501 if ( isSave ) { 502 filePromptDialog.setDialogType( JFileChooser.SAVE_DIALOG ); 503 filePromptDialog.setDialogTitle( "Save..." ); 504 str = "Save"; 505 506 507 } 508 else { 509 filePromptDialog.setDialogType( JFileChooser.OPEN_DIALOG ); 510 filePromptDialog.setDialogTitle( "Load..." ); 511 str = "Load"; 512 } 513 514 if (initFileName != null) 515 filePromptDialog.setSelectedFile( new File( initFileName ) ); 516 retVal = filePromptDialog.showDialog( this, str ); 517 518 if ( retVal == JFileChooser.APPROVE_OPTION ) { 519 File file = filePromptDialog.getSelectedFile(); 520 String fileName = file.getAbsolutePath(); 521 if ( fileName != null ) { 522 return fileName; 523 } 524 } 525 526 return null; 527 } 528 529 /// Converts user text into arrays of String, delimited at newline character 530 /// Also replaces any valid escape sequence with appropriate unicode character 531 /// Support \\UXXXXXX notation for surrogates 532 private String[] parseUserText( String orig ) { 533 int length = orig.length(); 534 StringTokenizer perLine = new StringTokenizer( orig, "\n" ); 535 String[] textLines = new String[ perLine.countTokens() ]; 536 int lineNumber = 0; 537 538 while ( perLine.hasMoreElements() ) { 539 StringBuffer converted = new StringBuffer(); 540 String oneLine = perLine.nextToken(); 541 int lineLength = oneLine.length(); 542 int prevEscapeEnd = 0; 543 int nextEscape = -1; 544 do { 545 int nextBMPEscape = oneLine.indexOf( "\\u", prevEscapeEnd ); 546 int nextSupEscape = oneLine.indexOf( "\\U", prevEscapeEnd ); 547 nextEscape = (nextBMPEscape < 0) 548 ? ((nextSupEscape < 0) 549 ? -1 550 : nextSupEscape) 551 : ((nextSupEscape < 0) 552 ? nextBMPEscape 553 : Math.min(nextBMPEscape, nextSupEscape)); 554 555 if ( nextEscape != -1 ) { 556 if ( prevEscapeEnd < nextEscape ) 557 converted.append( oneLine.substring( prevEscapeEnd, nextEscape )); 558 559 prevEscapeEnd = nextEscape + (nextEscape == nextBMPEscape ? 6 : 8); 560 try { 561 String hex = oneLine.substring( nextEscape + 2, prevEscapeEnd ); 562 if (nextEscape == nextBMPEscape) { 563 converted.append( (char) Integer.parseInt( hex, 16 )); 564 } else { 565 converted.append( new String( Character.toChars( Integer.parseInt( hex, 16 )))); 566 } 567 } 568 catch ( Exception e ) { 569 int copyLimit = Math.min(lineLength, prevEscapeEnd); 570 converted.append( oneLine.substring( nextEscape, copyLimit )); 571 } 572 } 573 } while (nextEscape != -1); 574 if ( prevEscapeEnd < lineLength ) 575 converted.append( oneLine.substring( prevEscapeEnd, lineLength )); 576 textLines[ lineNumber++ ] = converted.toString(); 577 } 578 return textLines; 579 } 580 581 /// Reads the text from specified file, detecting UTF-16 encoding 582 /// Then breaks the text into String array, delimited at every line break 583 private void readTextFile( String fileName ) { 584 try { 585 String fileText; 586 String[] textLines; 587 BufferedInputStream bis = 588 new BufferedInputStream( new FileInputStream( fileName )); 589 int numBytes = bis.available(); 590 if (numBytes == 0) { 591 throw new Exception("Text file " + fileName + " is empty"); 592 } 593 byte[] byteData = new byte[ numBytes ]; 594 bis.read( byteData, 0, numBytes ); 595 bis.close(); 596 597 /// If byte mark is found, then use UTF-16 encoding to convert bytes... 598 if (numBytes >= 2 && 599 (( byteData[0] == (byte) 0xFF && byteData[1] == (byte) 0xFE ) || 600 ( byteData[0] == (byte) 0xFE && byteData[1] == (byte) 0xFF ))) 601 fileText = new String( byteData, "UTF-16" ); 602 /// Otherwise, use system default encoding 603 else 604 fileText = new String( byteData ); 605 606 int length = fileText.length(); 607 StringTokenizer perLine = new StringTokenizer( fileText, "\n" ); 608 /// Determine "Return Char" used in this file 609 /// This simply finds first occurrence of CR, CR+LF or LF... 610 for ( int i = 0; i < length; i++ ) { 611 char iTh = fileText.charAt( i ); 612 if ( iTh == '\r' ) { 613 if ( i < length - 1 && fileText.charAt( i + 1 ) == '\n' ) 614 perLine = new StringTokenizer( fileText, "\r\n" ); 615 else 616 perLine = new StringTokenizer( fileText, "\r" ); 617 break; 618 } 619 else if ( iTh == '\n' ) 620 /// Use the one already created 621 break; 622 } 623 int lineNumber = 0, numLines = perLine.countTokens(); 624 textLines = new String[ numLines ]; 625 626 while ( perLine.hasMoreElements() ) { 627 String oneLine = perLine.nextToken(); 628 if ( oneLine == null ) 629 /// To make LineBreakMeasurer to return a valid TextLayout 630 /// on an empty line, simply feed it a space char... 631 oneLine = " "; 632 textLines[ lineNumber++ ] = oneLine; 633 } 634 fp.setTextToDraw( fp.FILE_TEXT, null, null, textLines ); 635 rm.setEnabled( false ); 636 methodsMenu.setEnabled( false ); 637 } 638 catch ( Exception ex ) { 639 fireChangeStatus( "ERROR: Failed to Read Text File; See Stack Trace", true ); 640 ex.printStackTrace(); 641 } 642 } 643 644 /// Returns a String storing current configuration 645 private void writeCurrentOptions( String fileName ) { 646 try { 647 String curOptions = fp.getCurrentOptions(); 648 BufferedOutputStream bos = 649 new BufferedOutputStream( new FileOutputStream( fileName )); 650 /// Prepend title and the option that is only obtainable here 651 int[] range = rm.getSelectedRange(); 652 String completeOptions = 653 ( "Font2DTest Option File\n" + 654 displayGridCBMI.getState() + "\n" + 655 force16ColsCBMI.getState() + "\n" + 656 showFontInfoCBMI.getState() + "\n" + 657 rm.getSelectedItem() + "\n" + 658 range[0] + "\n" + range[1] + "\n" + curOptions + tFileName); 659 byte[] toBeWritten = completeOptions.getBytes( "UTF-16" ); 660 bos.write( toBeWritten, 0, toBeWritten.length ); 661 bos.close(); 662 } 663 catch ( Exception ex ) { 664 fireChangeStatus( "ERROR: Failed to Save Options File; See Stack Trace", true ); 665 ex.printStackTrace(); 666 } 667 } 668 669 /// Updates GUI visibility/status after some parameters have changed 670 private void updateGUI() { 671 int selectedText = textMenu.getSelectedIndex(); 672 673 /// Set the visibility of User Text dialog 674 if ( selectedText == fp.USER_TEXT ) 675 userTextDialog.setVisible(true); 676 else 677 userTextDialog.setVisible(false); 678 /// Change the visibility/status/availability of Print JDialog buttons 679 printModeCBs[ fp.ONE_PAGE ].setSelected( true ); 680 if ( selectedText == fp.FILE_TEXT || selectedText == fp.USER_TEXT ) { 681 /// ABP 682 /// update methodsMenu to show that TextLayout.draw is being used 683 /// when we are in FILE_TEXT mode 684 if ( selectedText == fp.FILE_TEXT ) 685 methodsMenu.setSelectedItem("TextLayout.draw"); 686 methodsMenu.setEnabled( selectedText == fp.USER_TEXT ); 687 printModeCBs[ fp.CUR_RANGE ].setEnabled( false ); 688 printModeCBs[ fp.ALL_TEXT ].setEnabled( true ); 689 } 690 else { 691 /// ABP 692 /// update methodsMenu to show that drawGlyph is being used 693 /// when we are in ALL_GLYPHS mode 694 if ( selectedText == fp.ALL_GLYPHS ) 695 methodsMenu.setSelectedItem("drawGlyphVector"); 696 methodsMenu.setEnabled( selectedText == fp.RANGE_TEXT ); 697 printModeCBs[ fp.CUR_RANGE ].setEnabled( true ); 698 printModeCBs[ fp.ALL_TEXT ].setEnabled( false ); 699 } 700 /// Modify RangeMenu and fontInfo label availabilty 701 if ( selectedText == fp.RANGE_TEXT ) { 702 fontInfos[1].setVisible( true ); 703 rm.setEnabled( true ); 704 } 705 else { 706 fontInfos[1].setVisible( false ); 707 rm.setEnabled( false ); 708 } 709 } 710 711 /// Loads saved options and applies them 712 private void loadOptions( String fileName ) { 713 try { 714 BufferedInputStream bis = 715 new BufferedInputStream( new FileInputStream( fileName )); 716 int numBytes = bis.available(); 717 byte[] byteData = new byte[ numBytes ]; 718 bis.read( byteData, 0, numBytes ); 719 bis.close(); 720 if ( numBytes < 2 || 721 (byteData[0] != (byte) 0xFE || byteData[1] != (byte) 0xFF) ) 722 throw new Exception( "Not a Font2DTest options file" ); 723 724 String options = new String( byteData, "UTF-16" ); 725 StringTokenizer perLine = new StringTokenizer( options, "\n" ); 726 String title = perLine.nextToken(); 727 if ( !title.equals( "Font2DTest Option File" )) 728 throw new Exception( "Not a Font2DTest options file" ); 729 730 /// Parse all options 731 boolean displayGridOpt = Boolean.parseBoolean( perLine.nextToken() ); 732 boolean force16ColsOpt = Boolean.parseBoolean( perLine.nextToken() ); 733 boolean showFontInfoOpt = Boolean.parseBoolean( perLine.nextToken() ); 734 String rangeNameOpt = perLine.nextToken(); 735 int rangeStartOpt = Integer.parseInt( perLine.nextToken() ); 736 int rangeEndOpt = Integer.parseInt( perLine.nextToken() ); 737 String fontNameOpt = perLine.nextToken(); 738 float fontSizeOpt = Float.parseFloat( perLine.nextToken() ); 739 int fontStyleOpt = Integer.parseInt( perLine.nextToken() ); 740 int fontTransformOpt = Integer.parseInt( perLine.nextToken() ); 741 int g2TransformOpt = Integer.parseInt( perLine.nextToken() ); 742 int textToUseOpt = Integer.parseInt( perLine.nextToken() ); 743 int drawMethodOpt = Integer.parseInt( perLine.nextToken() ); 744 int antialiasOpt = Integer.parseInt(perLine.nextToken()); 745 int fractionalOpt = Integer.parseInt(perLine.nextToken()); 746 int lcdContrast = Integer.parseInt(perLine.nextToken()); 747 String[] userTextOpt = { "Font2DTest!" }; 748 String dialogEntry = "Font2DTest!"; 749 if (textToUseOpt == fp.USER_TEXT ) { 750 int numLines = perLine.countTokens(), lineNumber = 0; 751 if ( numLines != 0 ) { 752 userTextOpt = new String[ numLines ]; 753 dialogEntry = ""; 754 for ( ; perLine.hasMoreElements(); lineNumber++ ) { 755 userTextOpt[ lineNumber ] = perLine.nextToken(); 756 dialogEntry += userTextOpt[ lineNumber ] + "\n"; 757 } 758 } 759 } 760 761 /// Reset GUIs 762 displayGridCBMI.setState( displayGridOpt ); 763 force16ColsCBMI.setState( force16ColsOpt ); 764 showFontInfoCBMI.setState( showFontInfoOpt ); 765 rm.setSelectedRange( rangeNameOpt, rangeStartOpt, rangeEndOpt ); 766 fontMenu.setSelectedItem( fontNameOpt ); 767 sizeField.setText( String.valueOf( fontSizeOpt )); 768 styleMenu.setSelectedIndex( fontStyleOpt ); 769 transformMenu.setSelectedIndex( fontTransformOpt ); 770 transformMenuG2.setSelectedIndex( g2TransformOpt ); 771 textMenu.setSelectedIndex( textToUseOpt ); 772 methodsMenu.setSelectedIndex( drawMethodOpt ); 773 antiAliasMenu.setSelectedIndex( antialiasOpt ); 774 fracMetricsMenu.setSelectedIndex( fractionalOpt ); 775 contrastSlider.setValue(lcdContrast); 776 777 userTextArea.setText( dialogEntry ); 778 updateGUI(); 779 780 if ( textToUseOpt == fp.FILE_TEXT ) { 781 tFileName = perLine.nextToken(); 782 readTextFile(tFileName ); 783 } 784 785 /// Reset option variables and repaint 786 fp.loadOptions( displayGridOpt, force16ColsOpt, 787 rangeStartOpt, rangeEndOpt, 788 fontNameOpt, fontSizeOpt, 789 fontStyleOpt, fontTransformOpt, g2TransformOpt, 790 textToUseOpt, drawMethodOpt, 791 antialiasOpt, fractionalOpt, 792 lcdContrast, userTextOpt ); 793 if ( showFontInfoOpt ) { 794 fireUpdateFontInfo(); 795 fontInfoDialog.setVisible(true); 796 } 797 else 798 fontInfoDialog.setVisible(false); 799 } 800 catch ( Exception ex ) { 801 fireChangeStatus( "ERROR: Failed to Load Options File; See Stack Trace", true ); 802 ex.printStackTrace(); 803 } 804 } 805 806 /// Loads a previously saved image 807 private void loadComparisonPNG( String fileName ) { 808 try { 809 BufferedImage image = 810 javax.imageio.ImageIO.read(new File(fileName)); 811 JFrame f = new JFrame( "Comparison PNG" ); 812 ImagePanel ip = new ImagePanel( image ); 813 f.setResizable( false ); 814 f.getContentPane().add( ip ); 815 f.addWindowListener( new WindowAdapter() { 816 public void windowClosing( WindowEvent e ) { 817 ( (JFrame) e.getSource() ).dispose(); 818 } 819 }); 820 f.pack(); 821 f.setVisible(true); 822 } 823 catch ( Exception ex ) { 824 fireChangeStatus( "ERROR: Failed to Load PNG File; See Stack Trace", true ); 825 ex.printStackTrace(); 826 } 827 } 828 829 /// Interface functions... 830 831 /// ActionListener interface function 832 /// Responds to JMenuItem, JTextField and JButton actions 833 public void actionPerformed( ActionEvent e ) { 834 Object source = e.getSource(); 835 836 if ( source instanceof JMenuItem ) { 837 JMenuItem mi = (JMenuItem) source; 838 String itemName = mi.getText(); 839 840 if ( itemName.equals( "Save Selected Options..." )) { 841 String fileName = promptFile( true, "options.txt" ); 842 if ( fileName != null ) 843 writeCurrentOptions( fileName ); 844 } 845 else if ( itemName.equals( "Load Options..." )) { 846 String fileName = promptFile( false, "options.txt" ); 847 if ( fileName != null ) 848 loadOptions( fileName ); 849 } 850 else if ( itemName.equals( "Save as PNG..." )) { 851 String fileName = promptFile( true, fontMenu.getSelectedItem() + ".png" ); 852 if ( fileName != null ) 853 fp.doSavePNG( fileName ); 854 } 855 else if ( itemName.equals( "Load PNG File to Compare..." )) { 856 String fileName = promptFile( false, null ); 857 if ( fileName != null ) 858 loadComparisonPNG( fileName ); 859 } 860 else if ( itemName.equals( "Page Setup..." )) 861 fp.doPageSetup(); 862 else if ( itemName.equals( "Print..." )) 863 printDialog.setVisible(true); 864 else if ( itemName.equals( "Close" )) 865 parent.dispose(); 866 else if ( itemName.equals( "Exit" )) 867 System.exit(0); 868 } 869 870 else if ( source instanceof JTextField ) { 871 JTextField tf = (JTextField) source; 872 float sz = 12f; 873 try { 874 sz = Float.parseFloat(sizeField.getText()); 875 if (sz < 1f || sz > 120f) { 876 sz = 12f; 877 sizeField.setText("12"); 878 } 879 } catch (Exception se) { 880 sizeField.setText("12"); 881 } 882 if ( tf == sizeField ) 883 fp.setFontParams( fontMenu.getSelectedItem(), 884 sz, 885 styleMenu.getSelectedIndex(), 886 transformMenu.getSelectedIndex() ); 887 } 888 889 else if ( source instanceof JButton ) { 890 String itemName = ( (JButton) source ).getText(); 891 /// Print dialog buttons... 892 if ( itemName.equals( "Print" )) { 893 for ( int i = 0; i < printModeCBs.length; i++ ) 894 if ( printModeCBs[i].isSelected() ) { 895 printDialog.setVisible(false); 896 fp.doPrint( i ); 897 } 898 } 899 else if ( itemName.equals( "Cancel" )) 900 printDialog.setVisible(false); 901 /// Update button from Usert Text JDialog... 902 else if ( itemName.equals( "Update" )) 903 fp.setTextToDraw( fp.USER_TEXT, null, 904 parseUserText( userTextArea.getText() ), null ); 905 } 906 else if ( source instanceof JComboBox ) { 907 JComboBox<?> c = (JComboBox<?>) source; 908 909 /// RangeMenu handles actions by itself and then calls fireRangeChanged, 910 /// so it is not listed or handled here 911 if ( c == fontMenu || c == styleMenu || c == transformMenu ) { 912 float sz = 12f; 913 try { 914 sz = Float.parseFloat(sizeField.getText()); 915 if (sz < 1f || sz > 120f) { 916 sz = 12f; 917 sizeField.setText("12"); 918 } 919 } catch (Exception se) { 920 sizeField.setText("12"); 921 } 922 fp.setFontParams(fontMenu.getSelectedItem(), 923 sz, 924 styleMenu.getSelectedIndex(), 925 transformMenu.getSelectedIndex()); 926 } else if ( c == methodsMenu ) 927 fp.setDrawMethod( methodsMenu.getSelectedIndex() ); 928 else if ( c == textMenu ) { 929 930 if(canDisplayCheck) { 931 fireRangeChanged(); 932 } 933 934 int selected = textMenu.getSelectedIndex(); 935 936 if ( selected == fp.RANGE_TEXT ) 937 fp.setTextToDraw( fp.RANGE_TEXT, rm.getSelectedRange(), 938 null, null ); 939 else if ( selected == fp.USER_TEXT ) 940 fp.setTextToDraw( fp.USER_TEXT, null, 941 parseUserText( userTextArea.getText() ), null ); 942 else if ( selected == fp.FILE_TEXT ) { 943 String fileName = promptFile( false, null ); 944 if ( fileName != null ) { 945 tFileName = fileName; 946 readTextFile( fileName ); 947 } else { 948 /// User cancelled selection; reset to previous choice 949 c.setSelectedIndex( currentTextChoice ); 950 return; 951 } 952 } 953 else if ( selected == fp.ALL_GLYPHS ) 954 fp.setTextToDraw( fp.ALL_GLYPHS, null, null, null ); 955 956 updateGUI(); 957 currentTextChoice = selected; 958 } 959 else if ( c == transformMenuG2 ) { 960 fp.setTransformG2( transformMenuG2.getSelectedIndex() ); 961 } 962 else if (c == antiAliasMenu || c == fracMetricsMenu) { 963 if (c == antiAliasMenu) { 964 boolean enabled = FontPanel.AAValues. 965 isLCDMode(antiAliasMenu.getSelectedItem()); 966 contrastSlider.setEnabled(enabled); 967 } 968 fp.setRenderingHints(antiAliasMenu.getSelectedItem(), 969 fracMetricsMenu.getSelectedItem(), 970 contrastSlider.getValue()); 971 } 972 } 973 } 974 975 public void stateChanged(ChangeEvent e) { 976 Object source = e.getSource(); 977 if (source instanceof JSlider) { 978 fp.setRenderingHints(antiAliasMenu.getSelectedItem(), 979 fracMetricsMenu.getSelectedItem(), 980 contrastSlider.getValue()); 981 } 982 } 983 984 /// ItemListener interface function 985 /// Responds to JCheckBoxMenuItem, JComboBox and JCheckBox actions 986 public void itemStateChanged( ItemEvent e ) { 987 Object source = e.getSource(); 988 989 if ( source instanceof JCheckBoxMenuItem ) { 990 JCheckBoxMenuItem cbmi = (JCheckBoxMenuItem) source; 991 if ( cbmi == displayGridCBMI ) 992 fp.setGridDisplay( displayGridCBMI.getState() ); 993 else if ( cbmi == force16ColsCBMI ) 994 fp.setForce16Columns( force16ColsCBMI.getState() ); 995 else if ( cbmi == showFontInfoCBMI ) { 996 if ( showFontInfoCBMI.getState() ) { 997 fireUpdateFontInfo(); 998 fontInfoDialog.setVisible(true); 999 } 1000 else 1001 fontInfoDialog.setVisible(false); 1002 } 1003 } 1004 } 1005 1006 private static void printUsage() { 1007 String usage = "Usage: java -jar Font2DTest.jar [options]\n" + 1008 "\nwhere options include:\n" + 1009 " -dcdc | -disablecandisplaycheck disable canDisplay check for font\n" + 1010 " -? | -help print this help message\n" + 1011 "\nExample :\n" + 1012 " To disable canDisplay check on font for ranges\n" + 1013 " java -jar Font2DTest.jar -dcdc"; 1014 System.out.println(usage); 1015 System.exit(0); 1016 } 1017 1018 /// Main function 1019 public static void main(String[] argv) { 1020 1021 if(argv.length > 0) { 1022 if(argv[0].equalsIgnoreCase("-disablecandisplaycheck") || 1023 argv[0].equalsIgnoreCase("-dcdc")) { 1024 canDisplayCheck = false; 1025 } 1026 else { 1027 printUsage(); 1028 } 1029 } 1030 1031 UIManager.put("swing.boldMetal", Boolean.FALSE); 1032 final JFrame f = new JFrame( "Font2DTest" ); 1033 final Font2DTest f2dt = new Font2DTest( f, false ); 1034 f.addWindowListener( new WindowAdapter() { 1035 public void windowOpening( WindowEvent e ) { f2dt.repaint(); } 1036 public void windowClosing( WindowEvent e ) { System.exit(0); } 1037 }); 1038 1039 f.getContentPane().add( f2dt ); 1040 f.pack(); 1041 f.setVisible(true); 1042 } 1043 1044 /// Inner class definitions... 1045 1046 /// Class to display just an image file 1047 /// Used to show the comparison PNG image 1048 private final class ImagePanel extends JPanel { 1049 private final BufferedImage bi; 1050 1051 public ImagePanel( BufferedImage image ) { 1052 bi = image; 1053 } 1054 1055 public Dimension getPreferredSize() { 1056 return new Dimension( bi.getWidth(), bi.getHeight() ); 1057 } 1058 1059 public void paintComponent( Graphics g ) { 1060 g.drawImage( bi, 0, 0, this ); 1061 } 1062 } 1063 1064 /// Classes made to avoid repetitive calls... (being lazy) 1065 private final class ButtonV2 extends JButton { 1066 public ButtonV2( String name, ActionListener al ) { 1067 super( name ); 1068 this.addActionListener( al ); 1069 } 1070 } 1071 1072 private final class ChoiceV2 extends JComboBox<String> { 1073 1074 private BitSet bitSet = null; 1075 1076 public ChoiceV2() {;} 1077 1078 public ChoiceV2( ActionListener al ) { 1079 super(); 1080 this.addActionListener( al ); 1081 } 1082 1083 public ChoiceV2( ActionListener al, boolean fontChoice) { 1084 this(al); 1085 if(fontChoice) { 1086 //Register this component in ToolTipManager 1087 setToolTipText(""); 1088 bitSet = new BitSet(); 1089 setRenderer(new ChoiceV2Renderer(this)); 1090 } 1091 } 1092 1093 public String getToolTipText() { 1094 int index = this.getSelectedIndex(); 1095 String fontName = (String) this.getSelectedItem(); 1096 if(fontName != null && 1097 (textMenu.getSelectedIndex() == fp.RANGE_TEXT)) { 1098 if (getBit(index)) { 1099 return "Font \"" + fontName + "\" can display some characters in \"" + 1100 rm.getSelectedItem() + "\" range"; 1101 } 1102 else { 1103 return "Font \"" + fontName + "\" cannot display any characters in \"" + 1104 rm.getSelectedItem() + "\" range"; 1105 } 1106 } 1107 return super.getToolTipText(); 1108 } 1109 1110 public void setBit(int bitIndex, boolean value) { 1111 bitSet.set(bitIndex, value); 1112 } 1113 1114 public boolean getBit(int bitIndex) { 1115 return bitSet.get(bitIndex); 1116 } 1117 } 1118 1119 private final class ChoiceV2Renderer extends DefaultListCellRenderer { 1120 1121 private ImageIcon yesImage, blankImage; 1122 private ChoiceV2 choice = null; 1123 1124 public ChoiceV2Renderer(ChoiceV2 choice) { 1125 BufferedImage yes = 1126 new BufferedImage(10, 10, BufferedImage.TYPE_INT_ARGB); 1127 Graphics2D g = yes.createGraphics(); 1128 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 1129 RenderingHints.VALUE_ANTIALIAS_ON); 1130 g.setColor(Color.BLUE); 1131 g.drawLine(0, 5, 3, 10); 1132 g.drawLine(1, 5, 4, 10); 1133 g.drawLine(3, 10, 10, 0); 1134 g.drawLine(4, 9, 9, 0); 1135 g.dispose(); 1136 BufferedImage blank = 1137 new BufferedImage(10, 10, BufferedImage.TYPE_INT_ARGB); 1138 yesImage = new ImageIcon(yes); 1139 blankImage = new ImageIcon(blank); 1140 this.choice = choice; 1141 } 1142 1143 public Component getListCellRendererComponent(JList<?> list, 1144 Object value, 1145 int index, 1146 boolean isSelected, 1147 boolean cellHasFocus) { 1148 1149 if(textMenu.getSelectedIndex() == fp.RANGE_TEXT) { 1150 1151 super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); 1152 1153 //For JComboBox if index is -1, its rendering the selected index. 1154 if(index == -1) { 1155 index = choice.getSelectedIndex(); 1156 } 1157 1158 if(choice.getBit(index)) { 1159 setIcon(yesImage); 1160 } 1161 else { 1162 setIcon(blankImage); 1163 } 1164 1165 } else { 1166 super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); 1167 setIcon(blankImage); 1168 } 1169 1170 return this; 1171 } 1172 } 1173 1174 private final class LabelV2 extends JLabel { 1175 public LabelV2( String name ) { 1176 super( name ); 1177 } 1178 } 1179 1180 private final class MenuItemV2 extends JMenuItem { 1181 public MenuItemV2( String name, ActionListener al ) { 1182 super( name ); 1183 this.addActionListener( al ); 1184 } 1185 } 1186 1187 private final class CheckboxMenuItemV2 extends JCheckBoxMenuItem { 1188 public CheckboxMenuItemV2( String name, boolean b, ItemListener il ) { 1189 super( name, b ); 1190 this.addItemListener( il ); 1191 } 1192 } 1193 }