OSDN Git Service

2006-06-09 Thomas Fitzsimmons <fitzsim@redhat.com>
[pf3gnuchains/gcc-fork.git] / libjava / classpath / gnu / java / awt / print / PostScriptGraphics2D.java
1 /* PostScriptGraphics2D.java -- AWT printer rendering class.
2    Copyright (C) 2006  Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37
38 package gnu.java.awt.print;
39
40 import java.awt.BasicStroke;
41 import java.awt.Color;
42 import java.awt.Composite;
43 import java.awt.Paint;
44 import java.awt.Font;
45 import java.awt.FontMetrics;
46 import java.awt.GradientPaint;
47 import java.awt.Graphics;
48 import java.awt.GraphicsConfiguration;
49 import java.awt.Graphics2D;
50 import java.awt.Image;
51 import java.awt.Polygon;
52 import java.awt.Rectangle;
53 import java.awt.RenderingHints;
54 import java.awt.Shape;
55 import java.awt.Stroke;
56 import java.awt.geom.AffineTransform;
57 import java.awt.geom.Arc2D;
58 import java.awt.geom.Ellipse2D;
59 import java.awt.geom.RoundRectangle2D;
60 import java.awt.geom.PathIterator;
61 import java.awt.geom.Point2D;
62 import java.awt.geom.Rectangle2D;
63 import java.awt.font.FontRenderContext;
64 import java.awt.font.GlyphVector;
65 import java.awt.font.TextLayout;
66 import java.awt.image.BufferedImage;
67 import java.awt.image.BufferedImageOp;
68 import java.awt.image.renderable.RenderableImage;
69 import java.awt.image.RenderedImage;
70 import java.awt.image.ImageObserver;
71 import java.awt.image.PixelGrabber;
72 import java.awt.print.PageFormat;
73 import java.awt.print.Pageable;
74 import java.awt.print.Paper;
75 import java.awt.print.Printable;
76 import java.awt.print.PrinterException;
77 import java.awt.print.PrinterGraphics;
78 import java.awt.print.PrinterJob;
79 import java.io.BufferedWriter;
80 import java.io.File;
81 import java.io.FileOutputStream;
82 import java.io.IOException;
83 import java.io.OutputStreamWriter;
84 import java.io.PrintWriter;
85 import java.text.AttributedCharacterIterator;
86 import java.util.Map;
87
88 /**
89  * Class PostScriptGraphics2D - Class that implements the Graphics2D object,
90  * writing the output to a PostScript or EPS file
91  *
92  * @author Sven de Marothy
93  *
94  */
95 class PostScriptGraphics2D extends Graphics2D
96 {
97   /**
98    * The associated printer job.
99    */
100   private PrinterJob printerJob;
101
102   /**
103    * Output file.
104    */
105   private PrintWriter out;
106
107   // Graphics data
108   private AffineTransform currentTransform = new AffineTransform();
109   private AffineTransform pageTransform;
110   private RenderingHints renderingHints;
111   private Paint currentPaint = null;
112   private Shape clipShape = null;
113   private Font currentFont = null;
114   private Color currentColor = Color.black;
115   private Color backgroundColor = Color.white;
116   private Stroke currentStroke = null;
117   private static Stroke ordinaryStroke = new BasicStroke(0.0f,
118                                                          BasicStroke.CAP_BUTT,
119                                                          BasicStroke.JOIN_MITER);
120   private float cx; // current drawing position
121   private float cy; // current drawing position
122   private boolean currentFontIsPS; // set if currentFont is one of the above
123
124   // settings
125   private double pageX = 595;
126   private double pageY = 842;
127   private double Y = pageY;
128   private boolean gradientOn = false;
129
130   /** 
131    * Constructor
132    *
133    */
134   public PostScriptGraphics2D( PrinterJob pg )
135   {
136     printerJob = pg;
137     // create transform objects
138     pageTransform = new AffineTransform();
139     currentTransform = new AffineTransform();
140
141     /*
142       Create Rendering hints
143       No text aliasing
144       Quality color and rendering
145       Bicubic interpolation
146       Fractional metrics supported
147     */
148     renderingHints = new RenderingHints(null);
149     renderingHints.put(RenderingHints.KEY_RENDERING,
150                        RenderingHints.VALUE_RENDER_QUALITY);
151     renderingHints.put(RenderingHints.KEY_TEXT_ANTIALIASING,
152                        RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
153     renderingHints.put(RenderingHints.KEY_INTERPOLATION,
154                        RenderingHints.VALUE_INTERPOLATION_BICUBIC);
155     renderingHints.put(RenderingHints.KEY_FRACTIONALMETRICS,
156                        RenderingHints.VALUE_FRACTIONALMETRICS_ON);
157     renderingHints.put(RenderingHints.KEY_COLOR_RENDERING,
158                        RenderingHints.VALUE_COLOR_RENDER_QUALITY);
159   }
160
161   /**
162    * Spool a document to PostScript.
163    * If Pageable is non-null, it will print that, otherwise it will use
164    * the supplied printable and pageFormat.
165    */
166   public SpooledDocument spoolPostScript(Printable printable, 
167                                          PageFormat pageFormat,
168                                          Pageable pageable)
169     throws PrinterException
170   {
171     try 
172       {
173         // spool to a temporary file
174         File temp = File.createTempFile("cpspool", ".ps");
175         temp.deleteOnExit();
176         
177         out = new PrintWriter(new BufferedWriter
178                               (new OutputStreamWriter
179                                (new FileOutputStream(temp), 
180                                 "ISO8859_1"), 1000000));
181         
182         writePSHeader();
183         
184         if(pageable != null)
185           {
186             for(int index = 0; index < pageable.getNumberOfPages(); index++)
187               spoolPage(out, pageable.getPrintable(index),
188                         pageable.getPageFormat(index), index);
189           }
190         else
191           {
192             int index = 0;
193             while(spoolPage(out, printable, pageFormat, index++) ==
194                   Printable.PAGE_EXISTS);
195           }
196         out.println("%%Trailer");
197         out.println("%%EOF");
198         out.close();
199         return new SpooledDocument( temp );
200       } 
201     catch (IOException e) 
202       {
203         PrinterException pe = new PrinterException();
204         pe.initCause(e);
205         throw pe;
206       }
207   }
208
209   //--------------------------------------------------------------------------
210
211   /** 
212    * Write the postscript file header,
213    * setup the page format and transforms. 
214    */
215   private void writePSHeader()
216   {
217     out.println("%!PS-Adobe-3.0");      
218     out.println("%%Title: "+printerJob.getJobName());
219     out.println("%%Creator: GNU Classpath ");
220     out.println("%%DocumentData: Clean8Bit");
221
222     out.println("%%DocumentNeededResources: font Times-Roman Helvetica Courier");
223     out.println("%%EndComments");
224     
225     out.println("%%BeginProlog");
226     out.println("%%EndProlog");
227     out.println("%%BeginSetup");
228     
229     out.println("%%EndFeature");
230     setupFonts();
231     out.println("%%EndSetup");
232  
233     // set default fonts and colors
234     setFont( new Font("Dialog", Font.PLAIN, 12) );
235     currentColor = Color.white;
236     currentStroke = new BasicStroke();
237     setPaint(currentColor);
238     setStroke(currentStroke);
239   }
240
241   /**
242    * setupFonts - set up the font dictionaries for
243    * helvetica, times and courier
244    */
245   private void setupFonts()
246   {
247     out.println("/helveticaISO");
248     out.println("/Helvetica findfont dup length dict begin");
249     out.println("{ 1 index /FID eq { pop pop } { def } ifelse } forall");
250     out.println("/Encoding ISOLatin1Encoding def");
251     out.println("currentdict end definefont pop");
252
253     out.println("/timesISO");
254     out.println("/Times-Roman findfont dup length dict begin");
255     out.println("{ 1 index /FID eq { pop pop } { def } ifelse } forall");
256     out.println("/Encoding ISOLatin1Encoding def");
257     out.println("currentdict end definefont pop");
258
259     out.println("/courierISO");
260     out.println("/Courier findfont dup length dict begin");
261     out.println("{ 1 index /FID eq { pop pop } { def } ifelse } forall");
262     out.println("/Encoding ISOLatin1Encoding def");
263     out.println("currentdict end definefont pop");
264   }
265
266   /**
267    * Spools a single page, returns NO_SUCH_PAGE unsuccessful,
268    * PAGE_EXISTS if it was.
269    */
270   public int spoolPage(PrintWriter out,
271                        Printable printable, 
272                        PageFormat pageFormat, 
273                        int index) throws IOException, PrinterException
274   {
275     out.println("%%BeginPageSetup");
276
277     Paper p = pageFormat.getPaper();
278     pageX = p.getWidth();
279     pageY = p.getHeight();
280
281     if( pageFormat.getOrientation() == PageFormat.PORTRAIT )
282       out.println( "%%Orientation: Portrait" );
283     else
284       {
285         out.println( "%%Orientation: Landscape" );
286         double t = pageX;
287         pageX = pageY;
288         pageY = t;
289       }
290       
291     setClip(0, 0, (int)pageX, (int)pageY);
292
293     out.println("gsave % first save");
294     
295     // 595x842; 612x792 respectively
296     out.println("<< /PageSize [" +pageX + " "+pageY+ "] >> setpagedevice");
297
298     if( pageFormat.getOrientation() != PageFormat.LANDSCAPE )
299       {
300         pageTransform.translate(pageX, 0);
301         pageTransform.scale(-1.0, 1.0);
302       }
303
304     // save the original CTM
305     pushCTM();
306     concatCTM(pageTransform);
307     setTransform(new AffineTransform());
308
309     out.println("%%EndPageSetup");
310
311     out.println("gsave");
312
313     if( printable.print(this, pageFormat, index) == Printable.NO_SUCH_PAGE )
314       return Printable.NO_SUCH_PAGE;
315     
316     out.println("grestore");
317     out.println("showpage");
318
319     return Printable.PAGE_EXISTS;
320   }
321
322   /** push the Current Transformation Matrix onto the PS stack */
323   private void pushCTM()
324   {
325     out.println("matrix currentmatrix   % pushCTM()");
326   }
327
328   /** pop the Current Transformation Matrix from the PS stack */
329   private void popCTM()
330   {
331     out.println("setmatrix % restore CTM");
332   }
333
334   ///////////////////////////////////////////////////////////////////////////
335
336   public Graphics create()
337   {
338     return null;
339   }
340
341   public void drawOval(int x, int y, int width, int height)
342   {
343     out.println("% drawOval()");
344     setStroke(ordinaryStroke);
345     draw(new Ellipse2D.Double(x, y, width, height));
346     setStroke(currentStroke);
347   }
348
349   public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints)
350   {
351     if (nPoints <= 0 || xPoints.length < nPoints || yPoints.length < nPoints)
352       return;
353     out.println("newpath % drawPolyLine()");
354     out.println(xPoints[0] + " " + yPoints[0] + " moveto");
355     for (int i = 1; i < nPoints; i++)
356       out.println(xPoints[i] + " " + yPoints[i] + " lineto");
357     out.println("closepath");
358     out.println("stroke");
359   }
360
361   public void drawRoundRect(int x, int y, int width, int height, int arcWidth,
362                             int arcHeight)
363   {
364     out.println("% drawRoundRect()");
365     RoundRectangle2D.Double rr = new RoundRectangle2D.Double(x, y, width,
366                                                              height, arcWidth,
367                                                              arcHeight);
368     setStroke(ordinaryStroke);
369     draw(rr);
370     setStroke(currentStroke);
371   }
372
373   public void fillRoundRect(int x, int y, int width, int height, int arcWidth,
374                             int arcHeight)
375   {
376     out.println("% fillRoundRect()");
377     RoundRectangle2D.Double rr = new RoundRectangle2D.Double(x, y, width,
378                                                              height, arcWidth,
379                                                              arcHeight);
380     fill(rr);
381   }
382
383   public void drawArc(int x, int y, int width, int height, int startAngle,
384                       int arcAngle)
385   {
386     setStroke(ordinaryStroke);
387     draw(new Arc2D.Double(x, y, width, height, startAngle, arcAngle, Arc2D.OPEN));
388     setStroke(currentStroke);
389   }
390
391   public void fillArc(int x, int y, int width, int height, int startAngle,
392                       int arcAngle)
393   {
394     fill(new Arc2D.Double(x, y, width, height, startAngle, arcAngle, Arc2D.PIE));
395   }
396
397   public void fillOval(int x, int y, int width, int height)
398   {
399     out.println("% fillOval()");
400     fill( new Ellipse2D.Double(x, y, width, height) );
401   }
402
403   public void fillPolygon(int[] x, int[] y, int nPoints)
404   {
405     out.println("% fillPolygon()");
406     fill( new Polygon(x, y, nPoints) );
407   }
408
409   public void drawLine(int x1, int y1, int x2, int y2)
410   {
411     out.println("% drawLine()");
412     setStroke(ordinaryStroke);
413     out.println("newpath");
414     out.println(x1 + " " + (y1) + " moveto");
415     out.println(x2 + " " + (y2) + " lineto");
416     out.println("stroke");
417     setStroke(currentStroke);
418   }
419
420   //--------------- Image drawing ------------------------------------------   
421   public boolean drawImage(Image img, int x, int y, Color bgcolor,
422                            ImageObserver observer)
423   {
424     int w = img.getWidth(null);
425     int h = img.getHeight(null);
426
427     return drawImage(img, x, y, x + w, y + h, 0, 0, w - 1, h - 1, bgcolor,
428                      observer);
429   }
430
431   public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2,
432                            int sx1, int sy1, int sx2, int sy2, Color bgcolor,
433                            ImageObserver observer)
434   {
435     int n = 0;
436     boolean flipx = false;
437     boolean flipy = false;
438
439     // swap X and Y's
440     if (sx1 > sx2)
441       {
442         n = sx1;
443         sx1 = sx2;
444         sx2 = n;
445         flipx = ! flipx;
446       }
447     if (sy1 > sy2)
448       {
449         n = sy1;
450         sy1 = sy2;
451         sy2 = n;
452         flipy = ! flipy;
453       }
454     if (dx1 > dx2)
455       {
456         n = dx1;
457         dx1 = dx2;
458         dx2 = n;
459         flipx = ! flipx;
460       }
461     if (dy1 > dy2)
462       {
463         n = dy1;
464         dy1 = dy2;
465         dy2 = n;
466         flipy = ! flipy;
467       }
468     n = 0;
469     int sw = sx2 - sx1; // source width
470     int sh = sy2 - sy1; // source height
471     int[] pixels = new int[sw * sh]; // pixel buffer
472     int dw = dx2 - dx1; // destination width
473     int dh = dy2 - dy1; // destination height
474     double x_scale = ((double) dw) / ((double) sw);
475     double y_scale = ((double) dh) / ((double) sh);
476
477     out.println("% drawImage() 2");
478     out.println("gsave");
479     out.println(dx1 + " " + dy1 + " translate");
480     out.println(dw + " " + dh + " scale");
481     out.println(sw + " " + sh + " 8 [" + (flipx ? -sw : sw) + " 0 0 "
482                 + (flipy ? -sh : sh) + " " + (flipx ? sw : 0) + " "
483                 + (flipy ? sh : 0) + " ]");
484     out.println("{currentfile 3 string readhexstring pop} bind");
485     out.println("false 3 colorimage");
486
487     PixelGrabber pg = new PixelGrabber(img, sx1, sy1, sw, sh, pixels, 0, sw);
488     try
489       {
490         pg.grabPixels();
491       }
492     catch (InterruptedException e)
493       {
494         System.err.println("interrupted waiting for pixels!");
495         return (false);
496       }
497
498     if ((pg.getStatus() & ImageObserver.ABORT) != 0)
499       {
500         System.err.println("image fetch aborted or errored");
501         return (false);
502       }
503
504     for (int j = 0; j < sh; j++)
505       {
506         for (int i = 0; i < sw; i++)
507           {
508             out.print(colorTripleHex(new Color(pixels[j * sw + i])));
509             if (((++n) % 11) == 0)
510               out.println();
511           }
512       }
513
514     out.println();
515     out.println("%%EOF");
516     out.println("grestore");
517     return true;
518   }
519
520   public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2,
521                            int sx1, int sy1, int sx2, int sy2,
522                            ImageObserver observer)
523   {
524     return drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null,
525                      observer);
526   }
527
528   public boolean drawImage(Image img, int x, int y, ImageObserver observer)
529   {
530     return drawImage(img, x, y, null, observer);
531   }
532
533   public boolean drawImage(Image img, int x, int y, int width, int height,
534                            Color bgcolor, ImageObserver observer)
535   {
536     int sw = img.getWidth(null);
537     int sh = img.getHeight(null);
538     return drawImage(img, x, y, x + width, y + height, /* destination */
539                      0, 0, sw - 1, sh - 1, /* source */
540                      bgcolor, observer);
541     // correct?
542   }
543
544   public boolean drawImage(Image img, int x, int y, int width, int height,
545                            ImageObserver observer)
546   {
547     return drawImage(img, x, y, width, height, null, observer);
548   }
549
550   /** Renders a BufferedImage that is filtered with a BufferedImageOp. */
551   public void drawImage(BufferedImage img, BufferedImageOp op, int x, int y)
552   {
553     BufferedImage result = op.filter(img, null);
554     drawImage(result, x, y, null);
555   }
556
557   /** Renders an image, applying a transform from image space
558       into user space before drawing. */
559   public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs)
560   {
561     AffineTransform oldTransform = new AffineTransform(currentTransform);
562     boolean ret;
563
564     transform(xform);
565     ret = drawImage(img, 0, 0, null, obs);
566     setTransform(oldTransform);
567
568     return ret;
569   }
570
571   /** Renders a RenderableImage, applying a transform from image
572       space into user space before drawing. */
573   public void drawRenderableImage(RenderableImage img, AffineTransform xform)
574   {
575     // FIXME
576   }
577
578   /** Renders a RenderedImage, applying a transform from
579       image space into user space before drawing. */
580   public void drawRenderedImage(RenderedImage img, AffineTransform xform)
581   {
582     // FIXME
583   }
584
585   //-------------------------------------------------------------------------
586   public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints)
587   {
588     setStroke(ordinaryStroke);
589     draw(new Polygon(xPoints, yPoints, nPoints));
590     setStroke(currentStroke);
591   }
592
593   public void drawString(String str, int x, int y)
594   {
595     drawString(str, (float) x, (float) y);
596   }
597
598   public void drawString(String str, float x, float y)
599   {
600     if( str.trim().equals("") )
601       return; // don't draw whitespace, silly!
602
603     if( currentFontIsPS )
604       {
605         drawStringPSFont(str, x, y);
606         return;
607       }
608
609     TextLayout text = new TextLayout(str, currentFont, getFontRenderContext());
610     Shape s = text.getOutline(AffineTransform.getTranslateInstance(x, y));
611     drawStringShape(s);
612   }
613
614   private void drawStringPSFont(String str, float x, float y)
615   {
616     out.println("% drawString PS font");
617     out.println(x + " " + y + " moveto");
618     saveAndInvertAxis();
619     out.println("(" + str + ") show");
620     restoreAxis();
621   }
622
623   private void saveAndInvertAxis()
624   {
625     // Invert the Y axis of the CTM.
626     popCTM();
627     pushCTM();
628
629     double[] test = 
630       {
631         pageTransform.getScaleX(), pageTransform.getShearY(),
632         pageTransform.getShearX(), pageTransform.getScaleY(),
633         pageTransform.getTranslateX(),
634         -pageTransform.getTranslateY() + pageY
635       };
636
637     double[] test2 = 
638       {
639         currentTransform.getScaleX(),
640         currentTransform.getShearY(),
641         -currentTransform.getShearX(),
642         -currentTransform.getScaleY(),
643         currentTransform.getTranslateX(),
644         currentTransform.getTranslateY()
645       };
646
647     AffineTransform total = new AffineTransform(test);
648     total.concatenate(new AffineTransform(test2));
649     concatCTM(total);
650   }
651
652   private void restoreAxis()
653   {
654     // reset the CTM
655     popCTM();
656     pushCTM();
657     AffineTransform total = new AffineTransform(pageTransform);
658     total.concatenate(currentTransform);
659     concatCTM(total);
660   }
661
662   /**
663    * special drawing routine for string shapes,
664    * which need to be drawn with the Y axis uninverted.
665    */
666   private void drawStringShape(Shape s)
667   {
668     saveAndInvertAxis();
669
670     // draw the shape s with an inverted Y axis.
671     PathIterator pi = s.getPathIterator(new AffineTransform());
672     float[] coords = new float[6];
673
674     while (! pi.isDone())
675       {
676         switch (pi.currentSegment(coords))
677           {
678           case PathIterator.SEG_MOVETO:
679             out.println((coords[0]) + " " + (Y - coords[1]) + " moveto");
680             cx = coords[0];
681             cy = coords[1];
682             break;
683           case PathIterator.SEG_LINETO:
684             out.println((coords[0]) + " " + (Y - coords[1]) + " lineto");
685             cx = coords[0];
686             cy = coords[1];
687             break;
688           case PathIterator.SEG_QUADTO:
689             // convert to cubic bezier points
690             float x1 = (cx + 2 * coords[0]) / 3;
691             float y1 = (cy + 2 * coords[1]) / 3;
692             float x2 = (2 * coords[2] + coords[0]) / 3;
693             float y2 = (2 * coords[3] + coords[1]) / 3;
694
695             out.print((x1) + " " + (Y - y1) + " ");
696             out.print((x2) + " " + (Y - y2) + " ");
697             out.println((coords[2]) + " " + (Y - coords[3]) + " curveto");
698             cx = coords[2];
699             cy = coords[3];
700             break;
701           case PathIterator.SEG_CUBICTO:
702             out.print((coords[0]) + " " + (Y - coords[1]) + " ");
703             out.print((coords[2]) + " " + (Y - coords[3]) + " ");
704             out.println((coords[4]) + " " + (Y - coords[5]) + " curveto");
705             cx = coords[4];
706             cy = coords[5];
707             break;
708           case PathIterator.SEG_CLOSE:
709             out.println("closepath");
710             break;
711           }
712         pi.next();
713       }
714     out.println("fill");
715
716     restoreAxis();
717   }
718
719   public void setColor(Color c)
720   {
721     /* don't set the color if it's already set */
722     if (c.equals(currentColor))
723       return;
724     gradientOn = false;
725     currentColor = c;
726     currentPaint = c; // Graphics2D extends colors to paint
727
728     out.println(colorTriple(c) + " setrgbcolor");
729   }
730
731   public void clearRect(int x, int y, int width, int height)
732   {
733     out.println("% clearRect");
734     Color c = currentColor;
735     setColor(backgroundColor);
736     fill(new Rectangle2D.Double(x, y, width, height));
737     setColor(c);
738   }
739
740   public void clipRect(int x, int y, int width, int height)
741   {
742     clip(new Rectangle2D.Double(x, y, width, height));
743   }
744
745   public void copyArea(int x, int y, int width, int height, int dx, int dy)
746   {
747     // FIXME
748   }
749
750   public void fillRect(int x, int y, int width, int height)
751   {
752     fill(new Rectangle2D.Double(x, y, width, height));
753   }
754
755   public void dispose()
756   {
757   }
758
759   public void setClip(int x, int y, int width, int height)
760   {
761     out.println("% setClip()");
762     setClip(new Rectangle2D.Double(x, y, width, height));
763   }
764
765   public void setClip(Shape s)
766   {
767     clip(s);
768   }
769
770   public Shape getClip()
771   {
772     return clipShape;
773   }
774
775   public Rectangle getClipBounds()
776   {
777     return clipShape.getBounds();
778   }
779
780   public Color getColor()
781   {
782     return currentColor;
783   }
784
785   public Font getFont()
786   {
787     return currentFont;
788   }
789
790   public FontMetrics getFontMetrics()
791   {
792     return getFontMetrics(currentFont);
793   }
794
795   public FontMetrics getFontMetrics(Font f)
796   {
797     // FIXME
798     return null;
799   }
800
801   public void setFont(Font font)
802   {
803     out.println("% setfont()");
804     if (font == null)
805       // use the default font
806       font = new Font("Dialog", Font.PLAIN, 12);
807     currentFont = font;
808     setPSFont(); // set up the PostScript fonts
809   }
810
811   /**
812    * Setup the postscript font if the current font is one
813    */
814   private void setPSFont()
815   {
816     currentFontIsPS = false;
817
818     String s = currentFont.getName();
819     out.println("% setPSFont: Fontname: " + s);
820     if (s.equalsIgnoreCase("Helvetica") || s.equalsIgnoreCase("SansSerif"))
821       out.print("/helveticaISO findfont ");
822     else if (s.equalsIgnoreCase("Times New Roman"))
823       out.print("/timesISO findfont ");
824     else if (s.equalsIgnoreCase("Courier"))
825       out.print("/courierISO findfont ");
826     else
827       return;
828
829     currentFontIsPS = true;
830
831     out.print(currentFont.getSize() + " scalefont ");
832     out.println("setfont");
833   }
834
835   /** XOR mode is not supported */
836   public void setPaintMode()
837   {
838   }
839
840   /** XOR mode is not supported */
841   public void setXORMode(Color c1)
842   {
843   }
844
845   public void close()
846   {
847     out.println("showpage");
848     out.println("%%Trailer");
849     out.println("grestore % restore original stuff");
850     out.println("%%EOF");
851
852     try
853       {
854         out.close();
855       }
856     catch (Exception e)
857       {
858       }
859     out = null;
860   }
861
862   //----------------------------------------------------------------
863   // Graphics2D stuff ----------------------------------------------
864
865   /**  Sets the values of an arbitrary number of
866        preferences for the rendering algorithms. */
867   public void addRenderingHints(Map hints)
868   {
869     /* rendering hint changes are disallowed */
870   }
871
872   /** write a shape to the file */
873   private void writeShape(Shape s)
874   {
875     PathIterator pi = s.getPathIterator(new AffineTransform());
876     float[] coords = new float[6];
877
878     while (! pi.isDone())
879       {
880         switch (pi.currentSegment(coords))
881           {
882           case PathIterator.SEG_MOVETO:
883             out.println(coords[0] + " " + (coords[1]) + " moveto");
884             cx = coords[0];
885             cy = coords[1];
886             break;
887           case PathIterator.SEG_LINETO:
888             out.println(coords[0] + " " + (coords[1]) + " lineto");
889             cx = coords[0];
890             cy = coords[1];
891             break;
892           case PathIterator.SEG_QUADTO:
893             // convert to cubic bezier points
894             float x1 = (cx + 2 * coords[0]) / 3;
895             float y1 = (cy + 2 * coords[1]) / 3;
896             float x2 = (2 * coords[2] + coords[0]) / 3;
897             float y2 = (2 * coords[3] + coords[1]) / 3;
898
899             out.print(x1 + " " + (Y - y1) + " ");
900             out.print(x2 + " " + (Y - y2) + " ");
901             out.println(coords[2] + " " + (Y - coords[3]) + " curveto");
902             cx = coords[2];
903             cy = coords[3];
904             break;
905           case PathIterator.SEG_CUBICTO:
906             out.print(coords[0] + " " + coords[1] + " ");
907             out.print(coords[2] + " " + coords[3] + " ");
908             out.println(coords[4] + " " + coords[5] + " curveto");
909             cx = coords[4];
910             cy = coords[5];
911             break;
912           case PathIterator.SEG_CLOSE:
913             out.println("closepath");
914             break;
915           }
916         pi.next();
917       }
918   }
919
920   /** Intersects the current Clip with the interior of
921       the specified Shape and sets the Clip to the resulting intersection. */
922   public void clip(Shape s)
923   {
924     clipShape = s;
925     out.println("% clip INACTIVE");
926     //  writeShape(s);
927     //  out.println("clip");
928   }
929
930   /** Strokes the outline of a Shape using the
931       settings of the current Graphics2D context.*/
932   public void draw(Shape s)
933   {
934     if(!(currentStroke instanceof BasicStroke))
935       fill(currentStroke.createStrokedShape(s));
936
937     out.println("% draw");
938     writeShape(s);
939     out.println("stroke");
940   }
941
942   /** Renders the text of the specified GlyphVector using the
943       Graphics2D context's rendering attributes. */
944   public void drawGlyphVector(GlyphVector gv, float x, float y)
945   {
946     out.println("% drawGlyphVector");
947     Shape s = gv.getOutline();
948     drawStringShape(AffineTransform.getTranslateInstance(x, y)
949                     .createTransformedShape(s));
950   }
951
952   /** Renders the text of the specified iterator,
953       using the Graphics2D context's current Paint.*/
954   public void drawString(AttributedCharacterIterator iterator, float x, float y)
955   {
956     TextLayout text = new TextLayout(iterator, getFontRenderContext());
957     Shape s = text.getOutline(AffineTransform.getTranslateInstance(x, y));
958     drawStringShape(s);
959   }
960
961   /** Renders the text of the specified iterator,
962       using the Graphics2D context's current Paint. */
963   public void drawString(AttributedCharacterIterator iterator, int x, int y)
964   {
965     drawString(iterator, (float) x, (float) y);
966   }
967
968   /** Fills the interior of a Shape using the settings of the Graphics2D context. */
969   public void fill(Shape s)
970   {
971     out.println("% fill");
972     if (! gradientOn)
973       {
974         writeShape(s);
975         out.println("fill");
976       }
977     else
978       {
979         out.println("gsave");
980         writeShape(s);
981         out.println("clip");
982         writeGradient();
983         out.println("shfill");
984         out.println("grestore");
985       }
986   }
987
988   /** Returns the background color used for clearing a region. */
989   public Color getBackground()
990   {
991     return backgroundColor;
992   }
993
994   /** Returns the current Composite in the Graphics2D context. */
995   public Composite getComposite()
996   {
997     // FIXME
998     return null;
999   }
1000
1001   /** Returns the device configuration associated with this Graphics2D. */
1002   public GraphicsConfiguration getDeviceConfiguration()
1003   {
1004     // FIXME
1005     out.println("% getDeviceConfiguration()");
1006     return null;
1007   }
1008
1009   /** Get the rendering context of the Font within this Graphics2D context. */
1010   public FontRenderContext getFontRenderContext()
1011   {
1012     out.println("% getFontRenderContext()");
1013
1014     double[] scaling = 
1015       {
1016         pageTransform.getScaleX(), 0, 0,
1017         -pageTransform.getScaleY(), 0, 0
1018       };
1019
1020     return (new FontRenderContext(new AffineTransform(scaling), false, true));
1021   }
1022
1023   /** Returns the current Paint of the Graphics2D context. */
1024   public Paint getPaint()
1025   {
1026     return currentPaint;
1027   }
1028
1029   /** Returns the value of a single preference for the rendering algorithms. */
1030   public Object getRenderingHint(RenderingHints.Key hintKey)
1031   {
1032     return renderingHints.get(hintKey);
1033   }
1034
1035   /** Gets the preferences for the rendering algorithms. */
1036   public RenderingHints getRenderingHints()
1037   {
1038     return renderingHints;
1039   }
1040
1041   /** Returns the current Stroke in the Graphics2D context. */
1042   public Stroke getStroke()
1043   {
1044     return currentStroke;
1045   }
1046
1047   /** Returns a copy of the current Transform in the Graphics2D context. */
1048   public AffineTransform getTransform()
1049   {
1050     return currentTransform;
1051   }
1052
1053   /** 
1054    * Checks whether or not the specified Shape intersects 
1055    * the specified Rectangle, which is in device space. 
1056    */
1057   public boolean hit(Rectangle rect, Shape s, boolean onStroke)
1058   {
1059     Rectangle2D.Double r = new Rectangle2D.Double(rect.getX(), rect.getY(),
1060                                                   rect.getWidth(),
1061                                                   rect.getHeight());
1062     return s.intersects(r);
1063   }
1064
1065   /** Sets the background color for the Graphics2D context.*/
1066   public void setBackground(Color color)
1067   {
1068     out.println("% setBackground(" + color + ")");
1069     backgroundColor = color;
1070   }
1071
1072   /** Sets the Composite for the Graphics2D context.
1073       Not supported. */
1074   public void setComposite(Composite comp)
1075   {
1076   }
1077
1078   /** Sets the Paint attribute for the Graphics2D context.*/
1079   public void setPaint(Paint paint)
1080   {
1081     currentPaint = paint;
1082     gradientOn = false;
1083     if (paint instanceof Color)
1084       {
1085         setColor((Color) paint);
1086         return;
1087       }
1088     if (paint instanceof GradientPaint)
1089       {
1090         gradientOn = true;
1091         return;
1092       }
1093   }
1094
1095   /* get a space seperated 0.0 - 1.0 color RGB triple */
1096   private String colorTriple(Color c)
1097   {
1098     return (((double) c.getRed() / 255.0) + " "
1099             + ((double) c.getGreen() / 255.0) + " "
1100             + ((double) c.getBlue() / 255.0));
1101   }
1102
1103   /**
1104    * Get a nonsperated hex RGB triple, eg FFFFFF = white
1105    * used by writeGradient and drawImage 
1106    */
1107   private String colorTripleHex(Color c)
1108   {
1109     String r = "00" + Integer.toHexString(c.getRed());
1110     r = r.substring(r.length() - 2);
1111     String g = "00" + Integer.toHexString(c.getGreen());
1112     g = g.substring(g.length() - 2);
1113     String b = "00" + Integer.toHexString(c.getBlue());
1114     b = b.substring(b.length() - 2);
1115     return r + g + b;
1116   }
1117
1118   /* write the current gradient fill */
1119   private void writeGradient()
1120   {
1121     GradientPaint paint = (GradientPaint) currentPaint;
1122     out.println("% writeGradient()");
1123
1124     int n = 1;
1125     double x;
1126     double y;
1127     double dx;
1128     double dy;
1129     Point2D p1 = currentTransform.transform(paint.getPoint1(), null);
1130     Point2D p2 = currentTransform.transform(paint.getPoint2(), null);
1131     x = p1.getX();
1132     y = p1.getY();
1133     dx = p2.getX() - x;
1134     dy = p2.getY() - y;
1135
1136     // get number of repetitions
1137     while (x + n * dx < pageY && y + n * dy < pageX && x + n * dx > 0
1138            && y + n * dy > 0)
1139       n++;
1140
1141     out.println("<<"); // start
1142     out.println("/ShadingType 2"); // gradient fill 
1143     out.println("/ColorSpace [ /DeviceRGB ]"); // RGB colors
1144     out.print("/Coords [");
1145     out.print(x + " " + y + " " + (x + n * dx) + " " + (y + n * dy) + " ");
1146     out.println("]"); // coordinates defining the axis
1147     out.println("/Function <<");
1148     out.println("/FunctionType 0");
1149     out.println("/Order 1");
1150     out.println("/Domain [ 0 1 ]");
1151     out.println("/Range [ 0 1  0 1  0 1 ]");
1152     out.println("/BitsPerSample 8");
1153     out.println("/Size [ " + (1 + n) + " ]");
1154     out.print("/DataSource < " + colorTripleHex(paint.getColor1()) + " "
1155               + colorTripleHex(paint.getColor2()) + " ");
1156     for (; n > 1; n--)
1157       if (paint.isCyclic())
1158         {
1159           if ((n % 2) == 1)
1160             out.print(colorTripleHex(paint.getColor1()) + " ");
1161           else
1162             out.print(colorTripleHex(paint.getColor2()) + " ");
1163         }
1164       else
1165         out.print(colorTripleHex(paint.getColor2()) + " ");
1166     out.println(">");
1167     out.println(">>");
1168     out.println(">>");
1169   }
1170
1171   /** Sets the value of a single preference for the rendering algorithms. */
1172   public void setRenderingHint(RenderingHints.Key hintKey, Object hintValue)
1173   {
1174     /* we don't allow the changing of rendering hints. */
1175   }
1176
1177   /** Replaces the values of all preferences for the rendering algorithms
1178       with the specified hints. */
1179   public void setRenderingHints(Map hints)
1180   {
1181     /* we don't allow the changing of rendering hints. */
1182   }
1183
1184   /** 
1185    * Sets the Stroke for the Graphics2D context. BasicStroke fully implemented.
1186    */
1187   public void setStroke(Stroke s)
1188   {
1189     currentStroke = s;
1190
1191     if (! (s instanceof BasicStroke))
1192       return;
1193
1194     BasicStroke bs = (BasicStroke) s;
1195     out.println("% setStroke()");
1196     try
1197       {
1198         // set the line width
1199         out.println(bs.getLineWidth() + " setlinewidth");
1200
1201         // set the line dash
1202         float[] dashArray = bs.getDashArray();
1203         if (dashArray != null)
1204           {
1205             out.print("[ ");
1206             for (int i = 0; i < dashArray.length; i++)
1207               out.print(dashArray[i] + " ");
1208             out.println("] " + bs.getDashPhase() + " setdash");
1209           }
1210         else
1211           out.println("[] 0 setdash"); // set solid
1212
1213         // set the line cap
1214         switch (bs.getEndCap())
1215           {
1216           case BasicStroke.CAP_BUTT:
1217             out.println("0 setlinecap");
1218             break;
1219           case BasicStroke.CAP_ROUND:
1220             out.println("1 setlinecap");
1221             break;
1222           case BasicStroke.CAP_SQUARE:
1223             out.println("2 setlinecap");
1224             break;
1225           }
1226
1227         // set the line join
1228         switch (bs.getLineJoin())
1229           {
1230           case BasicStroke.JOIN_BEVEL:
1231             out.println("2 setlinejoin");
1232             break;
1233           case BasicStroke.JOIN_MITER:
1234             out.println("0 setlinejoin");
1235             out.println(bs.getMiterLimit() + " setmiterlimit");
1236             break;
1237           case BasicStroke.JOIN_ROUND:
1238             out.println("1 setlinejoin");
1239             break;
1240           }
1241       }
1242     catch (Exception e)
1243       {
1244         out.println("% Exception in setStroke()");
1245       }
1246   }
1247
1248   //////////////////// TRANSFORM SETTING /////////////////////////////////////
1249   private void concatCTM(AffineTransform Tx)
1250   {
1251     double[] matrixElements = new double[6];
1252     Tx.getMatrix(matrixElements);
1253
1254     out.print("[ ");
1255     for (int i = 0; i < 6; i++)
1256       out.print(matrixElements[i] + " ");
1257     out.println("] concat");
1258   }
1259
1260   /** Sets the Transform in the Graphics2D context. */
1261   public void setTransform(AffineTransform Tx)
1262   {
1263     // set the transformation matrix;
1264     currentTransform = Tx;
1265
1266     // concatenate the current transform and the page transform
1267     AffineTransform totalTransform = new AffineTransform(pageTransform);
1268     totalTransform.concatenate(currentTransform);
1269     out.println("% setTransform()");
1270     out.println("% pageTransform:" + pageTransform);
1271     out.println("% currentTransform:" + currentTransform);
1272     out.println("% totalTransform:" + totalTransform);
1273
1274     popCTM();
1275     pushCTM(); // set the CTM to it's original state
1276     concatCTM(totalTransform); // apply our transforms
1277   }
1278
1279   /** Composes an AffineTransform object with the Transform
1280       in this Graphics2D according to the rule last-specified-first-applied. */
1281   public void transform(AffineTransform Tx)
1282   {
1283     // concatenate the current transform
1284     currentTransform.concatenate(Tx);
1285     // and the PS CTM
1286     concatCTM(Tx);
1287   }
1288
1289   ////////////////////////// TRANSFORMS //////////////////////////////////////
1290
1291   /** shear transform */
1292   public void shear(double shx, double shy)
1293   {
1294     out.println("% shear()");
1295     AffineTransform Tx = new AffineTransform();
1296     Tx.shear(shx, shy);
1297     transform(Tx);
1298   }
1299
1300   /** Translates the origin of the Graphics2D context
1301       to the point (x, y) in the current coordinate system. */
1302   public void translate(int x, int y)
1303   {
1304     out.println("% translate()");
1305     AffineTransform Tx = new AffineTransform();
1306     Tx.translate(x, y);
1307     transform(Tx);
1308   }
1309
1310   /** Translates the origin of the Graphics2D context
1311       to the point (x, y) in the current coordinate system. */
1312   public void translate(double x, double y)
1313   {
1314     out.println("% translate(" + x + ", " + y + ")");
1315     AffineTransform Tx = new AffineTransform();
1316     Tx.translate(x, y);
1317     transform(Tx);
1318   }
1319
1320   /** Concatenates the current Graphics2D Transform with a rotation transform.*/
1321   public void rotate(double theta)
1322   {
1323     out.println("% rotate(" + theta + ")");
1324     AffineTransform Tx = new AffineTransform();
1325     Tx.rotate(theta);
1326     transform(Tx);
1327   }
1328
1329   /** Concatenates the current Graphics2D Transform with
1330       a translated rotation transform.*/
1331   public void rotate(double theta, double x, double y)
1332   {
1333     out.println("% rotate()");
1334     AffineTransform Tx = new AffineTransform();
1335     Tx.rotate(theta, x, y);
1336     transform(Tx);
1337   }
1338
1339   /** Concatenates the current Graphics2D Transform with a scaling
1340       transformation Subsequent rendering is resized according to the
1341       specified scaling factors relative to the previous scaling.*/
1342   public void scale(double sx, double sy)
1343   {
1344     out.println("% scale(" + sx + ", " + sy + ")");
1345     AffineTransform Tx = new AffineTransform();
1346     Tx.scale(sx, sy);
1347     transform(Tx);
1348   }
1349 }