View Javadoc

1   /*
2    * Copyright 2007 - 2008 JEuclid, http://jeuclid.sf.net
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  /* $Id: Converter.java,v 0bed7fec74b0 2010/08/06 15:59:34 max $ */
18  
19  package net.sourceforge.jeuclid.converter;
20  
21  import java.awt.Color;
22  import java.awt.Dimension;
23  import java.awt.Graphics2D;
24  import java.awt.Image;
25  import java.awt.image.BufferedImage;
26  import java.io.BufferedOutputStream;
27  import java.io.File;
28  import java.io.FileOutputStream;
29  import java.io.IOException;
30  import java.io.OutputStream;
31  
32  import javax.xml.parsers.ParserConfigurationException;
33  
34  import net.sourceforge.jeuclid.LayoutContext;
35  import net.sourceforge.jeuclid.MathMLParserSupport;
36  import net.sourceforge.jeuclid.MutableLayoutContext;
37  import net.sourceforge.jeuclid.context.LayoutContextImpl;
38  import net.sourceforge.jeuclid.converter.ConverterPlugin.DocumentWithDimension;
39  import net.sourceforge.jeuclid.layout.JEuclidView;
40  
41  import org.apache.commons.logging.Log;
42  import org.apache.commons.logging.LogFactory;
43  import org.w3c.dom.Document;
44  import org.w3c.dom.Node;
45  import org.xml.sax.SAXException;
46  
47  /**
48   * Generic converter which uses the registry to do its conversions.
49   * 
50   * @version $Revision: 0bed7fec74b0 $
51   */
52  public final class Converter {
53  
54      /**
55       * Mime type for SVG.
56       */
57      public static final String TYPE_SVG = "image/svg+xml";
58  
59      /**
60       * File extension for SVG.
61       */
62      public static final String EXTENSION_SVG = "svg";
63  
64      private static final String UNSUPPORTED_OUTPUT_TYPE = "Unsupported output type: ";
65  
66      private static final int MAX_RGB_VALUE = 255;
67  
68      private static final class SingletonHolder {
69          private static final Converter INSTANCE = new Converter();
70  
71          private SingletonHolder() {
72          }
73      }
74  
75      /**
76       * Logger for this class
77       */
78      private static final Log LOGGER = LogFactory.getLog(Converter.class);
79  
80      /**
81       * Default constructor.
82       */
83      protected Converter() {
84          // Empty on purpose.
85      }
86  
87      /**
88       * Retrieve an instance of the converter singleton class.
89       * 
90       * @return a Converter object.
91       */
92      public static Converter getInstance() {
93          return Converter.SingletonHolder.INSTANCE;
94      }
95  
96      /**
97       * @return Converter instance
98       * @deprecated use {@link #getInstance()} instead.
99       */
100     @Deprecated
101     public static Converter getConverter() {
102         return Converter.getInstance();
103     }
104 
105     /**
106      * Converts an existing file from MathML or ODF to the given type.
107      * 
108      * @param inFile
109      *            input file.
110      * @param outFile
111      *            output file.
112      * @param outFileType
113      *            mimetype for the output file.
114      * @return Dimension of converted image upon success, null otherwise
115      * @throws IOException
116      *             if an I/O error occurred during read or write.
117      */
118     public Dimension convert(final File inFile, final File outFile,
119             final String outFileType) throws IOException {
120         final MutableLayoutContext params = new LayoutContextImpl(
121                 LayoutContextImpl.getDefaultLayoutContext());
122         return this.convert(inFile, outFile, outFileType, params);
123     }
124 
125     /**
126      * Converts an existing file from MathML or ODF to the given type.
127      * 
128      * @param inFile
129      *            input file.
130      * @param outFile
131      *            output file.
132      * @param outFileType
133      *            mimetype for the output file.
134      * @param params
135      *            rendering parameters.
136      * @return Dimension of converted image upon success, null otherwise
137      * @throws IOException
138      *             if an I/O error occurred during read or write.
139      */
140     public Dimension convert(final File inFile, final File outFile,
141             final String outFileType, final LayoutContext params)
142             throws IOException {
143         Document doc;
144         try {
145             doc = MathMLParserSupport.parseFile(inFile);
146             return this.convert(doc, outFile, outFileType, params);
147         } catch (final SAXException e) {
148             Converter.LOGGER.error("Failed to parse file:" + inFile, e);
149             return null;
150         }
151     }
152 
153     /**
154      * Converts an existing document from MathML to the given type and store it
155      * in a file.
156      * 
157      * @param doc
158      *            input document. See
159      *            {@link net.sourceforge.jeuclid.DOMBuilder#DOMBuilder(Node, MathBase)}
160      *            for the list of valid node types.
161      * @param outFile
162      *            output file.
163      * @param outFileType
164      *            mimetype for the output file.
165      * @param params
166      *            parameter set to use for conversion.
167      * @return Dimension of converted image upon success, null otherwise
168      * @throws IOException
169      *             if an I/O error occurred during read or write.
170      */
171     public Dimension convert(final Node doc, final File outFile,
172             final String outFileType, final LayoutContext params)
173             throws IOException {
174 
175         final OutputStream outStream = new BufferedOutputStream(
176                 new FileOutputStream(outFile));
177         final Dimension result = this.convert(doc, outStream, outFileType,
178                 params);
179         if (result == null) {
180             if (!outFile.delete()) {
181                 Converter.LOGGER.debug("Could not delete " + outFile);
182             }
183         } else {
184             // should be closed by wrapper image streams, but just in case...
185             try {
186                 outStream.close();
187             } catch (final IOException e) {
188                 Converter.LOGGER.debug(e);
189             }
190         }
191         return result;
192     }
193 
194     /**
195      * Converts an existing document from MathML to the given XML based type and
196      * store it in a DOM document.
197      * 
198      * @param doc
199      *            input document. See
200      *            {@link net.sourceforge.jeuclid.DOMBuilder#DOMBuilder(Node, MathBase)}
201      *            for the list of valid node types.
202      * @param outFileType
203      *            mimetype for the output file.
204      * @param params
205      *            parameter set to use for conversion.
206      * @return an instance of Document, or the appropriate subtype for this
207      *         format (e.g. SVGDocument). If conversion is not supported to this
208      *         type, it may return null.
209      */
210     public DocumentWithDimension convert(final Node doc,
211             final String outFileType, final LayoutContext params) {
212         final ConverterPlugin plugin = ConverterRegistry.getInstance()
213                 .getConverter(outFileType);
214         DocumentWithDimension result = null;
215         if (plugin != null) {
216             result = plugin.convert(doc, params);
217         }
218         if (result == null) {
219             Converter.LOGGER.fatal(Converter.UNSUPPORTED_OUTPUT_TYPE
220                     + outFileType);
221         }
222         return result;
223     }
224 
225     /**
226      * Converts an existing document from MathML to the given XML based type and
227      * writes it to the provided output stream.
228      * 
229      * @param doc
230      *            input document. See
231      *            {@link net.sourceforge.jeuclid.DOMBuilder#DOMBuilder(Node, MathBase)}
232      *            for the list of valid node types.
233      * @param outStream
234      *            output stream.
235      * @param outFileType
236      *            mimetype for the output file.
237      * @param params
238      *            parameter set to use for conversion.
239      * @return Dimension of converted image upon success, null otherwise
240      * @throws IOException
241      *             if an I/O error occurred during read or write.
242      */
243     public Dimension convert(final Node doc, final OutputStream outStream,
244             final String outFileType, final LayoutContext params)
245             throws IOException {
246         final ConverterPlugin plugin = ConverterRegistry.getInstance()
247                 .getConverter(outFileType);
248         Dimension result = null;
249         if (plugin == null) {
250             Converter.LOGGER.fatal(Converter.UNSUPPORTED_OUTPUT_TYPE
251                     + outFileType);
252         } else {
253             try {
254                 result = plugin.convert(doc, params, outStream);
255             } catch (final IOException ex) {
256                 Converter.LOGGER.fatal("Failed to process: " + ex.getMessage(),
257                         ex);
258             }
259         }
260         return result;
261     }
262 
263     /**
264      * Converts an XML string from MathML to the given XML based type and writes
265      * it to the provided output stream.
266      * 
267      * @param docString
268      *            XML string representing a valid document
269      * @param outStream
270      *            output stream.
271      * @param outFileType
272      *            mimetype for the output file.
273      * @param params
274      *            parameter set to use for conversion.
275      * @return Dimension of converted image upon success, null otherwise
276      * @throws IOException
277      *             if an I/O error occurred during read or write.
278      */
279     public Dimension convert(final String docString,
280             final OutputStream outStream, final String outFileType,
281             final LayoutContext params) throws IOException {
282 
283         Dimension result = null;
284 
285         try {
286             final Document doc = MathMLParserSupport.parseString(docString);
287             result = this.convert(doc, outStream, outFileType, params);
288         } catch (final SAXException e) {
289             Converter.LOGGER.error("SAXException converting:" + docString, e);
290             result = null;
291         } catch (final ParserConfigurationException e) {
292             Converter.LOGGER.error("ParserConfigurationException converting:"
293                     + docString, e);
294             result = null;
295         }
296 
297         return result;
298     }
299 
300     /**
301      * Renders a document into an image.
302      * 
303      * @param node
304      *            Document / Node to render
305      * @param context
306      *            LayoutContext to use.
307      * @return the rendered image
308      * @throws IOException
309      *             if an I/O error occurred.
310      */
311     public BufferedImage render(final Node node, final LayoutContext context)
312             throws IOException {
313         return this.render(node, context, BufferedImage.TYPE_INT_ARGB);
314     }
315 
316     /**
317      * Renders a document into an image.
318      * 
319      * @param node
320      *            Document / Node to render
321      * @param context
322      *            LayoutContext to use.
323      * @param imageType
324      *            ImageType as defined by {@link BufferedImage}
325      * @return the rendered image
326      * @throws IOException
327      *             if an I/O error occurred.
328      */
329     public BufferedImage render(final Node node, final LayoutContext context,
330             final int imageType) throws IOException {
331         final Image tempimage = new BufferedImage(1, 1, imageType);
332         final Graphics2D tempg = (Graphics2D) tempimage.getGraphics();
333 
334         final JEuclidView view = new JEuclidView(node, context, tempg);
335 
336         final int width = Math.max(1, (int) Math.ceil(view.getWidth()));
337         final int ascent = (int) Math.ceil(view.getAscentHeight());
338         final int height = Math.max(1, (int) Math.ceil(view.getDescentHeight())
339                 + ascent);
340 
341         final BufferedImage image = new BufferedImage(width, height, imageType);
342         final Graphics2D g = image.createGraphics();
343 
344         final Color background;
345         if (image.getColorModel().hasAlpha()) {
346             background = new Color(Converter.MAX_RGB_VALUE,
347                     Converter.MAX_RGB_VALUE, Converter.MAX_RGB_VALUE, 0);
348         } else {
349             background = Color.WHITE;
350         }
351         g.setColor(background);
352         g.fillRect(0, 0, width, height);
353         g.setColor(Color.black);
354 
355         view.draw(g, 0, ascent);
356         return image;
357     }
358 }