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