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