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