KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > gnu > jpdf > PDFGraphics


1 /*
2
3  * $Id: PDFGraphics.java,v 1.2 2001/11/16 15:26:04 ezb Exp $
4
5  *
6
7  * $Date: 2001/11/16 15:26:04 $
8
9  *
10
11  *
12
13  * This library is free software; you can redistribute it and/or
14
15  * modify it under the terms of the GNU Lesser General Public
16
17  * License as published by the Free Software Foundation; either
18
19  * version 2.1 of the License, or (at your option) any later version.
20
21  *
22
23  * This library is distributed in the hope that it will be useful,
24
25  * but WITHOUT ANY WARRANTY; without even the implied warranty of
26
27  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28
29  * Lesser General Public License for more details.
30
31  *
32
33  * You should have received a copy of the GNU Lesser General Public
34
35  * License along with this library; if not, write to the Free Software
36
37  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
38
39  */

40
41 package gnu.jpdf;
42
43
44
45 import java.awt.*;
46
47 import java.awt.image.*;
48
49 import java.io.*;
50
51 import java.util.*;
52
53
54
55 /**
56
57  * This class is our implementation of AWT's Graphics class. It provides a
58
59  * Java standard way of rendering into a PDF Document's Page.
60
61  *
62
63  * @author Peter T Mount, http://www.retep.org.uk/pdf/
64
65  * @author Eric Z. Beard, ericzbeard@hotmail.com
66
67  * @author $Author: ezb $
68
69  * @version $Revision: 1.2 $, $Date: 2001/11/16 15:26:04 $
70
71  * @see gnu.jpdf.PDFGraphics
72
73  */

