Source code: jpl2/link/channels/WPRTChannel.java
1
2 /***********************************************************************
3 * JavaPsionLink 2.0, a java implementation of the psion link protocol
4 * Copyright (C) 2002, 2003 John S Montgomery (john.montgomery@lineone.net)
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 ************************************************************************/
20 package jpl2.link.channels;
21
22 import jpl2.common.*;
23 import jpl2.common.gui.*;
24 import jpl2.link.*;
25 import jpl2.documents.sections.*;
26 import jpl2.PsionLink;
27 import jpl2.convert.PsionFileConverter;
28
29 import java.util.Vector;
30 import java.io.IOException;
31 import java.awt.*;
32 import java.awt.image.*;
33 import java.lang.reflect.*;
34
35
36 /** Print server. Implements "Print Via PC" in the psion link protocol.
37 **/
38
39 public class WPRTChannel extends ServerChannel implements Preference.Listener {
40 public final static int LEVEL = 0x00;
41 public final static int DATA = 0xf0;
42 public final static int CANCEL = 0xf1;
43
44 public final static int PRIMITIVE_START = 0x00;
45 public final static int PRIMITIVE_END = 0x01;
46 public final static int SET_DRAW_MODE = 0x03;
47 public final static int SET_CLIPPING_RECT = 0x04;
48 public final static int RESET_CLIPPING_RECT = 0x05;
49 public final static int PRIMITIVE_06 = 0x06;
50 public final static int PRIMITIVE_FONT = 0x07;
51 public final static int PRIMITIVE_08 = 0x08;
52 public final static int PRIMITIVE_UNDERLINE = 0x09;
53 public final static int PRIMITIVE_STRIKETHROUGH = 0x0a;
54 public final static int PRIMITIVE_NEWLINE = 0x0b;
55 public final static int PRIMITIVE_CARRIAGERETURN = 0x0c;
56 public final static int PRIMITIVE_FOREGROUND = 0x0d;
57 public final static int SET_PEN_STYLE = 0x0e;
58 public final static int PRIMITIVE_OF = 0x0f;
59 public final static int PRIMITIVE_BACKGROUND = 0x10;
60 public final static int PRIMITIVE_11 = 0x11;
61 public final static int PRIMITIVE_17 = 0x17;
62 public final static int PRIMITIVE_LINE = 0x19;
63 public final static int PRIMITIVE_RECTANGLE = 0x20;
64 public final static int PRIMITIVE_POLYGON = 0x23;
65 public final static int DRAW_BITMAP = 0x25;
66 public final static int DRAW_BITMAP_SRC = 0x26;
67 public final static int PRIMITIVE_LABEL = 0x27;
68 public final static int PRIMITIVE_TEXT = 0x28;
69
70 private final static int NULL_PEN = 0x00;
71 private final static int SOLID_PEN = 0x01;
72 private final static int DOTTED_PEN = 0x02;
73 private final static int DASHED_PEN = 0x03;
74 private final static int DASH_DOT_PEN = 0x04;
75 private final static int DASH_DOT_DOT_PEN = 0x05;
76
77 private final String[] fontNames = {
78 "Monospaced", "SansSerif", "Monospaced", "Serif"
79 };
80
81 private boolean fontsFound = false;
82
83 // change this to true to print into a window
84 // instead of to a printer
85 private boolean testPrint = false;
86 private TestPrint canvas = null;
87
88 public WPRTChannel() {
89 // see if we have Arial, Times New Roman and/or Courier New
90 // fonts available otherwise use the java defaults
91 String[] fonts = JPLToolkit.getToolkit().getFonts();
92 for ( int i = 0; i < fonts.length; i++ ) {
93 String font = fonts[ i ];
94 if ( font.equalsIgnoreCase( "Arial" ) ) {
95 fontNames[ 1 ] = "Arial";
96 }
97 else if ( font.equalsIgnoreCase( "Times New Roman" ) ) {
98 fontNames[ 3 ] = "Times New Roman";
99 }
100 else if ( font.equalsIgnoreCase( "Courier New" ) ) {
101 fontNames[ 0 ] = "Courier New";
102 fontNames[ 2 ] = "Courier New";
103 }
104 }
105
106 Preference prefs = JPLToolkit.getPreferences();
107 fontNames[ 0 ] = prefs.getPreference( "wprt-courier_new", fontNames[ 0 ] );
108 fontNames[ 1 ] = prefs.getPreference( "wprt-arial", fontNames[ 1 ] );
109 fontNames[ 2 ] = prefs.getPreference( "wprt-courier_new", fontNames[ 2 ] );
110 fontNames[ 3 ] = prefs.getPreference( "wprt-times_new_roman", fontNames[ 3 ] );
111
112 testPrint = prefs.getPreference( "wprt-test_print", false );
113
114 prefs.addPreferenceListener( this );
115
116 if ( PsionLink.DEBUG ) {
117 System.out.println( "fonts being used:" );
118 for ( int i = 0; i < fontNames.length; i++ ) {
119 System.out.println( '\t' + fontNames[ i ] );
120 }
121 }
122 }
123
124
125 /** This method will be called on each registered Preference.Listener objects
126 * when a preference has been changed.
127 **/
128 public void preferenceChanged( String pref, String value ) {
129 if ( pref.startsWith( "wprt" ) ) {
130 if ( pref.equals( "wprt-courier_new" ) ) {
131 fontNames[ 0 ] = value;
132 fontNames[ 2 ] = value;
133 }
134 else if ( pref.equals( "wprt-arial" ) ) {
135 fontNames[ 1 ] = value;
136 }
137 else if ( pref.equals( "wprt-times_new_roman" ) ) {
138 fontNames[ 3 ] = "Times New Roman";
139 }
140 else if ( pref.equals( "wprt-test_print" ) ) {
141 testPrint = Boolean.valueOf( value ).booleanValue();
142 }
143 }
144 }
145
146 public final int twipsPerInch = 1440;
147
148 private int twipsToPixels( int twips ) {
149 return (int)Math.round( (twips*pageResolution)/(double)twipsPerInch );
150 }
151
152 private int twipsToPoint( int twips ) {
153 // same as pixels as we want the right point size to give us that
154 // height on the page
155 return twipsToPixels( twips );
156 }
157
158 // printer details
159 private PrintJob job = null;
160 private Dimension pageSize = null;
161 private int pageResolution = -1;
162 private Graphics g = null;
163
164 // current primitive values
165 private State state = new State();
166
167 // current line
168 private Line line = null;
169
170 // for inner classes
171 private WPRTChannel self = this;
172
173 private int channel = -1;
174 private Data frameData = new Data( 2048 );
175 private boolean frameReceived = false;
176
177 public int getChannel() {
178 return channel;
179 }
180
181 public void setChannel( int channel ) {
182 this.channel = channel;
183 }
184
185 public void setPrintToWindow( boolean printToWindow ) {
186 testPrint = printToWindow;
187 }
188
189 public void connected() {
190 //System.out.println( "WPRT connected" );
191 //setDstChannel( 4 );
192
193 (new Thread() {
194 public void run() {
195 try {
196 sendLevel();
197 runPrintServer();
198 }
199 catch( Exception e ) {
200 e.printStackTrace();
201 }
202 }
203 }).start();
204
205 }
206
207
208
209 public synchronized void sendLevel() throws IOException {
210 //System.out.println( "sending WPRT level" );
211 frameData.reset();
212 frameData.setLength( 0 );
213 frameData.writeByte( LEVEL );
214 frameData.writeByte( 2 ); // major version
215 frameData.writeByte( 0 ); // minor version
216 frameData.reset();
217
218 sendFrame( frameData );
219
220 Data received = receiveFrame();
221 int status = received.readUnsignedByte();
222 if ( status != ErrorCode.NONE )
223 ErrorCode.error( status );
224 }
225
226
227 public synchronized void data() throws IOException {
228 //System.out.println( "sending WPRT data" );
229 frameData.reset();
230 frameData.setLength( 0 );
231 frameData.writeByte( DATA );
232 frameData.reset();
233
234 sendFrame( frameData );
235 }
236
237 public synchronized void completeFrameReceived( Data data ) throws IOException {
238 frameData.append( data );
239 frameData.reset(); // set to beginning of data
240 frameReceived = true;
241 notifyAll(); // received a whole frame
242 }
243
244 public void partialFrameReceived( Data data ) throws IOException {
245 frameData.append( data );
246 }
247
248 public synchronized Data receiveFrame() throws IOException {
249 frameReceived = false;
250 frameData.reset();
251 frameData.setLength( 0 ); // set to beginning
252
253 try {
254 wait(); // maybe timeout after one minute?
255 }
256 catch( InterruptedException ie ) {
257 }
258
259
260 if ( !frameReceived )
261 throw new IOException( "Did not receive data within one minute" );
262
263 return frameData;
264 }
265
266 private int readLength( Data data ) throws IOException {
267 // $$$$ this is the wrong way around
268 int len = data.readUnsignedByte();
269 if ( (len & 0x01) == 0 ) {
270 //len = (len <<2) | 2;
271 len = len >> 2;
272 }
273 else {
274 len = len | (data.readUnsignedByte() << 8);
275 //len = (len << 3) | 5;
276 len = len >> 3;
277 }
278 return len;
279 }
280
281 private void makeCanvas() {
282 JPLToolkit tk = JPLToolkit.getToolkit();
283 canvas = new TestPrint();
284 Frame f = tk.makeFrame( "Test print" );
285 tk.add( f, canvas );
286 f.pack();
287 f.show();
288 pageSize = canvas.getSize();
289 pageResolution = canvas.getResolution();
290 g = canvas.getOffScreenGraphics();
291 }
292
293 private boolean startJob() {
294 if ( testPrint ) {
295 //makeCanvas();
296 return true;
297 }
298
299 job = requestPrintJob();
300 if ( job == null )
301 return false;
302
303 pageSize = job.getPageDimension();
304 pageResolution = job.getPageResolution();
305 if ( PsionLink.DEBUG )
306 System.out.println( "pageResolution="+pageResolution );
307 //g = job.getGraphics();
308
309 return true;
310 }
311
312 private PrintJob requestPrintJob() {
313 Frame frame = PsionLink.getMainFrame();
314 Toolkit tk = frame.getToolkit();
315 PrintJob job = null;
316 String title = "Print request from Psion";
317
318 // try and set this to 300 dpi
319 try {
320 Class pageAttributesClass = Class.forName( "java.awt.PageAttributes" );
321 Method setResolutionMethod = pageAttributesClass.getMethod( "setPrinterResolution", new Class[] { Integer.TYPE } );
322 Object pageAttributes = pageAttributesClass.newInstance();
323 // TODO make dpi changeable
324 setResolutionMethod.invoke( pageAttributes, new Object[] { new Integer( 300 ) } );
325
326 Class[] paramClasses = new Class[]
327 {
328 Frame.class, String.class,
329 Class.forName( "java.awt.JobAttributes" ),
330 pageAttributesClass
331 };
332
333 Method getPrintJobMethod = tk.getClass().getMethod( "getPrintJob", paramClasses );
334 Object printJob = getPrintJobMethod.invoke( tk, new Object[] {
335 frame, title, null, pageAttributes
336 } );
337 if ( printJob != null )
338 job = (PrintJob)printJob;
339
340 }
341 catch( Throwable t ) {
342 job = tk.getPrintJob( frame, title, new java.util.Properties() );
343 }
344
345 return job;
346 }
347
348
349 private void cancelJob() throws IOException {
350 //System.out.println( "job cancelled" );
351 frameData.reset();
352 frameData.setLength( 0 );
353 frameData.writeByte( CANCEL );
354 sendFrame( frameData );
355
356 Data received = receiveFrame();
357 }
358
359 private final int[] startPageBytes = {
360 0xe8, 0x03, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00
361 };
362
363 /** Print a page from the data given.
364 **/
365
366 private boolean printPage( Data pageData ) throws IOException {
367 for ( int i = 0; i < startPageBytes.length; i++ ) {
368 if ( startPageBytes[ i ] != pageData.readUnsignedByte() )
369 throw new IOException( "Page does not start correctly" );
370 }
371
372 if ( testPrint ) {
373 makeCanvas();
374 }
375 else {
376 g = job.getGraphics();
377 if ( g == null )
378 return false;
379 }
380
381 while( pageData.length() > 0 ) {
382 int id = pageData.readUnsignedByte();
383
384 switch( id ) {
385 case PRIMITIVE_START: {
386 int section = pageData.readInt();
387 int type = section & 0x03;
388 int page = section >> 2;
389 //System.out.println( "PRIMITIVE_START type " + type + ", page " + page );
390 }
391 break;
392
393 case PRIMITIVE_END: {
394 //System.out.println( "PRIMITIVE_END" );
395 }
396 break;
397
398 case SET_DRAW_MODE: {
399 int mode = pageData.readUnsignedByte();
400 // gonna ignore this for now
401 //System.out.println( "PRIMITIVE_03" );
402 }
403 break;
404
405 case SET_CLIPPING_RECT: {
406 int left = pageData.readInt();
407 int top = pageData.readInt();
408 int right = pageData.readInt();
409 int bottom = pageData.readInt();
410 //setClip( left, top, right, bottom );
411 //System.out.println( "SET_CLIPPING_RECT " + left + "," + top + "," + right + "," + bottom );
412 }
413 break;
414
415 case RESET_CLIPPING_RECT: {
416 //resetClip();
417 //System.out.println( "RESET_CLIPPING_RECT" );
418 }
419 break;
420
421 case PRIMITIVE_06: {
422 int b = pageData.readUnsignedByte();
423 int bool = pageData.readInt();
424 //System.out.println( "PRIMITIVE_06 " + b + " " + bool );
425 }
426 break;
427
428 case PRIMITIVE_FONT: {
429 int len = readLength( pageData );
430 String fontFace = pageData.readString( len );
431 int screenFont = pageData.readUnsignedByte();
432 int baseSize = pageData.readUnsignedShort();
433 int style = pageData.readInt();
434 int actualSize = pageData.readInt();
435 int baseline = pageData.readInt();
436
437 //System.out.println( "PRIMITIVE_FONT " + len + " " + fontFace + " " + screenFont + " " + baseSize + " " + style + " " + actualSize + " " + baseline );
438 String family = null;
439 if ( screenFont < fontNames.length ) {
440 family = fontNames[ screenFont ];
441 }
442 else {
443 family = fontFace;
444 }
445
446
447 int newstyle = Font.PLAIN;
448
449 if ( (style & 0x01) != 0 )
450 newstyle += Font.ITALIC;
451 if ( (style & 0x02) != 0 )
452 newstyle += Font.BOLD;
453
454 state.superscript = (style & 0x04) != 0;
455 state.subscript = (style & 0x08) != 0;
456
457 state.baseSize = twipsToPoint( baseSize );
458 state.baselineOffset = baseline;
459 // temporary hack, subtract 1 to make it look better
460 // as fonts are otherwise a little too big
461 int size = twipsToPoint( actualSize );
462 //-1;
463 //System.out.println( size );
464 state.font = new Font( family, newstyle, size );
465 state.fontMetrics = Toolkit.getDefaultToolkit().getFontMetrics( state.font );
466
467 }
468 break;
469
470 case PRIMITIVE_08: {
471 //System.out.println( "PRIMITIVE_08" );
472 }
473 break;
474
475 case PRIMITIVE_UNDERLINE: {
476 int enable = pageData.readUnsignedByte();
477 //System.out.println( "PRIMITIVE_UNDERLINE " + enable );
478 state.underline = (enable == 0x01);
479 }
480 break;
481
482 case PRIMITIVE_STRIKETHROUGH: {
483 int enable = pageData.readUnsignedByte();
484 //System.out.println( "PRIMITIVE_STRIKETHROUGH " + enable );
485 state.strikethrough = (enable == 0x01);
486 }
487 break;
488
489 case PRIMITIVE_NEWLINE: {
490 int i1 = pageData.readInt();
491 int i2 = pageData.readInt();
492 if ( line != null )
493 line.draw();
494 line = null;
495 //System.out.println( "PRIMITIVE_NEWLINE " + i1 + " " + i2 );
496 }
497 break;
498
499 case PRIMITIVE_CARRIAGERETURN: {
500 int i1 = pageData.readInt();
501 int i2 = pageData.readInt();
502
503 //System.out.println( "PRIMITIVE_CARRIAGERETURN " + i1 + " " + i2 );
504 }
505 break;
506
507 case PRIMITIVE_FOREGROUND: {
508 int red = pageData.readUnsignedByte();
509 int green = pageData.readUnsignedByte();
510 int blue = pageData.readUnsignedByte();
511 //System.out.println( "PRIMITIVE_FOREGROUND " + red + " " + green + " " + blue );
512 state.foreground = new Color( red, green, blue );
513 }
514 break;
515
516 case SET_PEN_STYLE: {
517 int b = pageData.readUnsignedByte();
518 //System.out.println( "PRIMITIVE_0E " + b );
519 }
520 break;
521
522 case PRIMITIVE_OF: {
523 int i1 = pageData.readInt();
524 int i2 = pageData.readInt();
525 //System.out.println( "PRIMITIVE_OF " + i1 + " " + i2 );
526 }
527 break;
528
529 case PRIMITIVE_BACKGROUND: {
530 int red = pageData.readUnsignedByte();
531 int green = pageData.readUnsignedByte();
532 int blue = pageData.readUnsignedByte();
533 //System.out.println( "PRIMITIVE_BACKGROUND " + red + " " + green + " " + blue );
534 state.background = new Color( red, green, blue );
535 }
536 break;
537
538 case PRIMITIVE_11: {
539 int b = pageData.readUnsignedByte();
540 //System.out.println( "PRIMITIVE_11 " + b );
541 }
542 break;
543
544 case PRIMITIVE_17: {
545 int left = pageData.readInt();
546 int top = pageData.readInt();
547 //System.out.println( "PRIMITIVE_17 " + left + " " + top );
548 }
549 break;
550
551 case PRIMITIVE_LINE: {
552 int x1 = pageData.readInt();
553 int y1 = pageData.readInt();
554 int x2 = pageData.readInt();
555 int y2 = pageData.readInt();
556 //System.out.println( "PRIMITIVE_LINE " + x1 +" " + y1 + " " + x2 + " " + y2 );
557 drawLine( x1, y1, x2, y2 );
558 }
559 break;
560
561 case PRIMITIVE_RECTANGLE: {
562 int left = pageData.readInt();
563 int top = pageData.readInt();
564 int right = pageData.readInt();
565 int bottom = pageData.readInt();
566 //System.out.println( "PRIMITIVE_RECTANGLE " + left + "," + top + "," + right + "," + bottom );
567 drawRect( left, top, right, bottom );
568 }
569 break;
570
571 case PRIMITIVE_POLYGON: {
572 int npoints = pageData.readInt();
573 int[] xpoints = new int[ npoints ];
574 int[] ypoints = new int[ npoints ];
575 for ( int i = 0; i < npoints; i++ ) {
576 xpoints[ i ] = pageData.readInt();
577 ypoints[ i ] = pageData.readInt();
578 }
579 int b = pageData.readUnsignedByte();
580 //System.out.println( "PRIMITIVE_POLYGON " + npoints );
581 drawPolygon( xpoints, ypoints, npoints );
582 }
583 break;
584
585 case DRAW_BITMAP: {
586 int left = pageData.readInt();
587 int top = pageData.readInt();
588 int right = pageData.readInt();
589 int bottom = pageData.readInt();
590 PaintDataSection pds = new PaintDataSection( pageData );
591 Image image = pds.getImage();
592 pageData.skipBytes( 16 );
593 drawImage( image, left, top, right, bottom );
594 }
595 break;
596
597 case DRAW_BITMAP_SRC: {
598 int left = pageData.readInt();
599 int top = pageData.readInt();
600 int right = pageData.readInt();
601 int bottom = pageData.readInt();
602 PaintDataSection pds = new PaintDataSection( pageData );
603 Image image = pds.getImage();
604 int srcLeft = pageData.readInt();
605 int srcTop = pageData.readInt();
606 int srcRight = pageData.readInt();
607 int srcBottom = pageData.readInt();
608 FilteredImageSource source =
609 new FilteredImageSource( image.getSource(), new CropImageFilter( srcLeft, srcTop, srcRight-srcLeft, srcBottom-srcTop ) );
610 image = Toolkit.getDefaultToolkit().createImage( source );
611 drawImage( image, left, top, right, bottom );
612 }
613 break;
614
615 case PRIMITIVE_LABEL: {
616 int len = readLength( pageData );
617 String text = pageData.readString( len );
618 int left = pageData.readInt();
619 int baseline = pageData.readInt();
620 //System.out.println( "PRIMITIVE_LABEL " + text + " " + left + " " + baseline );
621 drawLabel( text, left, baseline );
622 }
623 break;
624
625 case PRIMITIVE_TEXT: {
626 int len = readLength( pageData );
627 String text = pageData.readString( len );
628 int left = pageData.readInt();
629 int top = pageData.readInt();
630 int right = pageData.readInt();
631 int bottom = pageData.readInt();
632
633 int baseline = pageData.readInt();
634 int align = pageData.readUnsignedByte();
635 int margin = pageData.readInt();
636
637 //System.out.println( "TEXT " + b + " " + i1 + " " + marginLeft );
638
639 //System.out.println( "PRIMITIVE_TEXT " + text + " " + left + " " + top + " " + right + " " + bottom );
640 drawText( text, left, top, right, bottom, baseline, align, margin );
641 }
642 break;
643
644 default:
645 throw new IOException( "unknown print primitive identifier " + id );
646 }
647
648
649 }
650
651 pageDone();
652 return true;
653 }
654
655 private void pageDone() {
656 if ( line != null )
657 line.draw();
658 line = null;
659
660 if ( testPrint ) {
661 canvas.repaint();
662 }
663 else {
664 g.dispose();
665 g = null;
666 }
667 //System.out.println( "Page Done" );
668 }
669
670 private void endJob() {
671 if ( job != null )
672 job.end();
673 //System.out.println( "Job Done" );
674
675 }
676
677 private final int[] startJobBytes = {
678 0x2a, 0x2a, 0x09, 0x00, 0x00, 0x00, 0x82, 0x2e, 0x00, 0x00, 0xc6, 0x41, 0x00, 0x00, 0x00
679 };
680
681
682 private boolean isStartOfjob( Data data ) throws IOException {
683 for ( int i = 0; i < startJobBytes.length; i++ ) {
684 if ( startJobBytes[ i ] != data.readUnsignedByte() )
685 return false;
686 }
687 return data.length() == 0;
688 }
689
690 public synchronized void runPrintServer() throws IOException {
691 while( true ) {
692 //System.out.println( "waiting for new printjob" );
693
694 data();
695 Data received = receiveFrame();
696 if ( !isStartOfjob( received ) )
697 throw new RuntimeException( "Expected start of job" );
698
699
700 if ( !startJob() ) {
701 cancelJob();
702 continue;
703 }
704
705
706 while( true ) {
707 data();
708 received = receiveFrame();
709 int lastPacket = received.readUnsignedByte();
710 int lastPage = received.readUnsignedByte();
711 int pageLength = received.readInt();
712
713 //System.out.println( "page length " + pageLength );
714 int lenReceived = received.length();
715
716 Data pageData = new Data( pageLength );
717 pageData.append( received );
718
719 while( lastPacket != 0xFF && lenReceived < pageLength ) {
720 data();
721 received = receiveFrame();
722 lastPacket = received.readUnsignedByte();
723 lenReceived += received.length();
724 pageData.append( received );
725 //System.out.println( "page length " + lenReceived );
726 }
727
728 pageData.reset();
729 //System.out.println( "page length " + pageData.length() );
730
731 if ( pageData.length() != pageLength )
732 throw new IOException( "Error, conflicting page lengths " + pageLength + " != " + pageData.length() );
733
734 if ( !printPage( pageData ) ) {
735 JPLToolkit.getToolkit().showMessageDialog( PsionLink.getMainFrame(), "@res:error", "@res:printerErrorMsg" );
736 break;
737 }
738
739 if ( lastPage == 0xFF )
740 break;
741 }
742
743 endJob();
744
745 }
746 }
747
748 ///////////////////////////////////////
749 // Graphics routines.
750 ///////////////////////////////////////
751 public void setClip( int x1, int y1, int x2, int y2 ) {
752 int x = Math.min( x1, x2 ),
753 y = Math.min( y1, y2 ),
754 width = Math.abs( x1-x2 ),
755 height = Math.abs( y1-y2 );
756 g.setClip( twipsToPixels( x ), twipsToPixels( y ), twipsToPixels( width ), twipsToPixels( height ) );
757 }
758
759 public void resetClip() {
760 g.setClip( 0, 0, pageSize.width, pageSize.height );
761 }
762
763 public void drawLine( int x1, int y1, int x2, int y2 ) {
764 g.setColor( state.foreground );
765 g.drawLine( twipsToPixels( x1 ), twipsToPixels( y1 ), twipsToPixels( x2 ), twipsToPixels( y2 ) );
766 }
767
768 public void drawRect( int left, int top, int right, int bottom ) {
769 int x = Math.min( left, right );
770 int y = Math.min( top, bottom );
771 int width = Math.abs( left-right );
772 int height = Math.abs( top-bottom );
773
774 x = twipsToPixels( x );
775 y = twipsToPixels( y );
776 width = twipsToPixels( width );
777 height = twipsToPixels( height );
778 g.setColor( state.background );
779 g.fillRect( x, y, width, height );
780 g.setColor( state.foreground );
781 g.drawRect( x, y, width, height );
782 }
783
784 public void drawPolygon( int[] xpoints, int[] ypoints, int npoints ) {
785 for ( int i = 0; i < npoints; i++ ) {
786 xpoints[ i ] = twipsToPixels( xpoints[ i ] );
787 ypoints[ i ] = twipsToPixels( ypoints[ i ] );
788 }
789 g.setColor( state.background );
790 g.fillPolygon( xpoints, ypoints, npoints );
791 g.setColor( state.foreground );
792 g.drawPolygon( xpoints, ypoints, npoints );
793 }
794
795 public void drawLabel( String text, int left, int baseline ) {
796 g.setFont( state.font );
797 baseline = twipsToPixels( baseline );
798 g.setColor( state.foreground );
799 left = twipsToPixels( left );
800 drawString( text, left, baseline );
801 }
802
803 /** Attempt to justify the text in between the left and right values.
804 **/
805 public void drawText( String text, int left, int top, int right, int bottom, int baseline, int align, int margin ) {
806
807 if ( PsionLink.DEBUG )
808 drawRect( left, top, right, bottom );
809
810
811 switch( align ) {
812 case 0x00: // left
813 case 0x01: // centre
814 left += margin;
815 break;
816 case 0x02: // right
817 right -= margin;
818 break;
819 }
820
821
822
823 bottom = bottom + baseline + state.baselineOffset;
824 top = top + baseline + state.baselineOffset;
825
826 //drawRect( left, top, right, bottom );
827
828 bottom = twipsToPixels( bottom );
829 left = twipsToPixels( left );
830 right = twipsToPixels( right );
831
832 text = PsionFileConverter.convertPsionString( text );
833
834 Text textObj = new Text( text, state, align, new Rectangle( left, top, (right-left), (bottom-top) ) );
835 //textObj.draw();
836
837 if ( line == null ) {
838 line = new Line();
839 }
840 line.addText( textObj );
841
842
843 /*int len = fontMetrics.stringWidth( text );
844
845 int spaceAvailable = (right-left);
846 double extraSpace = spaceAvailable -len;
847
848 switch( align ) {
849 case 0x00:
850 drawString( text, left, bottom );
851 break;
852 case 0x01:
853 drawString( text, left + (right-left-len)/2, bottom );
854 break;
855 case 0x02:
856 drawString( text, right-len, bottom );
857 break;
858 }*/
859
860 //System.out.println( "extraSpace " + extraSpace );
861
862 /*if ( extraSpace == 0.0 )
863 drawString( text, left, bottom );
864 else {
865 Vector elements = new Vector();
866 StringTokenizer tok = new StringTokenizer( text, " \t\r\n", true );
867 int numWhiteSpace = 0;
868 while( tok.hasMoreTokens() ) {
869 String token = tok.nextToken();
870 if ( token.trim().equals( "" ) )
871 numWhiteSpace++;
872 elements.addElement( token );
873 }
874
875
876 if ( numWhiteSpace == 0 )
877 drawString( text, left, bottom );
878 else {
879 double extra = extraSpace/(double)numWhiteSpace;
880 for ( int i = 0; i < elements.size(); i++ ) {
881 String token = (String)elements.elementAt( i );
882 drawString( token, left, bottom );
883 int tokenLen = fontMetrics.stringWidth( token );
884 left += tokenLen;
885 if ( token.trim().equals( "" ) && i != (elements.size()-1) ) {
886 int n = (int)Math.round( extra );
887 left += n;
888 numWhiteSpace--;
889 extraSpace -= n;
890 extra = extraSpace/(double)numWhiteSpace;
891 }
892 }
893 }
894 //System.out.println();
895 }*/
896
897
898
899 /*StringTokenizer tok = new StringTokenizer( text, " \t\r\n", true );
900
901 int count = tok.countTokens();
902
903 double averageExtra = extraSpace/(double)count;
904 averageExtra = Math.ceil( averageExtra );
905
906 while( tok.hasMoreTokens() ) {
907 String token = tok.nextToken();
908 drawString( token, left, bottom );
909 int tokenLen = (int)(averageExtra + fontMetrics.stringWidth( token ));
910 left += tokenLen;
911 len -= tokenLen;
912 spaceAvailable = (right-left);
913 extraSpace = spaceAvailable -len;
914 if ( tok.hasMoreTokens() ) {
915 count = tok.countTokens();
916 averageExtra = extraSpace/(double)count;
917 averageExtra = Math.ceil( averageExtra );
918 }
919 }*/
920 }
921
922 public void drawString( String str, int x, int y ) {
923 str = PsionFileConverter.convertPsionString( str );
924 g.drawString( str, x, y );
925 if ( state.underline || state.strikethrough ) {
926 int length = state.fontMetrics.stringWidth( str );
927 if ( state.underline )
928 g.drawLine( x, y+1, x+length, y+1 );
929 if ( state.strikethrough ) {
930 int height = state.fontMetrics.getAscent()/2;
931 g.drawLine( x, y-height, x+length, y-height );
932 }
933 }
934 }
935
936 public void drawImage( Image image, int left, int top, int right, int bottom ) {
937 int x = Math.min( left, right );
938 int y = Math.min( top, bottom );
939 int width = Math.abs( left-right );
940 int height = Math.abs( top-bottom );
941
942 x = twipsToPixels( x );
943 y = twipsToPixels( y );
944 width = twipsToPixels( width );
945 height = twipsToPixels( height );
946
947 g.drawImage( image, x, y, width, height, PsionLink.getMainFrame() );
948 }
949 ///////////////////////////////////////
950 public class State implements Cloneable {
951 public Color background = Color.white;
952 public Color foreground = Color.black;
953 public int penStyle = 0x01;
954 public int penSize = 1;
955 public Font font = null;
956 public FontMetrics fontMetrics = null;
957 public int baseSize = -1;
958 public int baselineOffset = -1;
959 public boolean superscript = false;
960 public boolean subscript = false;
961 public boolean underline = false;
962 public boolean strikethrough = false;
963
964 public State copy() {
965 try {
966 return (State)clone();
967 }
968 catch( Exception e ) {
969 }
970 return null;
971 }
972
973 }
974
975 public class Line {
976 private Vector textElements = new Vector();
977
978 public void addText( Text text ) {
979 textElements.addElement( text );
980 }
981
982 /** Assumes left->right ordering. Adjusts a line of text
983 * to based on its alignment. This is needed as fonts don't always
984 * map properly.
985 **/
986 public void draw() {
987 //System.out.println( "line" );
988
989 if ( textElements.size() > 0 ) {
990 //System.out.println( "line " + textElements.size() );
991 Text first = (Text)textElements.elementAt( 0 );
992 int align = first.getAlign();
993
994 if ( align == 0x00 ) { // left align
995 Rectangle lastBounds = null;
996 for ( int i = 0; i < textElements.size(); i++ ) {
997 Text text = (Text)textElements.elementAt( i );
998 //System.out.print( text );
999 Rectangle bounds = text.getBounds();
1000 int length = text.length();
1001 bounds.width = length;
1002
1003 if ( lastBounds != null ) {
1004 int left = lastBounds.x + lastBounds.width;
1005 if ( left > bounds.x ) {
1006 //System.out.println( "intersects" );
1007 bounds.x = left;
1008 }
1009 }
1010
1011 text.setBounds( bounds );
1012 text.draw();
1013 lastBounds = bounds;
1014 }
1015 //System.out.println();
1016 }
1017 else if ( align == 0x01 ) { // centre
1018
1019 int minX = Integer.MAX_VALUE;
1020 int maxX = -Integer.MAX_VALUE;
1021 int totalLength = 0;
1022
1023 for ( int i = textElements.size()-1; i >= 0; i-- ) {
1024 Text text = (Text)textElements.elementAt( i );
1025 Rectangle bounds = text.getBounds();
1026 minX = Math.min( minX, bounds.x );
1027 maxX = Math.max( maxX, bounds.x + bounds.width );
1028
1029 int length = text.length();
1030 totalLength += length;
1031 bounds.width = length;
1032
1033 text.setBounds( bounds );
1034 }
1035
1036 // now adjust minX, to fit text properly
1037 minX += (maxX - minX - totalLength)/2;
1038
1039 for ( int i = textElements.size()-1; i >= 0; i-- ) {
1040 Text text = (Text)textElements.elementAt( i );
1041 Rectangle bounds = text.getBounds();
1042 bounds.x = minX;
1043 minX += text.length();
1044 text.setBounds( bounds );
1045 text.draw();
1046 }
1047
1048 }
1049 else if ( align == 0x02 ) { // right align
1050 Rectangle lastBounds = null;
1051 for ( int i = textElements.size()-1; i >= 0; i-- ) {
1052 Text text = (Text)textElements.elementAt( i );
1053 //System.out.print( text );
1054 Rectangle bounds = text.getBounds();
1055 int length = text.length();
1056 bounds.width = length;
1057
1058 if ( lastBounds != null ) {
1059 int right = lastBounds.x;
1060 if ( right < (bounds.x+bounds.width) ) {
1061 //System.out.println( "intersects" );
1062 bounds.x -= (bounds.x+bounds.width)-right;
1063 }
1064 }
1065
1066 text.setBounds( bounds );
1067 text.draw();
1068 lastBounds = bounds;
1069 }
1070 //System.out.println();
1071 }
1072
1073 }
1074 }
1075
1076 }
1077
1078 public class Text {
1079 private String text = null;
1080 private State state = null;
1081 private int align = -1;
1082 private Rectangle targetBounds = null;
1083
1084 public Text( String text, State currentState, int align, Rectangle targetBounds ) {
1085 this.text = text;
1086 this.state = currentState.copy();
1087 this.align = align;
1088 this.targetBounds = targetBounds;
1089 }
1090
1091 public int length() {
1092 return state.fontMetrics.stringWidth( text );
1093 }
1094
1095 public int getAlign() {
1096 return align;
1097 }
1098
1099 public Rectangle getBounds() {
1100 return targetBounds;
1101 }
1102
1103 public void setBounds( Rectangle bounds ) {
1104 targetBounds = bounds;
1105 }
1106
1107 public void draw() {
1108 State saved = self.state;
1109 self.state = state;
1110 g.setFont( state.font );
1111 g.setColor( state.foreground );
1112 int len = state.fontMetrics.stringWidth( text );
1113 int bottom = targetBounds.y + targetBounds.height;
1114 int left = targetBounds.x;
1115 int right = targetBounds.x + targetBounds.width;
1116 switch( align ) {
1117 case 0x00:
1118 drawString( text, left, bottom );
1119 break;
1120 case 0x01:
1121 drawString( text, left + (right-left-len)/2, bottom );
1122 break;
1123 case 0x02:
1124 drawString( text, right-len, bottom );
1125 break;
1126 }
1127
1128 self.state = saved;
1129 }
1130
1131 public String toString() {
1132 return text;
1133 }
1134
1135 }
1136
1137
1138 ///////////////////////////////////////
1139
1140 public class TestPrint extends Canvas {
1141 private Image offScrImage = null;
1142 private Graphics offScrGr = null;
1143
1144 public Dimension getPreferredSize() {
1145 return new Dimension( 8*getResolution() + getResolution()/4, 11*getResolution() + getResolution()/2 );
1146 }
1147
1148 public int getResolution() {
1149 return 72;
1150 }
1151
1152 public Graphics getOffScreenGraphics() {
1153 if ( offScrImage == null ) {
1154 offScrImage = createImage( getSize().width, getSize().height );
1155 offScrGr = offScrImage.getGraphics();
1156 }
1157 return offScrGr;
1158 }
1159
1160 public void paint( Graphics g ) {
1161 g.drawImage( offScrImage, 0, 0, this );
1162 }
1163
1164 }
1165
1166}