Bug 12212 fixed: Export a polyline in 2D broke it into several segments
[scilab.git] / scilab / modules / graphic_export / src / java / org / scilab / modules / graphic_export / Export.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2012 - Scilab Enterprises - Calixte Denizet
4  *
5  * This file must be used under the terms of the CeCILL.
6  * This source file is licensed as described in the file COPYING, which
7  * you should have received as part of this distribution.  The terms
8  * are also available at
9  * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt
10  *
11  */
12
13 package org.scilab.modules.graphic_export;
14
15 import java.awt.Dimension;
16 import java.awt.Font;
17 import java.awt.Graphics2D;
18 import java.awt.Shape;
19 import java.awt.geom.AffineTransform;
20 import java.awt.geom.Ellipse2D;
21 import java.awt.geom.Path2D;
22 import java.awt.geom.PathIterator;
23 import java.awt.image.BufferedImage;
24 import java.io.BufferedOutputStream;
25 import java.io.ByteArrayOutputStream;
26 import java.io.File;
27 import java.io.FileOutputStream;
28 import java.io.IOException;
29 import java.io.OutputStream;
30 import java.io.OutputStreamWriter;
31 import java.io.Writer;
32 import java.nio.charset.Charset;
33 import java.nio.charset.CharsetEncoder;
34 import java.text.AttributedCharacterIterator;
35 import java.util.HashMap;
36 import java.util.Map;
37 import java.util.WeakHashMap;
38
39 import org.apache.batik.dom.GenericDOMImplementation;
40 import org.apache.batik.svggen.SVGGeneratorContext;
41 import org.apache.batik.svggen.SVGGraphics2D;
42 import org.apache.fop.Version;
43 import org.apache.fop.svg.PDFDocumentGraphics2D;
44 import org.apache.xmlgraphics.java2d.GraphicContext;
45 import org.apache.xmlgraphics.java2d.ps.AbstractPSDocumentGraphics2D;
46 import org.apache.xmlgraphics.java2d.ps.EPSDocumentGraphics2D;
47 import org.apache.xmlgraphics.java2d.ps.PSDocumentGraphics2D;
48 import org.apache.xmlgraphics.ps.DSCConstants;
49 import org.apache.xmlgraphics.ps.PSGenerator;
50 import org.scilab.forge.scirenderer.Canvas;
51 import org.scilab.forge.scirenderer.implementation.g2d.G2DCanvas;
52 import org.scilab.forge.scirenderer.implementation.g2d.G2DCanvasFactory;
53 import org.scilab.forge.scirenderer.implementation.jogl.JoGLCanvas;
54 import org.scilab.forge.scirenderer.implementation.jogl.JoGLCanvasFactory;
55 import org.scilab.modules.commons.ScilabCommonsUtils;
56 import org.scilab.modules.graphic_export.convertToPPM.PPMEncoder;
57 import org.scilab.modules.graphic_objects.figure.Figure;
58 import org.scilab.modules.graphic_objects.graphicController.GraphicController;
59 import org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties;
60 import org.scilab.modules.renderer.JoGLView.DrawerVisitor;
61 import org.w3c.dom.DOMImplementation;
62 import org.w3c.dom.Document;
63
64 /**
65  * Main class to export
66  * Dependancies are put in inner classes to avoid the deps loading.
67  *
68  * @author Calixte DENIZET
69  */
70 public class Export {
71
72     public static final int SUCCESS = 0;
73     public static final int IOEXCEPTION_ERROR = 1;
74     public static final int INVALID_FILE = 2;
75     public static final int MEMORY_ERROR = 3;
76     public static final int UNKNOWN_ERROR = 4;
77
78     private static final float DEFAULT_JPEG_COMPRESSION = 0.95f;
79
80     private static final String CLASSPATH_PDF_PS_EPS_EXPORT_NAME = "pdf_ps_eps_graphic_export";
81     private static final String CLASSPATH_SVG_EXPORT_NAME = "svg_graphic_export";
82
83     private static final Map<DrawerVisitor, Exporter> visitorsToExp = new WeakHashMap<DrawerVisitor, Exporter>();
84
85     private static final Map<String, Integer> extToType = new HashMap<String, Integer>();
86     static {
87         extToType.put("bmp", 1);
88         extToType.put("gif", 2);
89         extToType.put("jpeg", 3);
90         extToType.put("jpg", 3);
91         extToType.put("png", 4);
92         extToType.put("ppm", 5);
93         extToType.put("eps", 6);
94         extToType.put("pdf", 7);
95         extToType.put("svg", 8);
96         extToType.put("ps", 9);
97         extToType.put("pos", 9);
98     }
99
100     private static boolean svgLoaded;
101     private static boolean pdfLoaded;
102
103     public enum TYPE { PNG, JPEG, GIF, BMP, PPM, SVG, PS, EPS, PDF }
104     private static final TYPE[] types = new TYPE[] {TYPE.PNG, TYPE.BMP, TYPE.GIF, TYPE.JPEG, TYPE.PNG, TYPE.PPM, TYPE.EPS, TYPE.PDF, TYPE.SVG, TYPE.PS};
105
106     /**
107      * @param type the image type
108      * @return true if bitmap image format
109      */
110     public static boolean isBitmapFormat(TYPE type) {
111         return type == TYPE.PNG || type == TYPE.JPEG || type == TYPE.GIF || type == TYPE.BMP || type == TYPE.PPM;
112     }
113
114     public static int getType(String ext) {
115         Integer type = extToType.get(ext.toLowerCase());
116         if (type == null) {
117             return -1;
118         }
119
120         return type;
121     }
122
123     /**
124      * Export in drawing in a Graphics2D
125      * @param uid the figure uid
126      * @param type the export type
127      * @param fileName the file name
128      * @param params the export paramaters
129      * @return the export status
130      */
131     public static int export(String uid, int type, String fileName, ExportParams params, boolean headless) {
132         // Check that the fileName contains an extension
133         int dotPosition = fileName.lastIndexOf('.'); // position of the dot
134         boolean extensionFound = false;
135         if (dotPosition > 0 && dotPosition <= fileName.length() - 2) {
136             extensionFound = true;
137         }
138
139         String extendedFilename = fileName;
140         if (!extensionFound) { // Add default extension if no one found
141             String[] extensions = {"png", "bmp", "gif", "jpeg", "png", "ppm", "eps", "pdf", "svg", "ps"};
142             extendedFilename = fileName + "." + extensions[type];
143         }
144
145         DrawerVisitor visitor = DrawerVisitor.getVisitor(uid);
146         if (visitor != null) {
147             Canvas canvas = visitor.getCanvas();
148             if (canvas instanceof JoGLCanvas && isBitmapFormat(types[type])) {
149                 try {
150                     return exportBitmap(uid, type, extendedFilename, true, params);
151                 } catch (OutOfMemoryError e) {
152                     return MEMORY_ERROR;
153                 } catch (Throwable e) {
154                     return UNKNOWN_ERROR;
155                 }
156             }
157         }
158
159         return exportVectorial(uid, type, extendedFilename, params, headless);
160     }
161
162
163     /**
164      * Export in drawing in a Graphics2D
165      * @param uid the figure uid
166      * @param type the export type
167      * @param fileName the file name
168      * @param params the export paramaters
169      * @return the export status
170      */
171     public static int exportVectorial(String uid, int type, String fileName, ExportParams params, boolean headless) {
172         if (fileName == null) {
173             return INVALID_FILE;
174         }
175
176         File f = new File(fileName);
177         int ret = Utils.checkWritePermission(f);
178         if (ret != SUCCESS) {
179             return ret;
180         }
181
182         try {
183             return exportVectorial(uid, types[type], f, params, headless);
184         } catch (IOException e) {
185             return IOEXCEPTION_ERROR;
186         }
187     }
188
189     /**
190      * Export in drawing in a Graphics2D
191      * @param uid the figure uid
192      * @param type the export type
193      * @param file the file where to export
194      * @param params the export paramaters
195      */
196     public static int exportVectorial(String uid, TYPE type, File file, ExportParams params, boolean headless) throws IOException {
197         Figure figure = (Figure) GraphicController.getController().getObjectFromId(uid);
198
199         if (!headless) {
200             Exporter exporter = getExporter(type);
201             Integer[] dims = figure.getAxesSize();
202             int width = dims[0];
203             int height = dims[1];
204
205             Graphics2D g2d = exporter.getGraphics2D(width, height, file, params);
206             params.setParamsOnGraphics(g2d);
207
208             Canvas canvas = G2DCanvasFactory.createCanvas(g2d, width, height);
209             DrawerVisitor oldVisitor = DrawerVisitor.getVisitor(uid);
210             DrawerVisitor visitor = new DrawerVisitor(null, canvas, figure) {
211                 @Override
212                 public void updateObject(String id, int property) {
213                     // Don't update during the export
214                 }
215             };
216
217             try {
218                 canvas.setMainDrawer(visitor);
219                 canvas.redraw();
220                 exporter.write();
221             } catch (OutOfMemoryError e) {
222                 return MEMORY_ERROR;
223             } catch (Throwable e) {
224                 return UNKNOWN_ERROR;
225             } finally {
226                 GraphicController.getController().unregister(visitor);
227                 DrawerVisitor.changeVisitor(figure, oldVisitor);
228                 exporter.dispose();
229                 exporter = null;
230                 visitorsToExp.remove(visitor);
231             }
232         } else {
233             DrawerVisitor visitor = DrawerVisitor.getVisitor(uid);
234             G2DCanvas canvas = (G2DCanvas) visitor.getCanvas();
235             canvas.enableDraw();
236             Exporter exporter = null;
237             try {
238                 canvas.redraw();
239                 exporter = visitorsToExp.get(visitor);
240                 if (exporter != null) {
241                     exporter.file = file;
242                     exporter.write();
243                 }
244             } catch (OutOfMemoryError e) {
245                 return MEMORY_ERROR;
246             } catch (Throwable e) {
247                 return UNKNOWN_ERROR;
248             } finally {
249                 if (exporter != null) {
250                     exporter.dispose();
251                     exporter = null;
252                     visitorsToExp.remove(visitor);
253                 }
254                 DrawerVisitor.changeVisitor(figure, null);
255                 GraphicController.getController().unregister(visitor);
256             }
257         }
258
259         return SUCCESS;
260     }
261
262     /**
263      * Export in getting a buffered image from JoGL
264      * @param uid the figure uid
265      * @param type the export type
266      * @param fileName the file name
267      * @param fromScreen if true, then use the screen view
268      * @param params the export paramaters
269      * @return the export status
270      */
271     public static int exportBitmap(String uid, int type, String fileName, boolean fromScreen, ExportParams params) {
272         if (fileName == null) {
273             return INVALID_FILE;
274         }
275
276         File f = new File(fileName);
277         int ret = Utils.checkWritePermission(f);
278         if (ret != SUCCESS) {
279             return ret;
280         }
281
282         try {
283             exportBitmap(uid, types[type], f, fromScreen, params);
284         } catch (IOException e) {
285             return IOEXCEPTION_ERROR;
286         }
287
288         return SUCCESS;
289     }
290
291     /**
292      * Export in getting a buffered image from JoGL
293      * @param uid the figure uid
294      * @param type the export type
295      * @param file the file where to export
296      * @param fromScreen if true, then use the screen view
297      * @param params the export paramaters
298      */
299     public static void exportBitmap(String uid, TYPE type, File file, boolean fromScreen, ExportParams params) throws IOException {
300         if (isBitmapFormat(type)) {
301             JoGLCanvas joglCanvas = null;
302             if (fromScreen) {
303                 DrawerVisitor visitor = DrawerVisitor.getVisitor(uid);
304                 Canvas canvas = visitor.getCanvas();
305                 if (canvas instanceof JoGLCanvas) {
306                     joglCanvas = (JoGLCanvas) canvas;
307                 }
308             } else {
309                 Figure figure = (Figure) GraphicController.getController().getObjectFromId(uid);
310                 Integer[] dims = figure.getAxesSize();
311                 DrawerVisitor oldVisitor = DrawerVisitor.getVisitor(uid);
312                 joglCanvas = (JoGLCanvas) JoGLCanvasFactory.createCanvas(dims[0], dims[1]);
313                 DrawerVisitor visitor = new DrawerVisitor(null, joglCanvas, figure) {
314                     @Override
315                     public void updateObject(String id, int property) {
316                         // Don't update during the export
317                     }
318
319                     @Override
320                     public void deleteObject(String id) {
321                         // Don't delete during the export
322                     }
323                 };
324                 joglCanvas.setMainDrawer(visitor);
325                 joglCanvas.redraw();
326                 GraphicController.getController().unregister(visitor);
327                 DrawerVisitor.changeVisitor(figure, oldVisitor);
328             }
329
330             if (joglCanvas != null) {
331                 BufferedImage image = joglCanvas.getImage();
332                 //joglCanvas.destroy();
333                 PNGExporter exporter = (PNGExporter) getExporter(type);
334                 exporter.setImage(file, image, params);
335                 exporter.write();
336                 exporter.dispose();
337             }
338         }
339     }
340
341     /**
342      * Export in drawing in a Graphics2D
343      * @param uid the figure uid
344      * @param type the export type
345      * @param file the file where to export
346      * @param params the export paramaters
347      */
348     public static void setVisitor(String uid, int type, final ExportParams params) {
349         final Exporter exporter = getExporter(types[type]);
350         Figure figure = (Figure) GraphicController.getController().getObjectFromId(uid);
351         final Integer[] dims = figure.getAxesSize();
352         int width = dims[0];
353         int height = dims[1];
354
355         final Graphics2D g2d = exporter.getGraphics2D(width, height, null, params);
356         params.setParamsOnGraphics(g2d);
357
358         final G2DCanvas canvas = G2DCanvasFactory.createCanvas(g2d, width, height);
359         canvas.disableDraw();
360         DrawerVisitor visitor = new DrawerVisitor(null, canvas, figure) {
361             @Override
362             public void deleteObject(String id) {
363                 // Don't delete during the export
364             }
365
366             @Override
367             public void updateObject(String id, int property) {
368                 if (needUpdate(id, property)) {
369                     axesDrawer.update(id, property);
370                     if (property == GraphicObjectProperties.__GO_AXES_SIZE__) {
371                         Integer[] size = getFigure().getAxesSize();
372                         if (size[0] != dims[0] || size[1] != dims[1]) {
373                             Graphics2D newg2d = exporter.getGraphics2D(size[0], size[1], null, params);
374                             params.setParamsOnGraphics(newg2d);
375                             canvas.setGraphics(newg2d, size[0], size[1]);
376                             dims[0] = size[0];
377                             dims[1] = size[1];
378
379                             g2d.dispose();
380                         }
381                     } else if (property == GraphicObjectProperties.__GO_ANTIALIASING__) {
382                         canvas.setAntiAliasingLevel(getFigure().getAntialiasing());
383                     }
384                 }
385             }
386         };
387         visitor.setDrawingTools(canvas.getDrawingTools());
388         canvas.setMainDrawer(visitor);
389         visitorsToExp.put(visitor, exporter);
390     }
391
392     /**
393      * Get an exporter from a type
394      * @param type the exporter type.
395      * @return the corresponding exporter/
396      */
397     private static Exporter getExporter(TYPE type) {
398         switch (type) {
399             case PNG :
400                 return new PNGExporter();
401             case GIF :
402                 return new GIFExporter();
403             case JPEG :
404                 return new JPEGExporter();
405             case BMP :
406                 return new BMPExporter();
407             case PPM :
408                 return new PPMExporter();
409             case SVG :
410                 if (!svgLoaded) {
411                     ScilabCommonsUtils.loadOnUse(CLASSPATH_SVG_EXPORT_NAME);
412                     svgLoaded = true;
413                 }
414                 return new SVGExporter();
415             case PDF :
416                 loadPDF();
417                 return new PDFExporter();
418             case PS :
419                 loadPDF();
420                 return new PSExporter();
421             case EPS :
422                 loadPDF();
423                 return new EPSExporter();
424             default :
425                 break;
426         }
427
428         return null;
429     }
430
431     /**
432      * Load the PDF/PS/EPS dependencies
433      */
434     private static final void loadPDF() {
435         if (!pdfLoaded) {
436             ScilabCommonsUtils.loadOnUse(CLASSPATH_PDF_PS_EPS_EXPORT_NAME);
437             pdfLoaded = true;
438         }
439     }
440
441     /**
442      * Interface to export
443      */
444     private static abstract class Exporter {
445
446         protected File file;
447
448         /**
449          * @param width graphics width
450          * @param height graphisc height
451          * @param file the file
452          * @param params the export parameters
453          */
454         abstract Graphics2D getGraphics2D(int width, int height, File file, ExportParams params);
455
456         /**
457          * Write the file
458          */
459         abstract void write() throws IOException;
460
461         abstract void dispose();
462     }
463
464     /**
465      * PNG Exporter
466      */
467     private static class PNGExporter extends Exporter {
468
469         protected BufferedImage image;
470         protected Graphics2D g2d;
471         protected ExportParams params;
472
473         public PNGExporter() { }
474
475         public void setImage(File file, BufferedImage image, ExportParams params) {
476             this.file = file;
477             this.image = image;
478             this.params = params;
479         }
480
481         @Override
482         public Graphics2D getGraphics2D(int width, int height, File file, ExportParams params) {
483             this.file = file;
484             this.params = params;
485             image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
486             g2d = image.createGraphics();
487
488             return g2d;
489         }
490
491         @Override
492         public void write() throws IOException {
493             ExportBitmap.writeFile(image, "png", file);
494         }
495
496         @Override
497         public void dispose() {
498             if (g2d != null) {
499                 g2d.dispose();
500             }
501         }
502     }
503
504     /**
505      * GIF Exporter
506      */
507     private static class GIFExporter extends PNGExporter {
508
509         public GIFExporter() { }
510
511         @Override
512         public void write() throws IOException {
513             ExportBitmap.writeFile(image, "gif", file);
514         }
515     }
516
517     /**
518      * BMP Exporter
519      */
520     private static class BMPExporter extends PNGExporter {
521
522         public BMPExporter() { }
523
524         @Override
525         public Graphics2D getGraphics2D(int width, int height, File file, ExportParams params) {
526             this.file = file;
527             this.params = params;
528             image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
529             g2d = image.createGraphics();
530
531             return g2d;
532         }
533
534         @Override
535         public void write() throws IOException {
536             ExportBitmap.writeFile(image, "bmp", file);
537         }
538     }
539
540     /**
541      * JPEG Exporter
542      */
543     private static class JPEGExporter extends BMPExporter {
544
545         public JPEGExporter() { }
546
547         @Override
548         public void write() throws IOException {
549             if (params.compressionQuality == -1) {
550                 ExportBitmap.writeJPEG(image, DEFAULT_JPEG_COMPRESSION, file);
551             } else {
552                 ExportBitmap.writeJPEG(image, params.compressionQuality, file);
553             }
554         }
555     }
556
557     /**
558      * PPM Exporter
559      */
560     private static class PPMExporter extends BMPExporter {
561
562         public PPMExporter() { }
563
564         @Override
565         public void write() throws IOException {
566             OutputStream out = new BufferedOutputStream(new FileOutputStream(file));
567             PPMEncoder encoder = new PPMEncoder(image, out);
568             int[] pixels = image.getRGB(0, 0, image.getWidth(), image.getHeight(), null, 0, image.getWidth());
569             encoder.encodeStart(image.getWidth(), image.getHeight());
570             encoder.encodePixels(0, 0, image.getWidth(), image.getHeight(), pixels, 0, image.getWidth());
571             out.flush();
572             out.close();
573         }
574     }
575
576     /**
577      * SVG Exporter
578      */
579     private static class SVGExporter extends Exporter {
580
581         private SVGGraphics2D g2d;
582         private ExportParams params;
583
584         public SVGExporter() { }
585
586         @Override
587         public Graphics2D getGraphics2D(int width, int height, File file, ExportParams params) {
588             this.file = file;
589             this.params = params;
590             DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation();
591             Document document = domImpl.createDocument("http://www.w3.org/2000/svg", "svg", null);
592             final SVGGeneratorContext ctx = SVGGeneratorContext.createDefault(document);
593             ctx.setComment("Generated by Scilab with Batik SVG Generator");
594             // TODO: better handle of LaTeX fonts (should remove the 'true' below and include the font in the SVG)
595             // same thing for PDF & co...
596             ctx.setEmbeddedFontsOn(true);
597             g2d = new SVGGraphics2D(ctx, false) {
598
599                 @Override
600                 public void drawString(String s, float x, float y) {
601                     textAsShapes = getFont().getFontName().startsWith("jlm");
602                     super.drawString(s, x, y);
603                 }
604
605                 @Override
606                 public void drawString(AttributedCharacterIterator ati, float x, float y) {
607                     textAsShapes = getFont().getFontName().startsWith("jlm");
608                     super.drawString(ati, x, y);
609                 }
610             };
611             if (params.orientation == ExportParams.LANDSCAPE) {
612                 g2d.setSVGCanvasSize(new Dimension(height, width));
613                 AffineTransform transf = AffineTransform.getRotateInstance(Math.PI / 2);
614                 transf.preConcatenate(AffineTransform.getTranslateInstance(height, 0));
615                 g2d.setTransform(transf);
616             } else {
617                 g2d.setSVGCanvasSize(new Dimension(width, height));
618             }
619             return g2d;
620         }
621
622         @Override
623         public void write() throws IOException {
624             boolean useCSS = true;
625             OutputStream svgs = new BufferedOutputStream(new FileOutputStream(file));
626             Writer out = new OutputStreamWriter(svgs, "UTF-8");
627             g2d.stream(out, useCSS);
628             svgs.flush();
629             svgs.close();
630         }
631
632         @Override
633         public void dispose() {
634             if (g2d != null) {
635                 g2d.dispose();
636             }
637         }
638     }
639
640     /**
641      * PDF Exporter
642      */
643     private static class PDFExporter extends Exporter {
644
645         private OutputStream out;
646         private PDFDocumentGraphics2D g2d;
647         private ExportParams params;
648         private ByteArrayOutputStream buffer;
649
650         public PDFExporter() { }
651
652         @Override
653         public Graphics2D getGraphics2D(int width, int height, File file, ExportParams params) {
654             this.file = file;
655             this.params = params;
656             try {
657                 if (file == null) {
658                     buffer = new ByteArrayOutputStream();
659                     out = new BufferedOutputStream(buffer);
660                 } else {
661                     out = new BufferedOutputStream(new FileOutputStream(file));
662                 }
663                 g2d = new PDFDocumentGraphics2D(true);
664                 g2d.setupDefaultFontInfo();
665                 g2d.getPDFDocument().getInfo().setProducer("Generated by Scilab with Apache FOP Version " + Version.getVersion());
666                 g2d.setGraphicContext(new GraphicContext());
667                 if (params.orientation == ExportParams.LANDSCAPE) {
668                     g2d.setupDocument(out, height, width);
669                     g2d.setSVGDimension(height, width);
670                     double s = PDFDocumentGraphics2D.NORMAL_PDF_RESOLUTION / g2d.getDeviceDPI();
671                     AffineTransform transf = AffineTransform.getRotateInstance(Math.PI / 2);
672                     transf.preConcatenate(AffineTransform.getTranslateInstance(height / s, 0));
673                     g2d.setTransform(transf);
674                 } else {
675                     g2d.setupDocument(out, width, height);
676                     g2d.setSVGDimension(width, height);
677                 }
678             } catch (IOException e) { }
679
680             return g2d;
681         }
682
683         @Override
684         public void write() throws IOException {
685             if (g2d != null) {
686                 g2d.finish();
687             }
688             if (buffer != null && file != null) {
689                 FileOutputStream fos = new FileOutputStream(file);
690                 buffer.writeTo(fos);
691                 buffer.close();
692                 fos.flush();
693                 fos.close();
694             }
695             if (out != null) {
696                 out.close();
697             }
698         }
699
700         @Override
701         public void dispose() {
702             g2d.dispose();
703         }
704     }
705
706     /**
707      * PS Exporter
708      */
709     private static class PSExporter extends Exporter {
710
711         protected OutputStream out;
712         protected AbstractPSDocumentGraphics2D g2d;
713         protected ExportParams params;
714         protected ByteArrayOutputStream buffer;
715
716         public PSExporter() { }
717
718         @Override
719         public Graphics2D getGraphics2D(int width, int height, File file, final ExportParams params) {
720             this.file = file;
721             this.params = params;
722             try {
723                 if (file == null) {
724                     buffer = new ByteArrayOutputStream();
725                     out = new BufferedOutputStream(buffer);
726                 } else {
727                     out = new BufferedOutputStream(new FileOutputStream(file));
728                 }
729                 g2d = new PSDocumentGraphics2D(true, out, width, height) {
730                     @Override
731                     protected void writePageHeader() throws IOException {
732                         super.writePageHeader();
733                         if (params.orientation == ExportParams.LANDSCAPE) {
734                             gen.writeDSCComment(DSCConstants.PAGE_ORIENTATION, "Landscape");
735                         } else {
736                             gen.writeDSCComment(DSCConstants.PAGE_ORIENTATION, "Portrait");
737                         }
738                         gen.writeln("/ReEncode { /MyEncoding exch def exch findfont dup length dict begin {def} forall /Encoding MyEncoding def currentdict end definefont } def");
739                         gen.writeln("/Helvetica /HelveticaLatin1 ISOLatin1Encoding ReEncode");
740                         gen.writeln("/Times /TimesLatin1 ISOLatin1Encoding ReEncode");
741                         gen.writeln("/DP {/Points exch def N Points 0 get Points 1 get M 2 2 Points length 1 sub {/i exch def Points i get Points i 1 add get L}for} def");
742                     }
743
744                     @Override
745                     public void drawString(String s, float x, float y) {
746                         if (s != null && !s.isEmpty()) {
747                             CharsetEncoder encoder = Charset.forName("ISO-8859-1").newEncoder();
748                             if (encoder.canEncode(s)) {
749                                 Font font = getFont();
750                                 boolean sserif = font.getName().equals("SansSerif");
751                                 boolean serif = font.getName().equals("Serif");
752                                 if (sserif || serif) {
753                                     try {
754                                         preparePainting();
755                                         establishColor(getColor());
756                                         gen.writeln((sserif ? "/HelveticaLatin1" : "/TimesLatin1") + " " + gen.formatDouble(getFont().getSize()) + " F");
757
758                                         gen.saveGraphicsState();
759                                         Shape imclip = getClip();
760                                         writeClip(imclip);
761
762                                         AffineTransform trans = getTransform();
763                                         boolean newTransform = gen.getCurrentState().checkTransform(trans) && !trans.isIdentity();
764
765                                         if (newTransform) {
766                                             gen.concatMatrix(trans);
767                                         }
768
769                                         gen.writeln(gen.formatDouble(x)
770                                                     + " " + gen.formatDouble(y)
771                                                     + " M 1 -1 scale");
772
773                                         StringBuffer buf = new StringBuffer("(");
774                                         for (int i = 0; i < s.length(); i++) {
775                                             PSGenerator.escapeChar(s.charAt(i), buf);
776                                         }
777                                         buf.append(") t");
778
779                                         gen.writeln(buf.toString());
780
781                                         gen.restoreGraphicsState();
782                                     } catch (IOException e) {
783                                         System.err.println(e);
784                                     }
785
786                                     return;
787                                 }
788                             }
789
790                             super.drawString(s, x, y);
791                         }
792                     }
793
794                     @Override
795                     public int processShape(Shape s) throws IOException {
796                         if (s instanceof Ellipse2D.Double) {
797                             Ellipse2D.Double ell = (Ellipse2D.Double) s;
798                             if (ell.height == ell.width) {
799                                 gen.writeln(gen.formatDouble(ell.x + ell.width / 2)
800                                             + " " + gen.formatDouble(ell.y + ell.height / 2)
801                                             + " " + gen.formatDouble(ell.width / 2)
802                                             + " " + gen.formatDouble(0d)
803                                             + " " + gen.formatDouble(360d)
804                                             + " arc cp");
805
806                                 return PathIterator.WIND_NON_ZERO;
807                             }
808                         } else if (s instanceof Path2D) {
809                             StringBuilder buffer = new StringBuilder();
810                             double[] coords = new double[2];
811                             PathIterator it = ((Path2D) s).getPathIterator(new AffineTransform());
812                             if (!it.isDone()) {
813                                 int type = it.currentSegment(coords);
814                                 if (type == PathIterator.SEG_MOVETO) {
815                                     buffer.append("[").append(gen.formatDouble(coords[0])).append(" ").append(gen.formatDouble(coords[1]));
816                                     it.next();
817                                 } else {
818                                     return super.processShape(s);
819                                 }
820                             } else {
821                                 return super.processShape(s);
822                             }
823
824                             for (; !it.isDone(); it.next()) {
825                                 int type = it.currentSegment(coords);
826                                 if (type == PathIterator.SEG_LINETO) {
827                                     buffer.append(" ").append(gen.formatDouble(coords[0])).append(" ").append(gen.formatDouble(coords[1]));
828                                 } else {
829                                     return super.processShape(s);
830                                 }
831                             }
832                             buffer.append("] DP");
833                             gen.writeln(buffer.toString());
834                             return PathIterator.WIND_NON_ZERO;
835                         }
836
837                         return super.processShape(s);
838                     }
839                 };
840                 g2d.setGraphicContext(new GraphicContext());
841             } catch (IOException e) { }
842
843             return g2d;
844         }
845
846         @Override
847         public void write() throws IOException {
848             if (g2d != null) {
849                 g2d.finish();
850             }
851             if (buffer != null && file != null) {
852                 FileOutputStream fos = new FileOutputStream(file);
853                 buffer.writeTo(fos);
854                 buffer.close();
855                 fos.flush();
856                 fos.close();
857             }
858             if (out != null) {
859                 out.close();
860             }
861         }
862
863         @Override
864         public void dispose() {
865             if (g2d != null) {
866                 g2d.dispose();
867             }
868         }
869     }
870
871     /**
872      * EPS Exporter
873      */
874     private static class EPSExporter extends PSExporter {
875
876         public EPSExporter() { }
877
878         @Override
879         public Graphics2D getGraphics2D(int width, int height, File file, final ExportParams params) {
880             this.file = file;
881             this.params = params;
882             try {
883                 if (file == null) {
884                     buffer = new ByteArrayOutputStream();
885                     out = new BufferedOutputStream(buffer);
886                 } else {
887                     out = new BufferedOutputStream(new FileOutputStream(file));
888                 }
889                 g2d = new EPSDocumentGraphics2D(true) {
890                     @Override
891                     protected void writePageHeader() throws IOException {
892                         super.writePageHeader();
893                         if (params.orientation == ExportParams.LANDSCAPE) {
894                             gen.writeDSCComment(DSCConstants.PAGE_ORIENTATION, "Landscape");
895                         } else {
896                             gen.writeDSCComment(DSCConstants.PAGE_ORIENTATION, "Portrait");
897                         }
898                         gen.writeln("/ReEncode { /MyEncoding exch def exch findfont dup length dict begin {def} forall /Encoding MyEncoding def currentdict end definefont } def");
899                         gen.writeln("/Helvetica /HelveticaLatin1 ISOLatin1Encoding ReEncode");
900                         gen.writeln("/Times /TimesLatin1 ISOLatin1Encoding ReEncode");
901                         gen.writeln("/DP {/Points exch def Points 0 get Points 1 get M 2 2 Points length 1 sub {/i exch def Points i get Points i 1 add get L}for} def");
902                     }
903
904                     @Override
905                     public void drawString(String s, float x, float y) {
906                         if (s != null && !s.isEmpty()) {
907                             CharsetEncoder encoder = Charset.forName("ISO-8859-1").newEncoder();
908                             if (encoder.canEncode(s)) {
909                                 Font font = getFont();
910                                 boolean sserif = font.getName().equals("SansSerif");
911                                 boolean serif = font.getName().equals("Serif");
912                                 if (sserif || serif) {
913                                     try {
914                                         preparePainting();
915                                         establishColor(getColor());
916                                         gen.writeln((sserif ? "/HelveticaLatin1" : "/TimesLatin1") + " " + gen.formatDouble(getFont().getSize()) + " F");
917
918                                         gen.saveGraphicsState();
919                                         Shape imclip = getClip();
920                                         writeClip(imclip);
921
922                                         AffineTransform trans = getTransform();
923                                         boolean newTransform = gen.getCurrentState().checkTransform(trans) && !trans.isIdentity();
924
925                                         if (newTransform) {
926                                             gen.concatMatrix(trans);
927                                         }
928
929                                         gen.writeln(gen.formatDouble(x)
930                                                     + " " + gen.formatDouble(y)
931                                                     + " M 1 -1 scale");
932
933                                         StringBuffer buf = new StringBuffer("(");
934                                         for (int i = 0; i < s.length(); i++) {
935                                             PSGenerator.escapeChar(s.charAt(i), buf);
936                                         }
937                                         buf.append(") t");
938
939                                         gen.writeln(buf.toString());
940
941                                         gen.restoreGraphicsState();
942                                     } catch (IOException e) {
943                                         System.err.println(e);
944                                     }
945
946                                     return;
947                                 }
948                             }
949
950                             super.drawString(s, x, y);
951                         }
952                     }
953
954                     @Override
955                     public int processShape(Shape s) throws IOException {
956                         if (s instanceof Ellipse2D.Double) {
957                             Ellipse2D.Double ell = (Ellipse2D.Double) s;
958                             if (ell.height == ell.width) {
959                                 gen.writeln(gen.formatDouble(ell.x + ell.width / 2)
960                                             + " " + gen.formatDouble(ell.y + ell.height / 2)
961                                             + " " + gen.formatDouble(ell.width / 2)
962                                             + " " + gen.formatDouble(0d)
963                                             + " " + gen.formatDouble(360d)
964                                             + " arc cp");
965
966                                 return PathIterator.WIND_NON_ZERO;
967                             }
968                         } else if (s instanceof Path2D) {
969                             StringBuilder buffer = new StringBuilder();
970                             double[] coords = new double[2];
971                             PathIterator it = ((Path2D) s).getPathIterator(new AffineTransform());
972                             if (!it.isDone()) {
973                                 int type = it.currentSegment(coords);
974                                 if (type == PathIterator.SEG_MOVETO) {
975                                     buffer.append("[").append(gen.formatDouble(coords[0])).append(" ").append(gen.formatDouble(coords[1]));
976                                     it.next();
977                                 } else {
978                                     return super.processShape(s);
979                                 }
980                             } else {
981                                 return super.processShape(s);
982                             }
983
984                             for (; !it.isDone(); it.next()) {
985                                 int type = it.currentSegment(coords);
986                                 if (type == PathIterator.SEG_LINETO) {
987                                     buffer.append(" ").append(gen.formatDouble(coords[0])).append(" ").append(gen.formatDouble(coords[1]));
988                                 } else {
989                                     return super.processShape(s);
990                                 }
991                             }
992                             buffer.append("] DP");
993                             gen.writeln(buffer.toString());
994                             return PathIterator.WIND_NON_ZERO;
995                         }
996
997                         return super.processShape(s);
998                     }
999
1000                 };
1001                 g2d.setupDocument(out, width, height);
1002                 g2d.setGraphicContext(new GraphicContext());
1003             } catch (IOException e) { }
1004
1005             return g2d;
1006         }
1007     }
1008 }