Browse Source

Initial upload of the visual testing facility. Docs in code. A Wiki page will follow shortly.

git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@232300 13f79535-47bb-0310-9956-ffa450edef68
pull/31/head
Jeremias Maerki 19 years ago
parent
commit
178ec4a3ad

+ 50
- 0
test/java/org/apache/fop/visual/AbstractBitmapProducer.java View File

@@ -0,0 +1,50 @@
/*
* Copyright 2005 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.visual;

import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
* Abstract base class for converters.
*/
public abstract class AbstractBitmapProducer implements BitmapProducer {

/** Logger */
protected static Log log = LogFactory.getLog(AbstractBitmapProducer.class);
/**
* Returns a new JAXP Transformer based on information in the ProducerContext.
* @param context context information for the process
* @return a new Transformer instance (identity or set up with a stylesheet)
* @throws TransformerConfigurationException in case creating the Transformer fails.
*/
protected Transformer getTransformer(ProducerContext context)
throws TransformerConfigurationException {
if (context.getTemplates() != null) {
return context.getTemplates().newTransformer();
} else {
return context.getTransformerFactory().newTransformer();
}
}
}

+ 153
- 0
test/java/org/apache/fop/visual/AbstractPSPDFBitmapProducer.java View File

@@ -0,0 +1,153 @@
/*
* Copyright 2005 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.visual;

import java.awt.image.BufferedImage;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.text.MessageFormat;

import javax.xml.transform.Transformer;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamSource;

import org.apache.avalon.framework.configuration.Configurable;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.commons.io.IOUtils;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.Fop;

/**
* BitmapProducer implementation that uses the PS or PDF renderer and an external converter
* to create bitmaps.
* <p>
* Here's what the configuration element looks like for the class:
* <p>
* <pre>
* <producer classname="<concrete class>">
* <converter>mypdf2bmp {0} {1} {2}</converter>
* <delete-temp-files>false</delete-temp-files>
* </producer>
* </pre>
* <p>
* You will notice the three parameters in curly braces (java.util.MessageFormat style) to the
* converter call:
* <ul>
* <li>0: the input file
* <li>1: the output bitmap file
* <li>2: the requested resolution in dpi for the generated bitmap
* </ul>
* <p>
* The "delete-temp-files" element is optional and defaults to true.
*/
public abstract class AbstractPSPDFBitmapProducer extends AbstractBitmapProducer
implements Configurable {

private String converter;
private boolean deleteTempFiles;
/** @see org.apache.avalon.framework.configuration.Configurable */
public void configure(Configuration cfg) throws ConfigurationException {
this.converter = cfg.getChild("converter").getValue();
this.deleteTempFiles = cfg.getChild("delete-temp-files").getValueAsBoolean(true);
}

/**
* Calls an external converter to convert the file generated by FOP to a bitmap.
* @param inFile the generated output file to be converted
* @param outFile the target filename for the bitmap
* @param context context information (required bitmap resolution etc.)
* @throws IOException in case the conversion fails
*/
public void convert(File inFile, File outFile, ProducerContext context) throws IOException {
outFile.delete();
//Build command-line
String cmd = MessageFormat.format(converter,
new Object[] {inFile.toString(), outFile.toString(),
Integer.toString(context.getResolution())});
ConvertUtils.convert(cmd, null, null, log);

if (!outFile.exists()) {
throw new IOException("The target file has not been generated");
}
}
/**
* @return the native extension generated by the output format, ex. "ps" or "pdf".
*/
protected abstract String getTargetExtension();
/**
* @return the output format constant for the FOP renderer, i.e. one of Constants.RENDER_*.
*/
protected abstract int getTargetFormat();
/** @see org.apache.fop.visual.BitmapProducer */
public BufferedImage produce(File src, ProducerContext context) {
try {
FOUserAgent userAgent = new FOUserAgent();
userAgent.setResolution(context.getResolution());
userAgent.setBaseURL(src.getParentFile().toURL().toString());

File tempOut = new File(context.getTargetDir(),
src.getName() + "." + getTargetExtension());
File tempPNG = new File(context.getTargetDir(),
src.getName() + "." + getTargetExtension() + ".png");
try {
OutputStream out = new FileOutputStream(tempOut);
out = new BufferedOutputStream(out);
try {
Fop fop = new Fop(getTargetFormat(), userAgent);
fop.setOutputStream(out);
SAXResult res = new SAXResult(fop.getDefaultHandler());
Transformer transformer = getTransformer(context);
transformer.transform(new StreamSource(src), res);
} finally {
IOUtils.closeQuietly(out);
}
convert(tempOut, tempPNG, context);
BufferedImage img = BitmapComparator.getImage(tempPNG);
return img;
} finally {
if (deleteTempFiles) {
if (!tempOut.delete()) {
log.warn("Can't delete " + tempOut);
tempOut.deleteOnExit();
}
if (!tempPNG.delete()) {
log.warn("Can't delete " + tempPNG);
tempPNG.deleteOnExit();
}
}
}
} catch (Exception e) {
e.printStackTrace();
log.error(e);
return null;
}
}

}

