package de.ralfebert.imageassert;
import static org.junit.Assert.fail;
import java.awt.GraphicsEnvironment;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import org.apache.commons.io.IOUtils;
import de.ralfebert.imageassert.compare.ICompareResultHandler;
import de.ralfebert.imageassert.compare.Page;
import de.ralfebert.imageassert.compare.junit.JUnitCompareResultHandler;
import de.ralfebert.imageassert.compare.swing.SwingCompareResultHandler;
import de.ralfebert.imageassert.pageimage.IPdfImageSplitter;
import de.ralfebert.imageassert.pageimage.PdfRendererImageSplitter;
import de.ralfebert.imageassert.utils.RuntimeIOException;
import de.ralfebert.imageassert.utils.TemporaryFolder;
/**
* ImageAssert compares two PDF documents in JUnit tests.
*
* It creates images for every page, by default using the pdf-renderer library,
* but it can also call ImageMagick or XPDf (see setPdfImageSplitter).
*
* The images are compared pixel-by-pixel. A Swing compare dialog is shown if
* not running with '-Djava.awt.headless=true'. You can also implement your own
* way to show the results, see setCompareResultHandler.
*
* Usage example:
*
* <pre>
* ImageAssert imageAssert = new ImageAssert();
* imageAssert.assertPdfEquals(SomeClass.class.getResourceAsStream("expected.pdf")),
* new FileInputStream(actualPdfFile));
* </pre>
*
* @author Ralf Ebert
*/
public class ImageAssert {
private ICompareResultHandler compareResultHandler = new JUnitCompareResultHandler();
private IPdfImageSplitter pdfImageSplitter = new PdfRendererImageSplitter();
public ImageAssert() {
this(!GraphicsEnvironment.isHeadless());
}
public ImageAssert(boolean showCompareDialog) {
if (showCompareDialog) {
setCompareResultHandler(new SwingCompareResultHandler());
}
}
private void assertImageEquals(Page expectedImage, Page actualImage) {
boolean equal = Arrays.equals(extractPixels(expectedImage.getImage()),
extractPixels(actualImage.getImage()));
if (!equal) {
compareResultHandler.onImageNotEqual(expectedImage, actualImage);
}
}
private int[] extractPixels(BufferedImage image) {
return image.getRaster().getPixels(0, 0, image.getWidth(), image.getHeight(), (int[]) null);
}
public void assertPdfEquals(InputStream expected, InputStream actual) {
TemporaryFolder temporaryFolder = new TemporaryFolder(this);
try {
Page[] expectedPages = extractPages(expected, "expected.pdf", temporaryFolder);
Page[] actualPages = extractPages(actual, "actual.pdf", temporaryFolder);
if (expectedPages.length <= 0)
fail("No pages in expected PDF!");
for (int i = 0; i < expectedPages.length; i++) {
Page expectedPage = expectedPages[i];
if (i >= actualPages.length) {
fail(String.format("PDF has not enough pages: was %d, expected %d",
actualPages.length, expectedPages.length));
}
Page actualPage = actualPages[i];
assertImageEquals(expectedPage, actualPage);
}
if (actualPages.length != expectedPages.length) {
fail(String.format("PDF has too many pages: was %d, expected %d",
actualPages.length, expectedPages.length));
}
} finally {
temporaryFolder.dispose();
}
}
private Page[] extractPages(InputStream pdfStream, String pdfName, TemporaryFolder temp) {
pdfImageSplitter.setTemporaryFolder(temp);
File actualFile = temp.createFile(pdfName);
FileOutputStream output = null;
try {
output = new FileOutputStream(actualFile);
IOUtils.copy(pdfStream, output);
} catch (IOException e) {
throw new RuntimeIOException(e);
} finally {
IOUtils.closeQuietly(output);
}
return pdfImageSplitter.convert(actualFile);
}
public void setCompareResultHandler(ICompareResultHandler compareResultHandler) {
this.compareResultHandler = compareResultHandler;
}
public void setPdfImageSplitter(IPdfImageSplitter pdfImageSplitter) {
this.pdfImageSplitter = pdfImageSplitter;
}
}