com.openhtmltopdf.test.Regress Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of openhtmltopdf-core Show documentation
Show all versions of openhtmltopdf-core Show documentation
Open HTML to PDF is a CSS 2.1 renderer written in Java. This artifact contains the core rendering and layout code.
package com.openhtmltopdf.test;/*
* {{{ header & license
* Copyright (c) 2006 Patrick Wright
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
* }}}
*/
import com.openhtmltopdf.layout.LayoutContext;
import com.openhtmltopdf.render.Box;
import com.openhtmltopdf.swing.BoxRenderer;
import com.openhtmltopdf.swing.Java2DRenderer;
import com.openhtmltopdf.util.FSImageWriter;
import com.openhtmltopdf.util.IOUtil;
import com.openhtmltopdf.util.Zipper;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
/**
* Creates a directory containing output from the renderer as text, given a set of input files (XHTML, XML).
* These files serve as the reference image for a comparison with later runs of the renderer. For any input
* file _input_, the zip will contain _input_, _input.layout.txt_, _input.rendered.txt_ and _input.png_. To use,
* create an instance of Regress with input directory, output directory and width, then call {@link #snapshot()}.
*
* Regress regress = new Regress(sourceDir, outputDir, width);
* regress.snapshot();
*
* One the regress is done, you can use Zipper to pack it up:
*
* new Zipper(outputDir, outputZip).zipDirectory();
*
* Regress will try to render all files in the source directory; a failure to render one file (e.g. if an exception
* is thrown) will not stop the rendering process.
*
* Files in the source directory with the following extensions are included: htm, html, xht, xhtml, and xml.
*
* You can also run this from the command line, passing in the source directory as argument 1, and the output file
* name as argument 2. The output is created in the standard temp directory in a subdirectory called "reference". The
* path to the output directory will be printed to the console when complete.
*
* org.xhtmlrenderer.test.Regress ./regress/input-html/ ./regress/output.zip
*
*/
public class Regress {
public static final List EXTENSIONS = Arrays.asList(new String[]{"htm", "html", "xht", "xhtml", "xml",});
public static final String RENDER_SFX = ".render.txt";
public static final String LAYOUT_SFX = ".layout.txt";
public static final String PNG_SFX = ".png";
private static final String LINE_SEPARATOR = "\n"; // use one single sep to avoid OS problems when moving ref files
// reading from...
private final File sourceDir;
// writing generated files to...
private final File outputDir;
// width, in points, used to constrain layout
private final int width;
// total files processed
private int fileCount;
// count failed in this run
private int failedCount;
public static void main(String[] args) throws Exception {
final File sourceDir = getArgSourceDir(args);
final File outputDir = sourceDir;
final int width = 1024;
System.out.println("Running regression against files in " + sourceDir);
Regress regress = new Regress(sourceDir, outputDir, width);
regress.snapshot();
System.out.println("Ran regressions against " + regress.getFileCount() + " files in source directory; " + regress.getFailedCount() + " failed to generate");
}
/**
* Initialize to read from sourceDir and generate files to outputDir, using width points to constrain layout.
*
* @param sourceDir directory to read from
* @param outputDir directory to write to
* @param width width to constrain layou to
*/
public Regress(File sourceDir, File outputDir, int width) {
this.sourceDir = sourceDir;
this.outputDir = outputDir;
this.width = width;
}
private int getFailedCount() {
return failedCount;
}
private int getFileCount() {
return fileCount;
}
/**
* For all files in the input directory, attempts to render and output a textual box model and a PNG file in the
* output directory. Does not zip the contents of the output directory. Any single file that fails to render
* will not halt the process. Low-level IO errors will may, however, interrupt it. Both the file count and the
* failed count are reset on starting; on finishing those values can be read through their respective accessors
* in this class.
*
* @throws IOException on reading contents or writing output
*/
public void snapshot() throws IOException {
fileCount = 0;
failedCount = 0;
final boolean wasLogging = enableLogging(false);
try {
Iterator iter = listInputFiles(sourceDir);
while (iter.hasNext()) {
File file = (File) iter.next();
saveBoxModel(file, outputDir, width);
saveImage(file, outputDir, width);
}
} finally {
enableLogging(wasLogging);
}
}
private void saveImage(File page, File outputDir, int width) throws IOException {
try {
Java2DRenderer j2d = new Java2DRenderer(page, width);
// this renders and returns the image, which is stored in the J2R; will not
// be re-rendered, calls to getImage() return the same instance
BufferedImage img = j2d.getImage();
// write it out, full size, PNG
// FSImageWriter instance can be reused for different ../images,
// defaults to PNG
FSImageWriter imageWriter = new FSImageWriter();
final File outputFile = new File(outputDir, page.getName() + ".png");
if (outputFile.exists() && !outputFile.delete()) {
throw new RuntimeException(
"On rendering image, could not delete new output file (.delete failed) " +
outputFile.getAbsolutePath()
);
}
String fileName = outputFile.getPath();
imageWriter.write(img, fileName);
} catch (Exception e) {
System.err.println("Could not render input file to image, skipping: " + page + " err: " + e.getMessage());
}
}
private void saveBoxModel(File page, File outputDir, int width) throws IOException {
BoxRenderer renderer = new BoxRenderer(page, width);
Box box;
try {
box = renderer.render();
} catch (Exception e) {
System.err.println("Could not render input file, skipping: " + page + " err: " + e.getMessage());
failedCount++;
return;
}
LayoutContext layoutContext = renderer.getLayoutContext();
String inputFileName = page.getName();
writeToFile(outputDir, inputFileName + RENDER_SFX, box.dump(layoutContext, "", Box.DUMP_RENDER));
writeToFile(outputDir, inputFileName + LAYOUT_SFX, box.dump(layoutContext, "", Box.DUMP_LAYOUT));
fileCount++;
}
private void writeToFile(File outputDir, String fileName, String output) throws IOException {
final File outputFile = new File(outputDir, fileName);
if (outputFile.exists() && !outputFile.delete()) {
throw new RuntimeException(
"On rendering, could not delete new output file (.delete failed) " + outputFile.getAbsolutePath()
);
}
FileOutputStream fos = new FileOutputStream(outputFile);
try {
OutputStreamWriter fw = new OutputStreamWriter(fos, "UTF-8");
PrintWriter pw = new PrintWriter(new BufferedWriter(fw));
try {
pw.print(output);
pw.print(LINE_SEPARATOR);
pw.flush();
} finally {
try {
pw.close();
} catch (Exception e) {
// swallow
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fos.close();
} catch (IOException e) {
// swallow
}
}
}
private static File getArgOutputZipFile(String[] args) throws IOException {
if (args.length < 2) {
usageAndExit("Need file name which will contain rendered files as a Zip.");
}
String path = args[1];
File file = new File(path);
final File parentFile = file.getAbsoluteFile().getParentFile();
if (!parentFile.exists()) {
usageAndExit("Output directory not found: " + parentFile.getPath());
}
if (file.exists() && !file.delete()) {
usageAndExit("Failed to .delete output Zip file " + file.getAbsoluteFile());
}
if (!file.createNewFile()) {
usageAndExit("Failed to create output Zip file " + file.getAbsoluteFile());
}
return file;
}
private static File getArgSourceDir(String[] args) {
if (args.length < 1) {
usageAndExit("Need directory name containing input files to render.");
}
String sourceDirPath = args[0];
File sourceDir = new File(sourceDirPath);
if (!sourceDir.exists()) {
usageAndExit("Source directory not found: " + sourceDirPath);
}
return sourceDir;
}
private boolean enableLogging(final boolean isEnabled) {
final String prop = "xr.util-logging.loggingEnabled";
final boolean orgVal = Boolean.valueOf(System.getProperty(prop)).booleanValue();
System.setProperty(prop, Boolean.valueOf(isEnabled).toString());
return orgVal;
}
private Iterator listInputFiles(final File sourceDir) {
File[] f = sourceDir.listFiles(new FilenameFilter() {
public boolean accept(File file, String s) {
return EXTENSIONS.contains(s.substring(s.lastIndexOf(".") + 1));
}
});
return Arrays.asList(f).iterator();
}
private static void usageAndExit(String msg) {
System.err.println(msg);
System.exit(-1);
}
}