+ 38
- 0
test/java/org/apache/fop/visual/AbstractRedirectorLineHandler.java View File

@@ -0,0 +1,38 @@
/*
* Copyright 2005 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.visual;

/**
* Abstract base implementation for a RedirectorLineHandler which provides empty notifyStart()
* and notifyEnd() methods.
*/
public abstract class AbstractRedirectorLineHandler
implements RedirectorLineHandler {

/** @see org.apache.fop.visual.RedirectorLineHandler#notifyStart() */
public void notifyStart() {
//nop
}

/** @see org.apache.fop.visual.RedirectorLineHandler#notifyEnd() */
public void notifyEnd() {
//nop
}

}

+ 259
- 0
test/java/org/apache/fop/visual/BatchDiffer.java View File

@@ -0,0 +1,259 @@
/*
* Copyright 2005 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.visual;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Collection;
import java.util.Iterator;

import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.stream.StreamSource;

import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder;
import org.apache.avalon.framework.container.ContainerUtil;

import org.apache.batik.ext.awt.image.codec.PNGEncodeParam;
import org.apache.batik.ext.awt.image.codec.PNGImageEncoder;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.filefilter.IOFileFilter;
import org.apache.commons.io.filefilter.SuffixFileFilter;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.apache.fop.layoutengine.LayoutEngineTestSuite;

import org.xml.sax.SAXException;

/**
* This class is used to visually diff bitmap images created through various sources.
* <p>
* Here's what the configuration format looks like:
* <p>
* <pre>
* <batch-diff>
* <source-directory>C:/Dev/FOP/trunk/test/layoutengine</source-directory>
* <filter-disabled>false</filter-disabled>
* <max-files>10</max-files>
* <target-directory>C:/Temp/diff-out</target-directory>
* <resolution>100</resolution>
* <stylesheet>C:/Dev/FOP/trunk/test/layoutengine/testcase2fo.xsl</stylesheet>
* <producers>
* <producer classname="org.apache.fop.visual.BitmapProducerJava2D">
* <delete-temp-files>false</delete-temp-files>
* </producer>
* <producer classname="org.apache.fop.visual.ReferenceBitmapLoader">
* <directory>C:/Temp/diff-bitmaps</directory>
* </producer>
* </producers>
* </batch-diff>
* </pre>
* <p>
* The optional "filter-disabled" element determines whether the source files should be filtered
* using the same "disabled-testcases.txt" file used for the layout engine tests. Default: true
* <p>
* The optional "max-files" element controls how many files at maximum should be processed.
* Default is to process all the files found.
* <p>
* The optional "resolution" element controls the requested bitmap resolution in dpi for the
* generated bitmaps. Defaults to 72dpi.
* <p>
* The optional "stylesheet" element allows you to supply an XSLT stylesheet to preprocess all
* source files with. Default: no stylesheet, identity transform.
* <p>
* The "producers" element contains a list of producer implementations with configuration.
* The "classname" attribute specifies the fully qualified class name for the implementation.
*/
public class BatchDiffer {

/** Logger */
protected static Log log = LogFactory.getLog(BatchDiffer.class);

/**
* Prints the usage of this app to stdout.
*/
public static void printUsage() {
System.out.println("Usage:");
System.out.println("java " + BatchDiffer.class.getName() + " <cfgfile>");
System.out.println();
System.out.println("<cfgfile>: Path to an XML file with the configuration"
+ " for the batch run.");
}

/**
* Saves a BufferedImage as a PNG file.
* @param bitmap the bitmap to encode
* @param outputFile the target file
* @throws IOException in case of an I/O problem
*/
public static void saveAsPNG(BufferedImage bitmap, File outputFile) throws IOException {
OutputStream out = new FileOutputStream(outputFile);
try {
PNGImageEncoder encoder = new PNGImageEncoder(
out,
PNGEncodeParam.getDefaultEncodeParam(bitmap));
encoder.encode(bitmap);
} finally {
IOUtils.closeQuietly(out);
}
}
/**
* Runs the batch.
* @param cfgFile configuration file to use
* @throws ConfigurationException In case of a problem with the configuration
* @throws SAXException In case of a problem during SAX processing
* @throws IOException In case of a I/O problem
*/
public void runBatch(File cfgFile)
throws ConfigurationException, SAXException, IOException {
DefaultConfigurationBuilder cfgBuilder = new DefaultConfigurationBuilder();
Configuration cfg = cfgBuilder.buildFromFile(cfgFile);
runBatch(cfg);
}

/**
* Runs the batch
* @param cfg Configuration for the batch
* @throws TransformerConfigurationException
*/
public void runBatch(Configuration cfg) {
try {
ProducerContext context = new ProducerContext();
context.setResolution(cfg.getChild("resolution").getValueAsInteger(72));
String xslt = cfg.getChild("stylesheet").getValue(null);
if (xslt != null) {
try {
context.setTemplates(context.getTransformerFactory().newTemplates(
new StreamSource(xslt)));
} catch (TransformerConfigurationException e) {
throw new RuntimeException("Error setting up stylesheet", e);
}
}
BitmapProducer[] producers = getProducers(cfg.getChild("producers"));
//Set up directories
File srcDir = new File(cfg.getChild("source-directory").getValue());
if (!srcDir.exists()) {
throw new RuntimeException("source-directory does not exist: " + srcDir);
}
File targetDir = new File(cfg.getChild("target-directory").getValue());
targetDir.mkdirs();
if (!targetDir.exists()) {
throw new RuntimeException("target-directory is invalid: " + targetDir);
}
context.setTargetDir(targetDir);
//RUN!
BufferedImage[] bitmaps = new BufferedImage[producers.length];
IOFileFilter filter = new SuffixFileFilter(new String[] {".xml", ".fo"});
//Same filtering as in layout engine tests
if (cfg.getChild("filter-disabled").getValueAsBoolean(true)) {
filter = LayoutEngineTestSuite.decorateWithDisabledList(filter);
}

int maxfiles = cfg.getChild("max-files").getValueAsInteger(-1);
Collection files = FileUtils.listFiles(srcDir, filter, null);
Iterator i = files.iterator();
while (i.hasNext()) {
File f = (File)i.next();
log.info("---=== " + f + " ===---");
for (int j = 0; j < producers.length; j++) {
bitmaps[j] = producers[j].produce(f, context);
}
//Create combined image
if (bitmaps[0] == null) {
throw new RuntimeException("First producer didn't return a bitmap."
+ " Cannot continue.");
}
BufferedImage combined = BitmapComparator.buildCompareImage(bitmaps);
//Save combined bitmap as PNG file
File outputFile = new File(targetDir, f.getName() + "._combined.png");
saveAsPNG(combined, outputFile);

for (int k = 1; k < bitmaps.length; k++) {
BufferedImage diff = BitmapComparator.buildDiffImage(bitmaps[0], bitmaps[k]);
outputFile = new File(targetDir, f.getName() + "._diff" + k + ".png");
saveAsPNG(diff, outputFile);
}
maxfiles = (maxfiles < 0 ? maxfiles : maxfiles - 1);
if (maxfiles == 0) {
break;
}
}
} catch (IOException ioe) {
log.error("I/O problem while processing", ioe);
throw new RuntimeException("I/O problem: " + ioe.getMessage());
} catch (ConfigurationException e) {
log.error("Error while configuring BatchDiffer", e);
throw new RuntimeException("Error while configuring BatchDiffer: " + e.getMessage());
}
}
private BitmapProducer[] getProducers(Configuration cfg) {
Configuration[] children = cfg.getChildren("producer");
BitmapProducer[] producers = new BitmapProducer[children.length];
for (int i = 0; i < children.length; i++) {
try {
Class clazz = Class.forName(children[i].getAttribute("classname"));
producers[i] = (BitmapProducer)clazz.newInstance();
ContainerUtil.configure(producers[i], children[i]);
} catch (Exception e) {
throw new RuntimeException("Error while setting up producers", e);
}
}
return producers;
}
/**
* Main method.
* @param args command-line arguments
*/
public static void main(String[] args) {
try {
if (args.length == 0) {
System.err.println("Configuration file is missing!");
printUsage();
System.exit(-1);
}
File cfgFile = new File(args[0]);
if (!cfgFile.exists()) {
System.err.println("Configuration file cannot be found: " + args[0]);
printUsage();
System.exit(-1);
}
BatchDiffer differ = new BatchDiffer();
differ.runBatch(cfgFile);
} catch (Exception e) {
e.printStackTrace();
}
}
}