74
75 public class PDFGraphics extends Graphics implements Serializable
76
77 {
78
79
80
81   /*
82
83    * NOTE: The original class is the work of Peter T. Mount, who released it
84
85    * in the uk.org.retep.pdf package. It was modified by Eric Z. Beard as
86
87    * follows:
88
89    * The package name was changed to gnu.pdf.
90
91    * The formatting was changed a little bit.
92
93    * This used to subclass an abstract class in a different package with
94
95    * the same name (confusing). Now it's one concrete class.
96
97    * drawImage() was implemented
98
99    * It is still licensed under the LGPL.
100
101    */

102
103
104
105
106
107   // Implementation notes:
108

109   //
110

111   // Pages 333-335 of the PDF Reference Manual
112

113   //
114

115   // Unless absolutely required, use the moveto, lineto and rectangle
116

117   // operators to perform those actions.
118

119   // They contain some extra optimisations
120

121   // which will reduce the output size by up to half in some cases.
122

123   //
124

125   // About fill operators: For correct operation, any fill operation should
126

127   // start with closeBlock(), which will ensure any previous path is completed,
128

129   // otherwise you may find the fill will include previous items
130

131     
132
133   /**
134
135    * This is the media we are working with
136
137    */

138
139   protected Rectangle media;
140
141     
142
143   /**
144
145    * The media's rotation, either 0,90,180 or 270.
146
147    */

148
149   private int mediaRot;
150
151      
152
153   /**
154
155    * This is used to translate coordinates
156
157    */

158
159   protected int trax;
160
161     
162
163   /**
164
165    * This is used to translate coordinates
166
167    */

168
169   protected int tray;
170
171     
172
173   /**
174
175    * Part of the optimiser:
176
177    * This is written to the stream when the newPath() is called. np then clears
178
179    * this value.
180
181    */

182
183   private String JavaDoc pre_np;
184
185     
186
187   /**
188
189    * Part of the optimiser:
190
191    * When true, we are drawing a path.
192
193    */

194
195   private boolean inStroke;
196
197     
198
199   /**
200
201    * Part of the optimiser:
202
203    * The last known moveto/lineto x coordinate
204
205    * @see #moveto
206
207    * @see #lineto
208
209    */

210
211   private int lx; // last known moveto/lineto coords
212

213     
214
215   /**
216
217    * Part of the optimiser:
218
219    * The last known moveto/lineto y coordinate
220
221    * @see #moveto
222
223    * @see #lineto
224
225    */

226
227   private int ly; // last known moveto/lineto coords
228

229     
230
231   /**
232
233    * Part of the optimiser:
234
235    * When true, we are within a Text Block.
236
237    */

238
239   private boolean inText; // true if within a Text Block - see newTextBlock()
240

241     
242
243   /**
244
245    * Part of the optimiser:
246
247    * When true, the font has changed.
248
249    */

250
251   private boolean newFont; // true if the font changes - see newTextBlock()
252

253     
254
255   /**
256
257    * Part of the optimiser:
258
259    * The last x coordinate when rendering text
260
261    */

262
263   private int tx; // the last coordinate for text rendering
264

265     
266
267   /**
268
269    * Part of the optimiser:
270
271    * The last y coordinate when rendering text
272
273    */

274
275   private int ty; // the last coordinate for text rendering
276

277     
278
279   /**
280
281    * This is the current pen/fill color
282
283    */

284
285   private Color color;
286
287     
288
289   /**
290
291    * This is the current font (in PDF format)
292
293    */

294
295   private PDFFont pdffont;
296
297     
298
299   /**
300
301    * This is the current font (in Java format)
302
303    */

304
305   private Font font;
306
307     
308
309   /**
310
311    * This is the PrintWriter used to write PDF drawing commands to the Stream
312
313    */

314
315   private PrintWriter pw;
316
317     
318
319   /**
320
321    * This is a reference to the PDFPage we are rendering to.
322
323    */

324
325   private PDFPage page;
326
327     
328
329   /**
330
331    * This is true for any Graphics instance that didn't create the stream.
332
333    * @see #create
334
335    */

336
337   private boolean child;
338
339     
340
341
342
343
344
345   /**
346
347    * This method creates a new instance of the class based on the page
348
349    * and a print writer.
350
351    *
352
353    * @param page the page to attach to
354
355    * @param pw the <code>PrintWriter</code> to attach to.
356
357    */

358
359   protected PDFGraphics createGraphic(PDFPage page,
360
361                       PrintWriter pw)
362
363   {
364
365     PDFGraphics g = new PDFGraphics();
366
367     g.init(page,pw);
368
369     return g;
370
371   }
372
373
374
375
376
377
378
379   /**
380
381    * This is called by PDFPage when creating a Graphcis instance.
382
383    * @param page The PDFPage to draw onto.
384
385    */

386
387   protected void init(PDFPage page) {
388
389     this.page = page;
390
391         
392
393     // We are the parent instance
394

395     child = false;
396
397         
398
399     // Now create a stream to store the graphics in
400

401     PDFStream stream = new PDFStream();
402
403     page.getPDFDocument().add(stream);
404
405     page.add(stream);
406
407     pw = stream.getWriter();
408
409         
410
411     // initially, we are limited to the page size
412

413     clipRectangle = new Rectangle(page.getMedia());
414
415         
416
417     // finally initialise the stream
418

419     init();
420
421   }
422
423     
424
425   /**
426
427    * This method is used internally by create() and by the PDFJob class
428
429    * @param page PDFPage to draw into
430
431    * @param pw PrintWriter to use
432
433    */

434
435   protected void init(PDFPage page,PrintWriter pw) {
436
437     this.page = page;
438
439     this.pw = pw;
440
441         
442
443     // In this case, we didn't create the stream (our parent did)
444

445     // so child is true (see dispose)
446

447     child = true;
448
449         
450
451     // finally initialise the stream
452

453     init();
454
455   }
456
457     
458
459   /**
460
461    * This initialises the stream by saving the current graphics state, and
462
463    * setting up the default line width (for us).
464
465    *
466
467    * It also sets up the instance ready for graphic operations and any
468
469    * optimisations.
470
471    *
472
473    * <p>For child instances, the stream is already open, so this should keep
474
475    * things happy.
476
477    */

478
479   private void init() {
480
481     // save graphics state (restored by dispose)
482

483     if(child) {
484
485       pw.print("q ");
486
487     }
488
489         
490
491     // Set the line width
492

493     setDefaultLineWidth();
494
495         
496
497     // now initialise the instance
498

499     //setColor(Color.black);
500

501     color = Color.black;
502
503     // possible: if parent.color is not black, then force black?
504

505     // must check to see what AWT does?
506

507         
508
509     // get the page dimensions (needed to get the orientation correct)
510

511     media = page.getMedia();
512
513     mediaRot = page.getOrientation();
514
515         
516
517     // Finally set the page Orientation
518

519     if(!child) {
520
521       setOrientation();
522
523     }
524
525   }
526
527     
528
529
530
531   /**
532
533    * Returns the PrintWriter handling the underlying stream
534
535    * @return the PrintWriter handling the underlying stream
536
537    */

538
539   public PrintWriter getWriter() {
540
541     return pw;
542
543   }
544
545
546
547     
548
549   /**
550
551    * Returns the associated PDFPage for this graphic
552
553    * @return the associated PDFPage for this graphic
554
555    */

556
557   public PDFPage getPage() {
558
559     return page;
560
561   }
562
563
564
565
566
567     
568
569   /**
570
571    * <p>This returns a child instance of this Graphics object. As with AWT, the
572
573    * affects of using the parent instance while the child exists, is not
574
575    * determined.</p>
576
577    *
578
579    * <p>Once complete, the child should be released with it's dispose()
580
581    * method which will restore the graphics state to it's parent.</p>
582
583    *
584
585    * @return Graphics object to render onto the page
586
587    */

588
589   public Graphics create() {
590
591     closeBlock();
592
593         
594
595     PDFGraphics g = createGraphic(page,pw);
596
597         
598
599     // The new instance inherits a few items
600

601     g.media = new Rectangle(media);
602
603     g.trax = trax;
604
605     g.tray = tray;
606
607     g.clipRectangle = new Rectangle(clipRectangle);
608
609         
610
611     return (Graphics) g;
612
613   } // end create()
614

615     
616
617    
618
619     
620
621   /**
622
623    * <p>This releases any resources used by this Graphics object. You must use
624
625    * this method once finished with it. Leaving it open will leave the PDF
626
627    * stream in an inconsistent state, and will produce errors.</p>
628
629    *
630
631    * <p>If this was created with Graphics.create() then the parent instance
632
633    * can be used again. If not, then this closes the graphics operations for
634
635    * this page when used with PDFJob.</p>
636
637    *
638
639    * <p>When using PDFPage, you can create another fresh Graphics instance,
640
641    * which will draw over this one.</p>
642
643    *
644
645    */

646
647   public void dispose() {
648
649     closeBlock();
650
651     if(child) {
652
653       pw.println("Q"); // restore graphics context
654

655     }
656
657     else {
658
659       pw.close(); // close the stream if were the parent
660

661     }
662
663   }
664
665     
666
667
668
669
670
671   // *********************************************
672

673   // **** Implementation of java.awt.Graphics ****
674

675   // *********************************************
676

677     
678
679   //============ Rectangle operations =======================
680

681     
682
683   /**
684
685    * This simply draws a White Rectangle to clear the area
686
687    * @param x coord
688
689    * @param y coord
690
691    * @param w width
692
693    * @param h height
694
695    */

696
697   public void clearRect(int x,int y,int w,int h) {
698
699     closeBlock();
700
701     pw.print("q 1 1 1 RG ");// save state, set colour to White
702

703     drawRect(x,y,w,h);
704
705     closeBlock("B Q"); // close fill & stroke, then restore state
706

707   }
708
709     
710
711
712
713   /**
714
715    * We override Graphics.drawRect as it doesn't join the 4 lines.
716
717    * Also, PDF provides us with a Rectangle operator, so we will use that.
718
719    * @param x coord
720
721    * @param y coord
722
723    * @param w width
724
725    * @param h height
726
727    */

728
729   public void drawRect(int x,int y,int w,int h) {
730
731     newPath();
732
733     pw.print(cxy(x,y)+cwh(w,h)+"re "); // rectangle
734

735     lx=x; // I don't know if this is correct, but lets see if PDF ends
736

737     ly=y; // the rectangle at it's start.
738

739     // stroke (optimised)
740

741   }
742
743     
744
745
746
747   /**
748
749    * <p>Not implemented</p>
750
751    *
752
753    * <p>Draws a 3-D highlighted outline of the specified rectangle.
754
755    * The edges of the rectangle are highlighted so that they appear
756
757    * to be beveled and lit from the upper left corner.
758
759    * The colors used for the highlighting effect are determined based on
760
761    * the current color. The resulting rectangle covers an area that
762
763    * is width + 1 pixels wide by height + 1 pixels tall.
764
765    *</p>
766
767    *
768
769    * @param x an <code>int</code> value
770
771    * @param y an <code>int</code> value
772
773    * @param width an <code>int</code> value
774
775    * @param height an <code>int</code> value
776
777    * @param raised a <code>boolean</code> value
778
779    */

780
781   public void draw3DRect(int x, int y,
782
783              int width, int height, boolean raised) {
784
785     // Not implemented
786

787   }
788
789
790
791     
792
793   /**
794
795    * <p>Not implemented</p>
796
797    *
798
799    * @param x an <code>int</code> value
800
801    * @param y an <code>int</code> value
802
803    * @param width an <code>int</code> value
804
805    * @param height an <code>int</code> value
806
807    * @param raised a <code>boolean</code> value
808
809    */

810
811   public void fill3DRect(int x, int y,
812
813              int width, int height, boolean raised) {
814
815     // Not implemented
816

817   }
818
819
820
821
822
823   /**
824
825    * Fills a rectangle with the current colour
826
827    *
828
829    * @param x coord
830
831    * @param y coord
832
833    * @param w width
834
835    * @param h height
836
837    */

838
839   public void fillRect(int x,int y,int w,int h) {
840
841     // end any path & stroke. This ensures the fill is on this
842

843     // rectangle, and not on any previous graphics
844

845     closeBlock();
846
847     drawRect(x,y,w,h);
848
849     closeBlock("B"); // rectangle, fill stroke
850

851   }
852
853     
854
855   //============ Round Rectangle operations =======================
856

857     
858
859   /**
860
861    * This is not yet implemented
862
863    *
864
865    * @param x coord
866
867    * @param y coord
868
869    * @param w width
870
871    * @param h height
872
873    * @param aw a-width
874
875    * @param ah a-height
876
877    */

878
879   public void fillRoundRect(int x,int y,int w,int h,int aw,int ah) {
880
881   }
882
883     
884
885   /**
886
887    * This is not yet implemented
888
889    *
890
891    * @param x coord
892
893    * @param y coord
894
895    * @param w width
896
897    * @param h height
898
899    * @param aw a-width
900
901    * @param ah a-height
902
903    */

904
905   public void drawRoundRect(int x,int y,int w,int h,int aw,int ah) {
906
907   }
908
909     
910
911   //============ Oval operations =======================
912

913     
914
915   /**
916
917    * <p>Draws an oval</p>
918
919    *
920
921    * @param x coord
922
923    * @param y coord
924
925    * @param w width
926
927    * @param h height
928
929    */

930
931   public void drawOval(int x,int y,int w,int h) {
932
933     drawArc(x, y, w, h, 0, 360);
934
935   }
936
937     
938
939   /**
940
941    * <p>Draws a filled oval</p>
942
943    *
944
945    * @param x coord
946
947    * @param y coord
948
949    * @param w width
950
951    * @param h height
952
953    */

954
955   public void fillOval(int x,int y,int w,int h) {
956
957     fillArc(x, y, w, h, 0, 360);
958
959   }
960
961     
962
963   //============ Polygon operations =======================
964

965     
966
967   /**
968
969    * Draws a polygon, linking the first and last coordinates.
970
971    * @param xp Array of x coordinates
972
973    * @param yp Array of y coordinates
974
975    * @param np number of points in polygon
976
977    */

978
979   public void drawPolygon(int[] xp,int[] yp,int np) {
980
981     polygon(xp,yp,np);
982
983     closeBlock("s"); // closepath and stroke
984

985   }
986
987     
988
989   /**
990
991    * Draws a polyline. The first and last coordinates are not linked.
992
993    * @param xp Array of x coordinates
994
995    * @param yp Array of y coordinates
996
997    * @param np number of points in polyline
998
999    */

1000
1001  public void drawPolyline(int[] xp,int[] yp,int np) {
1002
1003    polygon(xp,yp,np);
1004
1005    // no stroke, as we keep the optimiser in stroke state
1006

1007  }
1008
1009    
1010
1011  /**
1012
1013   * Fills a polygon.
1014
1015   * @param xp Array of x coordinates
1016
1017   * @param yp Array of y coordinates
1018
1019   * @param np number of points in polygon
1020
1021   */

1022
1023  public void fillPolygon(int[] xp,int[] yp,int np) {
1024
1025    closeBlock(); // finish off any previous paths
1026

1027    polygon(xp,yp,np);
1028
1029    closeBlock("b"); // closepath, fill and stroke
1030

1031  }
1032
1033    
1034
1035  //============ Image operations =======================
1036

1037    
1038
1039  /**
1040
1041   * Draw's an image onto the page
1042
1043   * @param img The java.awt.Image
1044
1045   * @param x coordinate on page
1046
1047   * @param y coordinate on page
1048
1049   * @param obs ImageObserver
1050
1051   * @return true if drawn
1052
1053   */

1054
1055  public boolean drawImage(Image img,int x,int y,ImageObserver obs) {
1056
1057    return drawImage(img,x,y,img.getWidth(obs),img.getHeight(obs),obs);
1058
1059  }
1060
1061    
1062
1063  /**
1064
1065   * <p>Draws an image onto the page.</p>
1066
1067   *
1068
1069   * <p>This method is implemented with ASCIIbase85 encoding and the
1070
1071   * zip stream deflater. It results in a stream that is anywhere
1072
1073   * from 3 to 10 times as big as the image. This obviusly needs some
1074
1075   * improvement, but it works well for small images</p>
1076
1077   *
1078
1079   * @param img The java.awt.Image
1080
1081   * @param x coordinate on page
1082
1083   * @param y coordinate on page
1084
1085   * @param w Width on page
1086
1087   * @param h height on page
1088
1089   * @param obs ImageObserver
1090
1091   * @return true if drawn
1092
1093   */

1094
1095  public boolean drawImage(Image img,int x,int y,int w,int h,
1096
1097               ImageObserver obs) {
1098
1099    closeBlock();
1100
1101    PDFImage image = new PDFImage(img,x,y,w,h,obs);
1102
1103    // The image needs to be registered in several places
1104

1105    page.getPDFDocument().setImageName(image);
1106
1107    page.getPDFDocument().add(image);
1108
1109    page.addToProcset("/ImageC");
1110
1111
1112
1113    // JM
1114

1115    /*page.addResource("/XObject << " + image.getName() + " " +
1116
1117      image.getSerialID() + " 0 R >>");*/

1118
1119    page.addImageResource(image.getName() + " " + image.getSerialID() +
1120
1121              " 0 R");
1122
1123
1124
1125    // q w 0 0 h x y cm % the coordinate matrix
1126

1127    pw.print("q " +
1128
1129         image.getWidth() +
1130
1131         " 0 0 " +
1132
1133         image.getHeight() +
1134
1135         " " + x + " " +
1136
1137         ((int)page.getDimension().getHeight()-y-image.getHeight()) +
1138
1139         " cm \n" + image.getName() + " Do\nQ\n");
1140
1141    return false;
1142
1143  }
1144
1145    
1146
1147
1148
1149  /**
1150
1151   * <p>Draw's an image onto the page, with a backing colour.</p>
1152
1153   *
1154
1155   * @param img The java.awt.Image
1156
1157   * @param x coordinate on page
1158
1159   * @param y coordinate on page
1160
1161   * @param bgcolor Background colour
1162
1163   * @param obs ImageObserver
1164
1165   * @return true if drawn
1166
1167   */

1168
1169  public boolean drawImage(Image img,int x,int y,Color bgcolor,
1170
1171               ImageObserver obs) {
1172
1173    return drawImage(img,x,y,img.getWidth(obs),img.getHeight(obs),
1174
1175             bgcolor,obs);
1176
1177  }
1178
1179    
1180
1181  /**
1182
1183   * <p>Draw's an image onto the page, with a backing colour.</p>
1184
1185   *
1186
1187   * @param img The java.awt.Image
1188
1189   * @param x coordinate on page
1190
1191   * @param y coordinate on page
1192
1193   * @param w Width on page
1194
1195   * @param h height on page
1196
1197   * @param bgcolor Background colour
1198
1199   * @param obs ImageObserver
1200
1201   * @return true if drawn
1202
1203   */

1204
1205  public boolean drawImage(Image img,int x,int y,int w,int h,
1206
1207               Color bgcolor,ImageObserver obs) {
1208
1209    closeBlock();
1210
1211    pw.print("q "); // save state
1212

1213    Color c = color; // save current colour
1214

1215    setColor(bgcolor); // change the colour
1216

1217    drawRect(x,y,w,h);
1218
1219    closeBlock("B Q"); // fill stroke, restore state
1220

1221    color = c; // restore original colour
1222

1223    return drawImage(img,x,y,img.getWidth(obs),img.getHeight(obs),obs);
1224
1225  }
1226
1227    
1228
1229  /**
1230
1231   * Draw's an image onto the page, with scaling
1232
1233   * <p>This is not yet supported.
1234
1235   *
1236
1237   * @param img The java.awt.Image
1238
1239   * @param dx1 coordinate on page
1240
1241   * @param dy1 coordinate on page
1242
1243   * @param dx2 coordinate on page
1244
1245   * @param dy2 coordinate on page
1246
1247   * @param sx1 coordinate on image
1248
1249   * @param sy1 coordinate on image
1250
1251   * @param sx2 coordinate on image
1252
1253   * @param sy2 coordinate on image
1254
1255   * @param obs ImageObserver
1256
1257   * @return true if drawn
1258
1259   */

1260
1261  public boolean drawImage(Image img,int dx1,int dy1,int dx2,
1262
1263               int dy2,int sx1,int sy1,int sx2,int sy2,
1264
1265               ImageObserver obs) {
1266
1267    // This shouldn't be too bad, just change the coordinate matrix
1268

1269    return false;
1270
1271  }
1272
1273    
1274
1275  /**
1276
1277   * Draw's an image onto the page, with scaling
1278
1279   * <p>This is not yet supported.
1280
1281   *
1282
1283   * @param img The java.awt.Image
1284
1285   * @param dx1 coordinate on page
1286
1287   * @param dy1 coordinate on page
1288
1289   * @param dx2 coordinate on page
1290
1291   * @param dy2 coordinate on page
1292
1293   * @param sx1 coordinate on image
1294
1295   * @param sy1 coordinate on image
1296
1297   * @param sx2 coordinate on image
1298
1299   * @param sy2 coordinate on image
1300
1301   * @param bgcolor Background colour
1302
1303   * @param obs ImageObserver
1304
1305   * @return true if drawn
1306
1307   */

1308
1309  public boolean drawImage(Image img,int dx1,int dy1,int dx2,
1310
1311               int dy2,int sx1,int sy1,int sx2,int sy2,
1312
1313               Color bgcolor,ImageObserver obs) {
1314
1315    return false;
1316
1317  }
1318
1319    
1320
1321  //============ Clipping operations =======================
1322

1323    
1324
1325  /**
1326
1327   * This holds the current clipRectangle
1328
1329   */

1330
1331  protected Rectangle clipRectangle;
1332
1333    
1334
1335  /**
1336
1337   * Clips to a set of coordinates
1338
1339   * @param x coord
1340
1341   * @param y coord
1342
1343   * @param w width
1344
1345   * @param h height
1346
1347   */

1348
1349  public void clipRect(int x,int y,int w,int h) {
1350
1351    setClip(x,y,w,h);
1352
1353  }
1354
1355    
1356
1357  /**
1358
1359   * Clips to a set of coordinates
1360
1361   * @param x coord
1362
1363   * @param y coord
1364
1365   * @param w width
1366
1367   * @param h height
1368
1369   */

1370
1371  public void setClip(int x,int y,int w,int h) {
1372
1373    clipRectangle = new Rectangle(x,y,w,h);
1374
1375    closeBlock(); // finish off any existing paths
1376

1377    drawRect(x,y,w,h);
1378
1379    closeBlock("W n"); // clip to current path
1380

1381  }
1382
1383    
1384
1385  /**
1386
1387   * As my JDK docs say, this may break with Java 2D.
1388
1389   * <p>Sets the clipping region to that of a Shape.
1390
1391   * @param s Shape to clip to.
1392
1393   */

1394
1395  public void setClip(Shape s) {
1396
1397    Rectangle r = s.getBounds();
1398
1399    setClip(r.x,r.y,r.width,r.height);
1400
1401  }
1402
1403    
1404
1405  /**
1406
1407   * This extra method allows PDF users to clip to a Polygon.
1408
1409   *
1410
1411   * <p>In theory you could use setClip(), except that java.awt.Graphics
1412
1413   * only supports Rectangle with that method, so we will have an extra
1414
1415   * method.
1416
1417   * @param p Polygon to clip to
1418
1419   */

1420
1421  public void clipPolygon(Polygon p) {
1422
1423    closeBlock(); // finish off any existing path
1424

1425    polygon(p.xpoints,p.ypoints,p.npoints);
1426
1427    closeBlock("W"); // clip to current path
1428

1429    clipRectangle = p.getBounds();
1430
1431  }
1432
1433    
1434
1435    
1436
1437  /**
1438
1439   * Returns the Rectangle that fits the current clipping region
1440
1441   * @return the Rectangle that fits the current clipping region
1442
1443   */

1444
1445  public Rectangle getClipBounds() {
1446
1447    return clipRectangle;
1448
1449  }
1450
1451    
1452
1453  //============ Colour operations =======================
1454

1455    
1456
1457  /**
1458
1459   * Returns the current pen Colour
1460
1461   * @return the current pen Colour
1462
1463   */

1464
1465  public Color getColor() {
1466
1467    return color;
1468
1469  }
1470
1471    
1472
1473  /**
1474
1475   * Sets the colour for drawing
1476
1477   * @param c Color to use
1478
1479   */

1480
1481  public void setColor(Color c) {
1482
1483    color = c;
1484
1485    double r = ((double)c.getRed())/255.0;
1486
1487    double g = ((double)c.getGreen())/255.0;
1488
1489    double b = ((double)c.getBlue())/255.0;
1490
1491    closeBlock(); // This ensures any paths are drawn in the previous colours
1492

1493    pw.println(""+r+" "+g+" "+b+" rg "+r+" "+g+" "+b+" RG");
1494
1495  }
1496
1497    
1498
1499  /**
1500
1501   * Not implemented, as this is not supported in the PDF specification.
1502
1503   */

1504
1505  public void setPaintMode() {
1506
1507  }
1508
1509    
1510
1511  /**
1512
1513   * Not implemented, as this is not supported in the PDF specification.
1514
1515   * @param c1 Color to xor with
1516
1517   */

1518
1519  public void setXORMode(Color c1) {
1520
1521  }
1522
1523    
1524
1525  //============ Text operations =======================
1526

1527    
1528
1529  /**
1530
1531   * Returns the FontMetrics for a font.
1532
1533   * <p>This doesn't work correctly. Perhaps having some way of mapping
1534
1535   * the base 14 fonts to our own FontMetrics implementation?
1536
1537   * @param font The java.awt.Font to return the metrics for
1538
1539   * @return FontMetrics for a font
1540
1541   */

1542
1543  public FontMetrics getFontMetrics(Font font) {
1544
1545    Frame dummy = new Frame();
1546
1547    dummy.addNotify();
1548
1549    Image image = dummy.createImage(100, 100);
1550
1551    if (image == null) {
1552
1553      System.err.println("getFontMetrics: image is null");
1554
1555    }
1556
1557    Graphics graphics = image.getGraphics();
1558
1559    return graphics.getFontMetrics(font);
1560
1561  
1562
1563  }
1564
1565    
1566
1567  /**
1568
1569   * Return's the current font.
1570
1571   * @return the current font.
1572
1573   */

1574
1575  public Font getFont() {
1576
1577    if(font==null)
1578
1579      setFont(new Font("SansSerif",Font.PLAIN,12));
1580
1581    return font;
1582
1583  }
1584
1585    
1586
1587  /**
1588
1589   * This sets the font.
1590
1591   * @param f java.awt.Font to set to.
1592
1593   */

1594
1595  public void setFont(Font f) {
1596
1597    // Optimise: Save some space if the font is already the current one.
1598

1599    if(font!=f) {
1600
1601      font = f;
1602
1603      pdffont = page.getFont("/Type1",f.getName(),f.getStyle());
1604
1605            
1606
1607      // mark the font as changed
1608

1609      newFont = true;
1610
1611    }
1612
1613  }
1614
1615    
1616
1617  /**
1618
1619   * This draws a string.
1620
1621   *
1622
1623   * @oaran s String to draw
1624
1625   * @param x coord
1626
1627   * @param y coord
1628
1629   */

1630
1631  public void drawString(String JavaDoc s,int x,int y) {
1632
1633    newTextBlock(x,y);
1634
1635    pw.println(PDFStringHelper.makePDFString(s)+" Tj");
1636
1637  }
1638
1639    
1640
1641
1642
1643
1644
1645  /**
1646
1647   * <p>Not implemented</p>
1648
1649   *
1650
1651   * @param data a <code>byte[]</code> value
1652
1653   * @param offset an <code>int</code> value
1654
1655   * @param length an <code>int</code> value
1656
1657   * @param x an <code>int</code> value
1658
1659   * @param y an <code>int</code> value
1660
1661   */

1662
1663  public void drawBytes(byte[] data, int offset, int length, int x, int y) {
1664
1665
1666
1667  }
1668
1669          
1670
1671
1672
1673  //============ Optimizers =======================
1674

1675    
1676
1677  /**
1678
1679   * All functions should call this to close any existing optimised blocks.
1680
1681   */

1682
1683  void closeBlock() {
1684
1685    closeBlock("S");
1686
1687  }
1688
1689    
1690
1691  /**
1692
1693   * <p>This is used by code that use the path in any way other than Stroke
1694
1695   * (like Fill, close path & Stroke etc). Usually this is used internally.</p>
1696
1697   *
1698
1699   * @param code PDF operators that will close the path
1700
1701   */

1702
1703  void closeBlock(String JavaDoc code) {
1704
1705    if(inText) {
1706
1707      pw.println("ET Q");
1708
1709      setOrientation(); // fixes Orientation matrix
1710

1711    }
1712
1713        
1714
1715    if(inStroke) {
1716
1717      pw.println(code);
1718
1719    }
1720
1721
1722
1723    inStroke=inText=false;
1724
1725  }
1726
1727    
1728
1729  /**
1730
1731   * Functions that draw lines should start by calling this. It starts a
1732
1733   * new path unless inStroke is set, in that case it uses the existing path
1734
1735   */

1736
1737  void newPath() {
1738
1739    if(inText) {
1740
1741      closeBlock();
1742
1743    }
1744
1745    if(!inStroke) {
1746
1747      if(pre_np!=null) {
1748
1749        pw.print(pre_np); // this is the prefix set by setOrientation()
1750

1751        pre_np = null;
1752
1753      }
1754
1755      pw.print("n ");
1756
1757    }
1758
1759        
1760
1761    inText=false;
1762
1763    inStroke=true;
1764
1765        
1766
1767    // an unlikely coordinate to fool the moveto() optimizer
1768

1769    lx = ly = -9999;
1770
1771  }
1772
1773    
1774
1775  /**
1776
1777   * <p>Functions that draw text should start by calling this. It starts a text
1778
1779   * block (accounting for media orientation) unless we are already in a Text
1780
1781   * block.</p>
1782
1783   *
1784
1785   * <p>It also handles if the font has been changed since the current text
1786
1787   * block was started, so your function will be current.</p>
1788
1789   *
1790
1791   * @param x x coord in java space
1792
1793   * @param y y coord in java space
1794
1795   */

1796
1797  void newTextBlock(int x,int y) {
1798
1799    // close the current path if there is one
1800

1801    if(inStroke) {
1802
1803      closeBlock();
1804
1805    }
1806
1807    // create the text block if one is not current. If we are, the newFont
1808

1809    // condition at the end catches font changes
1810

1811    if(!inText) {
1812
1813      // This ensures that there is a font available
1814

1815      getFont();
1816
1817        
1818
1819      pw.print("q BT ");
1820
1821      tx=ty=0;
1822
1823            
1824
1825      // produce the text matrix for the media
1826

1827      switch(mediaRot) {
1828
1829      case 0: // Portrait
1830

1831        //pw.println("1 0 0 1 0 0 Tm");
1832

1833        break;
1834
1835          
1836
1837      case 90: // Landscape
1838

1839        pw.println("0 1 -1 0 0 0 Tm"); // rotate
1840

1841        break;
1842
1843          
1844
1845      case 180: // Inverted Portrait
1846

1847        pw.println("1 0 0 -1 0 0 Tm");
1848
1849        break;
1850
1851          
1852
1853      case 270: // Seascape
1854

1855        pw.println("0 -1 1 0 0 0 Tm"); // rotate
1856

1857        break;
1858
1859      }
1860
1861        
1862
1863      // move the text cursor by an absolute amount
1864

1865      pw.print(txy(x,y)+"Td ");
1866
1867        
1868
1869    } else {
1870
1871      // move the text cursor by a relative amount
1872

1873      //int ox=x-tx, oy=ty-y;
1874

1875      //pw.print(""+ox+" "+oy+" Td ");
1876

1877      //pw.print(cwh(x-tx,y-ty)+"Td ");
1878

1879      pw.print(twh(x,y,tx,ty)+"Td ");
1880
1881    }
1882
1883      
1884
1885    // preserve the coordinates for the next time
1886

1887    tx = x;
1888
1889    ty = y;
1890
1891      
1892
1893    if(newFont || !inText)
1894
1895      pw.print(pdffont.getName()+" "+font.getSize()+" Tf ");
1896
1897      
1898
1899    // later add colour changes here (if required)
1900

1901      
1902
1903    inStroke = newFont = false;
1904
1905    inText = true;
1906
1907  }
1908
1909    
1910
1911    
1912
1913  /**
1914
1915   * This is unsupported - how do you do this with Vector graphics?
1916
1917   * @param x coord
1918
1919   * @param y coord
1920
1921   * @param w width
1922
1923   * @param h height
1924
1925   * @param dx coord
1926
1927   * @param dy coord
1928
1929   */

1930
1931  public void copyArea(int x,int y,int w,int h,int dx,int dy) {
1932
1933    // Hmm... Probably need to keep track of everything
1934

1935    // that has been drawn so far to get the contents of an area
1936

1937  }
1938
1939    
1940
1941  //============ Line operations =======================
1942

1943    
1944
1945  /**
1946
1947   * Draws a line between two coordinates.
1948
1949   *
1950
1951   * If the first coordinate is the same as the last one drawn
1952
1953   * (ie a previous drawLine, moveto, etc) it is ignored.
1954
1955   * @param x1 coord
1956
1957   * @param y1 coord
1958
1959   * @param x2 coord
1960
1961   * @param y2 coord
1962
1963   */

1964
1965  public void drawLine(int x1,int y1,int x2,int y2) {
1966
1967    moveto(x1,y1);
1968
1969    lineto(x2,y2);
1970
1971  }
1972
1973    
1974
1975  /**
1976
1977   * Translate the origin.
1978
1979   * @param x coord offset
1980
1981   * @param y coord offset
1982
1983   */

1984
1985  public void translate(int x,int y) {
1986
1987    trax+=x;
1988
1989    tray+=y;
1990
1991    //closeBlock();
1992

1993    //// we use cw & ch here as the coordinates are relative not absolute
1994

1995    //pw.println("1 0 0 1 "+cwh(x,y)+" cm");
1996

1997  }
1998
1999    
2000
2001  //============ Arcs operations ==============================
2002

2003  // These are the standard Graphics operators. They use the
2004

2005  // arc extension operators to achieve the affect.
2006

2007    
2008
2009  /**
2010
2011   * Draws an arc
2012
2013   * @param x coord
2014
2015   * @param y coord
2016
2017   * @param w width
2018
2019   * @param h height
2020
2021   * @param sa Start angle
2022
2023   * @param aa End angle
2024
2025   */

2026
2027  public void drawArc(int x,int y,int w,int h,int sa,int aa) {
2028
2029    w=w>>1;
2030
2031    h=h>>1;
2032
2033    x+=w;
2034
2035    y+=h;
2036
2037        
2038
2039    arc((double)x,(double)y,
2040
2041    (double)w,(double)h,
2042
2043    (double)-sa,(double)(-sa-aa),
2044
2045    false);
2046
2047  }
2048
2049    
2050
2051  /**
2052
2053   * Fills an arc, joining the start and end coordinates
2054
2055   * @param x coord
2056
2057   * @param y coord
2058
2059   * @param w width
2060
2061   * @param h height
2062
2063   * @param sa Start angle
2064
2065   * @param aa End angle
2066
2067   */

2068
2069  public void fillArc(int x,int y,int w,int h,int sa,int aa) {
2070
2071    // here we fool the optimizer. We force any open path to be closed,
2072

2073    // then draw the arc. Finally, as the optimizer hasn't stroke'd the
2074

2075    // path, we close and fill it, and mark the Stroke as closed.
2076

2077    //
2078

2079    // Note: The lineto to the centre of the object is required, because
2080

2081    // the fill only fills the arc. Skipping this includes an extra
2082

2083    // chord, which isn't correct. Peter May 31 2000
2084

2085    closeBlock();
2086
2087    drawArc(x,y,w,h,sa,aa);
2088
2089    lineto(x+(w>>1),y+(h>>1));
2090
2091    closeBlock("b"); // closepath and fill
2092

2093  }
2094
2095    
2096
2097  //============ Extension operations ==============================
2098

2099  // These are extensions, and provide access to PDF Specific
2100

2101  // operators.
2102

2103    
2104
2105  /**
2106
2107   * This moves the current drawing point.
2108
2109   * @param x coord
2110
2111   * @param y coord
2112
2113   */

2114
2115  public void moveto(int x,int y) {
2116
2117    newPath();
2118
2119    if(lx!=x && ly!=y)
2120
2121      pw.print(cxy(x,y)+"m ");
2122
2123    lx=x;
2124
2125    ly=y;
2126
2127  }
2128
2129    
2130
2131  /**
2132
2133   * This moves the current drawing point.
2134
2135   * @param x coord
2136
2137   * @param y coord
2138
2139   */

2140
2141  public void moveto(double x,double y) {
2142
2143    newPath();
2144
2145    // no optimisation here as it may introduce errors on decimal coords.
2146

2147    pw.print(cxy(x,y)+"m ");
2148
2149    lx=(int)x;
2150
2151    ly=(int)y;
2152
2153  }
2154
2155    
2156
2157  /**
2158
2159   * This adds a line segment to the current path
2160
2161   * @param x coord
2162
2163   * @param y coord
2164
2165   */

2166
2167  public void lineto(int x,int y) {
2168
2169    newPath();
2170
2171    if(lx!=x && ly!=y)
2172
2173      pw.print(cxy(x,y)+"l ");
2174
2175    lx=x;
2176
2177    ly=y;
2178
2179  }
2180
2181    
2182
2183  /**
2184
2185   * This adds a line segment to the current path
2186
2187   * @param x coord
2188
2189   * @param y coord
2190
2191   */

2192
2193  public void lineto(double x,double y) {
2194
2195    newPath();
2196
2197    // no optimisation here as it may introduce errors on decimal coords.
2198

2199    pw.print(cxy(x,y)+"l ");
2200
2201    lx=(int)x;
2202
2203    ly=(int)y;
2204
2205  }
2206
2207    
2208
2209  /**
2210
2211   * This extension allows the width of the drawn line to be set
2212
2213   * @param w Line width in mm
2214
2215   */

2216
2217  public void setLineWidth(double w) {
2218
2219    closeBlock(); // draw any path before we change the line width
2220

2221    pw.println(""+w+" w");
2222
2223  }
2224
2225    
2226
2227  /**
2228
2229   * This extension sets the line width to the default of 1mm which is what
2230
2231   * Java uses when drawing to a PrintJob.
2232
2233   */

2234
2235  public void setDefaultLineWidth() {
2236
2237    closeBlock(); // draw any path before we change the line width
2238

2239    pw.println("1 w");
2240
2241  }
2242
2243    
2244
2245  /**
2246
2247   * This is used to add a polygon to the current path.
2248
2249   * Used by drawPolygon(), drawPolyline() and fillPolygon() etal
2250
2251   * @param xp Array of x coordinates
2252
2253   * @param yp Array of y coordinates
2254
2255   * @param np number of points in polygon
2256
2257   * @see #drawPolygon
2258
2259   * @see #drawPolyline
2260
2261   * @see #fillPolygon
2262
2263   */

2264
2265  public void polygon(int[] xp,int[] yp,int np) {
2266
2267    // newPath() not needed here as moveto does it ;-)
2268

2269    moveto(xp[0],yp[0]);
2270
2271    for(int i=1;i<np;i++)
2272
2273      lineto(xp[i],yp[i]);
2274
2275  }
2276
2277    
2278
2279  /**
2280
2281   * This extension appends a Bezier curve to the path. The curve
2282
2283   * extends from the current point to (x3,y3) using (x1,y1) and
2284
2285   * (x2,y2) as the Bezier control points.
2286
2287   * <p>The new current point is (x3,y3)
2288
2289   *
2290
2291   * @param x1 First control point
2292
2293   * @param y1 First control point
2294
2295   * @param x2 Second control point
2296
2297   * @param y2 Second control point
2298
2299   * @param x3 Destination point
2300
2301   * @param y3 Destination point
2302
2303   */

2304
2305  public void curveto(int x1,int y1,int x2,int y2,int x3,int y3) {
2306
2307    newPath();
2308
2309    pw.println(cxy(x1,y1)+cxy(x2,y2)+cxy(x3,y3)+"c");
2310
2311    lx=x3;
2312
2313    ly=y3;
2314
2315  }
2316
2317    
2318
2319  /**
2320
2321   * This extension appends a Bezier curve to the path. The curve
2322
2323   * extends from the current point to (x3,y3) using (x1,y1) and
2324
2325   * (x2,y2) as the Bezier control points.
2326
2327   * <p>The new current point is (x3,y3)
2328
2329   *
2330
2331   * @param x1 First control point
2332
2333   * @param y1 First control point
2334
2335   * @param x2 Second control point
2336
2337   * @param y2 Second control point
2338
2339   * @param x3 Destination point
2340
2341   * @param y3 Destination point
2342
2343   */

2344
2345  public void curveto(double x1,double y1,double x2,double y2,double x3,double y3) {
2346
2347    newPath();
2348
2349    pw.println(cxy(x1,y1)+cxy(x2,y2)+cxy(x3,y3)+"c");
2350
2351    lx=(int)x3;
2352
2353    ly=(int)y3;
2354
2355  }
2356
2357    
2358
2359  /**
2360
2361   * This extension appends a Bezier curve to the path. The curve
2362
2363   * extends from the current point to (x2,y2) using the current
2364
2365   * point and (x1,y1) as the Bezier control points.
2366
2367   * <p>The new current point is (x2,y2)
2368
2369   *
2370
2371   * @param x1 Second control point
2372
2373   * @param y1 Second control point
2374
2375   * @param x2 Destination point
2376
2377   * @param y2 Destination point
2378
2379   */

2380
2381  public void curveto(int x1,int y1,int x2,int y2) {
2382
2383    newPath();
2384
2385    pw.println(cxy(x1,y1)+cxy(x2,y2)+"v");
2386
2387    lx=x2;
2388
2389    ly=y2;
2390
2391  }
2392
2393    
2394
2395  /**
2396
2397   * This extension appends a Bezier curve to the path. The curve
2398
2399   * extends from the current point to (x2,y2) using the current
2400
2401   * point and (x1,y1) as the Bezier control points.
2402
2403   * <p>The new current point is (x2,y2)
2404
2405   *
2406
2407   * @param x1 Second control point
2408
2409   * @param y1 Second control point
2410
2411   * @param x2 Destination point
2412
2413   * @param y2 Destination point
2414
2415   */

2416
2417  public void curveto(double x1,double y1,double x2,double y2) {
2418
2419    newPath();
2420
2421    pw.println(cxy(x1,y1)+cxy(x2,y2)+"v");
2422
2423    lx=(int)x2;
2424
2425    ly=(int)y2;
2426
2427  }
2428
2429    
2430
2431  /**
2432
2433   * This extension appends a Bezier curve to the path. The curve
2434
2435   * extends from the current point to (x2,y2) using (x1,y1) and
2436
2437   * the end point as the Bezier control points.
2438
2439   * <p>The new current point is (x2,y2)
2440
2441   *
2442
2443   * @param x1 Second control point
2444
2445   * @param y1 Second control point
2446
2447   * @param x2 Destination point
2448
2449   * @param y2 Destination point
2450
2451   */

2452
2453  public void curveto2(int x1,int y1,int x2,int y2) {
2454
2455    newPath();
2456
2457    pw.println(cxy(x1,y1)+cxy(x2,y2)+"y");
2458
2459    lx=x2;
2460
2461    ly=y2;
2462
2463  }
2464
2465    
2466
2467  /**
2468
2469   * This extension appends a Bezier curve to the path. The curve
2470
2471   * extends from the current point to (x2,y2) using (x1,y1) and
2472
2473   * the end point as the Bezier control points.
2474
2475   * <p>The new current point is (x2,y2)
2476
2477   *
2478
2479   * @param x1 Second control point
2480
2481   * @param y1 Second control point
2482
2483   * @param x2 Destination point
2484
2485   * @param y2 Destination point
2486
2487   */

2488
2489  public void curveto2(double x1,double y1,double x2,double y2) {
2490
2491    newPath();
2492
2493    pw.println(cxy(x1,y1)+cxy(x2,y2)+"y");
2494
2495    lx=(int)x2;
2496
2497    ly=(int)y2;
2498
2499  }
2500
2501    
2502
2503    
2504
2505  // Arcs are horrible and complex. They are at the end of the
2506

2507  // file, because they are the largest. This is because, unlike
2508

2509  // Postscript, PDF doesn't have any arc operators, so we must
2510

2511  // implement them by converting into one or more Bezier curves
2512

2513  // (which is how Postscript does them internally).
2514

2515  
2516
2517  /**
2518
2519   * One degree in radians
2520
2521   */

2522
2523  private static final double degrees_to_radians = Math.PI/180.0;
2524
2525    
2526
2527  /**
2528
2529   * This produces an arc by breaking it down into one or more Bezier curves.
2530
2531   * It is used internally to implement the drawArc and fillArc methods.
2532
2533   *
2534
2535   * @param axc X coordinate of arc centre
2536
2537   * @param ayc Y coordinate of arc centre
2538
2539   * @param width of bounding rectangle
2540
2541   * @param height of bounding rectangle
2542
2543   * @param ang1 Start angle
2544
2545   * @param ang2 End angle
2546
2547   * @param clockwise true to draw clockwise, false anti-clockwise
2548
2549   */

2550
2551  public void arc(double axc,double ayc,
2552
2553          double width,double height,
2554
2555          double ang1,double ang2,
2556
2557          boolean clockwise) {
2558
2559        
2560
2561    double adiff;
2562
2563    double x0, y0;
2564
2565    double x3r, y3r;
2566
2567    boolean first = true;
2568
2569        
2570
2571    // may not need this
2572

2573    //if( ar < 0 ) {
2574

2575    //ang1 += fixed_180;
2576

2577    //ang2 += fixed_180;
2578

2579    //ar = - ar;
2580

2581    //}
2582

2583        
2584
2585    double ang1r = (ang1%360.0)*degrees_to_radians;
2586
2587        
2588
2589    double sin0 = Math.sin(ang1r);
2590
2591    double cos0 = Math.cos(ang1r);
2592
2593        
2594
2595    x0 = axc + width*cos0;
2596
2597    y0 = ayc + height*sin0;
2598
2599        
2600
2601    // NB: !clockwise here as Java Space is inverted to User Space
2602

2603    if( !clockwise ) {
2604
2605      // Quadrant reduction
2606

2607      while ( ang1 < ang2 ) ang2 -= 360.0;
2608
2609      while ( (adiff = ang2 - ang1) < -90.0 ) {
2610
2611    double w = sin0; sin0 = -cos0; cos0 = w;
2612
2613    x3r = axc + width*cos0;
2614
2615    y3r = ayc + height*sin0;
2616
2617    arc_add(first,
2618
2619        width, height,
2620
2621        x0, y0,
2622
2623        x3r, y3r,
2624
2625        (x0 + width*cos0),
2626
2627        (y0 + height*sin0)
2628
2629        );
2630
2631                
2632
2633    x0 = x3r;
2634
2635    y0 = y3r;
2636
2637    ang1 -= 90.0;
2638
2639    first = false;
2640
2641      }
2642
2643    } else {
2644
2645      // Quadrant reduction
2646

2647      while ( ang2 < ang1 ) ang2 += 360.0;
2648
2649      while ( (adiff = ang2 - ang1) > 90.0 ) {
2650
2651    double w = cos0; cos0 = -sin0; sin0 = w;
2652
2653    x3r = axc + width*cos0;
2654
2655    y3r = ayc + height*sin0;
2656
2657    arc_add(first,
2658
2659        width, height,
2660
2661        x0, y0,
2662
2663        x3r, y3r,
2664
2665        (x0 + width*cos0),
2666
2667        (y0 + height*sin0)
2668
2669        );
2670
2671                
2672
2673    x0 = x3r;
2674
2675    y0 = y3r;
2676
2677    ang1 += 90.0;
2678
2679    first = false;
2680
2681      }
2682
2683    }
2684
2685        
2686
2687    // Compute the intersection of the tangents.
2688

2689    // We know that -fixed_90 <= adiff <= fixed_90.
2690

2691    double trad = Math.tan(adiff * (degrees_to_radians / 2));
2692
2693    double ang2r = ang2 * degrees_to_radians;
2694
2695    double xt = x0 - trad * width*sin0;
2696
2697    double yt = y0 + trad * height*cos0;
2698
2699    arc_add(first, width, height, x0, y0,
2700
2701        (axc + width * Math.cos(ang2r)),
2702
2703        (ayc + height * Math.sin(ang2r)),
2704
2705        xt, yt);
2706
2707  }
2708
2709    
2710
2711  /**
2712
2713   * Used by the arc method to actually add an arc to the path
2714
2715   * Important: We write directly to the stream here, because this method
2716
2717   * operates in User space, rather than Java space.
2718
2719   * @param first true if the first arc
2720
2721   * @param w width
2722
2723   * @param h height
2724
2725   * @param x0 coord
2726
2727   * @param y0 coord
2728
2729   * @param x3 coord
2730
2731   * @param y3 coord
2732
2733   * @param xt coord
2734
2735   * @param yt coord
2736
2737   */

2738
2739  private void arc_add(boolean first,
2740
2741               double w,double h,
2742
2743               double x0,double y0,
2744
2745               double x3,double y3,
2746
2747               double xt,double yt) {
2748
2749    double dx = xt - x0, dy = yt - y0;
2750
2751    double dist = dx*dx + dy*dy;
2752
2753    double w2 = w*w, h2=h*h;
2754
2755    double r2 = w2+h2;
2756
2757        
2758
2759    double fw = 0.0, fh = 0.0;
2760
2761    if(dist < (r2*1.0e8)) {
2762
2763      // JM
2764

2765      fw = (w2 != 0.0) ? ((4.0/3.0)/(1+Math.sqrt(1+dist/w2))) : 0.0;
2766
2767      fh = (h2 != 0.0) ? ((4.0/3.0)/(1+Math.sqrt(1+dist/h2))) : 0.0;
2768
2769    }
2770
2771        
2772
2773    // The path must have a starting point
2774

2775    if(first)
2776
2777      moveto(x0,y0);
2778
2779        
2780
2781    double x = x0+((xt-x0)*fw);
2782
2783    double y = y0+((yt-y0)*fh);
2784
2785    x0 = x3+((xt-x3)*fw);
2786
2787    y0 = y3+((yt-y3)*fh);
2788
2789
2790
2791    // Finally the actual curve.
2792

2793    curveto(x,y,x0,y0,x3,y3);
2794
2795  }
2796
2797    
2798
2799  /**
2800
2801   * This sets the media Orientation (0=Portrait, 90=Landscape,
2802
2803   * 180=Inverse, 270=Seascape).
2804
2805   *
2806
2807   * <p>Normally, this is called when the Graphics instance is created, but
2808
2809   * if the media is changed, then this must be called, especially when using
2810
2811   * the PDFJob class to create the file.
2812
2813   *
2814
2815   */

2816
2817  public void setOrientation() {
2818
2819    mediaRot = page.getOrientation();
2820
2821    switch(mediaRot)
2822
2823      {
2824
2825      case 0: // Portrait
2826

2827    //pre_np = "1 0 0 1 0 "+media.height+" cm 1 0 0 -1 0 0 cm ";
2828

2829    break;
2830
2831                
2832
2833      case 90: // Landscape
2834

2835    //pw.println("0.7071067 0.7071067 -0.7071067 0.7071067 0 0 Tm");
2836

2837    //pw.println("1 0 0 1 0 -"+page.getMedia().height+" Tm");
2838

2839    //pre_np = "1 0 0 1 "+page.getMedia().width+" 0 cm 0 1 -1 0 0 0 cm ";
2840

2841    break;
2842
2843                
2844
2845      case 180: // Inverted Portrait
2846

2847    //pre_np = "1 0 0 1 "+media.width+" 0 cm -1 0 0 1 0 0 cm ";
2848

2849    break;
2850
2851                
2852
2853      case 270: // Seascape
2854

2855    // check this
2856

2857    //pre_np = "1 0 0 1 -"+page.getMedia().width+" 0 cm 0 -1 1 0 0 0 cm ";
2858

2859    break;
2860
2861      }
2862
2863  }
2864
2865    
2866
2867  /**
2868
2869   * Converts the Java space coordinates into pdf.
2870
2871   * @param x coord
2872
2873   * @param y coord
2874
2875   * @return String containing the coordinates in PDF space
2876
2877   */

2878
2879  private String JavaDoc cxy(int x,int y) {
2880
2881    return cxy((double)x,(double)y);
2882
2883  }
2884
2885    
2886
2887  /**
2888
2889   * Converts the Java space coordinates into pdf.
2890
2891   * @param x coord
2892
2893   * @param y coord
2894
2895   * @return String containing the coordinates in PDF space
2896
2897   */

2898
2899  private String JavaDoc cxy(double x,double y) {
2900
2901    double nx=x,ny=y; // scratch
2902

2903    double mw = (double)(media.width);
2904
2905    double mh = (double)(media.height);
2906
2907        
2908
2909    // handle any translations
2910

2911    x-=trax;
2912
2913    y-=tray;
2914
2915        
2916
2917    switch(mediaRot) {
2918
2919    case 0:
2920
2921      // Portrait
2922

2923      //nx = x;
2924

2925      ny = mh - y;
2926
2927      break;
2928
2929          
2930
2931    case 90:
2932
2933      // Landscape
2934

2935      nx = y;
2936
2937      ny = x;
2938
2939      break;
2940
2941          
2942
2943    case 180:
2944
2945      // Inverse Portrait
2946

2947      nx = mw - x;
2948
2949      //ny = y;
2950

2951      break;
2952
2953          
2954
2955    case 270:
2956
2957      // Seascape
2958

2959      nx = mw - y;
2960
2961      ny = mh - x;
2962
2963      break;
2964
2965    }
2966
2967        
2968
2969    return ""+nx+" "+ny+" ";
2970
2971  }
2972
2973    
2974
2975  /**
2976
2977   * Converts the Java space dimension into pdf.
2978
2979   * @param w width
2980
2981   * @param h height
2982
2983   * @return String containing the coordinates in PDF space
2984
2985   */

2986
2987  private String JavaDoc cwh(int w,int h) {
2988
2989    return cwh((double)w,(double)h);
2990
2991  }
2992
2993    
2994
2995  /**
2996
2997   * Converts the Java space dimension into pdf.
2998
2999   * @param w width
3000
3001   * @param h height
3002
3003   * @return String containing the coordinates in PDF space
3004
3005   */

3006
3007  private String JavaDoc cwh(double w,double h) {
3008
3009    double nw=w,nh=h; // scratch
3010

3011      
3012
3013    switch(mediaRot) {
3014
3015    case 0:
3016
3017      // Portrait
3018

3019      //nw = w;
3020

3021      nh = -h;
3022
3023      break;
3024
3025        
3026
3027    case 90:
3028
3029      // Landscape
3030

3031      nw = h;
3032
3033      nh = w;
3034
3035      break;
3036
3037        
3038
3039    case 180:
3040
3041      // Inverse Portrait
3042

3043      nw = -w;
3044
3045      //nh = h;
3046

3047      break;
3048
3049        
3050
3051    case 270:
3052
3053      // Seascape
3054

3055      nw = -h;
3056
3057      nh = -w;
3058
3059      break;
3060
3061    }
3062
3063      
3064
3065    return ""+nw+" "+nh+" ";
3066
3067  }
3068
3069  
3070
3071  /**
3072
3073   * Converts the Java space coordinates into pdf text space.
3074
3075   * @param x coord
3076
3077   * @param y coord
3078
3079   * @return String containing the coordinates in PDF text space
3080
3081   */

3082
3083  private String JavaDoc txy(int x,int y) {
3084
3085    int nx=x, ny=y;
3086
3087    int mw = media.width;
3088
3089    int mh = media.height;
3090
3091        
3092
3093    // handle any translations
3094

3095    x+=trax;
3096
3097    y+=tray;
3098
3099        
3100
3101    switch(mediaRot)
3102
3103      {
3104
3105      case 0:
3106
3107    // Portrait
3108

3109    //nx = x;
3110

3111    ny = mh - y;
3112
3113    break;
3114
3115                
3116
3117      case 90:
3118
3119    // Landscape
3120

3121    //nx = y;
3122

3123    //ny = x;
3124

3125    nx = x;
3126
3127    ny = -y;
3128
3129    break;
3130
3131                
3132
3133      case 180:
3134
3135    // Inverse Portrait
3136

3137    // to be completed
3138

3139    nx = mw - x;
3140
3141    //ny = y;
3142

3143    break;
3144
3145                
3146
3147      case 270:
3148
3149    // Seascape
3150

3151    // to be completed
3152

3153    nx = mw - y;
3154
3155    ny = mh - x;
3156
3157    break;
3158
3159      }
3160
3161        
3162
3163    return ""+nx+" "+ny+" ";
3164
3165  }
3166
3167    
3168
3169  /**
3170
3171   * Converts the Java space coordinates into pdf text space.
3172
3173   * @param x coord
3174
3175   * @param y coord
3176
3177   * @param tx coord
3178
3179   * @param ty coord
3180
3181   * @return String containing the coordinates in PDF text space
3182
3183   */

3184
3185  private String JavaDoc twh(int x,int y,int tx,int ty) {
3186
3187    int nx=x, ny=y;
3188
3189    int ntx=tx, nty=ty;
3190
3191    int mw = media.width;
3192
3193    int mh = media.height;
3194
3195    int sx=1,sy=1;
3196
3197    switch(mediaRot)
3198
3199      {
3200
3201      case 0:
3202
3203    // Portrait
3204

3205    //nx = x;
3206

3207    ny = mh - y;
3208
3209    nty = mh - ty;
3210
3211    break;
3212
3213                
3214
3215      case 90:
3216
3217    // Landscape
3218

3219    //nx = y;
3220

3221    //ny = x;
3222

3223    //ntx = ty;
3224

3225    //nty = tx;
3226

3227    //sy=-1;
3228

3229    nx = x;
3230
3231    ny = -y;
3232
3233    ntx = tx;
3234
3235    nty = -ty;
3236
3237    //sy=-1;
3238

3239    break;
3240
3241                
3242
3243      case 180:
3244
3245    // Inverse Portrait
3246

3247    // to be completed
3248

3249    nx = mw - x;
3250
3251    //ny = y;
3252

3253    break;
3254
3255                
3256
3257      case 270:
3258
3259    // Seascape
3260

3261    // to be completed
3262

3263    nx = mw - y;
3264
3265    ny = mh - x;
3266
3267    break;
3268
3269      }
3270
3271        
3272
3273    nx = sx*(nx-ntx);
3274
3275    ny = sy*(ny-nty);
3276
3277    return ""+nx+" "+ny+" ";
3278
3279  }
3280
3281
3282
3283    
3284
3285    
3286
3287  /**
3288
3289   * Returns the Shape of the clipping region
3290
3291   * As my JDK docs say, this may break with Java 2D.
3292
3293   * @return Shape of the clipping region
3294
3295   */

3296
3297  public Shape getClip() {
3298
3299    return null;
3300
3301  }
3302
3303    
3304
3305  /**
3306
3307   * Draws a string using a AttributedCharacterIterator.
3308
3309   * <p>This is not supported yet, as I have no idea what an
3310
3311   * AttributedCharacterIterator is.
3312
3313   * <p>This method is new to the Java2 API.
3314
3315   */

3316
3317  public void drawString(java.text.AttributedCharacterIterator JavaDoc aci,
3318
3319             int x,int y) {
3320
3321  }
3322
3323    
3324
3325} // end class PDFGraphics
3326

3327
3328
3329
Popular Tags