001 /* 002 * Copyright 2002 - 2008 JEuclid, http://jeuclid.sf.net 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017 /* $Id: MathMLConverter.java,v ee8eb070689f 2008/06/25 14:51:09 maxberger $ */ 018 019 package net.sourceforge.jeuclid.ant; 020 021 import java.io.File; 022 import java.io.IOException; 023 import java.util.Arrays; 024 import java.util.List; 025 026 import net.sourceforge.jeuclid.MutableLayoutContext; 027 import net.sourceforge.jeuclid.context.LayoutContextImpl; 028 import net.sourceforge.jeuclid.context.Parameter; 029 import net.sourceforge.jeuclid.converter.Converter; 030 import net.sourceforge.jeuclid.converter.ConverterRegistry; 031 032 import org.apache.tools.ant.BuildException; 033 import org.apache.tools.ant.DirectoryScanner; 034 import org.apache.tools.ant.Project; 035 import org.apache.tools.ant.taskdefs.MatchingTask; 036 import org.apache.tools.ant.util.FileUtils; 037 038 /** 039 * This task converts MathML files to images. 040 * 041 * @version $Revision: ee8eb070689f $ 042 */ 043 public class MathMLConverter extends MatchingTask { 044 045 private static final String CURRENT_DIR = "./"; 046 047 private static final char EXTENSION_SEP = '.'; 048 049 /** 050 * 051 */ 052 private File mdestDir; 053 054 private File mbaseDir; 055 056 private File minFile; 057 058 private File moutFile; 059 060 private String moutType = "image/png"; 061 062 private boolean mforce; 063 064 private final MutableLayoutContext context; 065 066 private final FileUtils fileUtils; 067 068 /** 069 * Creates a new MathMLConverter Task. 070 */ 071 public MathMLConverter() { 072 this.context = new LayoutContextImpl(LayoutContextImpl 073 .getDefaultLayoutContext()); 074 this.fileUtils = FileUtils.getFileUtils(); 075 } 076 077 /** 078 * Executes the task. 079 * 080 */ 081 @Override 082 public void execute() { 083 DirectoryScanner scanner; 084 String[] list; 085 String[] dirs; 086 087 if (this.mbaseDir == null) { 088 this.mbaseDir = this.getProject().resolveFile( 089 MathMLConverter.CURRENT_DIR); 090 this.log("Base is not sets, sets to " + this.mbaseDir, 091 Project.MSG_WARN); 092 } 093 094 // if we have an in file and out then process them 095 if ((this.minFile != null) && (this.moutFile != null)) { 096 this.log("Transforming file: " + this.minFile + " --> " 097 + this.moutFile, Project.MSG_VERBOSE); 098 try { 099 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 <mfrac> 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 }