Class |
Line # |
Actions |
|||
---|---|---|---|---|---|
ImageExporter | 54 | 47 | 18 | ||
ImageExporter.ImageWriterI | 62 | 0 | 0 |
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.gui; | |
22 | ||
23 | import java.awt.Component; | |
24 | import java.awt.Graphics; | |
25 | import java.io.File; | |
26 | import java.util.concurrent.atomic.AtomicBoolean; | |
27 | ||
28 | import jalview.bin.Cache; | |
29 | import jalview.bin.Jalview; | |
30 | import jalview.io.JalviewFileChooser; | |
31 | import jalview.io.JalviewFileView; | |
32 | import jalview.io.exceptions.ImageOutputException; | |
33 | import jalview.util.ImageMaker; | |
34 | import jalview.util.ImageMaker.TYPE; | |
35 | import jalview.util.MessageManager; | |
36 | import jalview.util.Platform; | |
37 | import jalview.util.StringUtils; | |
38 | import jalview.util.imagemaker.BitmapImageSizing; | |
39 | ||
40 | /** | |
41 | * A class that marshals steps in exporting a view in image graphics format | |
42 | * <ul> | |
43 | * <li>prompts the user for the output file, if not already specified</li> | |
44 | * <li>prompts the user for Text or Lineart character rendering, if | |
45 | * necessary</li> | |
46 | * <li>instantiates an ImageMaker to create the appropriate Graphics output | |
47 | * context for the image format</li> | |
48 | * <li>invokes a callback to do the work of writing to the graphics</li> | |
49 | * </ul> | |
50 | * | |
51 | * @author gmcarstairs | |
52 | * | |
53 | */ | |
54 | public class ImageExporter | |
55 | { | |
56 | // todo move interface to jalview.api? or replace with lambda? | |
57 | /** | |
58 | * An interface for the callback that can be run to write the image on to the | |
59 | * graphics object. The callback should throw any exceptions arising so they | |
60 | * can be reported by this class. | |
61 | */ | |
62 | public interface ImageWriterI | |
63 | { | |
64 | void exportImage(Graphics g) throws Exception; | |
65 | } | |
66 | ||
67 | private IProgressIndicator messageBoard; | |
68 | ||
69 | private ImageWriterI imageWriter; | |
70 | ||
71 | TYPE imageType; | |
72 | ||
73 | private String title; | |
74 | ||
75 | /** | |
76 | * Constructor given a callback handler to write graphics data, an (optional) | |
77 | * target for status messages, image type and (optional) title for output file | |
78 | * | |
79 | * @param writer | |
80 | * @param statusBar | |
81 | * @param type | |
82 | * @param fileTitle | |
83 | */ | |
84 | 46 | public ImageExporter(ImageWriterI writer, IProgressIndicator statusBar, |
85 | TYPE type, String fileTitle) | |
86 | { | |
87 | 46 | this.imageWriter = writer; |
88 | 46 | this.messageBoard = statusBar; |
89 | 46 | this.imageType = type; |
90 | 46 | this.title = fileTitle; |
91 | } | |
92 | ||
93 | /** | |
94 | * Prompts the user for output file and Text/Lineart options as required, | |
95 | * configures a Graphics context for output, and makes a callback to the | |
96 | * client code to perform the image output | |
97 | * | |
98 | * @param file | |
99 | * output file (if null, user is prompted to choose) | |
100 | * @param parent | |
101 | * parent component for any dialogs shown | |
102 | * @param width | |
103 | * @param height | |
104 | * @param imageSource | |
105 | * what the image is of e.g. Tree, Alignment | |
106 | */ | |
107 | 0 | public void doExport(File file, Component parent, int width, int height, |
108 | String imageSource) throws ImageOutputException | |
109 | { | |
110 | 0 | doExport(file, parent, width, height, imageSource, null, |
111 | BitmapImageSizing.defaultBitmapImageSizing()); | |
112 | } | |
113 | ||
114 | 46 | public void doExport(File file, Component parent, int width, int height, |
115 | String imageSource, String renderer, BitmapImageSizing userBis) | |
116 | throws ImageOutputException | |
117 | { | |
118 | 46 | final long messageId = System.currentTimeMillis(); |
119 | 46 | setStatus( |
120 | MessageManager.formatMessage( | |
121 | "status.exporting_alignment_as_x_file", imageType), | |
122 | messageId); | |
123 | ||
124 | /* | |
125 | * prompt user for output file if not provided | |
126 | */ | |
127 | 46 | if (file == null && !Jalview.isHeadlessMode()) |
128 | { | |
129 | 0 | if (Desktop.instance.isInBatchMode()) |
130 | { | |
131 | // defensive error report - we could wait for user input.. I guess ? | |
132 | 0 | throw (new ImageOutputException( |
133 | "Need an output file to render to when exporting images in batch mode!")); | |
134 | } | |
135 | 0 | JalviewFileChooser chooser = imageType.getFileChooser(); |
136 | 0 | chooser.setFileView(new JalviewFileView()); |
137 | 0 | MessageManager.formatMessage("label.create_image_of", |
138 | imageType.getName(), imageSource); | |
139 | 0 | String title = "Create " + imageType.getName() |
140 | + " image from alignment"; | |
141 | 0 | chooser.setDialogTitle(title); |
142 | 0 | chooser.setToolTipText(MessageManager.getString("action.save")); |
143 | 0 | int value = chooser.showSaveDialog(parent); |
144 | 0 | if (value != JalviewFileChooser.APPROVE_OPTION) |
145 | { | |
146 | 0 | String msg = MessageManager.formatMessage( |
147 | "status.cancelled_image_export_operation", imageType.name); | |
148 | 0 | setStatus(msg, messageId); |
149 | 0 | return; |
150 | } | |
151 | 0 | Cache.setProperty("LAST_DIRECTORY", |
152 | chooser.getSelectedFile().getParent()); | |
153 | 0 | file = chooser.getSelectedFile(); |
154 | } | |
155 | ||
156 | /* | |
157 | * Prompt for Text or Lineart (EPS/SVG) unless a preference is already set | |
158 | * for this as EPS_RENDERING / SVG_RENDERING | |
159 | * Always set to Text for JalviewJS as Lineart (glyph fonts) not available | |
160 | */ | |
161 | 46 | String renderStyle = renderer == null |
162 | ? Cache.getDefault(imageType.getName() + "_RENDERING", | |
163 | LineartOptions.PROMPT_EACH_TIME) | |
164 | : renderer; | |
165 | 46 | if (Platform.isJS()) |
166 | { | |
167 | 0 | renderStyle = "Text"; |
168 | } | |
169 | 46 | AtomicBoolean textSelected = new AtomicBoolean( |
170 | !StringUtils.equalsIgnoreCase("lineart", renderStyle)); | |
171 | 46 | if ((imageType == TYPE.EPS || imageType == TYPE.SVG) && StringUtils |
172 | .equalsIgnoreCase(LineartOptions.PROMPT_EACH_TIME, renderStyle) | |
173 | && !Jalview.isHeadlessMode()) | |
174 | { | |
175 | 0 | final File chosenFile = file; |
176 | 0 | Runnable okAction = () -> { |
177 | 0 | exportImage(chosenFile, !textSelected.get(), width, height, |
178 | messageId, userBis); | |
179 | }; | |
180 | 0 | LineartOptions epsOption = new LineartOptions(TYPE.EPS.getName(), |
181 | textSelected); | |
182 | 0 | epsOption.setResponseAction(1, () -> { |
183 | 0 | setStatus(MessageManager.formatMessage( |
184 | "status.cancelled_image_export_operation", | |
185 | imageType.getName()), messageId); | |
186 | }); | |
187 | 0 | epsOption.setResponseAction(0, okAction); |
188 | 0 | epsOption.showDialog(); |
189 | /* no code here - JalviewJS cannot execute it */ | |
190 | } | |
191 | else | |
192 | { | |
193 | /* | |
194 | * character rendering not required, or preference already set | |
195 | * or we're in headless mode - just do the export | |
196 | */ | |
197 | 46 | exportImage(file, !textSelected.get(), width, height, messageId, |
198 | userBis); | |
199 | } | |
200 | } | |
201 | ||
202 | /** | |
203 | * Constructs a suitable graphics context and passes it to the callback | |
204 | * handler for the image to be written. Shows status messages for export in | |
205 | * progress, complete, or failed as appropriate. | |
206 | * | |
207 | * @param chosenFile | |
208 | * @param asLineart | |
209 | * @param width | |
210 | * @param height | |
211 | * @param messageId | |
212 | */ | |
213 | 46 | protected void exportImage(File chosenFile, boolean asLineart, int width, |
214 | int height, long messageId, BitmapImageSizing userBis) | |
215 | { | |
216 | 46 | String type = imageType.getName(); |
217 | 46 | try |
218 | { | |
219 | // setStatus( | |
220 | // MessageManager.formatMessage( | |
221 | // "status.exporting_alignment_as_x_file", type), | |
222 | // messageId); | |
223 | 46 | ImageMaker im = new ImageMaker(imageType, width, height, chosenFile, |
224 | title, asLineart, userBis); | |
225 | 46 | imageWriter.exportImage(im.getGraphics()); |
226 | 46 | im.writeImage(); |
227 | 46 | setStatus( |
228 | MessageManager.formatMessage("status.export_complete", type), | |
229 | messageId); | |
230 | } catch (Exception e) | |
231 | { | |
232 | 0 | jalview.bin.Console.error(String.format("Error creating %s file: %s", |
233 | type, e.toString()), e); | |
234 | 0 | setStatus(MessageManager.formatMessage("info.error_creating_file", |
235 | type), messageId); | |
236 | } | |
237 | } | |
238 | ||
239 | /** | |
240 | * Asks the callback to show a status message with given id | |
241 | * | |
242 | * @param msg | |
243 | * @param id | |
244 | */ | |
245 | 92 | void setStatus(String msg, long id) |
246 | { | |
247 | 92 | if (messageBoard != null && !Jalview.isHeadlessMode()) |
248 | { | |
249 | 26 | messageBoard.setProgressBar(msg, id); |
250 | } | |
251 | } | |
252 | ||
253 | } |