+ 219
- 0
test/java/org/apache/fop/visual/BitmapComparator.java View File

@@ -0,0 +1,219 @@
/*
* Copyright 1999-2005 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.visual;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.RenderedImage;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;

import org.apache.batik.ext.awt.image.GraphicsUtil;
import org.apache.batik.ext.awt.image.renderable.Filter;
import org.apache.batik.ext.awt.image.spi.ImageTagRegistry;
import org.apache.batik.util.ParsedURL;
import org.apache.commons.io.IOUtils;

/**
* Helper class to visually compare two bitmap images.
* <p>
* This class was created by extracting reusable code from
* org.apache.batik.test.util.ImageCompareText (Apache Batik)
* into this separate class.
* <p>
* TODO Move as utility class to XML Graphics Commons when possible
*/
public class BitmapComparator {

/**
* Builds a new BufferedImage that is the difference between the two input images
* @param ref the reference bitmap
* @param gen the newly generated bitmap
* @return the diff bitmap
*/
public static BufferedImage buildDiffImage(BufferedImage ref,
BufferedImage gen) {
BufferedImage diff = new BufferedImage(ref.getWidth(),
ref.getHeight(),
BufferedImage.TYPE_INT_ARGB);
WritableRaster refWR = ref.getRaster();
WritableRaster genWR = gen.getRaster();
WritableRaster dstWR = diff.getRaster();

boolean refPre = ref.isAlphaPremultiplied();
if (!refPre) {
ColorModel cm = ref.getColorModel();
cm = GraphicsUtil.coerceData(refWR, cm, true);
ref = new BufferedImage(cm, refWR, true, null);
}
boolean genPre = gen.isAlphaPremultiplied();
if (!genPre) {
ColorModel cm = gen.getColorModel();
cm = GraphicsUtil.coerceData(genWR, cm, true);
gen = new BufferedImage(cm, genWR, true, null);
}


int w = ref.getWidth();
int h = ref.getHeight();

int y, i, val;
int [] refPix = null;
int [] genPix = null;
for (y = 0; y < h; y++) {
refPix = refWR.getPixels (0, y, w, 1, refPix);
genPix = genWR.getPixels (0, y, w, 1, genPix);
for (i = 0; i < refPix.length; i++) {
// val = ((genPix[i] - refPix[i]) * 5) + 128;
val = ((refPix[i] - genPix[i]) * 10) + 128;
if ((val & 0xFFFFFF00) != 0) {
if ((val & 0x80000000) != 0) {
val = 0;
} else {
val = 255;
}
}
genPix[i] = val;
}
dstWR.setPixels(0, y, w, 1, genPix);
}

if (!genPre) {
ColorModel cm = gen.getColorModel();
cm = GraphicsUtil.coerceData(genWR, cm, false);
}
if (!refPre) {
ColorModel cm = ref.getColorModel();
cm = GraphicsUtil.coerceData(refWR, cm, false);
}

return diff;
}

/**
* Builds a combined image that places a number of images next to each other for
* manual, visual comparison.
* @param images the array of bitmaps
* @return the combined image
*/
public static BufferedImage buildCompareImage(BufferedImage[] images) {
BufferedImage cmp = new BufferedImage(
images[0].getWidth() * images.length,
images[0].getHeight(), BufferedImage.TYPE_INT_ARGB);

Graphics2D g = cmp.createGraphics();
g.setPaint(Color.white);
g.fillRect(0, 0, cmp.getWidth(), cmp.getHeight());
int lastWidth = 0;
for (int i = 0; i < images.length; i++) {
if (lastWidth > 0) {
g.translate(lastWidth, 0);
}
if (images[i] != null) {
g.drawImage(images[i], 0, 0, null);
lastWidth = images[i].getWidth();
} else {
lastWidth = 20; //Maybe add a special placeholder image instead later
}
}
g.dispose();

return cmp;
}

/**
* Builds a combined image that places two images next to each other for
* manual, visual comparison.
* @param ref the reference image
* @param gen the actual image
* @return the combined image
*/
public static BufferedImage buildCompareImage(BufferedImage ref,
BufferedImage gen) {
return buildCompareImage(new BufferedImage[] {ref, gen});
}
/**
* Loads an image from a URL
* @param url the URL to the image
* @return the bitmap as BufferedImage
* TODO This method doesn't close the InputStream opened by the URL.
*/
public static BufferedImage getImage(URL url) {
ImageTagRegistry reg = ImageTagRegistry.getRegistry();
Filter filt = reg.readURL(new ParsedURL(url));
if (filt == null) {
return null;
}

RenderedImage red = filt.createDefaultRendering();
if (red == null) {
return null;
}
BufferedImage img = new BufferedImage(red.getWidth(),
red.getHeight(),
BufferedImage.TYPE_INT_ARGB);
red.copyData(img.getRaster());

return img;
}

/**
* Loads an image from a URL
* @param bitmapFile the bitmap file
* @return the bitmap as BufferedImage
*/
public static BufferedImage getImage(File bitmapFile) {
try {
InputStream in = new java.io.FileInputStream(bitmapFile);
try {
in = new java.io.BufferedInputStream(in);
ImageTagRegistry reg = ImageTagRegistry.getRegistry();
Filter filt = reg.readStream(in);
if (filt == null) {
return null;
}

RenderedImage red = filt.createDefaultRendering();
if (red == null) {
return null;
}
BufferedImage img = new BufferedImage(red.getWidth(),
red.getHeight(),
BufferedImage.TYPE_INT_ARGB);
red.copyData(img.getRaster());
return img;
} finally {
IOUtils.closeQuietly(in);
}
} catch (IOException e) {
return null;
}
}

}

