1/*
2 * Copyright (c) 2000, 2013, 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
42import java.awt.BorderLayout;
43import java.awt.Color;
44import java.awt.Cursor;
45import java.awt.Dimension;
46import java.awt.Font;
47import java.awt.FontMetrics;
48import java.awt.Graphics;
49import java.awt.Graphics2D;
50import java.awt.GraphicsConfiguration;
51import java.awt.GraphicsEnvironment;
52import java.awt.Point;
53import java.awt.Rectangle;
54import java.awt.RenderingHints;
55import java.awt.Toolkit;
56import java.awt.event.AdjustmentEvent;
57import java.awt.event.AdjustmentListener;
58import java.awt.event.ComponentAdapter;
59import java.awt.event.ComponentEvent;
60import java.awt.event.MouseEvent;
61import java.awt.event.MouseListener;
62import java.awt.event.MouseMotionListener;
63import java.awt.font.FontRenderContext;
64import java.awt.font.GlyphVector;
65import java.awt.font.LineBreakMeasurer;
66import java.awt.font.TextLayout;
67import java.awt.geom.AffineTransform;
68import java.awt.geom.NoninvertibleTransformException;
69import java.awt.geom.Rectangle2D;
70import java.awt.image.BufferedImage;
71import java.awt.print.PageFormat;
72import java.awt.print.Printable;
73import java.awt.print.PrinterJob;
74import java.io.BufferedOutputStream;
75import java.io.FileOutputStream;
76import java.text.AttributedString;
77import java.util.EnumSet;
78import java.util.Vector;
79
80import javax.imageio.*;
81import javax.swing.*;
82
83import 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
95public 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
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
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
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
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
226    public void setFontParams(Object obj, float size,
227                              int style, int transform) {
228        setFontParams( (String)obj, size, style, transform );
229    }
230
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
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
269    public void setDrawMethod( int i ) {
270        drawMethod = i;
271        fc.repaint();
272    }
273
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
294    public void setGridDisplay( boolean b ) {
295        showGrid = b;
296        fc.repaint();
297    }
298
299    public void setForce16Columns( boolean b ) {
300        force16Cols = b;
301        updateFontMetrics = true;
302        fc.repaint();
303    }
304
305    /// Prints out the text display area
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
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
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
350    public String[] getFontInfo() {
351        return fontInfos;
352    }
353
354    /// Collects the currectly set options and returns them as string
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
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
399    public void doSavePNG( String fileName ) {
400        fc.writePNG( fileName );
401    }
402
403    /// When scrolled using the scroll bar, update the backbuffer
404    public void adjustmentValueChanged( AdjustmentEvent e ) {
405        fc.repaint();
406    }
407
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
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
496        public boolean firstTime() { return firstTime; }
497        public void refresh() {
498            firstTime = false;
499            repaint();
500        }
501
502        /// Sets the font, hints, according to the set parameters
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)
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)
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
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
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)
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)
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
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
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
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
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...
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
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
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
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
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
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
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
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
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
1212        public void mouseClicked( 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
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
1235        FMValues(String s, Object o) {
1236            name = s;
1237            hint = o;
1238        }
1239
1240        public String toString() {
1241            return name;
1242        }
1243
1244       public Object getHint() {
1245           return hint;
1246       }
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       }
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
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
1291        AAValues(String s, Object o) {
1292            name = s;
1293            hint = o;
1294        }
1295
1296        public String toString() {
1297            return name;
1298        }
1299
1300       public Object getHint() {
1301           return hint;
1302       }
1303
1304       public static boolean isLCDMode(Object o) {
1305           return (o instanceof AAValues &&
1306                   ((AAValues)o).ordinal() >= AALCDHRGB.ordinal());
1307       }
1308
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
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
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;
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