blob: ed86b9a380bb2998f7f51ecc0b79cfebb7e7c16d [file] [log] [blame]
/*******************************************************************************
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*******************************************************************************/
package org.ofbiz.webapp.view;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.URIResolver;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamSource;
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.Fop;
import org.apache.fop.apps.FopFactory;
import org.apache.fop.apps.MimeConstants;
import org.ofbiz.base.container.ClassLoaderContainer;
import org.ofbiz.base.location.FlexibleLocation;
import org.ofbiz.base.util.Debug;
import org.ofbiz.base.util.FileUtil;
import org.ofbiz.base.util.UtilProperties;
import org.ofbiz.base.util.UtilValidate;
/**
* Apache FOP worker class.
*/
public class ApacheFopWorker {
public static final String module = ApacheFopWorker.class.getName();
/** File name prefix used for temporary files. Currently set to
* <code>org.ofbiz.webapp.view.ApacheFopWorker-</code>.
*/
public static final String tempFilePrefix = "org.ofbiz.webapp.view.ApacheFopWorker-";
protected static FopFactory fopFactory = null;
/** Returns an instance of the FopFactory class. FOP documentation recommends
* the reuse of the factory instance because of the startup time.
* @return FopFactory The FopFactory instance
*/
public static FopFactory getFactoryInstance() {
if (fopFactory == null) {
synchronized (ApacheFopWorker.class) {
if (fopFactory != null) {
return fopFactory;
}
// Create the factory
fopFactory = FopFactory.newInstance();
// Limit the validation for backwards compatibility
fopFactory.setStrictValidation(false);
try {
String ofbizHome = System.getProperty("ofbiz.home");
String fopPath = UtilProperties.getPropertyValue("fop.properties", "fop.path", ofbizHome + "/framework/webapp/config");
File userConfigFile = FileUtil.getFile(fopPath + "/fop.xconf");
if (userConfigFile.exists()) {
fopFactory.setUserConfig(userConfigFile);
URL baseUrl = new URL(fopFactory.getBaseURL());
String protocol = baseUrl.getProtocol();
String host = baseUrl.getHost();
Integer baseport = baseUrl.getPort();
Integer port = baseport + ClassLoaderContainer.portOffset;
fopFactory.setBaseURL(protocol + "://" + host + ":" + port);
} else {
Debug.logWarning("FOP configuration file not found: " + userConfigFile, module);
}
String fopFontBaseProperty = UtilProperties.getPropertyValue("fop.properties", "fop.font.base.url", ofbizHome + "/framework/webapp/config/");
File fontBaseFile = FileUtil.getFile(fopFontBaseProperty);
if (fontBaseFile.isDirectory()) {
fopFactory.getFontManager().setFontBaseURL(fontBaseFile.toURI().toURL().toString());
} else {
Debug.logWarning("FOP font base URL not found: " + fontBaseFile, module);
}
Debug.logInfo("FOP FontBaseURL: " + fopFactory.getFontManager().getFontBaseURL(), module);
} catch (Exception e) {
Debug.logWarning(e, "Error reading FOP configuration: ", module);
}
}
}
return fopFactory;
}
/** Transform an xsl-fo file to the specified file format.
* @param srcFile The xsl-fo File instance
* @param destFile The target (result) File instance
* @param stylesheetFile Optional stylesheet File instance
* @param outputFormat Optional output format, defaults to "application/pdf"
*/
public static void transform(File srcFile, File destFile, File stylesheetFile, String outputFormat) throws IOException, FOPException {
StreamSource src = new StreamSource(srcFile);
StreamSource stylesheet = stylesheetFile == null ? null : new StreamSource(stylesheetFile);
BufferedOutputStream dest = new BufferedOutputStream(new FileOutputStream(destFile));
Fop fop = createFopInstance(dest, outputFormat);
if (fop.getUserAgent().getBaseURL() == null) {
String baseURL = null;
try {
File parentFile = new File(srcFile.getAbsolutePath()).getParentFile();
baseURL = parentFile.toURI().toURL().toExternalForm();
} catch (Exception e) {
baseURL = "";
}
fop.getUserAgent().setBaseURL(baseURL);
}
transform(src, stylesheet, fop);
dest.close();
}
/** Transform an xsl-fo InputStream to the specified OutputStream format.
* @param srcStream The xsl-fo InputStream instance
* @param destStream The target (result) OutputStream instance
* @param stylesheetStream Optional stylesheet InputStream instance
* @param outputFormat Optional output format, defaults to "application/pdf"
*/
public static void transform(InputStream srcStream, OutputStream destStream, InputStream stylesheetStream, String outputFormat) throws FOPException {
StreamSource src = new StreamSource(srcStream);
StreamSource stylesheet = stylesheetStream == null ? null : new StreamSource(stylesheetStream);
Fop fop = createFopInstance(destStream, outputFormat);
transform(src, stylesheet, fop);
}
/** Transform an xsl-fo StreamSource to the specified output format.
* @param src The xsl-fo StreamSource instance
* @param stylesheet Optional stylesheet StreamSource instance
* @param fop
*/
public static void transform(StreamSource src, StreamSource stylesheet, Fop fop) throws FOPException {
Result res = new SAXResult(fop.getDefaultHandler());
try {
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer;
if (stylesheet == null) {
transformer = factory.newTransformer();
} else {
transformer = factory.newTransformer(stylesheet);
}
transformer.setURIResolver(new LocalResolver(transformer.getURIResolver()));
transformer.transform(src, res);
} catch (Exception e) {
throw new FOPException(e);
}
}
/** Returns a new Fop instance. Note: FOP documentation recommends using
* a Fop instance for one transform run only.
* @param out The target (result) OutputStream instance
* @param outputFormat Optional output format, defaults to "application/pdf"
* @return Fop instance
*/
public static Fop createFopInstance(OutputStream out, String outputFormat) throws FOPException {
if (UtilValidate.isEmpty(outputFormat)) {
outputFormat = MimeConstants.MIME_PDF;
}
FopFactory fopFactory = getFactoryInstance();
FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
Fop fop;
if (out != null) {
fop = fopFactory.newFop(outputFormat, foUserAgent, out);
} else {
fop = fopFactory.newFop(outputFormat, foUserAgent);
}
return fop;
}
/** Returns a temporary File instance. The temporary file name starts with
* <a href="#tempFilePrefix">tempFilePrefix</a> and ends with ".xml".
* Calling methods are responsible for deleting the temporary file.<p>
* FOP performs transforms in memory, so if there is any chance FO output
* will be more than a few pages, it would be best to keep FO input in a temporary
* file.</p>
* @return File instance
*/
public static File createTempFoXmlFile() throws IOException {
File tempXmlFile = File.createTempFile(tempFilePrefix, ".xml");
tempXmlFile.deleteOnExit();
return tempXmlFile;
}
/** Returns a temporary File instance. The temporary file name starts with
* <a href="#tempFilePrefix">tempFilePrefix</a> and ends with ".res".
* Calling methods are responsible for deleting the temporary file.<p>
* FOP performs transforms in memory, so if there is any chance FO output
* will be more than a few pages, it would be best to keep FO output in a temporary
* file.</p>
* @return File instance
*/
public static File createTempResultFile() throws IOException {
File tempResultFile = File.createTempFile(tempFilePrefix, ".res");
tempResultFile.deleteOnExit();
return tempResultFile;
}
/** Local URI resolver for the Transformer class.
*/
public static class LocalResolver implements URIResolver {
private URIResolver defaultResolver;
protected LocalResolver() {}
public LocalResolver(URIResolver defaultResolver) {
this.defaultResolver = defaultResolver;
}
public Source resolve(String href, String base) throws TransformerException {
URL locationUrl = null;
try {
locationUrl = FlexibleLocation.resolveLocation(href);
if (locationUrl != null) {
return new StreamSource(locationUrl.openStream());
}
} catch (Exception e) {
throw new TransformerException(e.getMessage());
}
return defaultResolver.resolve(href, base);
}
}
}