package html;
import java.io.File;
import java.io.IOException;
import java.lang.ProcessBuilder.Redirect;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import com.google.common.collect.Lists;
import com.google.common.io.Files;
public class HtmlToPdf {
private static final String CMD_WEASYPRINT = "weasyprint";
public TempFiles toPdf(String name, String html, String playSession, String copts) {
String filename = FilenameUtils.removeExtension(name)+".html";
File htmlFile = new HtmlZip().toFolder(Files.createTempDir(), filename, html, playSession);
File pdfFile = pdf(htmlFile, copts);
return new TempFiles(pdfFile, pdfFile.getParentFile(), htmlFile.getParentFile());
}
private File pdf(File in, String copts) {
try {
File destfolder = Files.createTempDir();
File out = new File(destfolder, FilenameUtils.removeExtension(in.getName())+".pdf");
ProcessBuilder pb = converter(copts, in, out);
pb.directory(in.getParentFile());
File log = new File(destfolder, "converter.log");
pb.redirectErrorStream(true);
pb.redirectOutput(Redirect.to(log));
Process p = pb.start();
// TODO do not wait forever
int es = p.waitFor();
if(out.exists()) {
return out;
} else {
String msg = "pdf creation failed with exit status "+es;
if(log.exists()) {
throw new RuntimeException(msg+"\n"+FileUtils.readFileToString(log)+
"\nfrom "+log.getAbsolutePath());
} else {
throw new RuntimeException(msg+" "+destfolder.getAbsolutePath());
}
}
} catch(Exception e) {
throw new RuntimeException(String.format("failed to create pdf from %s",in.getName()),e);
}
}
private ProcessBuilder converter(String copts, File in, File out) throws IOException {
File css = new File(in.getParentFile(), "print.css");
FileUtils.copyInputStreamToFile(this.getClass().getResourceAsStream("weasyprint.css"), css);
return new ProcessBuilder(cmd(CMD_WEASYPRINT, "--stylesheet", css.getAbsolutePath(),
options(copts), in.getName(), out.getAbsolutePath()));
}
private List<String> cmd(Object... params) {
List<String> cmd = Lists.newArrayList();
for(Object o : params) {
if(o instanceof String) {
cmd.add((String)o);
} else if(o instanceof List<?>) {
for(Object o2 : (List<?>)o) {
if(o2 instanceof String) {
cmd.add((String)o2);
} else {
throw new RuntimeException();
}
}
} else {
throw new RuntimeException();
}
}
return cmd;
}
private List<String> options(String copts) {
return split(copts);
}
//from http://stackoverflow.com/a/366532
private List<String> split(String copts) {
List<String> matchList = Lists.newArrayList();
Pattern regex = Pattern.compile("[^\\s\"']+|\"([^\"]*)\"|'([^']*)'");
Matcher regexMatcher = regex.matcher(copts);
while (regexMatcher.find()) {
if (regexMatcher.group(1) != null) {
// Add double-quoted string without the quotes
matchList.add(regexMatcher.group(1));
} else if (regexMatcher.group(2) != null) {
// Add single-quoted string without the quotes
matchList.add(regexMatcher.group(2));
} else {
// Add unquoted word
matchList.add(regexMatcher.group());
}
}
return matchList;
}
}