View Javadoc

1   /*
2    * Copyright 2002 - 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: MathMLConverter.java,v ee8eb070689f 2008/06/25 14:51:09 maxberger $ */
18  
19  package net.sourceforge.jeuclid.ant;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.util.Arrays;
24  import java.util.List;
25  
26  import net.sourceforge.jeuclid.MutableLayoutContext;
27  import net.sourceforge.jeuclid.context.LayoutContextImpl;
28  import net.sourceforge.jeuclid.context.Parameter;
29  import net.sourceforge.jeuclid.converter.Converter;
30  import net.sourceforge.jeuclid.converter.ConverterRegistry;
31  
32  import org.apache.tools.ant.BuildException;
33  import org.apache.tools.ant.DirectoryScanner;
34  import org.apache.tools.ant.Project;
35  import org.apache.tools.ant.taskdefs.MatchingTask;
36  import org.apache.tools.ant.util.FileUtils;
37  
38  /**
39   * This task converts MathML files to images.
40   * 
41   * @version $Revision: ee8eb070689f $
42   */
43  public class MathMLConverter extends MatchingTask {
44  
45      private static final String CURRENT_DIR = "./";
46  
47      private static final char EXTENSION_SEP = '.';
48  
49      /**
50       * 
51       */
52      private File mdestDir;
53  
54      private File mbaseDir;
55  
56      private File minFile;
57  
58      private File moutFile;
59  
60      private String moutType = "image/png";
61  
62      private boolean mforce;
63  
64      private final MutableLayoutContext context;
65  
66      private final FileUtils fileUtils;
67  
68      /**
69       * Creates a new MathMLConverter Task.
70       */
71      public MathMLConverter() {
72          this.context = new LayoutContextImpl(LayoutContextImpl
73                  .getDefaultLayoutContext());
74          this.fileUtils = FileUtils.getFileUtils();
75      }
76  
77      /**
78       * Executes the task.
79       * 
80       */
81      @Override
82      public void execute() {
83          DirectoryScanner scanner;
84          String[] list;
85          String[] dirs;
86  
87          if (this.mbaseDir == null) {
88              this.mbaseDir = this.getProject().resolveFile(
89                      MathMLConverter.CURRENT_DIR);
90              this.log("Base is not sets, sets to " + this.mbaseDir,
91                      Project.MSG_WARN);
92          }
93  
94          // if we have an in file and out then process them
95          if ((this.minFile != null) && (this.moutFile != null)) {
96              this.log("Transforming file: " + this.minFile + " --> "
97                      + this.moutFile, Project.MSG_VERBOSE);
98              try {
99                  Converter.getInstance().convert(this.minFile, this.moutFile,
100                         this.moutType, this.context);
101             } catch (final IOException io) {
102                 throw new BuildException(io);
103             }
104             return;
105         }
106 
107         /*
108          * if we get here, in and out have not been specified, we are in batch
109          * processing mode.
110          */
111 
112         // -- make sure Source directory exists...
113         if (this.mdestDir == null) {
114             throw new BuildException("m_destDir attributes must be set!");
115         }
116         scanner = this.getDirectoryScanner(this.mbaseDir);
117         this.log("Transforming into " + this.mdestDir, Project.MSG_INFO);
118 
119         // Process all the files marked for styling
120         list = scanner.getIncludedFiles();
121         this.log("Included files: " + Arrays.toString(list),
122                 Project.MSG_VERBOSE);
123         this.process(this.mbaseDir, Arrays.asList(list), this.mdestDir);
124 
125         // Process all the directories marked for styling
126         dirs = scanner.getIncludedDirectories();
127         this.log("Included directories: " + Arrays.toString(dirs),
128                 Project.MSG_VERBOSE);
129         for (final String dir : dirs) {
130             list = this.fileUtils.resolveFile(this.mbaseDir, dir).list();
131             this.process(this.mbaseDir, Arrays.asList(list), this.mdestDir);
132         }
133     }
134 
135     /**
136      * Sets support for anti alias (default is <i>true</i>).
137      * 
138      * @param antiAlias
139      *            Flag for support anti alias.
140      */
141     public void setAntiAlias(final boolean antiAlias) {
142         this.setOption(Parameter.ANTIALIAS, antiAlias);
143     }
144 
145     /**
146      * Sets minimal size for turn on anti alias (default is <i>10.0</i>).
147      * 
148      * @param antiAliasMinSize
149      *            Minimal size in float number.
150      */
151     public void setAntiAliasMinSize(final float antiAliasMinSize) {
152         this.setOption(Parameter.ANTIALIAS_MINSIZE, antiAliasMinSize);
153     }
154 
155     /**
156      * Sets background color.
157      * 
158      * @param color
159      *            String representation of color.
160      */
161     public void setBackgroundColor(final String color) {
162         if (this.isNullOrEmpty(color)) {
163             this.log("Attribute \"backgroundcolor\" is empty, not used",
164                     Project.MSG_WARN);
165         } else {
166             this.setOption(Parameter.MATHBACKGROUND, color);
167         }
168     }
169 
170     /**
171      * Sets support for debug (default is <i>false</i>).
172      * 
173      * @param debug
174      *            Flag for support debug.
175      */
176     public void setDebug(final boolean debug) {
177         this.setOption(Parameter.DEBUG, debug);
178     }
179 
180     /**
181      * Sets display style (default is <i>BLOCK</i>.
182      * 
183      * @param display
184      *            String value of display style.
185      * 
186      * @see net.sourceforge.jeuclid.context.Display
187      */
188     public void setDisplay(final String display) {
189         this.setOption(Parameter.DISPLAY, display);
190     }
191 
192     /**
193      * Sets list of supported font families for <i>Double-Struck</i>.
194      * 
195      * @param fonts
196      *            List separated by comma.
197      */
198     public void setFontsDoublestruck(final String fonts) {
199         this.setOption(Parameter.FONTS_DOUBLESTRUCK, fonts);
200     }
201 
202     /**
203      * Sets list of supported font families for <i>Fraktur</i>.
204      * 
205      * @param fonts
206      *            List separated by comma.
207      */
208     public void setFontsFraktur(final String fonts) {
209         this.setOption(Parameter.FONTS_FRAKTUR, fonts);
210     }
211 
212     /**
213      * Sets font size of text.
214      * 
215      * @param fontSize
216      *            Font size as float value.
217      */
218     public void setFontSize(final float fontSize) {
219         this.setOption(Parameter.MATHSIZE, fontSize);
220     }
221 
222     /**
223      * Sets list of supported font families for <i>Monospaced</i>.
224      * 
225      * @param fonts
226      *            List separated by comma.
227      */
228     public void setFontsMonospaced(final String fonts) {
229         this.setOption(Parameter.FONTS_MONOSPACED, fonts);
230     }
231 
232     /**
233      * Sets list of supported font families for <i>Sans-Serif</i>.
234      * 
235      * @param fonts
236      *            List separated by comma.
237      */
238     public void setFontsSansSerif(final String fonts) {
239         this.setOption(Parameter.FONTS_SANSSERIF, fonts);
240     }
241 
242     /**
243      * Sets list of supported font families for <i>Script</i>.
244      * 
245      * @param fonts
246      *            List separated by comma.
247      */
248     public void setFontsScript(final String fonts) {
249         this.setOption(Parameter.FONTS_SCRIPT, fonts);
250     }
251 
252     /**
253      * Sets list of supported font families for <i>Serif</i>.
254      * 
255      * @param fonts
256      *            List separated by comma.
257      */
258     public void setFontsSerif(final String fonts) {
259         this.setOption(Parameter.FONTS_SERIF, fonts);
260     }
261 
262     /**
263      * Sets foreground color.
264      * 
265      * @param color
266      *            String representation of color.
267      */
268     public void setForegroundColor(final String color) {
269         if (this.isNullOrEmpty(color)) {
270             this
271                     .log(
272                             "Attribute \"foregroundcolor\" is empty, use default color",
273                             Project.MSG_WARN);
274         } else {
275             this.setOption(Parameter.MATHCOLOR, color);
276         }
277     }
278 
279     /**
280      * Sets &lt;mfrac&gt; keep scriptlevel.
281      * 
282      * @param keepScriptLevel
283      *            if true, element will NEVER increase children's scriptlevel
284      *            (in violation of the spec).
285      */
286     public void setMfracKeepScriptLevel(final boolean keepScriptLevel) {
287         this.setOption(Parameter.MFRAC_KEEP_SCRIPTLEVEL, keepScriptLevel);
288     }
289 
290     /**
291      * Sets scripts level (default is <i>0</i>).
292      * 
293      * @param level
294      *            Script level.
295      */
296     public void setScriptLevel(final int level) {
297         this.setOption(Parameter.SCRIPTLEVEL, level);
298     }
299 
300     /**
301      * Sets minimal size of smallest font size (default is <i>8.0</i>).
302      * 
303      * @param minSize
304      *            Size of font.
305      */
306     public void setScriptMinSize(final float minSize) {
307         this.setOption(Parameter.SCRIPTMINSIZE, minSize);
308     }
309 
310     /**
311      * Sets size of multiplier (default is <i>0.71</i>).
312      * 
313      * @param multSize
314      *            Size of multiplier.
315      */
316     public void setScriptSizeMult(final float multSize) {
317         this.setOption(Parameter.SCRIPTSIZEMULTIPLIER, multSize);
318     }
319 
320     /**
321      * Set whether to check dependencies, or always generate.
322      * 
323      * @param force
324      *            True, if the task should always generate the images.
325      */
326     public void setForce(final boolean force) {
327         this.logProperty("force", force);
328         this.mforce = force;
329     }
330 
331     /**
332      * Set the base directory.
333      * 
334      * @param dir
335      *            Base directory
336      */
337     public void setBasedir(final File dir) {
338         this.logProperty("basedir", dir);
339         this.mbaseDir = dir;
340     }
341 
342     /**
343      * Set the destination directory into which the result files should be
344      * copied to.
345      * 
346      * @param dir
347      *            Destination directory
348      */
349     public void setDestdir(final File dir) {
350         this.logProperty("destdir", dir);
351         this.mdestDir = dir;
352     }
353 
354     /**
355      * Sets an out file.
356      * 
357      * @param outFile
358      *            Output file
359      */
360     public void setOut(final File outFile) {
361         this.logProperty("out", outFile);
362         this.moutFile = outFile;
363     }
364 
365     /**
366      * Sets an input xml file to be converted.
367      * 
368      * @param inFile
369      *            Input file
370      */
371     public void setIn(final File inFile) {
372         this.logProperty("in", inFile);
373         this.minFile = inFile;
374     }
375 
376     /**
377      * Sets output file mimetype.
378      * 
379      * @param mimetype
380      *            mimetype for output file.
381      */
382     public void setType(final String mimetype) {
383         this.logProperty("type", mimetype);
384         this.moutType = mimetype;
385     }
386 
387     /**
388      * Processes the given input XML file and stores the result in the given
389      * resultFile.
390      * 
391      * @param baseDir
392      *            Base directory
393      * @param xmlFiles
394      *            Source file
395      * @param destDir
396      *            Destination directory
397      */
398     private void process(final File baseDir, final List<String> xmlFiles,
399             final File destDir) {
400         for (final String xmlFile : xmlFiles) {
401             File outFile = null;
402             File inFile = null;
403             final String suffix = MathMLConverter.EXTENSION_SEP
404                     + ConverterRegistry.getInstance().getSuffixForMimeType(
405                             this.moutType);
406             this.log("Found extension: " + suffix, Project.MSG_DEBUG);
407             try {
408                 inFile = this.fileUtils.resolveFile(baseDir, xmlFile);
409                 final int dotPos = xmlFile
410                         .lastIndexOf(MathMLConverter.EXTENSION_SEP);
411 
412                 if (dotPos > 0) {
413                     outFile = this.fileUtils.resolveFile(destDir, xmlFile
414                             .substring(0, dotPos)
415                             + suffix);
416                 } else {
417                     outFile = this.fileUtils.resolveFile(destDir, xmlFile
418                             + suffix);
419                 }
420                 this.log("Input file: " + inFile, Project.MSG_DEBUG);
421                 this.log("Output file: " + outFile, Project.MSG_DEBUG);
422                 if (this.mforce
423                         || !this.fileUtils.isUpToDate(inFile, outFile)) {
424                     this.fileUtils.createNewFile(outFile, true);
425                     Converter.getInstance().convert(inFile, outFile,
426                             this.moutType, this.context);
427                 }
428             } catch (final IOException ex) {
429                 // If failed to process document, must delete target document,
430                 // or it will not attempt to process it the second time
431                 this.log("Failed to process " + inFile, Project.MSG_ERR);
432                 FileUtils.delete(outFile);
433 
434                 throw new BuildException(ex);
435             }
436         }
437     }
438 
439     /**
440      * Convert string value of parameter and sets to current context.
441      * 
442      * @param param
443      *            Type of parameter.
444      * @param value
445      *            String value of parameter.
446      */
447     private void setOption(final Parameter param, final String value) {
448         this.setOption(param, param.fromString(value));
449     }
450 
451     /**
452      * Sets parameter for current context.
453      * 
454      * @param param
455      *            Type of parameter.
456      * @param value
457      *            Object with value of parameter.
458      */
459     private void setOption(final Parameter param, final Object value) {
460         this.logProperty(param.getOptionName(), value);
461         this.context.setParameter(param, value);
462     }
463 
464     /**
465      * Tests if string is null or is empty.
466      * 
467      * @param s
468      *            Tested string.
469      * 
470      * @return <code>true</code> if string is null or empty else
471      *         <code>false</code>.
472      */
473     private boolean isNullOrEmpty(final String s) {
474         // TODO: use .isEmpty() when JEuclid moves to 1.6
475         return s == null || s.length() == 0;
476     }
477 
478     /**
479      * Logs property, which is sets.
480      * 
481      * @param param
482      *            Parameter name.
483      * @param value
484      *            Parameter value.
485      */
486     private void logProperty(final String param, final Object value) {
487         this.log("Sets property \"" + param + "\" with value: " + value,
488                 Project.MSG_DEBUG);
489     }
490 }