+ 38
- 0
test/java/org/apache/fop/visual/BitmapProducer.java View File

@@ -0,0 +1,38 @@
/*
* Copyright 2005 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.visual;

import java.awt.image.BufferedImage;
import java.io.File;

/**
* Interface for a converter.
*/
public interface BitmapProducer {

/**
* Produces a BufferedImage from the source file by invoking the FO processor and
* converting the generated output file to a bitmap image if necessary.
* @param src the source FO or XML file
* @param context context information for the conversion
* @return the generated BufferedImage
*/
BufferedImage produce(File src, ProducerContext context);
}

+ 97
- 0
test/java/org/apache/fop/visual/BitmapProducerJava2D.java View File

@@ -0,0 +1,97 @@
/*
* Copyright 2005 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.visual;

import java.awt.image.BufferedImage;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;

import javax.xml.transform.Transformer;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamSource;

import org.apache.avalon.framework.configuration.Configurable;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.commons.io.IOUtils;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.Fop;
import org.apache.fop.fo.Constants;

/**
* BitmapProducer implementation that uses the Java2DRenderer to create bitmaps.
* <p>
* Here's what the configuration element looks like for the class:
* <p>
* <pre>
* <producer classname="org.apache.fop.visual.BitmapProducerJava2D">
* <delete-temp-files>false</delete-temp-files>
* </producer>
* </pre>
* <p>
* The "delete-temp-files" element is optional and defaults to true.
*/
public class BitmapProducerJava2D extends AbstractBitmapProducer implements Configurable {

private boolean deleteTempFiles;

/** @see org.apache.avalon.framework.configuration.Configurable */
public void configure(Configuration cfg) throws ConfigurationException {
this.deleteTempFiles = cfg.getChild("delete-temp-files").getValueAsBoolean(true);
}

/** @see org.apache.fop.visual.BitmapProducer */
public BufferedImage produce(File src, ProducerContext context) {
try {
FOUserAgent userAgent = new FOUserAgent();
userAgent.setResolution(context.getResolution());
userAgent.setBaseURL(src.getParentFile().toURL().toString());
File outputFile = new File(context.getTargetDir(), src.getName() + ".java2d.png");
OutputStream out = new FileOutputStream(outputFile);
out = new BufferedOutputStream(out);
try {
Fop fop = new Fop(Constants.RENDER_PNG, userAgent);
fop.setOutputStream(out);
SAXResult res = new SAXResult(fop.getDefaultHandler());
Transformer transformer = getTransformer(context);
transformer.transform(new StreamSource(src), res);
} finally {
IOUtils.closeQuietly(out);
}
BufferedImage img = BitmapComparator.getImage(outputFile);
if (deleteTempFiles) {
if (!outputFile.delete()) {
log.warn("Cannot delete " + outputFile);
outputFile.deleteOnExit();
}
}
return img;
} catch (Exception e) {
e.printStackTrace();
log.error(e);
return null;
}
}

}

