1 /*
2  * Copyright (c) 2000, 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.BorderLayout;
43 import java.awt.Color;
44 import java.awt.Cursor;
45 import java.awt.Dimension;
46 import java.awt.Font;
47 import java.awt.FontMetrics;
48 import java.awt.Graphics;
49 import java.awt.Graphics2D;
50 import java.awt.GraphicsConfiguration;
51 import java.awt.GraphicsEnvironment;
52 import java.awt.Point;
53 import java.awt.Rectangle;
54 import java.awt.RenderingHints;
55 import java.awt.Toolkit;
56 import java.awt.event.AdjustmentEvent;
57 import java.awt.event.AdjustmentListener;
58 import java.awt.event.ComponentAdapter;
59 import java.awt.event.ComponentEvent;
60 import java.awt.event.MouseEvent;
61 import java.awt.event.MouseListener;
62 import java.awt.event.MouseMotionListener;
63 import java.awt.font.FontRenderContext;
64 import java.awt.font.GlyphVector;
65 import java.awt.font.LineBreakMeasurer;
66 import java.awt.font.TextLayout;
67 import java.awt.geom.AffineTransform;
68 import java.awt.geom.NoninvertibleTransformException;
69 import java.awt.geom.Rectangle2D;
70 import java.awt.image.BufferedImage;
71 import java.awt.print.PageFormat;
72 import java.awt.print.Printable;
73 import java.awt.print.PrinterJob;
74 import java.io.BufferedOutputStream;
75 import java.io.FileOutputStream;
76 import java.text.AttributedString;
77 import java.util.EnumSet;
78 import java.util.Vector;
79 
80 import javax.imageio.*;
81 import javax.swing.*;
82 
83 import static java.awt.RenderingHints.*;
84 
85 /**
86  * FontPanel.java
87  *
88  * @author Shinsuke Fukuda
89  * @author Ankit Patel [Conversion to Swing - 01/07/30]
90  */
91 
92 /// This panel is combination of the text drawing area of Font2DTest
93 /// and the custom controlled scroll bar
94 
95 public final class FontPanel extends JPanel implements AdjustmentListener {
96 
97     /// Drawing Option Constants
98     private final String[] STYLES =
99       { "plain", "bold", "italic", "bold italic" };
100 
101     private final int NONE = 0;
102     private final int SCALE = 1;
103     private final int SHEAR = 2;
104     private final int ROTATE = 3;
105     private final String[] TRANSFORMS =
106       { "with no transforms", "with scaling", "with Shearing", "with rotation" };
107 
108     private final int DRAW_STRING = 0;
109     private final int DRAW_CHARS = 1;
110     private final int DRAW_BYTES = 2;
111     private final int DRAW_GLYPHV = 3;
112     private final int TL_DRAW = 4;
113     private final int GV_OUTLINE = 5;
114     private final int TL_OUTLINE = 6;
115     private final String[] METHODS = {
116         "drawString", "drawChars", "drawBytes", "drawGlyphVector",
117         "TextLayout.draw", "GlyphVector.getOutline", "TextLayout.getOutline" };
118 
119     public final int RANGE_TEXT = 0;
120     public final int ALL_GLYPHS = 1;
121     public final int USER_TEXT = 2;
122     public final int FILE_TEXT = 3;
123     private final String[] MS_OPENING =
124       { " Unicode ", " Glyph Code ", " lines ", " lines " };
125     private final String[] MS_CLOSING =
126       { "", "", " of User Text ", " of LineBreakMeasurer-reformatted Text " };
127 
128     /// General Graphics Variable
129     private final JScrollBar verticalBar;
130     private final FontCanvas fc;
131     private boolean updateFontMetrics = true;
132     private boolean updateFont = true;
133     private boolean force16Cols = false;
134     public boolean showingError = false;
135     private int g2Transform = NONE; /// ABP
136 
137     /// Printing constants and variables
138     public final int ONE_PAGE = 0;
139     public final int CUR_RANGE = 1;
140     public final int ALL_TEXT = 2;
141     private int printMode = ONE_PAGE;
142     private PageFormat page = null;
143     private PrinterJob printer = null;
144 
145     /// Text drawing variables
146     private String fontName = "Dialog";
147     private float fontSize = 12;
148     private int fontStyle = Font.PLAIN;
149     private int fontTransform = NONE;
150     private Font testFont = null;
151     private Object antiAliasType = VALUE_TEXT_ANTIALIAS_DEFAULT;
152     private Object fractionalMetricsType = VALUE_FRACTIONALMETRICS_DEFAULT;
153     private Object lcdContrast = getDefaultLCDContrast();
154     private int drawMethod = DRAW_STRING;
155     private int textToUse = RANGE_TEXT;
156     private String[] userText = null;
157     private String[] fileText = null;
158     private int[] drawRange = { 0x0000, 0x007f };
159     private String[] fontInfos = new String[2];
160     private boolean showGrid = true;
161 
162     /// Parent Font2DTest panel
163     private final Font2DTest f2dt;
164     private final JFrame parent;
165 
FontPanel( Font2DTest demo, JFrame f )166     public FontPanel( Font2DTest demo, JFrame f ) {
167         f2dt = demo;
168         parent = f;
169 
170         verticalBar = new JScrollBar ( JScrollBar.VERTICAL );
171         fc = new FontCanvas();
172 
173         this.setLayout( new BorderLayout() );
174         this.add( "Center", fc );
175         this.add( "East", verticalBar );
176 
177         verticalBar.addAdjustmentListener( this );
178         this.addComponentListener( new ComponentAdapter() {
179             public void componentResized( ComponentEvent e ) {
180                 updateFontMetrics = true;
181             }
182         });
183 
184         /// Initialize font and its infos
185         testFont = new Font(fontName, fontStyle, (int)fontSize);
186         if ((float)((int)fontSize) != fontSize) {
187             testFont = testFont.deriveFont(fontSize);
188         }
189         updateFontInfo();
190     }
191 
getPreferredSize()192     public Dimension getPreferredSize() {
193         return new Dimension(600, 200);
194     }
195 
196     /// Functions called by the main programs to set the various parameters
197 
setTransformG2( int transform )198     public void setTransformG2( int transform ) {
199         g2Transform = transform;
200         updateFontMetrics = true;
201         fc.repaint();
202     }
203 
204     /// convenience fcn to create AffineTransform of appropriate type
getAffineTransform( int transform )205     private AffineTransform getAffineTransform( int transform ) {
206             /// ABP
207             AffineTransform at = new AffineTransform();
208             switch ( transform )
209             {
210             case SCALE:
211               at.setToScale( 1.5f, 1.5f ); break;
212             case ROTATE:
213               at.setToRotation( Math.PI / 6 ); break;
214             case SHEAR:
215               at.setToShear( 0.4f, 0 ); break;
216             case NONE:
217               break;
218             default:
219               //System.err.println( "Illegal G2 Transform Arg: " + transform);
220               break;
221             }
222 
223             return at;
224     }
225 
setFontParams(Object obj, float size, int style, int transform)226     public void setFontParams(Object obj, float size,
227                               int style, int transform) {
228         setFontParams( (String)obj, size, style, transform );
229     }
230 
setFontParams(String name, float size, int style, int transform)231     public void setFontParams(String name, float size,
232                               int style, int transform) {
233         boolean fontModified = false;
234         if ( !name.equals( fontName ) || style != fontStyle )
235           fontModified = true;
236 
237         fontName = name;
238         fontSize = size;
239         fontStyle = style;
240         fontTransform = transform;
241 
242         /// Recreate the font as specified
243         testFont = new Font(fontName, fontStyle, (int)fontSize);
244         if ((float)((int)fontSize) != fontSize) {
245             testFont = testFont.deriveFont(fontSize);
246         }
247 
248         if ( fontTransform != NONE ) {
249             AffineTransform at = getAffineTransform( fontTransform );
250             testFont = testFont.deriveFont( at );
251         }
252         updateFontMetrics = true;
253         fc.repaint();
254         if ( fontModified ) {
255             /// Tell main panel to update the font info
256             updateFontInfo();
257             f2dt.fireUpdateFontInfo();
258         }
259     }
260 
setRenderingHints( Object aa, Object fm, Object contrast)261     public void setRenderingHints( Object aa, Object fm, Object contrast) {
262         antiAliasType = ((AAValues)aa).getHint();
263         fractionalMetricsType = ((FMValues)fm).getHint();
264         lcdContrast = contrast;
265         updateFontMetrics = true;
266         fc.repaint();
267     }
268 
setDrawMethod( int i )269     public void setDrawMethod( int i ) {
270         drawMethod = i;
271         fc.repaint();
272     }
273 
setTextToDraw( int i, int[] range, String[] textSet, String[] fileData )274     public void setTextToDraw( int i, int[] range,
275                                String[] textSet, String[] fileData ) {
276         textToUse = i;
277 
278         if ( textToUse == RANGE_TEXT )
279           drawRange = range;
280         else if ( textToUse == ALL_GLYPHS )
281           drawMethod = DRAW_GLYPHV;
282         else if ( textToUse == USER_TEXT )
283           userText = textSet;
284         else if ( textToUse == FILE_TEXT ) {
285             fileText = fileData;
286             drawMethod = TL_DRAW;
287         }
288 
289         updateFontMetrics = true;
290         fc.repaint();
291         updateFontInfo();
292     }
293 
setGridDisplay( boolean b )294     public void setGridDisplay( boolean b ) {
295         showGrid = b;
296         fc.repaint();
297     }
298 
setForce16Columns( boolean b )299     public void setForce16Columns( boolean b ) {
300         force16Cols = b;
301         updateFontMetrics = true;
302         fc.repaint();
303     }
304 
305     /// Prints out the text display area
doPrint( int i )306     public void doPrint( int i ) {
307         if ( printer == null ) {
308             printer = PrinterJob.getPrinterJob();
309             page = printer.defaultPage();
310         }
311         printMode = i;
312         printer.setPrintable( fc, page );
313 
314         if ( printer.printDialog() ) {
315             try {
316                 printer.print();
317             }
318             catch ( Exception e ) {
319                 f2dt.fireChangeStatus( "ERROR: Printing Failed; See Stack Trace", true );
320             }
321         }
322     }
323 
324     /// Displays the page setup dialog and updates PageFormat info
doPageSetup()325     public void doPageSetup() {
326         if ( printer == null ) {
327             printer = PrinterJob.getPrinterJob();
328             page = printer.defaultPage();
329         }
330         page = printer.pageDialog( page );
331     }
332 
333     /// Obtains the information about selected font
updateFontInfo()334     private void updateFontInfo() {
335         int numGlyphs = 0, numCharsInRange = drawRange[1] - drawRange[0] + 1;
336         fontInfos[0] = "Font Face Name: " + testFont.getFontName();
337         fontInfos[1] = "Glyphs in This Range: ";
338 
339         if ( textToUse == RANGE_TEXT ) {
340             for ( int i = drawRange[0]; i < drawRange[1]; i++ )
341               if ( testFont.canDisplay( i ))
342                 numGlyphs++;
343             fontInfos[1] = fontInfos[1] + numGlyphs + " / " + numCharsInRange;
344         }
345         else
346           fontInfos[1] = null;
347     }
348 
349     /// Accessor for the font information
getFontInfo()350     public String[] getFontInfo() {
351         return fontInfos;
352     }
353 
354     /// Collects the currectly set options and returns them as string
getCurrentOptions()355     public String getCurrentOptions() {
356         /// Create a new String to store the options
357         /// The array will contain all 8 setting (font name, size...) and
358         /// character range or user text data used (no file text data)
359         int userTextSize = 0;
360         String options;
361 
362         options = ( fontName + "\n" + fontSize  + "\n" + fontStyle + "\n" +
363                     fontTransform + "\n"  + g2Transform + "\n"+
364                     textToUse + "\n" + drawMethod + "\n" +
365                     AAValues.getHintVal(antiAliasType) + "\n" +
366                     FMValues.getHintVal(fractionalMetricsType) + "\n" +
367                     lcdContrast + "\n");
368         if ( textToUse == USER_TEXT )
369           for ( int i = 0; i < userText.length; i++ )
370             options += ( userText[i] + "\n" );
371 
372         return options;
373     }
374 
375     /// Reload all options and refreshes the canvas
loadOptions( boolean grid, boolean force16, int start, int end, String name, float size, int style, int transform, int g2transform, int text, int method, int aa, int fm, int contrast, String[] user )376     public void loadOptions( boolean grid, boolean force16, int start, int end,
377                              String name, float size, int style,
378                              int transform, int g2transform,
379                              int text, int method, int aa, int fm,
380                              int contrast, String[] user ) {
381         int[] range = { start, end };
382 
383         /// Since repaint call has a low priority, these functions will finish
384         /// before the actual repainting is done
385         setGridDisplay( grid );
386         setForce16Columns( force16 );
387         // previous call to readTextFile has already set the text to draw
388         if (textToUse != FILE_TEXT) {
389           setTextToDraw( text, range, user, null );
390         }
391         setFontParams( name, size, style, transform );
392         setTransformG2( g2transform ); // ABP
393         setDrawMethod( method );
394         setRenderingHints(AAValues.getValue(aa), FMValues.getValue(fm),
395                           new Integer(contrast));
396     }
397 
398     /// Writes the current screen to PNG file
doSavePNG( String fileName )399     public void doSavePNG( String fileName ) {
400         fc.writePNG( fileName );
401     }
402 
403     /// When scrolled using the scroll bar, update the backbuffer
adjustmentValueChanged( AdjustmentEvent e )404     public void adjustmentValueChanged( AdjustmentEvent e ) {
405         fc.repaint();
406     }
407 
paintComponent( Graphics g )408     public void paintComponent( Graphics g ) {
409         // Windows does not repaint correctly, after
410         // a zoom. Thus, we need to force the canvas
411         // to repaint, but only once. After the first repaint,
412         // everything stabilizes. [ABP]
413         fc.repaint();
414     }
415 
416     /// Inner class definition...
417 
418     /// Inner panel that holds the actual drawing area and its routines
419     private class FontCanvas extends JPanel implements MouseListener, MouseMotionListener, Printable {
420 
421         /// Number of characters that will fit across and down this canvas
422         private int numCharAcross, numCharDown;
423 
424         /// First and last character/line that will be drawn
425         /// Limit is the end of range/text where no more draw will be done
426         private int drawStart, drawEnd, drawLimit;
427 
428         /// FontMetrics variables
429         /// Here, gridWidth is equivalent to maxAdvance (slightly bigger though)
430         /// and gridHeight is equivalent to lineHeight
431         private int maxAscent, maxDescent, gridWidth = 0, gridHeight = 0;
432 
433         /// Offset from the top left edge of the canvas where the draw will start
434         private int canvasInset_X = 5, canvasInset_Y = 5;
435 
436         /// LineBreak'ed TextLayout vector
437         private Vector lineBreakTLs = null;
438 
439         /// Whether the current draw command requested is for printing
440         private boolean isPrinting = false;
441 
442         /// Other printing infos
443         private int lastPage, printPageNumber, currentlyShownChar = 0;
444         private final int PR_OFFSET = 10;
445         private final int PR_TITLE_LINEHEIGHT = 30;
446 
447         /// Information about zooming (used with range text draw)
448         private final JWindow zoomWindow;
449         private BufferedImage zoomImage = null;
450         private int mouseOverCharX = -1, mouseOverCharY = -1;
451         private int currMouseOverChar = -1, prevZoomChar = -1;
452         private float ZOOM = 2.0f;
453         private boolean nowZooming = false;
454         private boolean firstTime = true;
455 // ABP
456 
457         /// Status bar message backup
458         private String backupStatusString = null;
459 
460         /// Error constants
461         private final String[] ERRORS = {
462             "ERROR: drawBytes cannot handle characters beyond 0x00FF. Select different range or draw methods.",
463             "ERROR: Cannot fit text with the current font size. Resize the window or use smaller font size.",
464             "ERROR: Cannot print with the current font size. Use smaller font size.",
465         };
466 
467         private final int DRAW_BYTES_ERROR = 0;
468         private final int CANT_FIT_DRAW = 1;
469         private final int CANT_FIT_PRINT = 2;
470 
471         /// Other variables
472         private final Cursor blankCursor;
473 
FontCanvas()474         public FontCanvas() {
475             this.addMouseListener( this );
476             this.addMouseMotionListener( this );
477             this.setForeground( Color.black );
478             this.setBackground( Color.white );
479 
480             /// Creates an invisble pointer by giving it bogus image
481             /// Possibly find a workaround for this...
482             Toolkit tk = Toolkit.getDefaultToolkit();
483             byte[] bogus = { (byte) 0 };
484             blankCursor =
485               tk.createCustomCursor( tk.createImage( bogus ), new Point(0, 0), "" );
486 
487             zoomWindow = new JWindow( parent ) {
488                 public void paint( Graphics g ) {
489                     g.drawImage( zoomImage, 0, 0, zoomWindow );
490                 }
491             };
492             zoomWindow.setCursor( blankCursor );
493             zoomWindow.pack();
494         }
495 
firstTime()496         public boolean firstTime() { return firstTime; }
refresh()497         public void refresh() {
498             firstTime = false;
499             repaint();
500         }
501 
502         /// Sets the font, hints, according to the set parameters
setParams( Graphics2D g2 )503         private void setParams( Graphics2D g2 ) {
504             g2.setFont( testFont );
505             g2.setRenderingHint(KEY_TEXT_ANTIALIASING, antiAliasType);
506             g2.setRenderingHint(KEY_FRACTIONALMETRICS, fractionalMetricsType);
507             g2.setRenderingHint(KEY_TEXT_LCD_CONTRAST, lcdContrast);
508             /* I am preserving a somewhat dubious behaviour of this program.
509              * Outline text would be drawn anti-aliased by setting the
510              * graphics anti-aliasing hint if the text anti-aliasing hint
511              * was set. The dubious element here is that people simply
512              * using this program may think this is built-in behaviour
513              * but its not - at least not when the app explicitly draws
514              * outline text.
515              * This becomes more dubious in cases such as "GASP" where the
516              * size at which text is AA'ed is not something you can easily
517              * calculate, so mimicing that behaviour isn't going to be easy.
518              * So I precisely preserve the behaviour : this is done only
519              * if the AA value is "ON". Its not applied in the other cases.
520              */
521             if (antiAliasType == VALUE_TEXT_ANTIALIAS_ON &&
522                 (drawMethod == TL_OUTLINE || drawMethod == GV_OUTLINE)) {
523                 g2.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON);
524             } else {
525                 g2.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_OFF);
526             }
527         }
528 
529         /// Draws the grid (Used for unicode/glyph range drawing)
drawGrid( Graphics2D g2 )530         private void drawGrid( Graphics2D g2 ) {
531             int totalGridWidth = numCharAcross * gridWidth;
532             int totalGridHeight = numCharDown * gridHeight;
533 
534             g2.setColor( Color.black );
535             for ( int i = 0; i < numCharDown + 1; i++ )
536               g2.drawLine( canvasInset_X, i * gridHeight + canvasInset_Y,
537                            canvasInset_X + totalGridWidth, i * gridHeight + canvasInset_Y );
538             for ( int i = 0; i < numCharAcross + 1; i++ )
539               g2.drawLine( i * gridWidth + canvasInset_X, canvasInset_Y,
540                            i * gridWidth + canvasInset_X, canvasInset_Y + totalGridHeight );
541         }
542 
543         /// Draws one character at time onto the canvas according to
544         /// the method requested (Used for RANGE_TEXT and ALL_GLYPHS)
modeSpecificDrawChar( Graphics2D g2, int charCode, int baseX, int baseY )545         public void modeSpecificDrawChar( Graphics2D g2, int charCode,
546                                           int baseX, int baseY ) {
547             GlyphVector gv;
548             int[] oneGlyph = { charCode };
549             char[] charArray = Character.toChars( charCode );
550 
551             FontRenderContext frc = g2.getFontRenderContext();
552             AffineTransform oldTX = g2.getTransform();
553 
554             /// Create GlyphVector to measure the exact visual advance
555             /// Using that number, adjust the position of the character drawn
556             if ( textToUse == ALL_GLYPHS )
557               gv = testFont.createGlyphVector( frc, oneGlyph );
558             else
559               gv = testFont.createGlyphVector( frc, charArray );
560             Rectangle2D r2d2 = gv.getPixelBounds(frc, 0, 0);
561             int shiftedX = baseX;
562             // getPixelBounds returns a result in device space.
563             // we need to convert back to user space to be able to
564             // calculate the shift as baseX is in user space.
565             try {
566                  double[] pt = new double[4];
567                  pt[0] = r2d2.getX();
568                  pt[1] = r2d2.getY();
569                  pt[2] = r2d2.getX()+r2d2.getWidth();
570                  pt[3] = r2d2.getY()+r2d2.getHeight();
571                  oldTX.inverseTransform(pt,0,pt,0,2);
572                  shiftedX = baseX - (int) ( pt[2] / 2 + pt[0] );
573             } catch (NoninvertibleTransformException e) {
574             }
575 
576             /// ABP - keep track of old tform, restore it later
577 
578             g2.translate( shiftedX, baseY );
579             g2.transform( getAffineTransform( g2Transform ) );
580 
581             if ( textToUse == ALL_GLYPHS )
582               g2.drawGlyphVector( gv, 0f, 0f );
583             else {
584                 if ( testFont.canDisplay( charCode ))
585                   g2.setColor( Color.black );
586                 else {
587                   g2.setColor( Color.lightGray );
588                 }
589 
590                 switch ( drawMethod ) {
591                   case DRAW_STRING:
592                     g2.drawString( new String( charArray ), 0, 0 );
593                     break;
594                   case DRAW_CHARS:
595                     g2.drawChars( charArray, 0, 1, 0, 0 );
596                     break;
597                   case DRAW_BYTES:
598                     if ( charCode > 0xff )
599                       throw new CannotDrawException( DRAW_BYTES_ERROR );
600                     byte[] oneByte = { (byte) charCode };
601                     g2.drawBytes( oneByte, 0, 1, 0, 0 );
602                     break;
603                   case DRAW_GLYPHV:
604                     g2.drawGlyphVector( gv, 0f, 0f );
605                     break;
606                   case TL_DRAW:
607                     TextLayout tl = new TextLayout( new String( charArray ), testFont, frc );
608                     tl.draw( g2, 0f, 0f );
609                     break;
610                   case GV_OUTLINE:
611                     r2d2 = gv.getVisualBounds();
612                     shiftedX = baseX - (int) ( r2d2.getWidth() / 2 + r2d2.getX() );
613                     g2.draw( gv.getOutline( 0f, 0f ));
614                     break;
615                   case TL_OUTLINE:
616                     r2d2 = gv.getVisualBounds();
617                     shiftedX = baseX - (int) ( r2d2.getWidth() / 2 + r2d2.getX() );
618                     TextLayout tlo =
619                       new TextLayout( new String( charArray ), testFont,
620                                       g2.getFontRenderContext() );
621                     g2.draw( tlo.getOutline( null ));
622                 }
623             }
624 
625             /// ABP - restore old tform
626             g2.setTransform ( oldTX );
627         }
628 
629         /// Draws one line of text at given position
modeSpecificDrawLine( Graphics2D g2, String line, int baseX, int baseY )630         private void modeSpecificDrawLine( Graphics2D g2, String line,
631                                            int baseX, int baseY ) {
632             /// ABP - keep track of old tform, restore it later
633             AffineTransform oldTx = null;
634             oldTx = g2.getTransform();
635             g2.translate( baseX, baseY );
636             g2.transform( getAffineTransform( g2Transform ) );
637 
638             switch ( drawMethod ) {
639               case DRAW_STRING:
640                 g2.drawString( line, 0, 0 );
641                 break;
642               case DRAW_CHARS:
643                 g2.drawChars( line.toCharArray(), 0, line.length(), 0, 0 );
644                 break;
645               case DRAW_BYTES:
646                 try {
647                     byte[] lineBytes = line.getBytes( "ISO-8859-1" );
648                     g2.drawBytes( lineBytes, 0, lineBytes.length, 0, 0 );
649                 }
650                 catch ( Exception e ) {
651                     e.printStackTrace();
652                 }
653                 break;
654               case DRAW_GLYPHV:
655                 GlyphVector gv =
656                   testFont.createGlyphVector( g2.getFontRenderContext(), line );
657                 g2.drawGlyphVector( gv, (float) 0, (float) 0 );
658                 break;
659               case TL_DRAW:
660                 TextLayout tl = new TextLayout( line, testFont,
661                                                 g2.getFontRenderContext() );
662                 tl.draw( g2, (float) 0, (float) 0 );
663                 break;
664               case GV_OUTLINE:
665                 GlyphVector gvo =
666                   testFont.createGlyphVector( g2.getFontRenderContext(), line );
667                 g2.draw( gvo.getOutline( (float) 0, (float) 0 ));
668                 break;
669               case TL_OUTLINE:
670                 TextLayout tlo =
671                   new TextLayout( line, testFont,
672                                   g2.getFontRenderContext() );
673                 AffineTransform at = new AffineTransform();
674                 g2.draw( tlo.getOutline( at ));
675             }
676 
677             /// ABP - restore old tform
678             g2.setTransform ( oldTx );
679 
680         }
681 
682         /// Draws one line of text at given position
tlDrawLine( Graphics2D g2, TextLayout tl, float baseX, float baseY )683         private void tlDrawLine( Graphics2D g2, TextLayout tl,
684                                            float baseX, float baseY ) {
685             /// ABP - keep track of old tform, restore it later
686             AffineTransform oldTx = null;
687             oldTx = g2.getTransform();
688             g2.translate( baseX, baseY );
689             g2.transform( getAffineTransform( g2Transform ) );
690 
691             tl.draw( g2, (float) 0, (float) 0 );
692 
693             /// ABP - restore old tform
694             g2.setTransform ( oldTx );
695 
696         }
697 
698 
699         /// If textToUse is set to range drawing, then convert
700         /// int to hex string and prepends 0s to make it length 4
701         /// Otherwise line number was fed; simply return number + 1 converted to String
702         /// (This is because first line is 1, not 0)
modeSpecificNumStr( int i )703         private String modeSpecificNumStr( int i ) {
704             if ( textToUse == USER_TEXT || textToUse == FILE_TEXT )
705               return String.valueOf( i + 1 );
706 
707             StringBuffer s = new StringBuffer( Integer.toHexString( i ));
708             while ( s.length() < 4 )
709               s.insert( 0, "0" );
710             return s.toString().toUpperCase();
711         }
712 
713         /// Resets the scrollbar to display correct range of text currently on screen
714         /// (This scrollbar is not part of a "ScrollPane". It merely simulates its effect by
715         ///  indicating the necessary area to be drawn within the panel.
716         ///  By doing this, it prevents creating gigantic panel when large text range,
717         ///  i.e. CJK Ideographs, is requested)
resetScrollbar( int oldValue )718         private void resetScrollbar( int oldValue ) {
719             int totalNumRows = 1, numCharToDisplay;
720             if ( textToUse == RANGE_TEXT || textToUse == ALL_GLYPHS ) {
721                 if ( textToUse == RANGE_TEXT )
722                   numCharToDisplay = drawRange[1] - drawRange[0];
723                 else /// textToUse == ALL_GLYPHS
724                   numCharToDisplay = testFont.getNumGlyphs();
725 
726                 totalNumRows = numCharToDisplay / numCharAcross;
727                 if ( numCharToDisplay % numCharAcross != 0 )
728                   totalNumRows++;
729                 if ( oldValue / numCharAcross > totalNumRows )
730                   oldValue = 0;
731 
732                 verticalBar.setValues( oldValue / numCharAcross,
733                                        numCharDown, 0, totalNumRows );
734             }
735             else {
736                 if ( textToUse == USER_TEXT )
737                   totalNumRows = userText.length;
738                 else /// textToUse == FILE_TEXT;
739                   totalNumRows = lineBreakTLs.size();
740                 verticalBar.setValues( oldValue, numCharDown, 0, totalNumRows );
741             }
742             if ( totalNumRows <= numCharDown && drawStart == 0) {
743               verticalBar.setEnabled( false );
744             }
745             else {
746               verticalBar.setEnabled( true );
747             }
748         }
749 
750         /// Calculates the font's metrics that will be used for draw
calcFontMetrics( Graphics2D g2d, int w, int h )751         private void calcFontMetrics( Graphics2D g2d, int w, int h ) {
752             FontMetrics fm;
753             Graphics2D g2 = (Graphics2D)g2d.create();
754 
755             /// ABP
756             if ( g2Transform != NONE && textToUse != FILE_TEXT ) {
757                 g2.setFont( g2.getFont().deriveFont( getAffineTransform( g2Transform )) );
758                 fm = g2.getFontMetrics();
759             }
760             else {
761                 fm = g2.getFontMetrics();
762             }
763 
764             maxAscent = fm.getMaxAscent();
765             maxDescent = fm.getMaxDescent();
766             if (maxAscent == 0) maxAscent = 10;
767             if (maxDescent == 0) maxDescent = 5;
768             if ( textToUse == RANGE_TEXT || textToUse == ALL_GLYPHS ) {
769                 /// Give slight extra room for each character
770                 maxAscent += 3;
771                 maxDescent += 3;
772                 gridWidth = fm.getMaxAdvance() + 6;
773                 gridHeight = maxAscent + maxDescent;
774                 if ( force16Cols )
775                   numCharAcross = 16;
776                 else
777                   numCharAcross = ( w - 10 ) / gridWidth;
778                 numCharDown = ( h - 10 ) / gridHeight;
779 
780                 canvasInset_X = ( w - numCharAcross * gridWidth ) / 2;
781                 canvasInset_Y = ( h - numCharDown * gridHeight ) / 2;
782                 if ( numCharDown == 0 || numCharAcross == 0 )
783                   throw new CannotDrawException( isPrinting ? CANT_FIT_PRINT : CANT_FIT_DRAW );
784 
785                 if ( !isPrinting )
786                   resetScrollbar( verticalBar.getValue() * numCharAcross );
787             }
788             else {
789                 maxDescent += fm.getLeading();
790                 canvasInset_X = 5;
791                 canvasInset_Y = 5;
792                 /// gridWidth and numCharAcross will not be used in this mode...
793                 gridHeight = maxAscent + maxDescent;
794                 numCharDown = ( h - canvasInset_Y * 2 ) / gridHeight;
795 
796                 if ( numCharDown == 0 )
797                   throw new CannotDrawException( isPrinting ? CANT_FIT_PRINT : CANT_FIT_DRAW );
798                 /// If this is text loaded from file, prepares the LineBreak'ed
799                 /// text layout at this point
800                 if ( textToUse == FILE_TEXT ) {
801                     if ( !isPrinting )
802                       f2dt.fireChangeStatus( "LineBreaking Text... Please Wait", false );
803                     lineBreakTLs = new Vector();
804                     for ( int i = 0; i < fileText.length; i++ ) {
805                         AttributedString as =
806                           new AttributedString( fileText[i], g2.getFont().getAttributes() );
807 
808                         LineBreakMeasurer lbm =
809                           new LineBreakMeasurer( as.getIterator(), g2.getFontRenderContext() );
810 
811                         while ( lbm.getPosition() < fileText[i].length() )
812                           lineBreakTLs.add( lbm.nextLayout( (float) w ));
813 
814                     }
815                 }
816                 if ( !isPrinting )
817                   resetScrollbar( verticalBar.getValue() );
818             }
819         }
820 
821         /// Calculates the amount of text that will be displayed on screen
calcTextRange()822         private void calcTextRange() {
823             String displaying = null;
824 
825             if ( textToUse == RANGE_TEXT || textToUse == ALL_GLYPHS ) {
826                 if ( isPrinting )
827                   if ( printMode == ONE_PAGE )
828                     drawStart = currentlyShownChar;
829                   else /// printMode == CUR_RANGE
830                     drawStart = numCharAcross * numCharDown * printPageNumber;
831                 else
832                   drawStart = verticalBar.getValue() * numCharAcross;
833                 if ( textToUse == RANGE_TEXT ) {
834                     drawStart += drawRange[0];
835                     drawLimit = drawRange[1];
836                 }
837                 else
838                   drawLimit = testFont.getNumGlyphs();
839                 drawEnd = drawStart + numCharAcross * numCharDown - 1;
840 
841                 if ( drawEnd >= drawLimit )
842                   drawEnd = drawLimit;
843             }
844             else {
845                 if ( isPrinting )
846                   if ( printMode == ONE_PAGE )
847                     drawStart = currentlyShownChar;
848                   else /// printMode == ALL_TEXT
849                     drawStart = numCharDown * printPageNumber;
850                 else {
851                     drawStart = verticalBar.getValue();
852                 }
853 
854                 drawEnd = drawStart + numCharDown - 1;
855 
856                 if ( textToUse == USER_TEXT )
857                   drawLimit = userText.length - 1;
858                 else
859                   drawLimit = lineBreakTLs.size() - 1;
860 
861                 if ( drawEnd >= drawLimit )
862                   drawEnd = drawLimit;
863             }
864 
865             // ABP
866             if ( drawStart > drawEnd ) {
867               drawStart = 0;
868               verticalBar.setValue(drawStart);
869             }
870 
871 
872             /// Change the status bar if not printing...
873             if ( !isPrinting ) {
874                 backupStatusString = ( "Displaying" + MS_OPENING[textToUse] +
875                                        modeSpecificNumStr( drawStart ) + " to " +
876                                        modeSpecificNumStr( drawEnd ) +
877                                        MS_CLOSING[textToUse] );
878                 f2dt.fireChangeStatus( backupStatusString, false );
879             }
880         }
881 
882         /// Draws text according to the parameters set by Font2DTest GUI
drawText( Graphics g, int w, int h )883         private void drawText( Graphics g, int w, int h ) {
884             Graphics2D g2 = (Graphics2D) g;
885             g2.setColor(Color.white);
886             g2.fillRect(0, 0, w, h);
887             g2.setColor(Color.black);
888 
889             /// sets font, RenderingHints.
890             setParams( g2 );
891 
892             /// If flag is set, recalculate fontMetrics and reset the scrollbar
893             if ( updateFontMetrics || isPrinting ) {
894                 /// NOTE: re-calculates in case G2 transform
895                 /// is something other than NONE
896                 calcFontMetrics( g2, w, h );
897                 updateFontMetrics = false;
898             }
899             /// Calculate the amount of text that can be drawn...
900             calcTextRange();
901 
902             /// Draw according to the set "Text to Use" mode
903             if ( textToUse == RANGE_TEXT || textToUse == ALL_GLYPHS ) {
904                 int charToDraw = drawStart;
905                 if ( showGrid )
906                   drawGrid( g2 );
907 
908                 for ( int i = 0; i < numCharDown && charToDraw <= drawEnd; i++ ) {
909                   for ( int j = 0; j < numCharAcross && charToDraw <= drawEnd; j++, charToDraw++ ) {
910                       int gridLocX = j * gridWidth + canvasInset_X;
911                       int gridLocY = i * gridHeight + canvasInset_Y;
912 
913                       modeSpecificDrawChar( g2, charToDraw,
914                                             gridLocX + gridWidth / 2,
915                                             gridLocY + maxAscent );
916 
917                   }
918                 }
919             }
920             else if ( textToUse == USER_TEXT ) {
921                 g2.drawRect( 0, 0, w - 1, h - 1 );
922                 for ( int i = drawStart; i <= drawEnd; i++ ) {
923                     int lineStartX = canvasInset_Y;
924                     int lineStartY = ( i - drawStart ) * gridHeight + maxAscent;
925                     modeSpecificDrawLine( g2, userText[i], lineStartX, lineStartY );
926                 }
927             }
928             else {
929                 float xPos, yPos = (float) canvasInset_Y;
930                 g2.drawRect( 0, 0, w - 1, h - 1 );
931                 for ( int i = drawStart; i <= drawEnd; i++ ) {
932                     TextLayout oneLine = (TextLayout) lineBreakTLs.elementAt( i );
933                     xPos =
934                       oneLine.isLeftToRight() ?
935                       canvasInset_X : ( (float) w - oneLine.getAdvance() - canvasInset_X );
936 
937                     float[] fmData = {0, oneLine.getAscent(), 0, oneLine.getDescent(), 0, oneLine.getLeading()};
938                     if (g2Transform != NONE) {
939                         AffineTransform at = getAffineTransform(g2Transform);
940                         at.transform( fmData, 0, fmData, 0, 3);
941                     }
942                     //yPos += oneLine.getAscent();
943                     yPos += fmData[1]; // ascent
944                     //oneLine.draw( g2, xPos, yPos );
945                     tlDrawLine( g2, oneLine, xPos, yPos );
946                     //yPos += oneLine.getDescent() + oneLine.getLeading();
947                     yPos += fmData[3] + fmData[5]; // descent + leading
948                 }
949             }
950             g2.dispose();
951         }
952 
953         /// Component paintComponent function...
954         /// Draws/Refreshes canvas according to flag(s) set by other functions
paintComponent( Graphics g )955         public void paintComponent( Graphics g ) {
956               super.paintComponent(g);
957 
958                 Dimension d = this.getSize();
959                 isPrinting = false;
960                 try {
961                     drawText( g, d.width, d.height );
962                 }
963                 catch ( CannotDrawException e ) {
964                     super.paintComponent(g);
965                     f2dt.fireChangeStatus( ERRORS[ e.id ], true );
966                     return;
967                 }
968 
969             showingError = false;
970         }
971 
972         /// Printable interface function
973         /// Component print function...
print( Graphics g, PageFormat pf, int pageIndex )974         public int print( Graphics g, PageFormat pf, int pageIndex ) {
975             if ( pageIndex == 0 ) {
976                 /// Reset the last page index to max...
977                 lastPage = Integer.MAX_VALUE;
978                 currentlyShownChar = verticalBar.getValue() * numCharAcross;
979             }
980 
981             if ( printMode == ONE_PAGE ) {
982                 if ( pageIndex > 0 )
983                   return NO_SUCH_PAGE;
984             }
985             else {
986                 if ( pageIndex > lastPage )
987                   return NO_SUCH_PAGE;
988             }
989 
990             int pageWidth = (int) pf.getImageableWidth();
991             int pageHeight = (int) pf.getImageableHeight();
992             /// Back up metrics and other drawing info before printing modifies it
993             int backupDrawStart = drawStart, backupDrawEnd = drawEnd;
994             int backupNumCharAcross = numCharAcross, backupNumCharDown = numCharDown;
995             Vector backupLineBreakTLs = null;
996             if ( textToUse == FILE_TEXT )
997               backupLineBreakTLs = (Vector) lineBreakTLs.clone();
998 
999             printPageNumber = pageIndex;
1000             isPrinting = true;
1001             /// Push the actual draw area 60 down to allow info to be printed
1002             g.translate( (int) pf.getImageableX(), (int) pf.getImageableY() + 60 );
1003             try {
1004                 drawText( g, pageWidth, pageHeight - 60 );
1005             }
1006             catch ( CannotDrawException e ) {
1007                 f2dt.fireChangeStatus( ERRORS[ e.id ], true );
1008                 return NO_SUCH_PAGE;
1009             }
1010 
1011             /// Draw information about what is being printed
1012             String hints = ( " with antialias " + antiAliasType + "and" +
1013                              " fractional metrics " + fractionalMetricsType +
1014                              " and lcd contrast = " + lcdContrast);
1015             String infoLine1 = ( "Printing" + MS_OPENING[textToUse] +
1016                                  modeSpecificNumStr( drawStart ) + " to " +
1017                                  modeSpecificNumStr( drawEnd ) + MS_CLOSING[textToUse] );
1018             String infoLine2 = ( "With " + fontName + " " + STYLES[fontStyle] + " at " +
1019                                  fontSize + " point size " + TRANSFORMS[fontTransform] );
1020             String infoLine3 = "Using " + METHODS[drawMethod] + hints;
1021             String infoLine4 = "Page: " + ( pageIndex + 1 );
1022             g.setFont( new Font( "dialog", Font.PLAIN, 12 ));
1023             g.setColor( Color.black );
1024             g.translate( 0, -60 );
1025             g.drawString( infoLine1, 15, 10 );
1026             g.drawString( infoLine2, 15, 22 );
1027             g.drawString( infoLine3, 15, 34 );
1028             g.drawString( infoLine4, 15, 46 );
1029 
1030             if ( drawEnd == drawLimit )
1031               /// This indicates that the draw will be completed with this page
1032               lastPage = pageIndex;
1033 
1034             /// Restore the changed values back...
1035             /// This is important for JScrollBar settings and LineBreak'ed TLs
1036             drawStart = backupDrawStart;
1037             drawEnd = backupDrawEnd;
1038             numCharAcross = backupNumCharAcross;
1039             numCharDown = backupNumCharDown;
1040             if ( textToUse == FILE_TEXT )
1041               lineBreakTLs = backupLineBreakTLs;
1042             return PAGE_EXISTS;
1043         }
1044 
1045         /// Ouputs the current canvas into a given PNG file
writePNG( String fileName )1046         public void writePNG( String fileName ) {
1047             try {
1048                 int w = this.getSize().width;
1049                 int h = this.getSize().height;
1050                 BufferedImage buffer = (BufferedImage) this.createImage( w, h );
1051                 Graphics2D g2 = buffer.createGraphics();
1052                 g2.setColor(Color.white);
1053                 g2.fillRect(0, 0, w, h);
1054                 g2.setColor(Color.black);
1055                 updateFontMetrics = true;
1056                 drawText(g2, w, h);
1057                 updateFontMetrics = true;
1058                 ImageIO.write(buffer, "png", new java.io.File(fileName));
1059             }
1060             catch ( Exception e ) {
1061                 f2dt.fireChangeStatus( "ERROR: Failed to Save PNG image; See stack trace", true );
1062                 e.printStackTrace();
1063             }
1064         }
1065 
1066         /// Figures out whether a character at the pointer location is valid
1067         /// And if so, updates mouse location informations, as well as
1068         /// the information on the status bar
checkMouseLoc( MouseEvent e )1069         private boolean checkMouseLoc( MouseEvent e ) {
1070             if ( gridWidth != 0 && gridHeight != 0 )
1071               if ( textToUse == RANGE_TEXT || textToUse == ALL_GLYPHS ) {
1072                   int charLocX = ( e.getX() - canvasInset_X ) / gridWidth;
1073                   int charLocY = ( e.getY() - canvasInset_Y ) / gridHeight;
1074 
1075                   /// Check to make sure the mouse click location is within drawn area
1076                   if ( charLocX >= 0 && charLocY >= 0 &&
1077                        charLocX < numCharAcross && charLocY < numCharDown ) {
1078                       int mouseOverChar =
1079                         charLocX + ( verticalBar.getValue() + charLocY ) * numCharAcross;
1080                       if ( textToUse == RANGE_TEXT )
1081                         mouseOverChar += drawRange[0];
1082                       if ( mouseOverChar > drawEnd )
1083                         return false;
1084 
1085                       mouseOverCharX = charLocX;
1086                       mouseOverCharY = charLocY;
1087                       currMouseOverChar = mouseOverChar;
1088                       /// Update status bar
1089                       f2dt.fireChangeStatus( "Pointing to" + MS_OPENING[textToUse] +
1090                                              modeSpecificNumStr( mouseOverChar ), false );
1091                       return true;
1092                   }
1093               }
1094             return false;
1095         }
1096 
1097         /// Shows (updates) the character zoom window
showZoomed()1098         public void showZoomed() {
1099             GlyphVector gv;
1100             Font backup = testFont;
1101             Point canvasLoc = this.getLocationOnScreen();
1102 
1103             /// Calculate the zoom area's location and size...
1104             int dialogOffsetX = (int) ( gridWidth * ( ZOOM - 1 ) / 2 );
1105             int dialogOffsetY = (int) ( gridHeight * ( ZOOM - 1 ) / 2 );
1106             int zoomAreaX =
1107               mouseOverCharX * gridWidth + canvasInset_X - dialogOffsetX;
1108             int zoomAreaY =
1109               mouseOverCharY * gridHeight + canvasInset_Y - dialogOffsetY;
1110             int zoomAreaWidth = (int) ( gridWidth * ZOOM );
1111             int zoomAreaHeight = (int) ( gridHeight * ZOOM );
1112 
1113             /// Position and set size of zoom window as needed
1114             zoomWindow.setLocation( canvasLoc.x + zoomAreaX, canvasLoc.y + zoomAreaY );
1115             if ( !nowZooming ) {
1116                 if ( zoomWindow.getWarningString() != null )
1117                   /// If this is not opened as a "secure" window,
1118                   /// it has a banner below the zoom dialog which makes it look really BAD
1119                   /// So enlarge it by a bit
1120                   zoomWindow.setSize( zoomAreaWidth + 1, zoomAreaHeight + 20 );
1121                 else
1122                   zoomWindow.setSize( zoomAreaWidth + 1, zoomAreaHeight + 1 );
1123             }
1124 
1125             /// Prepare zoomed image
1126             zoomImage =
1127               (BufferedImage) zoomWindow.createImage( zoomAreaWidth + 1,
1128                                                       zoomAreaHeight + 1 );
1129             Graphics2D g2 = (Graphics2D) zoomImage.getGraphics();
1130             testFont = testFont.deriveFont( fontSize * ZOOM );
1131             setParams( g2 );
1132             g2.setColor( Color.white );
1133             g2.fillRect( 0, 0, zoomAreaWidth, zoomAreaHeight );
1134             g2.setColor( Color.black );
1135             g2.drawRect( 0, 0, zoomAreaWidth, zoomAreaHeight );
1136             modeSpecificDrawChar( g2, currMouseOverChar,
1137                                   zoomAreaWidth / 2, (int) ( maxAscent * ZOOM ));
1138             g2.dispose();
1139             if ( !nowZooming )
1140               zoomWindow.show();
1141             /// This is sort of redundant... since there is a paint function
1142             /// inside zoomWindow definition that does the drawImage.
1143             /// (I should be able to call just repaint() here)
1144             /// However, for some reason, that paint function fails to respond
1145             /// from second time and on; So I have to force the paint here...
1146             zoomWindow.getGraphics().drawImage( zoomImage, 0, 0, this );
1147 
1148             nowZooming = true;
1149             prevZoomChar = currMouseOverChar;
1150             testFont = backup;
1151 
1152             // Windows does not repaint correctly, after
1153             // a zoom. Thus, we need to force the canvas
1154             // to repaint, but only once. After the first repaint,
1155             // everything stabilizes. [ABP]
1156             if ( firstTime() ) {
1157                 refresh();
1158             }
1159         }
1160 
1161         /// Listener Functions
1162 
1163         /// MouseListener interface function
1164         /// Zooms a character when mouse is pressed above it
mousePressed( MouseEvent e )1165         public void mousePressed( MouseEvent e ) {
1166             if ( !showingError) {
1167                 if ( checkMouseLoc( e )) {
1168                     showZoomed();
1169                     this.setCursor( blankCursor );
1170                 }
1171             }
1172         }
1173 
1174         /// MouseListener interface function
1175         /// Redraws the area that was drawn over by zoomed character
mouseReleased( MouseEvent e )1176         public void mouseReleased( MouseEvent e ) {
1177             if ( textToUse == RANGE_TEXT || textToUse == ALL_GLYPHS ) {
1178                 if ( nowZooming )
1179                   zoomWindow.hide();
1180                 nowZooming = false;
1181             }
1182             this.setCursor( Cursor.getDefaultCursor() );
1183         }
1184 
1185         /// MouseListener interface function
1186         /// Resets the status bar to display range instead of a specific character
mouseExited( MouseEvent e )1187         public void mouseExited( MouseEvent e ) {
1188             if ( !showingError && !nowZooming )
1189               f2dt.fireChangeStatus( backupStatusString, false );
1190         }
1191 
1192         /// MouseMotionListener interface function
1193         /// Adjusts the status bar message when mouse moves over a character
mouseMoved( MouseEvent e )1194         public void mouseMoved( MouseEvent e ) {
1195             if ( !showingError ) {
1196                 if ( !checkMouseLoc( e ))
1197                   f2dt.fireChangeStatus( backupStatusString, false );
1198             }
1199         }
1200 
1201         /// MouseMotionListener interface function
1202         /// Scrolls the zoomed character when mouse is dragged
mouseDragged( MouseEvent e )1203         public void mouseDragged( MouseEvent e ) {
1204             if ( !showingError )
1205               if ( nowZooming ) {
1206                   if ( checkMouseLoc( e ) && currMouseOverChar != prevZoomChar )
1207                     showZoomed();
1208               }
1209         }
1210 
1211         /// Empty function to comply with interface requirement
mouseClicked( MouseEvent e )1212         public void mouseClicked( MouseEvent e ) {}
mouseEntered( MouseEvent e )1213         public void mouseEntered( MouseEvent e ) {}
1214     }
1215 
1216     private final class CannotDrawException extends RuntimeException {
1217         /// Error ID
1218         public final int id;
1219 
CannotDrawException( int i )1220         public CannotDrawException( int i ) {
1221             id = i;
1222         }
1223     }
1224 
1225     enum FMValues {
1226        FMDEFAULT ("DEFAULT",  VALUE_FRACTIONALMETRICS_DEFAULT),
1227        FMOFF     ("OFF",      VALUE_FRACTIONALMETRICS_OFF),
1228        FMON      ("ON",       VALUE_FRACTIONALMETRICS_ON);
1229 
1230         private String name;
1231         private Object hint;
1232 
1233         private static FMValues[] valArray;
1234 
FMValues(String s, Object o)1235         FMValues(String s, Object o) {
1236             name = s;
1237             hint = o;
1238         }
1239 
toString()1240         public String toString() {
1241             return name;
1242         }
1243 
getHint()1244        public Object getHint() {
1245            return hint;
1246        }
getValue(int ordinal)1247        public static Object getValue(int ordinal) {
1248            if (valArray == null) {
1249                valArray = (FMValues[])EnumSet.allOf(FMValues.class).toArray(new FMValues[0]);
1250            }
1251            for (int i=0;i<valArray.length;i++) {
1252                if (valArray[i].ordinal() == ordinal) {
1253                    return valArray[i];
1254                }
1255            }
1256            return valArray[0];
1257        }
getArray()1258        private static FMValues[] getArray() {
1259            if (valArray == null) {
1260                valArray = (FMValues[])EnumSet.allOf(FMValues.class).toArray(new FMValues[0]);
1261            }
1262            return valArray;
1263        }
1264 
getHintVal(Object hint)1265        public static int getHintVal(Object hint) {
1266            getArray();
1267            for (int i=0;i<valArray.length;i++) {
1268                if (valArray[i].getHint() == hint) {
1269                    return i;
1270                }
1271            }
1272            return 0;
1273        }
1274     }
1275 
1276    enum AAValues {
1277        AADEFAULT ("DEFAULT",  VALUE_TEXT_ANTIALIAS_DEFAULT),
1278        AAOFF     ("OFF",      VALUE_TEXT_ANTIALIAS_OFF),
1279        AAON      ("ON",       VALUE_TEXT_ANTIALIAS_ON),
1280        AAGASP    ("GASP",     VALUE_TEXT_ANTIALIAS_GASP),
1281        AALCDHRGB ("LCD_HRGB", VALUE_TEXT_ANTIALIAS_LCD_HRGB),
1282        AALCDHBGR ("LCD_HBGR", VALUE_TEXT_ANTIALIAS_LCD_HBGR),
1283        AALCDVRGB ("LCD_VRGB", VALUE_TEXT_ANTIALIAS_LCD_VRGB),
1284        AALCDVBGR ("LCD_VBGR", VALUE_TEXT_ANTIALIAS_LCD_VBGR);
1285 
1286         private String name;
1287         private Object hint;
1288 
1289         private static AAValues[] valArray;
1290 
AAValues(String s, Object o)1291         AAValues(String s, Object o) {
1292             name = s;
1293             hint = o;
1294         }
1295 
toString()1296         public String toString() {
1297             return name;
1298         }
1299 
getHint()1300        public Object getHint() {
1301            return hint;
1302        }
1303 
isLCDMode(Object o)1304        public static boolean isLCDMode(Object o) {
1305            return (o instanceof AAValues &&
1306                    ((AAValues)o).ordinal() >= AALCDHRGB.ordinal());
1307        }
1308 
getValue(int ordinal)1309        public static Object getValue(int ordinal) {
1310            if (valArray == null) {
1311                valArray = (AAValues[])EnumSet.allOf(AAValues.class).toArray(new AAValues[0]);
1312            }
1313            for (int i=0;i<valArray.length;i++) {
1314                if (valArray[i].ordinal() == ordinal) {
1315                    return valArray[i];
1316                }
1317            }
1318            return valArray[0];
1319        }
1320 
getArray()1321        private static AAValues[] getArray() {
1322            if (valArray == null) {
1323                Object [] oa = EnumSet.allOf(AAValues.class).toArray(new AAValues[0]);
1324                valArray = (AAValues[])(EnumSet.allOf(AAValues.class).toArray(new AAValues[0]));
1325            }
1326            return valArray;
1327        }
1328 
getHintVal(Object hint)1329        public static int getHintVal(Object hint) {
1330            getArray();
1331            for (int i=0;i<valArray.length;i++) {
1332                if (valArray[i].getHint() == hint) {
1333                    return i;
1334                }
1335            }
1336            return 0;
1337        }
1338 
1339     }
1340 
1341     private static Integer defaultContrast;
getDefaultLCDContrast()1342     static Integer getDefaultLCDContrast() {
1343         if (defaultContrast == null) {
1344             GraphicsConfiguration gc =
1345             GraphicsEnvironment.getLocalGraphicsEnvironment().
1346                 getDefaultScreenDevice().getDefaultConfiguration();
1347         Graphics2D g2d =
1348             (Graphics2D)(gc.createCompatibleImage(1,1).getGraphics());
1349         defaultContrast = (Integer)
1350             g2d.getRenderingHint(RenderingHints.KEY_TEXT_LCD_CONTRAST);
1351         }
1352         return defaultContrast;
1353     }
1354 }
1355