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