+ 41
- 0
test/java/org/apache/fop/visual/BitmapProducerPDF.java View File

@@ -0,0 +1,41 @@
/*
* Copyright 2005 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.visual;

import org.apache.fop.fo.Constants;

/**
* BitmapProducer implementation that uses the PDFRenderer and an external converter
* to create bitmaps.
* <p>
* See the superclass' javadoc for info on the configuration format.
*/
public class BitmapProducerPDF extends AbstractPSPDFBitmapProducer {

/** @see org.apache.fop.visual.AbstractPSPDFBitmapProducer#getTargetExtension() */
protected String getTargetExtension() {
return "pdf";
}
/** @see org.apache.fop.visual.AbstractPSPDFBitmapProducer#getTargetFormat() */
protected int getTargetFormat() {
return Constants.RENDER_PDF;
}
}

+ 42
- 0
test/java/org/apache/fop/visual/BitmapProducerPS.java View File

@@ -0,0 +1,42 @@
/*
* Copyright 2005 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.visual;

import org.apache.fop.fo.Constants;

/**
* BitmapProducer implementation that uses the PSRenderer and an external converter
* to create bitmaps.
* <p>
* See the superclass' javadoc for info on the configuration format.
*/
public class BitmapProducerPS extends AbstractPSPDFBitmapProducer {

/** @see org.apache.fop.visual.AbstractPSPDFBitmapProducer#getTargetExtension() */
protected String getTargetExtension() {
return "ps";
}
/** @see org.apache.fop.visual.AbstractPSPDFBitmapProducer#getTargetFormat() */
protected int getTargetFormat() {
return Constants.RENDER_PS;
}

}

