JAL-3048 JalviewJS compliant use of LineartOptions dialog
[jalview.git] / src / jalview / util / ImageMaker.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.util;
22
23 import jalview.bin.Cache;
24 import jalview.bin.Jalview;
25 import jalview.gui.IProgressIndicator;
26 import jalview.gui.LineartOptions;
27 import jalview.io.JalviewFileChooser;
28 import jalview.util.dialogrunner.RunResponse;
29
30 import java.awt.Component;
31 import java.awt.Graphics;
32 import java.awt.Graphics2D;
33 import java.awt.RenderingHints;
34 import java.awt.image.BufferedImage;
35 import java.io.File;
36 import java.io.FileOutputStream;
37 import java.util.concurrent.atomic.AtomicBoolean;
38
39 import javax.imageio.ImageIO;
40 import javax.swing.JOptionPane;
41
42 import org.jfree.graphics2d.svg.SVGGraphics2D;
43 import org.jfree.graphics2d.svg.SVGHints;
44 import org.jibble.epsgraphics.EpsGraphics2D;
45
46 public class ImageMaker
47 {
48   public static final String SVG_DESCRIPTION = "Scalable Vector Graphics";
49
50   public static final String SVG_EXTENSION = "svg";
51
52   public static final String EPS_DESCRIPTION = "Encapsulated Postscript";
53
54   public static final String EPS_EXTENSION = "eps";
55
56   public static final String PNG_EXTENSION = "png";
57
58   public static final String PNG_DESCRIPTION = "Portable  network graphics";
59
60   public static final String HTML_EXTENSION = "html";
61
62   public static final String HTML_DESCRIPTION = "Hypertext Markup Language";
63
64   EpsGraphics2D pg;
65
66   Graphics graphics;
67
68   FileOutputStream out;
69
70   BufferedImage bi;
71
72   TYPE type;
73
74   private IProgressIndicator pIndicator;
75
76   private long pSessionId;
77
78   private boolean headless;
79
80   public enum TYPE
81   {
82     EPS("EPS", MessageManager.getString("label.eps_file"), EPS_EXTENSION,
83             EPS_DESCRIPTION),
84     PNG("PNG", MessageManager.getString("label.png_image"), PNG_EXTENSION,
85             PNG_DESCRIPTION),
86     SVG("SVG", "SVG", SVG_EXTENSION, SVG_DESCRIPTION);
87
88     public final String name;
89
90     public final String label;
91
92     public final String extension;
93
94     public final String description;
95
96     TYPE(String name, String label, String ext, String desc)
97     {
98       this.name = name;
99       this.label = label;
100       this.extension = ext;
101       this.description = desc;
102     }
103
104     public String getName()
105     {
106       return name;
107     }
108
109     public JalviewFileChooser getFileChooser()
110     {
111       return new JalviewFileChooser(extension, description);
112     }
113
114     public String getLabel()
115     {
116       return label;
117     }
118
119   }
120
121   /**
122    * Constructor builds the image and writes it to file. If the supplied file
123    * name is null, the user is prompted for the output file.
124    * 
125    * @param parent
126    * @param type
127    * @param title
128    * @param width
129    * @param height
130    * @param file
131    * @param fileTitle
132    * @param pIndicator
133    * @param pSessionId
134    * @param headless
135    */
136   public ImageMaker(Component parent, TYPE type, String title, int width,
137           int height, File file, String fileTitle,
138           IProgressIndicator pIndicator, long pSessionId, boolean headless)
139   {
140     this.pIndicator = pIndicator;
141     this.type = type;
142     this.pSessionId = pSessionId;
143     this.headless = headless;
144     if (file == null && !Jalview.isHeadlessMode())
145     {
146       setProgressMessage(MessageManager.formatMessage(
147               "status.waiting_for_user_to_select_output_file", type.name));
148       JalviewFileChooser chooser = type.getFileChooser();
149       chooser.setFileView(new jalview.io.JalviewFileView());
150       chooser.setDialogTitle(title);
151       chooser.setToolTipText(MessageManager.getString("action.save"));
152       int value = chooser.showSaveDialog(parent);
153
154       if (value == JalviewFileChooser.APPROVE_OPTION)
155       {
156         Cache.setProperty("LAST_DIRECTORY",
157                 chooser.getSelectedFile().getParent());
158         file = chooser.getSelectedFile();
159       }
160       else
161       {
162         setProgressMessage(MessageManager.formatMessage(
163                 "status.cancelled_image_export_operation", type.name));
164       }
165     }
166
167     if (file != null)
168     {
169       try
170       {
171         out = new FileOutputStream(file);
172         setProgressMessage(null);
173         setProgressMessage(MessageManager.formatMessage(
174                 "status.exporting_alignment_as_x_file", type.getName()));
175         if (type == TYPE.SVG)
176         {
177           setupSVG(width, height, fileTitle);
178         }
179         else if (type == TYPE.EPS)
180         {
181           setupEPS(width, height, fileTitle);
182         }
183         else if (type == TYPE.PNG)
184         {
185           setupPNG(width, height);
186         }
187
188       } catch (Exception ex)
189       {
190         System.out.println("Error creating " + type.getName() + " file.");
191
192         setProgressMessage(MessageManager
193                 .formatMessage("info.error_creating_file", type.getName()));
194       }
195     }
196   }
197
198   public Graphics getGraphics()
199   {
200     return graphics;
201   }
202
203   /**
204    * For SVG or PNG, writes the generated graphics data to the file output
205    * stream. For EPS, flushes the output graphics (which is written to file as
206    * it is generated).
207    */
208   public void writeImage()
209   {
210     try
211     {
212       switch (type)
213       {
214       case EPS:
215         pg.flush();
216         pg.close();
217         break;
218       case SVG:
219         String svgData = ((SVGGraphics2D) getGraphics()).getSVGDocument();
220         out.write(svgData.getBytes());
221         out.flush();
222         out.close();
223         break;
224       case PNG:
225         ImageIO.write(bi, PNG_EXTENSION, out);
226         out.flush();
227         out.close();
228         break;
229       }
230     } catch (Exception ex)
231     {
232       ex.printStackTrace();
233     }
234   }
235
236   /**
237    * Generates an EPS image and writes it to the (previously set) output buffer.
238    * The user is first prompted for choice of Text or Lineart rendering, unless
239    * a preference for this has been set.
240    * 
241    * @param width
242    * @param height
243    * @param title
244    */
245   void setupEPS(int width, int height, String title)
246   {
247     String renderStyle = Cache.getDefault("EPS_RENDERING",
248             "Prompt each time");
249     AtomicBoolean textOption = new AtomicBoolean(
250             !"Lineart".equals(renderStyle));
251
252     /*
253      * configure the action to run on OK in the dialog
254      */
255     RunResponse okAction = new RunResponse(JOptionPane.OK_OPTION)
256     {
257       @Override
258       public void run()
259       {
260         writeEPS(width, height, title, textOption.get());
261       }
262     };
263
264     /*
265      * Prompt for character rendering style if preference is not set
266      */
267     if (renderStyle.equalsIgnoreCase("Prompt each time")
268             && !(System.getProperty("java.awt.headless") != null && System
269                     .getProperty("java.awt.headless").equals("true")))
270     {
271       LineartOptions epsOption = new LineartOptions("EPS_RENDERING",
272               TYPE.EPS.getName(), textOption);
273       epsOption.setResponseAction(new RunResponse(JOptionPane.NO_OPTION)
274       {
275         @Override
276         public void run()
277         {
278           setProgressMessage(MessageManager.formatMessage(
279                   "status.cancelled_image_export_operation", "EPS"));
280         }
281       });
282       epsOption.setResponseAction(okAction);
283       epsOption.showDialog();
284       /* no code here - JalviewJS cannot execute it */
285     }
286     else
287     {
288       /*
289        * else (if preference set) just do the export action
290        */
291       writeEPS(width, height, title, textOption.get());
292     }
293   }
294
295   /**
296    * Sets up a graphics object for the PNG image to be written on
297    * 
298    * @param width
299    * @param height
300    */
301   void setupPNG(int width, int height)
302   {
303     bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
304     graphics = bi.getGraphics();
305     Graphics2D ig2 = (Graphics2D) graphics;
306     ig2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
307             RenderingHints.VALUE_ANTIALIAS_ON);
308     setProgressMessage(MessageManager
309             .formatMessage("status.export_complete", type.getName()));
310   }
311
312   /**
313    * Sets up a graphics object for the SVG image to be written on. The user is
314    * first prompted for choice of Text or Lineart rendering, unless a preference
315    * for this has been set.
316    * 
317    * @param width
318    * @param height
319    * @param title
320    */
321   void setupSVG(int width, int height, String title)
322   {
323     String renderStyle = Cache.getDefault("SVG_RENDERING",
324             "Prompt each time");
325     AtomicBoolean textOption = new AtomicBoolean(
326             !"Lineart".equals(renderStyle));
327
328     /*
329      * configure the action to run on OK in the dialog
330      */
331     RunResponse okAction = new RunResponse(JOptionPane.OK_OPTION)
332     {
333       @Override
334       public void run()
335       {
336         setupSVG(width, height, textOption.get());
337       }
338     };
339
340     /*
341      * Prompt for character rendering style if preference is not set
342      */
343     if (renderStyle.equalsIgnoreCase("Prompt each time")
344             && !(System.getProperty("java.awt.headless") != null && System
345                     .getProperty("java.awt.headless").equals("true")))
346     {
347       LineartOptions svgOption = new LineartOptions("SVG_RENDERING",
348               TYPE.SVG.getName(), textOption);
349       svgOption.setResponseAction(new RunResponse(JOptionPane.NO_OPTION)
350       {
351         @Override
352         public void run()
353         {
354           setProgressMessage(MessageManager.formatMessage(
355                   "status.cancelled_image_export_operation", "SVG"));
356         }
357       });
358       svgOption.setResponseAction(okAction);
359       svgOption.showDialog();
360       /* no code here - JalviewJS cannot execute it */
361     }
362     else
363     {
364       /*
365        * else (if preference set) just do the export action
366        */
367       setupSVG(width, height, textOption.get());
368     }
369   }
370
371   void setProgressMessage(String message)
372   {
373     if (pIndicator != null && !headless)
374     {
375       pIndicator.setProgressBar(message, pSessionId);
376     }
377   }
378
379   /**
380    * A helper method to configure the SVG output graphics, with choice of Text
381    * or Lineart character rendering
382    * 
383    * @param width
384    * @param height
385    * @param textOption
386    *          true for Text, false for Lineart
387    */
388   protected void setupSVG(int width, int height, boolean textOption)
389   {
390     SVGGraphics2D g2 = new SVGGraphics2D(width, height);
391     if (!textOption) // Lineart selected
392     {
393       g2.setRenderingHint(SVGHints.KEY_DRAW_STRING_TYPE,
394               SVGHints.VALUE_DRAW_STRING_TYPE_VECTOR);
395     }
396     setProgressMessage(MessageManager
397             .formatMessage("status.export_complete", type.getName()));
398     graphics = g2;
399   }
400
401   /**
402    * A helper method that sets up the EPS graphics output with user choice of
403    * Text or Lineart character rendering
404    * 
405    * @param width
406    * @param height
407    * @param title
408    * @param textOption
409    *          true for Text, false for Lineart
410    */
411   protected void writeEPS(int width, int height, String title,
412           boolean textOption)
413   {
414     try
415     {
416       pg = new EpsGraphics2D(title, out, 0, 0, width, height);
417       Graphics2D ig2 = pg;
418       ig2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
419               RenderingHints.VALUE_ANTIALIAS_ON);
420       pg.setAccurateTextMode(!textOption); // true = Lineart
421       graphics = pg;
422       setProgressMessage(MessageManager
423               .formatMessage("status.export_complete", type.getName()));
424     } catch (Exception ex)
425     {
426       System.err.println("Error writing PNG: " + ex.toString());
427     }
428   }
429
430   static JalviewFileChooser getSVGChooser()
431   {
432     if (Jalview.isHeadlessMode())
433     {
434       return null;
435     }
436     return new JalviewFileChooser(SVG_EXTENSION, SVG_DESCRIPTION);
437   }
438 }