e6e698ca62681cdaa7981e7bcb421cc75d103315
[jalview.git] / src / jalview / io / HtmlSvgOutput.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.io;
22
23 import jalview.api.AlignExportSettingI;
24 import jalview.api.FeatureRenderer;
25 import jalview.datamodel.AlignmentExportData;
26 import jalview.gui.AlignViewport;
27 import jalview.gui.AlignmentPanel;
28 import jalview.gui.HTMLOptions;
29 import jalview.gui.IProgressIndicator;
30 import jalview.gui.OOMWarning;
31 import jalview.math.AlignmentDimension;
32 import jalview.util.MessageManager;
33
34 import java.awt.Graphics;
35 import java.awt.print.PrinterException;
36 import java.io.File;
37 import java.io.FileOutputStream;
38 import java.io.IOException;
39
40 import org.jfree.graphics2d.svg.SVGGraphics2D;
41 import org.jfree.graphics2d.svg.SVGHints;
42
43 public class HtmlSvgOutput
44 {
45   AlignViewport av;
46
47   FeatureRenderer fr;
48
49   AlignmentPanel ap;
50
51   private IProgressIndicator pIndicator;
52
53   private long pSessionId;
54
55   private boolean headless;
56
57   public HtmlSvgOutput(AlignmentPanel ap)
58   {
59     this.av = ap.av;
60     this.ap = ap;
61     fr = ap.cloneFeatureRenderer();
62   }
63
64   public void generateHtmlSvgOutput(File file)
65   {
66     pIndicator = ap.alignFrame;
67     pSessionId = System.currentTimeMillis();
68     try
69     {
70       headless = (System.getProperty("java.awt.headless") != null && System
71               .getProperty("java.awt.headless").equals("true"));
72       if (file == null)
73       {
74         setProgressMessage(MessageManager.formatMessage(
75                 "status.waiting_for_user_to_select_output_file", "HTML"));
76         JalviewFileChooser chooser = getHTMLChooser();
77         chooser.setFileView(new jalview.io.JalviewFileView());
78         chooser.setDialogTitle(ap.alignFrame.getTitle());
79         chooser.setToolTipText(MessageManager.getString("action.save"));
80         int value = chooser.showSaveDialog(ap.alignFrame);
81
82         if (value == jalview.io.JalviewFileChooser.APPROVE_OPTION)
83         {
84           jalview.bin.Cache.setProperty("LAST_DIRECTORY", chooser
85                   .getSelectedFile().getParent());
86           file = chooser.getSelectedFile();
87           ap.alignFrame.repaint();
88         }
89         else
90         {
91           setProgressMessage(MessageManager.formatMessage(
92                   "status.cancelled_image_export_operation", "HTML"));
93           return;
94         }
95       }
96     } catch (Exception e)
97     {
98       pIndicator.setProgressBar(MessageManager.formatMessage(
99               "info.error_creating_file", "HTML"), pSessionId);
100       e.printStackTrace();
101       return;
102     }
103     final File fileX = file;
104     new Thread()
105     {
106       @Override
107       public void run()
108       {
109         try
110         {
111           setProgressMessage(null);
112           setProgressMessage(MessageManager.formatMessage(
113                   "status.exporting_alignment_as_x_file", "HTML"));
114           AlignmentDimension aDimension = ap.getAlignmentDimension();
115           SVGGraphics2D idPanelGraphics = new SVGGraphics2D(
116                   aDimension.getWidth(),
117                   aDimension.getHeight());
118           SVGGraphics2D alignPanelGraphics = new SVGGraphics2D(
119                   aDimension.getWidth(),
120                   aDimension.getHeight());
121
122           String renderStyle = jalview.bin.Cache.getDefault(
123                   "HTML_RENDERING", "Prompt each time");
124
125           // If we need to prompt, and if the GUI is visible then
126           // Prompt for rendering style
127           if (renderStyle.equalsIgnoreCase("Prompt each time")
128                   && !(System.getProperty("java.awt.headless") != null && System
129                           .getProperty("java.awt.headless").equals("true")))
130           {
131             HTMLOptions svgOption = new HTMLOptions();
132             renderStyle = svgOption.getValue();
133
134             if (renderStyle == null || svgOption.cancelled)
135             {
136               setProgressMessage(MessageManager.formatMessage(
137                       "status.cancelled_image_export_operation", "HTML"));
138               return;
139             }
140           }
141
142           if (renderStyle.equalsIgnoreCase("Lineart"))
143           {
144             idPanelGraphics.setRenderingHint(SVGHints.KEY_DRAW_STRING_TYPE,
145                     SVGHints.VALUE_DRAW_STRING_TYPE_VECTOR);
146             alignPanelGraphics.setRenderingHint(
147                     SVGHints.KEY_DRAW_STRING_TYPE,
148                     SVGHints.VALUE_DRAW_STRING_TYPE_VECTOR);
149           }
150           if (av.getWrapAlignment())
151           {
152             printWrapped(aDimension.getWidth(), aDimension.getHeight(), 0,
153                     alignPanelGraphics);
154           }
155           else
156           {
157           printUnwrapped(aDimension.getWidth(), aDimension.getHeight(), 0,
158  idPanelGraphics, alignPanelGraphics);
159           }
160
161           String idPanelSvgData = idPanelGraphics.getSVGDocument();
162           String alignPanelSvgData = alignPanelGraphics.getSVGDocument();
163           String jsonData = null;
164           boolean isEmbbedBioJSON = Boolean.valueOf(jalview.bin.Cache
165                   .getDefault("EXPORT_EMBBED_BIOJSON", "true"));
166           if (isEmbbedBioJSON)
167           {
168             AlignExportSettingI exportSettings = new AlignExportSettingI()
169             {
170               @Override
171               public boolean isExportHiddenSequences()
172               {
173                 return true;
174               }
175
176               @Override
177               public boolean isExportHiddenColumns()
178               {
179                 return true;
180               }
181
182               @Override
183               public boolean isExportAnnotations()
184               {
185                 return true;
186               }
187
188               @Override
189               public boolean isExportFeatures()
190               {
191                 return true;
192               }
193
194               @Override
195               public boolean isExportGroups()
196               {
197                 return true;
198               }
199
200               @Override
201               public boolean isCancelled()
202               {
203                 return false;
204               }
205
206             };
207             AlignmentExportData exportData = jalview.gui.AlignFrame
208                     .getAlignmentForExport(JSONFile.FILE_DESC, av,
209                             exportSettings);
210             jsonData = new FormatAdapter(ap, exportData.getSettings())
211                     .formatSequences(JSONFile.FILE_DESC,
212                             exportData.getAlignment(),
213                             exportData.getOmitHidden(),
214                             exportData.getStartEndPostions(),
215                             av.getColumnSelection());
216           }
217           String htmlData = getHtml(idPanelSvgData, alignPanelSvgData, jsonData,
218                   av.getWrapAlignment());
219           FileOutputStream out = new FileOutputStream(fileX);
220           out.write(htmlData.getBytes());
221           out.flush();
222           out.close();
223           if (!(System.getProperty("java.awt.headless") != null && System
224                   .getProperty("java.awt.headless").equals("true")))
225           {
226             jalview.util.BrowserLauncher.openURL("file:///" + fileX);
227           }
228         } catch (OutOfMemoryError err)
229         {
230           System.out.println("########################\n"
231                   + "OUT OF MEMORY " + fileX + "\n"
232                   + "########################");
233           new OOMWarning("Creating Image for " + fileX, err);
234         } catch (Exception e)
235         {
236           e.printStackTrace();
237           pIndicator.setProgressBar(MessageManager.formatMessage(
238                   "info.error_creating_file", "HTML"), pSessionId);
239         }
240         setProgressMessage(MessageManager.formatMessage(
241                 "status.export_complete", "HTML"));
242       }
243     }.start();
244
245   }
246
247   private void setProgressMessage(String message)
248   {
249     if (pIndicator != null && !headless)
250     {
251       pIndicator.setProgressBar(message, pSessionId);
252     }
253     else
254     {
255       System.out.println(message);
256     }
257   }
258
259   static JalviewFileChooser getHTMLChooser()
260   {
261     return new jalview.io.JalviewFileChooser(
262             jalview.bin.Cache.getProperty("LAST_DIRECTORY"),
263             new String[] { "html" },
264             new String[] { "Hypertext Markup Language" },
265             "Hypertext Markup Language");
266   }
267
268   public int printUnwrapped(int pwidth, int pheight, int pi, Graphics... pg)
269           throws PrinterException
270   {
271     return ap.printUnwrapped(pwidth, pheight, pi, pg);
272   }
273
274   public int printWrapped(int pwidth, int pheight, int pi, Graphics... pg)
275           throws PrinterException
276   {
277     return ap.printWrappedAlignment(pwidth, pheight, pi, pg[0]);
278   }
279
280   private String getHtml(String titleSvg, String alignmentSvg,
281           String jsonData, boolean wrapped)
282   {
283     StringBuilder htmlSvg = new StringBuilder();
284     htmlSvg.append("<html>\n");
285     if (jsonData != null)
286     {
287       htmlSvg.append("<button onclick=\"javascipt:openJalviewUsingCurrentUrl();\">Launch in Jalview</button> &nbsp;");
288       htmlSvg.append("<input type=\"submit\" value=\"View raw BioJSON Data\" onclick=\"jQuery.facebox({ div:'#seqData' }); return false;\" />");
289       htmlSvg.append("<div style=\"display: none;\" name=\"seqData\" id=\"seqData\" >"
290               + jsonData + "</div>");
291       htmlSvg.append("<br/>&nbsp;");
292     }
293     htmlSvg.append("\n<style type=\"text/css\"> "
294             + "div.parent{ width:100%;<!-- overflow: auto; -->}\n"
295             + "div.titlex{ width:11%; float: left; }\n"
296             + "div.align{ width:89%; float: right; }\n"
297             + "div.main-container{ border: 2px solid blue; border: 2px solid blue; width: 99%;   min-height: 99%; }\n"
298             + ".sub-category-container {overflow-y: scroll; overflow-x: hidden; width: 100%; height: 100%;}\n"
299             + "object {pointer-events: none;}");
300     if (jsonData != null)
301     {
302       // facebox style sheet for displaying raw BioJSON data
303       htmlSvg.append("#facebox { position: absolute;  top: 0;   left: 0; z-index: 100; text-align: left; }\n"
304               + "#facebox .popup{ position:relative; border:3px solid rgba(0,0,0,0); -webkit-border-radius:5px;"
305               + "-moz-border-radius:5px; border-radius:5px; -webkit-box-shadow:0 0 18px rgba(0,0,0,0.4); -moz-box-shadow:0 0 18px rgba(0,0,0,0.4);"
306               + "box-shadow:0 0 18px rgba(0,0,0,0.4); }\n"
307               + "#facebox .content { display:table; width: 98%; padding: 10px; background: #fff; -webkit-border-radius:4px; -moz-border-radius:4px;"
308               + " border-radius:4px; }\n"
309               + "#facebox .content > p:first-child{ margin-top:0; }\n"
310               + "#facebox .content > p:last-child{ margin-bottom:0; }\n"
311               + "#facebox .close{ position:absolute; top:5px; right:5px; padding:2px; background:#fff; }\n"
312               + "#facebox .close img{ opacity:0.3; }\n"
313               + "#facebox .close:hover img{ opacity:1.0; }\n"
314               + "#facebox .loading { text-align: center; }\n"
315               + "#facebox .image { text-align: center;}\n"
316               + "#facebox img { border: 0;  margin: 0; }\n"
317               + "#facebox_overlay { position: fixed; top: 0px; left: 0px; height:100%; width:100%; }\n"
318               + ".facebox_hide { z-index:-100; }\n"
319               + ".facebox_overlayBG { background-color: #000;  z-index: 99;  }");
320     }
321     htmlSvg.append("</style>");
322     if (!wrapped)
323     {
324     htmlSvg.append("<div class=\"main-container\" \n>");
325     htmlSvg.append("<div class=\"titlex\">\n");
326     htmlSvg.append("<div class=\"sub-category-container\"> \n");
327     htmlSvg.append(titleSvg);
328     htmlSvg.append("</div>");
329     htmlSvg.append("</div>\n\n<!-- ========================================================================================== -->\n\n");
330     htmlSvg.append("<div class=\"align\" >");
331     htmlSvg.append(
332             "<div class=\"sub-category-container\"> <div style=\"overflow-x: scroll;\">")
333             .append(alignmentSvg).append("</div></div>").append("</div>");
334     htmlSvg.append("</div>");
335
336     htmlSvg.append("<script language=\"JavaScript\" type=\"text/javascript\" src=\"http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js\"></script>\n"
337             + "<script language=\"JavaScript\" type=\"text/javascript\"  src=\"http://ajax.googleapis.com/ajax/libs/jqueryui/1.11.2/jquery-ui.min.js\"></script>\n"
338             + "<script>\n"
339             + "var subCatContainer = $(\".sub-category-container\");\n"
340             + "subCatContainer.scroll(\nfunction() {\n"
341             + "subCatContainer.scrollTop($(this).scrollTop());\n});\n");
342
343     htmlSvg.append("</script>\n");
344     }
345     else
346     {
347       htmlSvg.append("<div>\n")
348               .append(alignmentSvg).append("</div>");
349       htmlSvg.append("<script language=\"JavaScript\" type=\"text/javascript\" src=\"http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js\"></script>\n"
350               + "<script language=\"JavaScript\" type=\"text/javascript\"  src=\"http://ajax.googleapis.com/ajax/libs/jqueryui/1.11.2/jquery-ui.min.js\"></script>\n");
351
352     }
353
354     // javascript for launching file in Jalview
355
356     htmlSvg.append("<script language=\"JavaScript\">\n");
357     htmlSvg.append("function openJalviewUsingCurrentUrl(){\n");
358     htmlSvg.append("    var json = JSON.parse(document.getElementById(\"seqData\").innerHTML);\n");
359     htmlSvg.append("    var jalviewVersion = json['appSettings'].version;\n");
360     htmlSvg.append("    var url = json['appSettings'].webStartUrl;\n");
361     htmlSvg.append("    var myForm = document.createElement(\"form\");\n\n");
362     htmlSvg.append("    var heap = document.createElement(\"input\");\n");
363     htmlSvg.append("    heap.setAttribute(\"name\", \"jvm-max-heap\") ;\n");
364     htmlSvg.append("    heap.setAttribute(\"value\", \"2G\");\n\n");
365     htmlSvg.append("    var target = document.createElement(\"input\");\n");
366     htmlSvg.append("    target.setAttribute(\"name\", \"open\");\n");
367     htmlSvg.append("    target.setAttribute(\"value\", document.URL);\n\n");
368     htmlSvg.append("    var jvVersion = document.createElement(\"input\");\n");
369     htmlSvg.append("    jvVersion.setAttribute(\"name\", \"version\") ;\n");
370     htmlSvg.append("    jvVersion.setAttribute(\"value\", jalviewVersion);\n\n");
371     htmlSvg.append("    myForm.action = url;\n");
372     htmlSvg.append("    myForm.appendChild(heap);\n");
373     htmlSvg.append("    myForm.appendChild(target);\n");
374     htmlSvg.append("    myForm.appendChild(jvVersion);\n");
375     htmlSvg.append("    document.body.appendChild(myForm);\n");
376     htmlSvg.append("    myForm.submit() ;\n");
377     htmlSvg.append("    document.body.removeChild(myForm);\n");
378     htmlSvg.append("}\n");
379
380     // jquery facebox for displaying raw BioJSON data");
381     if (jsonData != null)
382     {
383       File faceBoxJsFile = new File("examples/javascript/facebox-1.3.js");
384       try
385       {
386         htmlSvg.append(HtmlFile.readFileAsString(faceBoxJsFile));
387       } catch (IOException e)
388       {
389         e.printStackTrace();
390       }
391     }
392
393     htmlSvg.append("</script>\n");
394     htmlSvg.append("</html>");
395     return htmlSvg.toString();
396   }
397 }