git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@232300 13f79535-47bb-0310-9956-ffa450edef68pull/31/head
@@ -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(); | |||
} | |||
} | |||
} |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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 | |||
} | |||
} |
@@ -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(); | |||
} | |||
} | |||
} |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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); | |||
} |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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(); | |||
} |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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; | |||
} | |||
} | |||
} |