819f93f0c542a7ced8f16f6c49349236861e6892
[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         if (!compareMD5(code, imageFile.getName())) {
242             if (isLocalized || language.equals("en_US")) {
243                 System.err.println("Info: Create image " + imageFile.getName() + " from line " + lineNumber + " in " + current.getName());
244             } else if (!language.equals("en_US") && imageFile.exists()) {
245                 System.err.println("Warning: Overwrite image " + imageFile.getName() + " from line " + lineNumber + " in " + current.getName() + ". Check the code or use scilab:localized=\"true\" attribute.");
246             }
247
248             return imgConv.convertToImage(currentFile, code, attrs, imageFile, imageName);
249         }
250
251         return conv.generateImageCode(conv.getBaseImagePath() + imageName, attrs);
252     }
253
254     /**
255      * Test if an image file exists.
256      * @param path of the parsed file
257      * @param image the image name
258      * @return null if the image exists, the expected file path otherwise.
259      */
260     public static File imageExists(String path, String image) {
261         File f = new File(image);
262         if (!f.isAbsolute()) {
263             f = new File(path + File.separator + image);
264         }
265
266         if (f.exists()) {
267             return null;
268         } else {
269             return f;
270         }
271     }
272
273     /**
274      * @param f the file to copy
275      * @param destDir the destination directory
276      */
277     public static void copyImageFile(File f, String destDir) {
278         FileChannel src = null;
279         FileChannel dest = null;
280         try {
281             File destFile = new File(destDir + File.separator + f.getName());
282             if (!destFile.exists()) {
283                 destFile.createNewFile();
284             } else if (f.lastModified() <= destFile.lastModified()) {
285                 return;
286             }
287
288             src = new FileInputStream(f).getChannel();
289             dest = new FileOutputStream(destFile).getChannel();
290             dest.transferFrom(src, 0, src.size());
291         } catch (IOException e) {
292             System.err.println(e);
293         } finally {
294             try {
295                 if (src != null) {
296                     src.close();
297                 }
298                 if (dest != null) {
299                     dest.close();
300                 }
301             } catch (IOException e) {
302                 System.err.println(e);
303             }
304         }
305     }
306
307     /**
308      * @param icon the icon to convert into PNG
309      * @param imageFile the destination file
310      * @return true if all is ok
311      */
312     public static boolean convertIconToPNG(Icon icon, File imageFile) {
313         BufferedImage image = new BufferedImage(icon.getIconWidth(), icon.getIconHeight(), BufferedImage.TYPE_INT_ARGB);
314         Graphics2D g2d = image.createGraphics();
315         icon.paintIcon(null, g2d, 0, 0);
316
317         try {
318             ImageIO.write(image, "png", imageFile.getAbsoluteFile());
319         } catch (IOException ex) {
320             return false;
321         }
322
323         g2d.dispose();
324
325         return true;
326     }
327 }