Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

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}