+ 85
- 0
test/java/org/apache/fop/visual/ConvertUtils.java View File

@@ -0,0 +1,85 @@
/*
* Copyright 2005 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.visual;

import java.io.File;
import java.io.IOException;

import org.apache.commons.logging.Log;

/**
* Utilities for converting files with external converters.
*/
public class ConvertUtils {

/**
* Calls an external converter application (GhostScript, for example).
* @param cmd the full command
* @param envp array of strings, each element of which has environment variable settings
* in format name=value.
* @param workDir the working directory of the subprocess, or null if the subprocess should
* inherit the working directory of the current process.
* @param log the logger to log output by the external application to
* @throws IOException in case the external call fails
*/
public static void convert(String cmd, String[] envp, File workDir, final Log log)
throws IOException {
log.debug(cmd);

Process process = null;
try {
process = Runtime.getRuntime().exec(cmd, envp, null);

//Redirect stderr output
RedirectorLineHandler errorHandler = new AbstractRedirectorLineHandler() {
public void handleLine(String line) {
log.error("ERR> " + line);
}
};
StreamRedirector errorRedirector
= new StreamRedirector(process.getErrorStream(), errorHandler);

//Redirect stdout output
RedirectorLineHandler outputHandler = new AbstractRedirectorLineHandler() {
public void handleLine(String line) {
log.debug("OUT> " + line);
}
};
StreamRedirector outputRedirector
= new StreamRedirector(process.getInputStream(), outputHandler);
new Thread(errorRedirector).start();
new Thread(outputRedirector).start();
process.waitFor();
} catch (java.lang.InterruptedException ie) {
throw new IOException("The call to the external converter failed: " + ie.getMessage());
} catch (java.io.IOException ioe) {
throw new IOException("The call to the external converter failed: " + ioe.getMessage());
}

int exitValue = process.exitValue();
if (exitValue != 0) {
throw new IOException("The call to the external converter failed. Result: "
+ exitValue);
}

}
}

