Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/demo/jfc/Font2DTest/FontPanel.java
38829 views
/*1* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.2*3* Redistribution and use in source and binary forms, with or without4* modification, are permitted provided that the following conditions5* are met:6*7* - Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9*10* - Redistributions in binary form must reproduce the above copyright11* notice, this list of conditions and the following disclaimer in the12* documentation and/or other materials provided with the distribution.13*14* - Neither the name of Oracle nor the names of its15* contributors may be used to endorse or promote products derived16* from this software without specific prior written permission.17*18* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS19* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,20* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR21* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR22* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,23* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,24* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR25* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF26* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING27* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS28* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.29*/3031/*32* This source code is provided to illustrate the usage of a given feature33* or technique and has been deliberately simplified. Additional steps34* required for a production-quality application, such as security checks,35* input validation and proper error handling, might not be present in36* this sample code.37*/38394041import java.awt.BorderLayout;42import java.awt.Color;43import java.awt.Cursor;44import java.awt.Dimension;45import java.awt.Font;46import java.awt.FontMetrics;47import java.awt.Graphics;48import java.awt.Graphics2D;49import java.awt.GraphicsConfiguration;50import java.awt.GraphicsEnvironment;51import java.awt.Point;52import java.awt.Rectangle;53import java.awt.RenderingHints;54import java.awt.Toolkit;55import java.awt.event.AdjustmentEvent;56import java.awt.event.AdjustmentListener;57import java.awt.event.ComponentAdapter;58import java.awt.event.ComponentEvent;59import java.awt.event.MouseEvent;60import java.awt.event.MouseListener;61import java.awt.event.MouseMotionListener;62import java.awt.font.FontRenderContext;63import java.awt.font.GlyphVector;64import java.awt.font.LineBreakMeasurer;65import java.awt.font.TextLayout;66import java.awt.geom.AffineTransform;67import java.awt.geom.NoninvertibleTransformException;68import java.awt.geom.Rectangle2D;69import java.awt.image.BufferedImage;70import java.awt.print.PageFormat;71import java.awt.print.Printable;72import java.awt.print.PrinterJob;73import java.io.BufferedOutputStream;74import java.io.FileOutputStream;75import java.text.AttributedString;76import java.util.EnumSet;77import java.util.Vector;7879import javax.imageio.*;80import javax.swing.*;8182import static java.awt.RenderingHints.*;8384/**85* FontPanel.java86*87* @author Shinsuke Fukuda88* @author Ankit Patel [Conversion to Swing - 01/07/30]89*/9091/// This panel is combination of the text drawing area of Font2DTest92/// and the custom controlled scroll bar9394public final class FontPanel extends JPanel implements AdjustmentListener {9596/// Drawing Option Constants97private final String STYLES[] =98{ "plain", "bold", "italic", "bold italic" };99100private final int NONE = 0;101private final int SCALE = 1;102private final int SHEAR = 2;103private final int ROTATE = 3;104private final String TRANSFORMS[] =105{ "with no transforms", "with scaling", "with Shearing", "with rotation" };106107private final int DRAW_STRING = 0;108private final int DRAW_CHARS = 1;109private final int DRAW_BYTES = 2;110private final int DRAW_GLYPHV = 3;111private final int TL_DRAW = 4;112private final int GV_OUTLINE = 5;113private final int TL_OUTLINE = 6;114private final String METHODS[] = {115"drawString", "drawChars", "drawBytes", "drawGlyphVector",116"TextLayout.draw", "GlyphVector.getOutline", "TextLayout.getOutline" };117118public final int RANGE_TEXT = 0;119public final int ALL_GLYPHS = 1;120public final int USER_TEXT = 2;121public final int FILE_TEXT = 3;122private final String MS_OPENING[] =123{ " Unicode ", " Glyph Code ", " lines ", " lines " };124private final String MS_CLOSING[] =125{ "", "", " of User Text ", " of LineBreakMeasurer-reformatted Text " };126127/// General Graphics Variable128private final JScrollBar verticalBar;129private final FontCanvas fc;130private boolean updateBackBuffer = true;131private boolean updateFontMetrics = true;132private boolean updateFont = true;133private boolean force16Cols = false;134public boolean showingError = false;135private int g2Transform = NONE; /// ABP136137/// Printing constants and variables138public final int ONE_PAGE = 0;139public final int CUR_RANGE = 1;140public final int ALL_TEXT = 2;141private int printMode = ONE_PAGE;142private PageFormat page = null;143private PrinterJob printer = null;144145/// Text drawing variables146private String fontName = "Dialog";147private float fontSize = 12;148private int fontStyle = Font.PLAIN;149private int fontTransform = NONE;150private Font testFont = null;151private Object antiAliasType = VALUE_TEXT_ANTIALIAS_DEFAULT;152private Object fractionalMetricsType = VALUE_FRACTIONALMETRICS_DEFAULT;153private Object lcdContrast = getDefaultLCDContrast();154private int drawMethod = DRAW_STRING;155private int textToUse = RANGE_TEXT;156private String userText[] = null;157private String fileText[] = null;158private int drawRange[] = { 0x0000, 0x007f };159private String fontInfos[] = new String[2];160private boolean showGrid = true;161162/// Parent Font2DTest panel163private final Font2DTest f2dt;164private final JFrame parent;165166public FontPanel( Font2DTest demo, JFrame f ) {167f2dt = demo;168parent = f;169170verticalBar = new JScrollBar ( JScrollBar.VERTICAL );171fc = new FontCanvas();172173this.setLayout( new BorderLayout() );174this.add( "Center", fc );175this.add( "East", verticalBar );176177verticalBar.addAdjustmentListener( this );178this.addComponentListener( new ComponentAdapter() {179public void componentResized( ComponentEvent e ) {180updateBackBuffer = true;181updateFontMetrics = true;182}183});184185/// Initialize font and its infos186testFont = new Font(fontName, fontStyle, (int)fontSize);187if ((float)((int)fontSize) != fontSize) {188testFont = testFont.deriveFont(fontSize);189}190updateFontInfo();191}192193public Dimension getPreferredSize() {194return new Dimension(600, 200);195}196197/// Functions called by the main programs to set the various parameters198199public void setTransformG2( int transform ) {200g2Transform = transform;201updateBackBuffer = true;202updateFontMetrics = true;203fc.repaint();204}205206/// convenience fcn to create AffineTransform of appropriate type207private AffineTransform getAffineTransform( int transform ) {208/// ABP209AffineTransform at = new AffineTransform();210switch ( transform )211{212case SCALE:213at.setToScale( 1.5f, 1.5f ); break;214case ROTATE:215at.setToRotation( Math.PI / 6 ); break;216case SHEAR:217at.setToShear( 0.4f, 0 ); break;218case NONE:219break;220default:221//System.err.println( "Illegal G2 Transform Arg: " + transform);222break;223}224225return at;226}227228public void setFontParams(Object obj, float size,229int style, int transform) {230setFontParams( (String)obj, size, style, transform );231}232233public void setFontParams(String name, float size,234int style, int transform) {235boolean fontModified = false;236if ( !name.equals( fontName ) || style != fontStyle )237fontModified = true;238239fontName = name;240fontSize = size;241fontStyle = style;242fontTransform = transform;243244/// Recreate the font as specified245testFont = new Font(fontName, fontStyle, (int)fontSize);246if ((float)((int)fontSize) != fontSize) {247testFont = testFont.deriveFont(fontSize);248}249250if ( fontTransform != NONE ) {251AffineTransform at = getAffineTransform( fontTransform );252testFont = testFont.deriveFont( at );253}254updateBackBuffer = true;255updateFontMetrics = true;256fc.repaint();257if ( fontModified ) {258/// Tell main panel to update the font info259updateFontInfo();260f2dt.fireUpdateFontInfo();261}262}263264public void setRenderingHints( Object aa, Object fm, Object contrast) {265antiAliasType = ((AAValues)aa).getHint();266fractionalMetricsType = ((FMValues)fm).getHint();267lcdContrast = contrast;268updateBackBuffer = true;269updateFontMetrics = true;270fc.repaint();271}272273public void setDrawMethod( int i ) {274drawMethod = i;275updateBackBuffer = true;276fc.repaint();277}278279public void setTextToDraw( int i, int range[],280String textSet[], String fileData[] ) {281textToUse = i;282283if ( textToUse == RANGE_TEXT )284drawRange = range;285else if ( textToUse == ALL_GLYPHS )286drawMethod = DRAW_GLYPHV;287else if ( textToUse == USER_TEXT )288userText = textSet;289else if ( textToUse == FILE_TEXT ) {290fileText = fileData;291drawMethod = TL_DRAW;292}293294updateBackBuffer = true;295updateFontMetrics = true;296fc.repaint();297updateFontInfo();298}299300public void setGridDisplay( boolean b ) {301showGrid = b;302updateBackBuffer = true;303fc.repaint();304}305306public void setForce16Columns( boolean b ) {307force16Cols = b;308updateBackBuffer = true;309updateFontMetrics = true;310fc.repaint();311}312313/// Prints out the text display area314public void doPrint( int i ) {315if ( printer == null ) {316printer = PrinterJob.getPrinterJob();317page = printer.defaultPage();318}319printMode = i;320printer.setPrintable( fc, page );321322if ( printer.printDialog() ) {323try {324printer.print();325}326catch ( Exception e ) {327f2dt.fireChangeStatus( "ERROR: Printing Failed; See Stack Trace", true );328}329}330}331332/// Displays the page setup dialog and updates PageFormat info333public void doPageSetup() {334if ( printer == null ) {335printer = PrinterJob.getPrinterJob();336page = printer.defaultPage();337}338page = printer.pageDialog( page );339}340341/// Obtains the information about selected font342private void updateFontInfo() {343int numGlyphs = 0, numCharsInRange = drawRange[1] - drawRange[0] + 1;344fontInfos[0] = "Font Face Name: " + testFont.getFontName();345fontInfos[1] = "Glyphs in This Range: ";346347if ( textToUse == RANGE_TEXT ) {348for ( int i = drawRange[0]; i < drawRange[1]; i++ )349if ( testFont.canDisplay( i ))350numGlyphs++;351fontInfos[1] = fontInfos[1] + numGlyphs + " / " + numCharsInRange;352}353else354fontInfos[1] = null;355}356357/// Accessor for the font information358public String[] getFontInfo() {359return fontInfos;360}361362/// Collects the currectly set options and returns them as string363public String getCurrentOptions() {364/// Create a new String to store the options365/// The array will contain all 8 setting (font name, size...) and366/// character range or user text data used (no file text data)367int userTextSize = 0;368String options;369370options = ( fontName + "\n" + fontSize + "\n" + fontStyle + "\n" +371fontTransform + "\n" + g2Transform + "\n"+372textToUse + "\n" + drawMethod + "\n" +373AAValues.getHintVal(antiAliasType) + "\n" +374FMValues.getHintVal(fractionalMetricsType) + "\n" +375lcdContrast + "\n");376if ( textToUse == USER_TEXT )377for ( int i = 0; i < userText.length; i++ )378options += ( userText[i] + "\n" );379380return options;381}382383/// Reload all options and refreshes the canvas384public void loadOptions( boolean grid, boolean force16, int start, int end,385String name, float size, int style,386int transform, int g2transform,387int text, int method, int aa, int fm,388int contrast, String user[] ) {389int range[] = { start, end };390391/// Since repaint call has a low priority, these functions will finish392/// before the actual repainting is done393setGridDisplay( grid );394setForce16Columns( force16 );395// previous call to readTextFile has already set the text to draw396if (textToUse != FILE_TEXT) {397setTextToDraw( text, range, user, null );398}399setFontParams( name, size, style, transform );400setTransformG2( g2transform ); // ABP401setDrawMethod( method );402setRenderingHints(AAValues.getValue(aa), FMValues.getValue(fm),403new Integer(contrast));404}405406/// Writes the current screen to PNG file407public void doSavePNG( String fileName ) {408fc.writePNG( fileName );409}410411/// When scrolled using the scroll bar, update the backbuffer412public void adjustmentValueChanged( AdjustmentEvent e ) {413updateBackBuffer = true;414fc.repaint();415}416417public void paintComponent( Graphics g ) {418// Windows does not repaint correctly, after419// a zoom. Thus, we need to force the canvas420// to repaint, but only once. After the first repaint,421// everything stabilizes. [ABP]422fc.repaint();423}424425/// Inner class definition...426427/// Inner panel that holds the actual drawing area and its routines428private class FontCanvas extends JPanel implements MouseListener, MouseMotionListener, Printable {429430/// Number of characters that will fit across and down this canvas431private int numCharAcross, numCharDown;432433/// First and last character/line that will be drawn434/// Limit is the end of range/text where no more draw will be done435private int drawStart, drawEnd, drawLimit;436437/// FontMetrics variables438/// Here, gridWidth is equivalent to maxAdvance (slightly bigger though)439/// and gridHeight is equivalent to lineHeight440private int maxAscent, maxDescent, gridWidth = 0, gridHeight = 0;441442/// Offset from the top left edge of the canvas where the draw will start443private int canvasInset_X = 5, canvasInset_Y = 5;444445/// Offscreen buffer of this canvas446private BufferedImage backBuffer = null;447448/// LineBreak'ed TextLayout vector449private Vector lineBreakTLs = null;450451/// Whether the current draw command requested is for printing452private boolean isPrinting = false;453454/// Other printing infos455private int lastPage, printPageNumber, currentlyShownChar = 0;456private final int PR_OFFSET = 10;457private final int PR_TITLE_LINEHEIGHT = 30;458459/// Information about zooming (used with range text draw)460private final JWindow zoomWindow;461private BufferedImage zoomImage = null;462private int mouseOverCharX = -1, mouseOverCharY = -1;463private int currMouseOverChar = -1, prevZoomChar = -1;464private float ZOOM = 2.0f;465private boolean nowZooming = false;466private boolean firstTime = true;467// ABP468469/// Status bar message backup470private String backupStatusString = null;471472/// Error constants473private final String ERRORS[] = {474"ERROR: drawBytes cannot handle characters beyond 0x00FF. Select different range or draw methods.",475"ERROR: Cannot fit text with the current font size. Resize the window or use smaller font size.",476"ERROR: Cannot print with the current font size. Use smaller font size.",477};478479private final int DRAW_BYTES_ERROR = 0;480private final int CANT_FIT_DRAW = 1;481private final int CANT_FIT_PRINT = 2;482483/// Other variables484private final Cursor blankCursor;485486public FontCanvas() {487this.addMouseListener( this );488this.addMouseMotionListener( this );489this.setForeground( Color.black );490this.setBackground( Color.white );491492/// Creates an invisble pointer by giving it bogus image493/// Possibly find a workaround for this...494Toolkit tk = Toolkit.getDefaultToolkit();495byte bogus[] = { (byte) 0 };496blankCursor =497tk.createCustomCursor( tk.createImage( bogus ), new Point(0, 0), "" );498499zoomWindow = new JWindow( parent ) {500public void paint( Graphics g ) {501g.drawImage( zoomImage, 0, 0, zoomWindow );502}503};504zoomWindow.setCursor( blankCursor );505zoomWindow.pack();506}507508public boolean firstTime() { return firstTime; }509public void refresh() {510firstTime = false;511updateBackBuffer = true;512repaint();513}514515/// Sets the font, hints, according to the set parameters516private void setParams( Graphics2D g2 ) {517g2.setFont( testFont );518g2.setRenderingHint(KEY_TEXT_ANTIALIASING, antiAliasType);519g2.setRenderingHint(KEY_FRACTIONALMETRICS, fractionalMetricsType);520g2.setRenderingHint(KEY_TEXT_LCD_CONTRAST, lcdContrast);521/* I am preserving a somewhat dubious behaviour of this program.522* Outline text would be drawn anti-aliased by setting the523* graphics anti-aliasing hint if the text anti-aliasing hint524* was set. The dubious element here is that people simply525* using this program may think this is built-in behaviour526* but its not - at least not when the app explicitly draws527* outline text.528* This becomes more dubious in cases such as "GASP" where the529* size at which text is AA'ed is not something you can easily530* calculate, so mimicing that behaviour isn't going to be easy.531* So I precisely preserve the behaviour : this is done only532* if the AA value is "ON". Its not applied in the other cases.533*/534if (antiAliasType == VALUE_TEXT_ANTIALIAS_ON &&535(drawMethod == TL_OUTLINE || drawMethod == GV_OUTLINE)) {536g2.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON);537} else {538g2.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_OFF);539}540}541542/// Draws the grid (Used for unicode/glyph range drawing)543private void drawGrid( Graphics2D g2 ) {544int totalGridWidth = numCharAcross * gridWidth;545int totalGridHeight = numCharDown * gridHeight;546547g2.setColor( Color.black );548for ( int i = 0; i < numCharDown + 1; i++ )549g2.drawLine( canvasInset_X, i * gridHeight + canvasInset_Y,550canvasInset_X + totalGridWidth, i * gridHeight + canvasInset_Y );551for ( int i = 0; i < numCharAcross + 1; i++ )552g2.drawLine( i * gridWidth + canvasInset_X, canvasInset_Y,553i * gridWidth + canvasInset_X, canvasInset_Y + totalGridHeight );554}555556/// Draws one character at time onto the canvas according to557/// the method requested (Used for RANGE_TEXT and ALL_GLYPHS)558public void modeSpecificDrawChar( Graphics2D g2, int charCode,559int baseX, int baseY ) {560GlyphVector gv;561int oneGlyph[] = { charCode };562char charArray[] = Character.toChars( charCode );563564FontRenderContext frc = g2.getFontRenderContext();565AffineTransform oldTX = g2.getTransform();566567/// Create GlyphVector to measure the exact visual advance568/// Using that number, adjust the position of the character drawn569if ( textToUse == ALL_GLYPHS )570gv = testFont.createGlyphVector( frc, oneGlyph );571else572gv = testFont.createGlyphVector( frc, charArray );573Rectangle2D r2d2 = gv.getPixelBounds(frc, 0, 0);574int shiftedX = baseX;575// getPixelBounds returns a result in device space.576// we need to convert back to user space to be able to577// calculate the shift as baseX is in user space.578try {579double pt[] = new double[4];580pt[0] = r2d2.getX();581pt[1] = r2d2.getY();582pt[2] = r2d2.getX()+r2d2.getWidth();583pt[3] = r2d2.getY()+r2d2.getHeight();584oldTX.inverseTransform(pt,0,pt,0,2);585shiftedX = baseX - (int) ( pt[2] / 2 + pt[0] );586} catch (NoninvertibleTransformException e) {587}588589/// ABP - keep track of old tform, restore it later590591g2.translate( shiftedX, baseY );592g2.transform( getAffineTransform( g2Transform ) );593594if ( textToUse == ALL_GLYPHS )595g2.drawGlyphVector( gv, 0f, 0f );596else {597if ( testFont.canDisplay( charCode ))598g2.setColor( Color.black );599else {600g2.setColor( Color.lightGray );601}602603switch ( drawMethod ) {604case DRAW_STRING:605g2.drawString( new String( charArray ), 0, 0 );606break;607case DRAW_CHARS:608g2.drawChars( charArray, 0, 1, 0, 0 );609break;610case DRAW_BYTES:611if ( charCode > 0xff )612throw new CannotDrawException( DRAW_BYTES_ERROR );613byte oneByte[] = { (byte) charCode };614g2.drawBytes( oneByte, 0, 1, 0, 0 );615break;616case DRAW_GLYPHV:617g2.drawGlyphVector( gv, 0f, 0f );618break;619case TL_DRAW:620TextLayout tl = new TextLayout( new String( charArray ), testFont, frc );621tl.draw( g2, 0f, 0f );622break;623case GV_OUTLINE:624r2d2 = gv.getVisualBounds();625shiftedX = baseX - (int) ( r2d2.getWidth() / 2 + r2d2.getX() );626g2.draw( gv.getOutline( 0f, 0f ));627break;628case TL_OUTLINE:629r2d2 = gv.getVisualBounds();630shiftedX = baseX - (int) ( r2d2.getWidth() / 2 + r2d2.getX() );631TextLayout tlo =632new TextLayout( new String( charArray ), testFont,633g2.getFontRenderContext() );634g2.draw( tlo.getOutline( null ));635}636}637638/// ABP - restore old tform639g2.setTransform ( oldTX );640}641642/// Draws one line of text at given position643private void modeSpecificDrawLine( Graphics2D g2, String line,644int baseX, int baseY ) {645/// ABP - keep track of old tform, restore it later646AffineTransform oldTx = null;647oldTx = g2.getTransform();648g2.translate( baseX, baseY );649g2.transform( getAffineTransform( g2Transform ) );650651switch ( drawMethod ) {652case DRAW_STRING:653g2.drawString( line, 0, 0 );654break;655case DRAW_CHARS:656g2.drawChars( line.toCharArray(), 0, line.length(), 0, 0 );657break;658case DRAW_BYTES:659try {660byte lineBytes[] = line.getBytes( "ISO-8859-1" );661g2.drawBytes( lineBytes, 0, lineBytes.length, 0, 0 );662}663catch ( Exception e ) {664e.printStackTrace();665}666break;667case DRAW_GLYPHV:668GlyphVector gv =669testFont.createGlyphVector( g2.getFontRenderContext(), line );670g2.drawGlyphVector( gv, (float) 0, (float) 0 );671break;672case TL_DRAW:673TextLayout tl = new TextLayout( line, testFont,674g2.getFontRenderContext() );675tl.draw( g2, (float) 0, (float) 0 );676break;677case GV_OUTLINE:678GlyphVector gvo =679testFont.createGlyphVector( g2.getFontRenderContext(), line );680g2.draw( gvo.getOutline( (float) 0, (float) 0 ));681break;682case TL_OUTLINE:683TextLayout tlo =684new TextLayout( line, testFont,685g2.getFontRenderContext() );686AffineTransform at = new AffineTransform();687g2.draw( tlo.getOutline( at ));688}689690/// ABP - restore old tform691g2.setTransform ( oldTx );692693}694695/// Draws one line of text at given position696private void tlDrawLine( Graphics2D g2, TextLayout tl,697float baseX, float baseY ) {698/// ABP - keep track of old tform, restore it later699AffineTransform oldTx = null;700oldTx = g2.getTransform();701g2.translate( baseX, baseY );702g2.transform( getAffineTransform( g2Transform ) );703704tl.draw( g2, (float) 0, (float) 0 );705706/// ABP - restore old tform707g2.setTransform ( oldTx );708709}710711712/// If textToUse is set to range drawing, then convert713/// int to hex string and prepends 0s to make it length 4714/// Otherwise line number was fed; simply return number + 1 converted to String715/// (This is because first line is 1, not 0)716private String modeSpecificNumStr( int i ) {717if ( textToUse == USER_TEXT || textToUse == FILE_TEXT )718return String.valueOf( i + 1 );719720StringBuffer s = new StringBuffer( Integer.toHexString( i ));721while ( s.length() < 4 )722s.insert( 0, "0" );723return s.toString().toUpperCase();724}725726/// Resets the scrollbar to display correct range of text currently on screen727/// (This scrollbar is not part of a "ScrollPane". It merely simulates its effect by728/// indicating the necessary area to be drawn within the panel.729/// By doing this, it prevents creating gigantic panel when large text range,730/// i.e. CJK Ideographs, is requested)731private void resetScrollbar( int oldValue ) {732int totalNumRows = 1, numCharToDisplay;733if ( textToUse == RANGE_TEXT || textToUse == ALL_GLYPHS ) {734if ( textToUse == RANGE_TEXT )735numCharToDisplay = drawRange[1] - drawRange[0];736else /// textToUse == ALL_GLYPHS737numCharToDisplay = testFont.getNumGlyphs();738739totalNumRows = numCharToDisplay / numCharAcross;740if ( numCharToDisplay % numCharAcross != 0 )741totalNumRows++;742if ( oldValue / numCharAcross > totalNumRows )743oldValue = 0;744745verticalBar.setValues( oldValue / numCharAcross,746numCharDown, 0, totalNumRows );747}748else {749if ( textToUse == USER_TEXT )750totalNumRows = userText.length;751else /// textToUse == FILE_TEXT;752totalNumRows = lineBreakTLs.size();753verticalBar.setValues( oldValue, numCharDown, 0, totalNumRows );754}755if ( totalNumRows <= numCharDown && drawStart == 0) {756verticalBar.setEnabled( false );757}758else {759verticalBar.setEnabled( true );760}761}762763/// Calculates the font's metrics that will be used for draw764private void calcFontMetrics( Graphics2D g2d, int w, int h ) {765FontMetrics fm;766Graphics2D g2 = (Graphics2D)g2d.create();767768/// ABP769if ( g2Transform != NONE && textToUse != FILE_TEXT ) {770g2.setFont( g2.getFont().deriveFont( getAffineTransform( g2Transform )) );771fm = g2.getFontMetrics();772}773else {774fm = g2.getFontMetrics();775}776777maxAscent = fm.getMaxAscent();778maxDescent = fm.getMaxDescent();779if (maxAscent == 0) maxAscent = 10;780if (maxDescent == 0) maxDescent = 5;781if ( textToUse == RANGE_TEXT || textToUse == ALL_GLYPHS ) {782/// Give slight extra room for each character783maxAscent += 3;784maxDescent += 3;785gridWidth = fm.getMaxAdvance() + 6;786gridHeight = maxAscent + maxDescent;787if ( force16Cols )788numCharAcross = 16;789else790numCharAcross = ( w - 10 ) / gridWidth;791numCharDown = ( h - 10 ) / gridHeight;792793canvasInset_X = ( w - numCharAcross * gridWidth ) / 2;794canvasInset_Y = ( h - numCharDown * gridHeight ) / 2;795if ( numCharDown == 0 || numCharAcross == 0 )796throw new CannotDrawException( isPrinting ? CANT_FIT_PRINT : CANT_FIT_DRAW );797798if ( !isPrinting )799resetScrollbar( verticalBar.getValue() * numCharAcross );800}801else {802maxDescent += fm.getLeading();803canvasInset_X = 5;804canvasInset_Y = 5;805/// gridWidth and numCharAcross will not be used in this mode...806gridHeight = maxAscent + maxDescent;807numCharDown = ( h - canvasInset_Y * 2 ) / gridHeight;808809if ( numCharDown == 0 )810throw new CannotDrawException( isPrinting ? CANT_FIT_PRINT : CANT_FIT_DRAW );811/// If this is text loaded from file, prepares the LineBreak'ed812/// text layout at this point813if ( textToUse == FILE_TEXT ) {814if ( !isPrinting )815f2dt.fireChangeStatus( "LineBreaking Text... Please Wait", false );816lineBreakTLs = new Vector();817for ( int i = 0; i < fileText.length; i++ ) {818AttributedString as =819new AttributedString( fileText[i], g2.getFont().getAttributes() );820821LineBreakMeasurer lbm =822new LineBreakMeasurer( as.getIterator(), g2.getFontRenderContext() );823824while ( lbm.getPosition() < fileText[i].length() )825lineBreakTLs.add( lbm.nextLayout( (float) w ));826827}828}829if ( !isPrinting )830resetScrollbar( verticalBar.getValue() );831}832}833834/// Calculates the amount of text that will be displayed on screen835private void calcTextRange() {836String displaying = null;837838if ( textToUse == RANGE_TEXT || textToUse == ALL_GLYPHS ) {839if ( isPrinting )840if ( printMode == ONE_PAGE )841drawStart = currentlyShownChar;842else /// printMode == CUR_RANGE843drawStart = numCharAcross * numCharDown * printPageNumber;844else845drawStart = verticalBar.getValue() * numCharAcross;846if ( textToUse == RANGE_TEXT ) {847drawStart += drawRange[0];848drawLimit = drawRange[1];849}850else851drawLimit = testFont.getNumGlyphs();852drawEnd = drawStart + numCharAcross * numCharDown - 1;853854if ( drawEnd >= drawLimit )855drawEnd = drawLimit;856}857else {858if ( isPrinting )859if ( printMode == ONE_PAGE )860drawStart = currentlyShownChar;861else /// printMode == ALL_TEXT862drawStart = numCharDown * printPageNumber;863else {864drawStart = verticalBar.getValue();865}866867drawEnd = drawStart + numCharDown - 1;868869if ( textToUse == USER_TEXT )870drawLimit = userText.length - 1;871else872drawLimit = lineBreakTLs.size() - 1;873874if ( drawEnd >= drawLimit )875drawEnd = drawLimit;876}877878// ABP879if ( drawStart > drawEnd ) {880drawStart = 0;881verticalBar.setValue(drawStart);882}883884885/// Change the status bar if not printing...886if ( !isPrinting ) {887backupStatusString = ( "Displaying" + MS_OPENING[textToUse] +888modeSpecificNumStr( drawStart ) + " to " +889modeSpecificNumStr( drawEnd ) +890MS_CLOSING[textToUse] );891f2dt.fireChangeStatus( backupStatusString, false );892}893}894895/// Draws text according to the parameters set by Font2DTest GUI896private void drawText( Graphics g, int w, int h ) {897Graphics2D g2;898899/// Create back buffer when not printing, and its Graphics2D900/// Then set drawing parameters for that Graphics2D object901if ( isPrinting )902g2 = (Graphics2D) g;903else {904backBuffer = (BufferedImage) this.createImage( w, h );905g2 = backBuffer.createGraphics();906g2.setColor(Color.white);907g2.fillRect(0, 0, w, h);908g2.setColor(Color.black);909}910911/// sets font, RenderingHints.912setParams( g2 );913914/// If flag is set, recalculate fontMetrics and reset the scrollbar915if ( updateFontMetrics || isPrinting ) {916/// NOTE: re-calculates in case G2 transform917/// is something other than NONE918calcFontMetrics( g2, w, h );919updateFontMetrics = false;920}921/// Calculate the amount of text that can be drawn...922calcTextRange();923924/// Draw according to the set "Text to Use" mode925if ( textToUse == RANGE_TEXT || textToUse == ALL_GLYPHS ) {926int charToDraw = drawStart;927if ( showGrid )928drawGrid( g2 );929if ( !isPrinting )930g.drawImage( backBuffer, 0, 0, this );931932for ( int i = 0; i < numCharDown && charToDraw <= drawEnd; i++ ) {933for ( int j = 0; j < numCharAcross && charToDraw <= drawEnd; j++, charToDraw++ ) {934int gridLocX = j * gridWidth + canvasInset_X;935int gridLocY = i * gridHeight + canvasInset_Y;936937modeSpecificDrawChar( g2, charToDraw,938gridLocX + gridWidth / 2,939gridLocY + maxAscent );940//if ( !isPrinting ) {941// g.setClip( gridLocX, gridLocY, gridWidth + 1, gridHeight + 1 );942// g.drawImage( backBuffer, 0, 0, this );943//}944945}946}947}948else if ( textToUse == USER_TEXT ) {949g2.drawRect( 0, 0, w - 1, h - 1 );950if ( !isPrinting )951g.drawImage( backBuffer, 0, 0, this );952953for ( int i = drawStart; i <= drawEnd; i++ ) {954int lineStartX = canvasInset_Y;955int lineStartY = ( i - drawStart ) * gridHeight + maxAscent;956modeSpecificDrawLine( g2, userText[i], lineStartX, lineStartY );957}958}959else {960float xPos, yPos = (float) canvasInset_Y;961g2.drawRect( 0, 0, w - 1, h - 1 );962if ( !isPrinting )963g.drawImage( backBuffer, 0, 0, this );964965for ( int i = drawStart; i <= drawEnd; i++ ) {966TextLayout oneLine = (TextLayout) lineBreakTLs.elementAt( i );967xPos =968oneLine.isLeftToRight() ?969canvasInset_X : ( (float) w - oneLine.getAdvance() - canvasInset_X );970971float fmData[] = {0, oneLine.getAscent(), 0, oneLine.getDescent(), 0, oneLine.getLeading()};972if (g2Transform != NONE) {973AffineTransform at = getAffineTransform(g2Transform);974at.transform( fmData, 0, fmData, 0, 3);975}976//yPos += oneLine.getAscent();977yPos += fmData[1]; // ascent978//oneLine.draw( g2, xPos, yPos );979tlDrawLine( g2, oneLine, xPos, yPos );980//yPos += oneLine.getDescent() + oneLine.getLeading();981yPos += fmData[3] + fmData[5]; // descent + leading982}983}984if ( !isPrinting )985g.drawImage( backBuffer, 0, 0, this );986g2.dispose();987}988989/// Component paintComponent function...990/// Draws/Refreshes canvas according to flag(s) set by other functions991public void paintComponent( Graphics g ) {992if ( updateBackBuffer ) {993Dimension d = this.getSize();994isPrinting = false;995try {996drawText( g, d.width, d.height );997}998catch ( CannotDrawException e ) {999f2dt.fireChangeStatus( ERRORS[ e.id ], true );1000super.paintComponent(g);1001return;1002}1003}1004else {1005/// Screen refresh1006g.drawImage( backBuffer, 0, 0, this );1007}10081009showingError = false;1010updateBackBuffer = false;1011}10121013/// Printable interface function1014/// Component print function...1015public int print( Graphics g, PageFormat pf, int pageIndex ) {1016if ( pageIndex == 0 ) {1017/// Reset the last page index to max...1018lastPage = Integer.MAX_VALUE;1019currentlyShownChar = verticalBar.getValue() * numCharAcross;1020}10211022if ( printMode == ONE_PAGE ) {1023if ( pageIndex > 0 )1024return NO_SUCH_PAGE;1025}1026else {1027if ( pageIndex > lastPage )1028return NO_SUCH_PAGE;1029}10301031int pageWidth = (int) pf.getImageableWidth();1032int pageHeight = (int) pf.getImageableHeight();1033/// Back up metrics and other drawing info before printing modifies it1034int backupDrawStart = drawStart, backupDrawEnd = drawEnd;1035int backupNumCharAcross = numCharAcross, backupNumCharDown = numCharDown;1036Vector backupLineBreakTLs = null;1037if ( textToUse == FILE_TEXT )1038backupLineBreakTLs = (Vector) lineBreakTLs.clone();10391040printPageNumber = pageIndex;1041isPrinting = true;1042/// Push the actual draw area 60 down to allow info to be printed1043g.translate( (int) pf.getImageableX(), (int) pf.getImageableY() + 60 );1044try {1045drawText( g, pageWidth, pageHeight - 60 );1046}1047catch ( CannotDrawException e ) {1048f2dt.fireChangeStatus( ERRORS[ e.id ], true );1049return NO_SUCH_PAGE;1050}10511052/// Draw information about what is being printed1053String hints = ( " with antialias " + antiAliasType + "and" +1054" fractional metrics " + fractionalMetricsType +1055" and lcd contrast = " + lcdContrast);1056String infoLine1 = ( "Printing" + MS_OPENING[textToUse] +1057modeSpecificNumStr( drawStart ) + " to " +1058modeSpecificNumStr( drawEnd ) + MS_CLOSING[textToUse] );1059String infoLine2 = ( "With " + fontName + " " + STYLES[fontStyle] + " at " +1060fontSize + " point size " + TRANSFORMS[fontTransform] );1061String infoLine3 = "Using " + METHODS[drawMethod] + hints;1062String infoLine4 = "Page: " + ( pageIndex + 1 );1063g.setFont( new Font( "dialog", Font.PLAIN, 12 ));1064g.setColor( Color.black );1065g.translate( 0, -60 );1066g.drawString( infoLine1, 15, 10 );1067g.drawString( infoLine2, 15, 22 );1068g.drawString( infoLine3, 15, 34 );1069g.drawString( infoLine4, 15, 46 );10701071if ( drawEnd == drawLimit )1072/// This indicates that the draw will be completed with this page1073lastPage = pageIndex;10741075/// Restore the changed values back...1076/// This is important for JScrollBar settings and LineBreak'ed TLs1077drawStart = backupDrawStart;1078drawEnd = backupDrawEnd;1079numCharAcross = backupNumCharAcross;1080numCharDown = backupNumCharDown;1081if ( textToUse == FILE_TEXT )1082lineBreakTLs = backupLineBreakTLs;1083return PAGE_EXISTS;1084}10851086/// Ouputs the current canvas into a given PNG file1087public void writePNG( String fileName ) {1088try {1089ImageIO.write(backBuffer, "png", new java.io.File(fileName));1090}1091catch ( Exception e ) {1092f2dt.fireChangeStatus( "ERROR: Failed to Save PNG image; See stack trace", true );1093e.printStackTrace();1094}1095}10961097/// Figures out whether a character at the pointer location is valid1098/// And if so, updates mouse location informations, as well as1099/// the information on the status bar1100private boolean checkMouseLoc( MouseEvent e ) {1101if ( gridWidth != 0 && gridHeight != 0 )1102if ( textToUse == RANGE_TEXT || textToUse == ALL_GLYPHS ) {1103int charLocX = ( e.getX() - canvasInset_X ) / gridWidth;1104int charLocY = ( e.getY() - canvasInset_Y ) / gridHeight;11051106/// Check to make sure the mouse click location is within drawn area1107if ( charLocX >= 0 && charLocY >= 0 &&1108charLocX < numCharAcross && charLocY < numCharDown ) {1109int mouseOverChar =1110charLocX + ( verticalBar.getValue() + charLocY ) * numCharAcross;1111if ( textToUse == RANGE_TEXT )1112mouseOverChar += drawRange[0];1113if ( mouseOverChar > drawEnd )1114return false;11151116mouseOverCharX = charLocX;1117mouseOverCharY = charLocY;1118currMouseOverChar = mouseOverChar;1119/// Update status bar1120f2dt.fireChangeStatus( "Pointing to" + MS_OPENING[textToUse] +1121modeSpecificNumStr( mouseOverChar ), false );1122return true;1123}1124}1125return false;1126}11271128/// Shows (updates) the character zoom window1129public void showZoomed() {1130GlyphVector gv;1131Font backup = testFont;1132Point canvasLoc = this.getLocationOnScreen();11331134/// Calculate the zoom area's location and size...1135int dialogOffsetX = (int) ( gridWidth * ( ZOOM - 1 ) / 2 );1136int dialogOffsetY = (int) ( gridHeight * ( ZOOM - 1 ) / 2 );1137int zoomAreaX =1138mouseOverCharX * gridWidth + canvasInset_X - dialogOffsetX;1139int zoomAreaY =1140mouseOverCharY * gridHeight + canvasInset_Y - dialogOffsetY;1141int zoomAreaWidth = (int) ( gridWidth * ZOOM );1142int zoomAreaHeight = (int) ( gridHeight * ZOOM );11431144/// Position and set size of zoom window as needed1145zoomWindow.setLocation( canvasLoc.x + zoomAreaX, canvasLoc.y + zoomAreaY );1146if ( !nowZooming ) {1147if ( zoomWindow.getWarningString() != null )1148/// If this is not opened as a "secure" window,1149/// it has a banner below the zoom dialog which makes it look really BAD1150/// So enlarge it by a bit1151zoomWindow.setSize( zoomAreaWidth + 1, zoomAreaHeight + 20 );1152else1153zoomWindow.setSize( zoomAreaWidth + 1, zoomAreaHeight + 1 );1154}11551156/// Prepare zoomed image1157zoomImage =1158(BufferedImage) zoomWindow.createImage( zoomAreaWidth + 1,1159zoomAreaHeight + 1 );1160Graphics2D g2 = (Graphics2D) zoomImage.getGraphics();1161testFont = testFont.deriveFont( fontSize * ZOOM );1162setParams( g2 );1163g2.setColor( Color.white );1164g2.fillRect( 0, 0, zoomAreaWidth, zoomAreaHeight );1165g2.setColor( Color.black );1166g2.drawRect( 0, 0, zoomAreaWidth, zoomAreaHeight );1167modeSpecificDrawChar( g2, currMouseOverChar,1168zoomAreaWidth / 2, (int) ( maxAscent * ZOOM ));1169g2.dispose();1170if ( !nowZooming )1171zoomWindow.show();1172/// This is sort of redundant... since there is a paint function1173/// inside zoomWindow definition that does the drawImage.1174/// (I should be able to call just repaint() here)1175/// However, for some reason, that paint function fails to respond1176/// from second time and on; So I have to force the paint here...1177zoomWindow.getGraphics().drawImage( zoomImage, 0, 0, this );11781179nowZooming = true;1180prevZoomChar = currMouseOverChar;1181testFont = backup;11821183// Windows does not repaint correctly, after1184// a zoom. Thus, we need to force the canvas1185// to repaint, but only once. After the first repaint,1186// everything stabilizes. [ABP]1187if ( firstTime() ) {1188refresh();1189}1190}11911192/// Listener Functions11931194/// MouseListener interface function1195/// Zooms a character when mouse is pressed above it1196public void mousePressed( MouseEvent e ) {1197if ( !showingError) {1198if ( checkMouseLoc( e )) {1199showZoomed();1200this.setCursor( blankCursor );1201}1202}1203}12041205/// MouseListener interface function1206/// Redraws the area that was drawn over by zoomed character1207public void mouseReleased( MouseEvent e ) {1208if ( textToUse == RANGE_TEXT || textToUse == ALL_GLYPHS ) {1209if ( nowZooming )1210zoomWindow.hide();1211nowZooming = false;1212}1213this.setCursor( Cursor.getDefaultCursor() );1214}12151216/// MouseListener interface function1217/// Resets the status bar to display range instead of a specific character1218public void mouseExited( MouseEvent e ) {1219if ( !showingError && !nowZooming )1220f2dt.fireChangeStatus( backupStatusString, false );1221}12221223/// MouseMotionListener interface function1224/// Adjusts the status bar message when mouse moves over a character1225public void mouseMoved( MouseEvent e ) {1226if ( !showingError ) {1227if ( !checkMouseLoc( e ))1228f2dt.fireChangeStatus( backupStatusString, false );1229}1230}12311232/// MouseMotionListener interface function1233/// Scrolls the zoomed character when mouse is dragged1234public void mouseDragged( MouseEvent e ) {1235if ( !showingError )1236if ( nowZooming ) {1237if ( checkMouseLoc( e ) && currMouseOverChar != prevZoomChar )1238showZoomed();1239}1240}12411242/// Empty function to comply with interface requirement1243public void mouseClicked( MouseEvent e ) {}1244public void mouseEntered( MouseEvent e ) {}1245}12461247private final class CannotDrawException extends RuntimeException {1248/// Error ID1249public final int id;12501251public CannotDrawException( int i ) {1252id = i;1253}1254}12551256enum FMValues {1257FMDEFAULT ("DEFAULT", VALUE_FRACTIONALMETRICS_DEFAULT),1258FMOFF ("OFF", VALUE_FRACTIONALMETRICS_OFF),1259FMON ("ON", VALUE_FRACTIONALMETRICS_ON);12601261private String name;1262private Object hint;12631264private static FMValues[] valArray;12651266FMValues(String s, Object o) {1267name = s;1268hint = o;1269}12701271public String toString() {1272return name;1273}12741275public Object getHint() {1276return hint;1277}1278public static Object getValue(int ordinal) {1279if (valArray == null) {1280valArray = (FMValues[])EnumSet.allOf(FMValues.class).toArray(new FMValues[0]);1281}1282for (int i=0;i<valArray.length;i++) {1283if (valArray[i].ordinal() == ordinal) {1284return valArray[i];1285}1286}1287return valArray[0];1288}1289private static FMValues[] getArray() {1290if (valArray == null) {1291valArray = (FMValues[])EnumSet.allOf(FMValues.class).toArray(new FMValues[0]);1292}1293return valArray;1294}12951296public static int getHintVal(Object hint) {1297getArray();1298for (int i=0;i<valArray.length;i++) {1299if (valArray[i].getHint() == hint) {1300return i;1301}1302}1303return 0;1304}1305}13061307enum AAValues {1308AADEFAULT ("DEFAULT", VALUE_TEXT_ANTIALIAS_DEFAULT),1309AAOFF ("OFF", VALUE_TEXT_ANTIALIAS_OFF),1310AAON ("ON", VALUE_TEXT_ANTIALIAS_ON),1311AAGASP ("GASP", VALUE_TEXT_ANTIALIAS_GASP),1312AALCDHRGB ("LCD_HRGB", VALUE_TEXT_ANTIALIAS_LCD_HRGB),1313AALCDHBGR ("LCD_HBGR", VALUE_TEXT_ANTIALIAS_LCD_HBGR),1314AALCDVRGB ("LCD_VRGB", VALUE_TEXT_ANTIALIAS_LCD_VRGB),1315AALCDVBGR ("LCD_VBGR", VALUE_TEXT_ANTIALIAS_LCD_VBGR);13161317private String name;1318private Object hint;13191320private static AAValues[] valArray;13211322AAValues(String s, Object o) {1323name = s;1324hint = o;1325}13261327public String toString() {1328return name;1329}13301331public Object getHint() {1332return hint;1333}13341335public static boolean isLCDMode(Object o) {1336return (o instanceof AAValues &&1337((AAValues)o).ordinal() >= AALCDHRGB.ordinal());1338}13391340public static Object getValue(int ordinal) {1341if (valArray == null) {1342valArray = (AAValues[])EnumSet.allOf(AAValues.class).toArray(new AAValues[0]);1343}1344for (int i=0;i<valArray.length;i++) {1345if (valArray[i].ordinal() == ordinal) {1346return valArray[i];1347}1348}1349return valArray[0];1350}13511352private static AAValues[] getArray() {1353if (valArray == null) {1354Object [] oa = EnumSet.allOf(AAValues.class).toArray(new AAValues[0]);1355valArray = (AAValues[])(EnumSet.allOf(AAValues.class).toArray(new AAValues[0]));1356}1357return valArray;1358}13591360public static int getHintVal(Object hint) {1361getArray();1362for (int i=0;i<valArray.length;i++) {1363if (valArray[i].getHint() == hint) {1364return i;1365}1366}1367return 0;1368}13691370}13711372private static Integer defaultContrast;1373static Integer getDefaultLCDContrast() {1374if (defaultContrast == null) {1375GraphicsConfiguration gc =1376GraphicsEnvironment.getLocalGraphicsEnvironment().1377getDefaultScreenDevice().getDefaultConfiguration();1378Graphics2D g2d =1379(Graphics2D)(gc.createCompatibleImage(1,1).getGraphics());1380defaultContrast = (Integer)1381g2d.getRenderingHint(RenderingHints.KEY_TEXT_LCD_CONTRAST);1382}1383return defaultContrast;1384}1385}138613871388