c92c08184b5c05af82f7ae4340f8e31be93ed82c
[scilab.git] / scilab / modules / helptools / src / java / org / scilab / modules / helptools / image / ImageConverter.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2010 - 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.helptools.image;
14
15 import java.awt.Graphics2D;
16 import java.awt.image.BufferedImage;
17 import java.io.BufferedReader;
18 import java.io.BufferedWriter;
19 import java.io.File;
20 import java.io.FileInputStream;
21 import java.io.FileOutputStream;
22 import java.io.FileReader;
23 import java.io.FileWriter;
24 import java.io.IOException;
25 import java.nio.channels.FileChannel;
26 import java.util.HashMap;
27 import java.util.Map;
28 import java.util.TreeMap;
29
30 import javax.activation.MimetypesFileTypeMap;
31 import javax.imageio.ImageIO;
32 import javax.swing.Icon;
33
34 import org.scilab.modules.commons.ScilabCommonsUtils;
35 import org.scilab.modules.helptools.DocbookTagConverter;
36
37 /**
38  * Class to handle the image conversion
39  * @author Calixte DENIZET
40  */
41 public final class ImageConverter {
42
43     private static final String MD5_FILE = "images_md5.txt";
44
45     private DocbookTagConverter conv;
46     private Map<String, ExternalImageConverter> externalConverters = new HashMap<String, ExternalImageConverter>();
47     private MimetypesFileTypeMap mimeMap = new MimetypesFileTypeMap();
48     private Map<String, String> md5s = null;
49
50     public ImageConverter() {
51         mimeMap.addMimeTypes("type=image/latex exts=tex,latex");
52         mimeMap.addMimeTypes("type=image/mathml exts=mml,mathml");
53         mimeMap.addMimeTypes("type=image/svg exts=svg");
54         mimeMap.addMimeTypes("type=image/scilab exts=sce");
55         mimeMap.addMimeTypes("type=image/scilab-xcos exts=xcos,zcos");
56     }
57
58     public void setDocbookTagConverter(DocbookTagConverter conv) {
59         this.conv = conv;
60     }
61
62     /**
63      * Register a new ExternalImageConverter
64      * @param c the converter to register
65      */
66     public void registerExternalImageConverter(ExternalImageConverter c) {
67         if (c != null) {
68             externalConverters.put(c.getMimeType(), c);
69         }
70     }
71
72     /**
73      * Get the current Scilab image converter
74      * @return a Scilab image converter
75      */
76     public ScilabImageConverter getScilabImageConverter() {
77         return (ScilabImageConverter) externalConverters.get("image/scilab");
78     }
79
80     /**
81      * Load file containing md5s
82      * @param dir the directory where to find the file
83      */
84     public void loadMD5s(String dir) {
85         md5s = getMD5s(dir);
86     }
87
88     /**
89      * Save file containing md5s
90      * @param dir the directory where to find the file
91      */
92     public void saveMD5s(String dir) {
93         writeMD5s(dir, md5s);
94     }
95
96     /**
97      * Compare md5
98      * @param code the code to compare
99      * @param file the file name of the future image
100      */
101     public boolean compareMD5(String code, String file) {
102         if (md5s != null && code != null && file != null && !file.isEmpty()) {
103             code = code.trim().replaceAll("[ \t]*[\n]+[ \t]*", "");
104             String md5 = ScilabCommonsUtils.getMD5(code);
105             String oldmd5 = md5s.get(file);
106             if (oldmd5 != null && md5.equals(oldmd5)) {
107                 return true;
108             }
109             md5s.put(file, md5);
110         }
111
112         return false;
113     }
114
115     /**
116      * Get the generated md5s
117      * @param dir the dir where to find the files
118      * @return a map filename -&gt; md5 of the code
119      */
120     private static Map<String, String> getMD5s(String dir) {
121         File f = new File(dir + File.separator + MD5_FILE);
122         Map<String, String> map = new HashMap<String, String>();
123         if (f.exists() && f.canRead()) {
124             BufferedReader reader = null;
125             try {
126                 reader = new BufferedReader(new FileReader(f));
127                 String line = reader.readLine();
128                 while (line != null) {
129                     String[] arr = line.split("=");
130                     map.put(arr[0], arr[1]);
131                     line = reader.readLine();
132                 }
133             } catch (IOException e) {
134                 System.err.println(e);
135             } finally {
136                 if (reader != null) {
137                     try {
138                         reader.close();
139                     } catch (IOException e) {
140
141                     }
142                 }
143             }
144         }
145
146         return map;
147     }
148
149     /**
150      * Write the generated md5s
151      * @param dir the dir where to find the files
152      * @param a map filename -&gt; md5 of the code
153      */
154     private static void writeMD5s(String dir, Map<String, String> map) {
155         File f = new File(dir + File.separator + MD5_FILE);
156         if (!f.exists()) {
157             try {
158                 f.createNewFile();
159             } catch (IOException e) {
160                 System.err.println(e);
161                 return;
162             }
163         }
164
165         if (f.canWrite()) {
166             BufferedWriter writer = null;
167             try {
168                 writer = new BufferedWriter(new FileWriter(f));
169                 Map<String, String> tree = new TreeMap<String, String>(map);
170                 for (Map.Entry<String, String> entry : tree.entrySet()) {
171                     String s = entry.getKey() + "=" + entry.getValue();
172                     writer.write(s, 0, s.length());
173                     writer.newLine();
174                 }
175                 writer.flush();
176             } catch (IOException e) {
177                 System.err.println(e);
178             } finally {
179                 if (writer != null) {
180                     try {
181                         writer.close();
182                     } catch (IOException e) {
183
184                     }
185                 }
186             }
187         }
188     }
189
190     /**
191      * @param attrs the attribute of the image
192      * @param path the current XML file which is parsed
193      * @param image the filename
194      * @param destDir the destination directory
195      * @return true if the code has been rendered into {@code imageFile}
196      */
197     public String getImageByFile(Map<String, String> attrs, String path, String image, String outputDir, String destDir, String baseImagePath) {
198         File f = new File(image);
199         if (!f.isAbsolute()) {
200             f = new File(path + File.separator + image);
201         }
202
203         String destFile = outputDir + File.separator + destDir + File.separator + f.getName();
204
205         ExternalImageConverter imgConv = externalConverters.get(mimeMap.getContentType(f));
206         if (imgConv != null) {
207             destFile += ".png";
208         }
209         File imageFile = new File(destFile);
210         String imageName = destDir + "/" + imageFile.getName();
211
212         if (f.lastModified() > imageFile.lastModified()) {
213             if (imgConv != null) {
214                 return imgConv.convertToImage(f, attrs, imageFile, imageName);
215             }
216             copyImageFile(f, outputDir + File.separator + destDir);
217         }
218
219         return conv.generateImageCode(conv.getBaseImagePath() + imageName, attrs);
220     }
221
222     /**
223      * @param code the code to translate
224      * @param attrs the attribute of the image
225      * @param mime type
226      * @param imageFile the filename
227      * @return true if the code has been rendered into {@code imageFile}
228      */
229     public String getImageByCode(String currentFile, String code, Map<String, String> attrs, String mime, File imageFile, String imageName, String baseImagePath, int lineNumber, String language, Boolean isLocalized) {
230         ExternalImageConverter imgConv = externalConverters.get(mime);
231         if (imgConv == null) {
232             System.err.println("In file " + currentFile + " at line " + lineNumber + ": invalid code:\n" + code);
233             return null;
234         }
235
236         if (!imageFile.exists() && md5s != null) {
237             md5s.remove(imageFile.getName());
238         }
239
240         File current = new File(currentFile);
241
242         if ((language.equals("en_US") || !imageFile.exists() || isLocalized != null) && !compareMD5(code, imageFile.getName())) {
243             if ((isLocalized != null && isLocalized.booleanValue()) || language.equals("en_US")) {
244                 System.err.println("Info: Create image " + imageFile.getName() + " from line " + lineNumber + " in " + current.getName());
245             } else if (!language.equals("en_US") && imageFile.exists()) {
246                 System.err.println("Warning: Overwrite image " + imageFile.getName() + " from line " + lineNumber + " in " + current.getName() + ". Check the code or use scilab:localized=\"true\" attribute.");
247             }
248
249             return imgConv.convertToImage(currentFile, code, attrs, imageFile, imageName);
250         }
251
252         return conv.generateImageCode(conv.getBaseImagePath() + imageName, attrs);
253     }
254
255     /**
256      * Test if an image file exists.
257      * @param path of the parsed file
258      * @param image the image name
259      * @return null if the image exists, the expected file path otherwise.
260      */
261     public static File imageExists(String path, String image) {
262         File f = new File(image);
263         if (!f.isAbsolute()) {
264             f = new File(path + File.separator + image);
265         }
266
267         if (f.exists()) {
268             return null;
269         } else {
270             return f;
271         }
272     }
273
274     /**
275      * @param f the file to copy
276      * @param destDir the destination directory
277      */
278     public static void copyImageFile(File f, String destDir) {
279         FileChannel src = null;
280         FileChannel dest = null;
281         try {
282             File destFile = new File(destDir + File.separator + f.getName());
283             if (!destFile.exists()) {
284                 destFile.createNewFile();
285             } else if (f.lastModified() <= destFile.lastModified()) {
286                 return;
287             }
288
289             src = new FileInputStream(f).getChannel();
290             dest = new FileOutputStream(destFile).getChannel();
291             dest.transferFrom(src, 0, src.size());
292         } catch (IOException e) {
293             System.err.println(e);
294         } finally {
295             try {
296                 if (src != null) {
297                     src.close();
298                 }
299                 if (dest != null) {
300                     dest.close();
301                 }
302             } catch (IOException e) {
303                 System.err.println(e);
304             }
305         }
306     }
307
308     /**
309      * @param icon the icon to convert into PNG
310      * @param imageFile the destination file
311      * @return true if all is ok
312      */
313     public static boolean convertIconToPNG(Icon icon, File imageFile) {
314         BufferedImage image = new BufferedImage(icon.getIconWidth(), icon.getIconHeight(), BufferedImage.TYPE_INT_ARGB);
315         Graphics2D g2d = image.createGraphics();
316         icon.paintIcon(null, g2d, 0, 0);
317
318         try {
319             ImageIO.write(image, "png", imageFile.getAbsoluteFile());
320         } catch (IOException ex) {
321             return false;
322         }
323
324         g2d.dispose();
325
326         return true;
327     }
328 }