+ 90
- 0
test/java/org/apache/fop/visual/ProducerContext.java View File

@@ -0,0 +1,90 @@
/*
* Copyright 2005 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.visual;

import java.io.File;

import javax.xml.transform.Templates;
import javax.xml.transform.TransformerFactory;

/**
* Context object for the bitmap production.
*/
public class ProducerContext {

private TransformerFactory tFactory;
private Templates templates;
private int resolution;
private File targetDir;
/**
* @return the TransformerFactory to be used.
*/
public TransformerFactory getTransformerFactory() {
if (tFactory == null) {
tFactory = TransformerFactory.newInstance();
}
return tFactory;
}
/**
* @return the requested bitmap resolution in dpi for all bitmaps.
*/
public int getResolution() {
return resolution;
}

/**
* Sets the requested bitmap resolution in dpi for all bitmaps.
* @param resolution the resolution in dpi
*/
public void setResolution(int resolution) {
this.resolution = resolution;
}

/**
* @return the XSLT stylesheet to preprocess the input files with.
*/
public Templates getTemplates() {
return templates;
}

/**
* Sets an optional XSLT stylesheet which is used to preprocess all input files with.
* @param templates the XSLT stylesheet
*/
public void setTemplates(Templates templates) {
this.templates = templates;
}
/**
* @return the target directory for all produced bitmaps
*/
public File getTargetDir() {
return targetDir;
}
/**
* Sets the target directory for all produced bitmaps.
* @param targetDir the target directory
*/
public void setTargetDir(File targetDir) {
this.targetDir = targetDir;
}
}

