/* * Copyright 1999-2004 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* $Id$ */ package org.apache.fop.tools.anttasks; // Ant import org.apache.tools.ant.BuildException; import org.apache.tools.ant.DirectoryScanner; import org.apache.tools.ant.Project; import org.apache.tools.ant.Task; import org.apache.tools.ant.types.FileSet; import org.apache.tools.ant.util.GlobPatternMapper; // Java import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.net.MalformedURLException; import java.util.List; // FOP import org.apache.fop.apps.InputHandler; import org.apache.fop.apps.FOFileHandler; import org.apache.fop.apps.Driver; import org.apache.fop.apps.FOPException; import org.apache.fop.apps.FOUserAgent; // Avalon import org.apache.avalon.framework.logger.AbstractLogEnabled; import org.apache.avalon.framework.logger.ConsoleLogger; import org.apache.avalon.framework.logger.Logger; /** * Wrapper for FOP which allows it to be accessed from within an Ant task. * Accepts the inputs: * */ public class Fop extends Task { private File foFile; private List filesets = new java.util.ArrayList(); private File outFile; private File outDir; private String format; //MIME type private File baseDir; private File userConfig; private int messageType = Project.MSG_VERBOSE; private boolean logFiles = true; private boolean force = false; private boolean relativebase = false; /** * Sets the filename for the userconfig.xml. * @param userConfig Configuration to use */ public void setUserconfig(File userConfig) { this.userConfig = userConfig; } /** * Returns the file for the userconfig.xml. * @return the userconfig.xml file */ public File getUserconfig() { return this.userConfig; } /** * Sets the input XSL-FO file. * @param foFile input XSL-FO file */ public void setFofile(File foFile) { this.foFile = foFile; } /** * Gets the input XSL-FO file. * @return input XSL-FO file */ public File getFofile() { return foFile; } /** * Adds a set of XSL-FO files (nested fileset attribute). * @param set a fileset */ public void addFileset(FileSet set) { filesets.add(set); } /** * Returns the current list of filesets. * @return the filesets */ public List getFilesets() { return this.filesets; } /** * Set whether to include files (external-grpahics, instream-foreign-object) * from a path relative to the .fo file (true) or the working directory (false, default) * only useful for filesets * * @param relbase true if paths are relative to file. */ public void setRelativebase(boolean relbase) { this.relativebase = relbase; } /** * Gets the relative base attribute * @return the relative base attribute */ public boolean getRelativebase() { return relativebase; } /** * Set whether to check dependencies, or to always generate; * optional, default is false. * * @param force true if always generate. */ public void setForce(boolean force) { this.force = force; } /** * Gets the force attribute * @return the force attribute */ public boolean getForce() { return force; } /** * Sets the output file. * @param outFile File to output to */ public void setOutfile(File outFile) { this.outFile = outFile; } /** * Gets the output file. * @return the output file */ public File getOutfile() { return this.outFile; } /** * Sets the output directory. * @param outDir Directory to output to */ public void setOutdir(File outDir) { this.outDir = outDir; } /** * Gets the output directory. * @return the output directory */ public File getOutdir() { return this.outDir; } /** * Sets output format (MIME type). * @param format the output format */ public void setFormat(String format) { this.format = format; } /** * Gets the output format (MIME type). * @return the output format */ public String getFormat() { return this.format; } /** * Sets the message level to be used while processing. * @param messageLevel (error | warn| info | verbose | debug) */ public void setMessagelevel(String messageLevel) { if (messageLevel.equalsIgnoreCase("info")) { messageType = Project.MSG_INFO; } else if (messageLevel.equalsIgnoreCase("verbose")) { messageType = Project.MSG_VERBOSE; } else if (messageLevel.equalsIgnoreCase("debug")) { messageType = Project.MSG_DEBUG; } else if (messageLevel.equalsIgnoreCase("err") || messageLevel.equalsIgnoreCase("error")) { messageType = Project.MSG_ERR; } else if (messageLevel.equalsIgnoreCase("warn")) { messageType = Project.MSG_WARN; } else { log("messagelevel set to unknown value \"" + messageLevel + "\"", Project.MSG_ERR); throw new BuildException("unknown messagelevel"); } } /** * Returns the message type corresponding to Project.MSG_* * representing the current message level. * @see org.apache.tools.ant.Project */ public int getMessageType() { return messageType; } /** * Sets the base directory; currently ignored. * @param baseDir File to use as a working directory */ public void setBasedir(File baseDir) { this.baseDir = baseDir; } /** * Gets the base directory. * @return the base directory */ public File getBasedir() { return (baseDir != null) ? baseDir : getProject().resolveFile("."); } /** * Controls whether the filenames of the files that are processed are logged * or not. * @param logFiles True if the feature should be enabled */ public void setLogFiles(boolean logFiles) { this.logFiles = logFiles; } /** * Returns True if the filename of each file processed should be logged. * @return True if the filenames should be logged. */ public boolean getLogFiles() { return this.logFiles; } /** * @see org.apache.tools.ant.Task#execute() */ public void execute() throws BuildException { int logLevel = ConsoleLogger.LEVEL_INFO; switch (getMessageType()) { case Project.MSG_DEBUG : logLevel = ConsoleLogger.LEVEL_DEBUG; break; case Project.MSG_INFO : logLevel = ConsoleLogger.LEVEL_INFO; break; case Project.MSG_WARN : logLevel = ConsoleLogger.LEVEL_WARN; break; case Project.MSG_ERR : logLevel = ConsoleLogger.LEVEL_ERROR; break; case Project.MSG_VERBOSE: logLevel = ConsoleLogger.LEVEL_DEBUG; break; } Logger log = new ConsoleLogger(logLevel); try { FOPTaskStarter starter = new FOPTaskStarter(this); starter.enableLogging(log); starter.run(); } catch (FOPException ex) { throw new BuildException(ex); } } } class FOPTaskStarter extends AbstractLogEnabled { private Fop task; private String baseURL = null; FOPTaskStarter(Fop task) throws FOPException { this.task = task; } private int determineRenderer(String format) { if ((format == null) || format.equalsIgnoreCase("application/pdf") || format.equalsIgnoreCase("pdf")) { return Driver.RENDER_PDF; } else if (format.equalsIgnoreCase("application/postscript") || format.equalsIgnoreCase("ps")) { return Driver.RENDER_PS; } else if (format.equalsIgnoreCase("application/vnd.mif") || format.equalsIgnoreCase("mif")) { return Driver.RENDER_MIF; } else if (format.equalsIgnoreCase("application/msword") || format.equalsIgnoreCase("application/rtf") || format.equalsIgnoreCase("rtf")) { return Driver.RENDER_RTF; } else if (format.equalsIgnoreCase("application/vnd.hp-PCL") || format.equalsIgnoreCase("pcl")) { return Driver.RENDER_PCL; } else if (format.equalsIgnoreCase("text/plain") || format.equalsIgnoreCase("txt")) { return Driver.RENDER_TXT; } else if (format.equalsIgnoreCase("text/xml") || format.equalsIgnoreCase("at") || format.equalsIgnoreCase("xml")) { return Driver.RENDER_XML; } else { String err = "Couldn't determine renderer to use: " + format; throw new BuildException(err); } } private String determineExtension(int renderer) { switch (renderer) { case Driver.RENDER_PDF: return ".pdf"; case Driver.RENDER_PS: return ".ps"; case Driver.RENDER_MIF: return ".mif"; case Driver.RENDER_RTF: return ".rtf"; case Driver.RENDER_PCL: return ".pcl"; case Driver.RENDER_TXT: return ".txt"; case Driver.RENDER_XML: return ".xml"; default: String err = "Unknown renderer: " + renderer; throw new BuildException(err); } } private File replaceExtension(File file, String expectedExt, String newExt) { String name = file.getName(); if (name.toLowerCase().endsWith(expectedExt)) { name = name.substring(0, name.length() - expectedExt.length()); } name = name.concat(newExt); return new File(file.getParentFile(), name); } /** * @see org.apache.fop.apps.Starter#run() */ public void run() throws FOPException { //Setup configuration if (task.getUserconfig() != null) { /**@todo implement me */ } //Set base directory if (task.getBasedir() != null) { try { this.baseURL = task.getBasedir().toURL().toExternalForm(); } catch (MalformedURLException mfue) { getLogger().error("Error creating base URL from base directory", mfue); } } else { try { if (task.getFofile() != null) { this.baseURL = task.getFofile().getParentFile().toURL(). toExternalForm(); } } catch (MalformedURLException mfue) { getLogger().error("Error creating base URL from XSL-FO input file", mfue); } } task.log("Using base URL: " + baseURL, Project.MSG_DEBUG); int rint = determineRenderer(task.getFormat()); String newExtension = determineExtension(rint); // actioncount = # of fofiles actually processed through FOP int actioncount = 0; // skippedcount = # of fofiles which haven't changed (force = "false") int skippedcount = 0; // deal with single source file if (task.getFofile() != null) { if (task.getFofile().exists()) { File outf = task.getOutfile(); if (outf == null) { throw new BuildException("outfile is required when fofile is used"); } if (task.getOutdir() != null) { outf = new File(task.getOutdir(), outf.getName()); } // Render if "force" flag is set OR // OR output file doesn't exist OR // output file is older than input file if (task.getForce() || !outf.exists() || (task.getFofile().lastModified() > outf.lastModified() )) { render(task.getFofile(), outf, rint); actioncount++; } else if (outf.exists() && (task.getFofile().lastModified() <= outf.lastModified() )) { skippedcount++; } } } GlobPatternMapper mapper = new GlobPatternMapper(); mapper.setFrom("*.fo"); mapper.setTo("*" + newExtension); // deal with the filesets for (int i = 0; i < task.getFilesets().size(); i++) { FileSet fs = (FileSet) task.getFilesets().get(i); DirectoryScanner ds = fs.getDirectoryScanner(task.getProject()); String[] files = ds.getIncludedFiles(); for (int j = 0; j < files.length; j++) { File f = new File(fs.getDir(task.getProject()), files[j]); File outf = null; if (task.getOutdir() != null && files[j].endsWith(".fo")) { String[] sa = mapper.mapFileName(files[j]); outf = new File(task.getOutdir(), sa[0]); } else { outf = replaceExtension(f, ".fo", newExtension); if (task.getOutdir() != null) { outf = new File(task.getOutdir(), outf.getName()); } } try { if (task.getRelativebase()) { this.baseURL = f.getParentFile().toURL(). toExternalForm(); } if (this.baseURL == null) { this.baseURL = fs.getDir(task.getProject()).toURL(). toExternalForm(); } } catch (Exception e) { task.log("Error setting base URL", Project.MSG_DEBUG); } // Render if "force" flag is set OR // OR output file doesn't exist OR // output file is older than input file if (task.getForce() || !outf.exists() || (f.lastModified() > outf.lastModified() )) { render(f, outf, rint); actioncount++; } else if (outf.exists() && (f.lastModified() <= outf.lastModified() )) { skippedcount++; } } } if (actioncount + skippedcount == 0) { task.log("No files processed. No files were selected by the filesets " + "and no fofile was set." , Project.MSG_WARN); } else if (skippedcount > 0) { task.log(skippedcount + " xslfo file(s) skipped (no change found" + " since last generation; set force=\"true\" to override)." , Project.MSG_INFO); } } private void render(File foFile, File outFile, int renderer) throws FOPException { InputHandler inputHandler = new FOFileHandler(foFile); OutputStream out = null; try { out = new java.io.FileOutputStream(outFile); } catch (Exception ex) { throw new BuildException("Failed to open " + outFile, ex); } if (task.getLogFiles()) { task.log(foFile + " -> " + outFile, Project.MSG_INFO); } try { Driver driver = new Driver(); setupLogger(driver); FOUserAgent userAgent = new FOUserAgent(); userAgent.setBaseURL(this.baseURL); userAgent.enableLogging(getLogger()); driver.setUserAgent(userAgent); driver.setRenderer(renderer); driver.setOutputStream(out); driver.render(inputHandler); } catch (Exception ex) { throw new BuildException(ex); } finally { try { out.close(); } catch (IOException ioe) { getLogger().error("Error closing output file", ioe); } } } }