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