+ 41
- 0
test/java/org/apache/fop/visual/RedirectorLineHandler.java View File

@@ -0,0 +1,41 @@
/*
* Copyright 2005 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.visual;

/**
* This interface is used to redirect output from an external application elsewhere.
*/
public interface RedirectorLineHandler {

/**
* Called before the first handleLine() call.
*/
void notifyStart();

/**
* Called for each line of output to be processed.
* @param line a line of application output
*/
void handleLine(String line);
/**
* Called after the last handleLine() call.
*/
void notifyEnd();
}

+ 67
- 0
test/java/org/apache/fop/visual/ReferenceBitmapLoader.java View File

@@ -0,0 +1,67 @@
/*
* Copyright 2005 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.visual;

import java.awt.image.BufferedImage;
import java.io.File;

import org.apache.avalon.framework.configuration.Configurable;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;

/**
* BitmapProducer implementation that simply loads preproduced reference bitmaps from a
* certain directory.
* <p>
* Here's what the configuration element looks like for the class:
* <p>
* <pre>
* <producer classname="org.apache.fop.visual.ReferenceBitmapLoader">
* <directory>C:/Temp/ref-bitmaps</directory>
* </producer>
* </pre>
*/
public class ReferenceBitmapLoader extends AbstractBitmapProducer implements Configurable {

private File bitmapDirectory;
/** @see org.apache.avalon.framework.configuration.Configurable */
public void configure(Configuration cfg) throws ConfigurationException {
this.bitmapDirectory = new File(cfg.getChild("directory").getValue(null));
if (!bitmapDirectory.exists()) {
throw new ConfigurationException("Directory could not be found: " + bitmapDirectory);
}
}

/** @see org.apache.fop.visual.BitmapProducer */
public BufferedImage produce(File src, ProducerContext context) {
try {
File bitmap = new File(bitmapDirectory, src.getName() + ".png");
if (bitmap.exists()) {
return BitmapComparator.getImage(bitmap);
} else {
return null;
}
} catch (Exception e) {
log.error(e);
return null;
}
}

}

+ 81
- 0
test/java/org/apache/fop/visual/StreamRedirector.java View File

@@ -0,0 +1,81 @@
/*
* Copyright 2005 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.visual;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;

/**
* Redirects the content coming in through an InputStream using a separate thread to a
* RedirectorLineHandler instance. The default text encoding is used.
*/
public class StreamRedirector implements Runnable {

private InputStream in;
private RedirectorLineHandler handler;
private Exception exception;

/**
* @param in the InputStream to read the content from
* @param handler the handler that receives all the lines
*/
public StreamRedirector(InputStream in, RedirectorLineHandler handler) {
this.in = in;
this.handler = handler;
}
/**
* @return true if the run() method was terminated by an exception.
*/
public boolean hasFailed() {
return (this.exception != null);
}
/**
* @return the exception if the run() method was terminated by an exception, or null
*/
public Exception getException() {
return this.exception;
}

/** @see java.lang.Runnable#run() */
public void run() {
this.exception = null;
try {
Reader inr = new java.io.InputStreamReader(in);
BufferedReader br = new BufferedReader(inr);
if (handler != null) {
handler.notifyStart();
}
String line = null;
while ((line = br.readLine()) != null) {
if (handler != null) {
handler.handleLine(line);
}
}
if (handler != null) {
handler.notifyStart();
}
} catch (IOException ioe) {
this.exception = ioe;
}
}
}

Loading…
Cancel
Save