blob: c33c918c0271bdf82e96d8f0d92f7b83aad10d05 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package org.apache.camel.maven;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.net.MalformedURLException;
import java.net.URL;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Node;
import org.apache.camel.converter.jaxp.XmlConverter;
import org.apache.camel.dataformat.tagsoup.TidyMarkupDataFormat;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectHelper;
import org.codehaus.plexus.util.cli.CommandLineException;
import org.codehaus.plexus.util.cli.CommandLineUtils;
import org.codehaus.plexus.util.cli.Commandline;
import org.codehaus.plexus.util.cli.Commandline.Argument;
import org.codehaus.plexus.util.cli.StreamConsumer;
/**
* Goal which extracts the content div from the html page and converts to PDF
* using Prince
*
* @goal compile
* @phase compile
*/
public class HtmlToPdfMojo extends AbstractMojo {
/**
* The URL to the confluence page to convert.
*
* @parameter expression="${page}"
* @required
*/
private String page;
/**
* The output file name for the pdf.
*
* @parameter expression="${pdf}"
* default-value="${project.build.directory}/site/manual/${project.artifactId}-${project.version}.pdf"
*/
private String pdf;
/**
* The css style sheets that should be linked.
*
* @parameter
*/
private String[] styleSheets;
/**
* Content that should be added in the head element of the html file.
*
* @parameter
*/
private String head;
/**
* Regex to search for in the html file. This will be replaced with the value of the
* replaceValue parameter.
*
* @parameter
*/
private String replaceToken;
/**
* String that the replaceToken will be replaced with.
*
* @parameter
*/
private String replaceValue;
/**
* The first div with who's class matches the contentDivClass will be
* assumed to be the content section of the HTML and is what will be used as
* the content in the PDF.
*
* @parameter default-value="wiki-content"
*/
private String contentDivClass = "wiki-content";
/**
* Arguments that should be passed to the prince html to pdf processor.
*
* @parameter
*/
private String[] princeArgs;
/**
* If there is an error converting the HTML to PDF should the build fail?
* default to false since this requires the prince tool to be installed and
* on the PATH of the system.
*
* @parameter default-value="false"
*/
private boolean errorOnConverionFailure;
/**
* If there is an error downloading the HTML should the build fail? default
* to false since this usually requires the user to be online.
*
* @parameter default-value="false"
*/
private boolean errorOnDownloadFailure;
/**
* The maven project.
*
* @parameter expression="${project}"
* @required
* @readonly
*/
private MavenProject project;
/**
* @component
*/
private MavenProjectHelper projectHelper;
/**
* The type used when attaching the artifact to the deployment.
*
* @parameter default-value="pdf"
*/
private String type;
/**
* Classifier to add to the artifact generated.
*
* @parameter
*/
private String classifier;
public void execute() throws MojoExecutionException {
File outputDir = new File(pdf).getParentFile();
if (!outputDir.exists()) {
outputDir.mkdirs();
}
try {
// Download
String content = downloadContent();
if (content == null) {
// create dummy file so the build can continue
storeDummyFile();
return;
}
// Store
storeHTMLFile(content);
// Run Prince
if (convert() == 0) {
File pdfFile = new File(getPDFFileName());
projectHelper.attachArtifact(project, type, classifier, pdfFile);
}
} catch (MojoExecutionException e) {
throw e;
} catch (Exception e) {
throw new MojoExecutionException("Download of '" + page + "' failed: " + e.getMessage(), e);
}
}
private int convert() throws CommandLineException, MojoExecutionException {
getLog().info("Converting to PDF with prince...");
Commandline cl = new Commandline("prince");
Argument arg;
if (princeArgs != null) {
for (int i = 0; i < princeArgs.length; i++) {
arg = new Argument();
arg.setValue(princeArgs[i]);
cl.addArg(arg);
}
}
arg = new Argument();
arg.setValue(getHTMLFileName());
cl.addArg(arg);
arg = new Argument();
arg.setValue(getPDFFileName());
cl.addArg(arg);
StreamConsumer out = new StreamConsumer() {
public void consumeLine(String line) {
getLog().info("[prince] " + line);
}
};
getLog().info("About to execute PrinceXml (see www.princexml.com)");
String[] lines = cl.getCommandline();
StringBuffer buffer = new StringBuffer();
for (String line : lines) {
buffer.append(" ");
buffer.append(line);
}
getLog().info(buffer);
int rc = CommandLineUtils.executeCommandLine(cl, out, out);
if (rc == 0) {
getLog().info("Stored: " + getPDFFileName());
} else {
if (errorOnConverionFailure) {
throw new MojoExecutionException("PDF Conversion failed rc=" + rc);
} else {
getLog().error("PDF Conversion failed due to return code: " + rc);
}
}
return rc;
}
private String getPDFFileName() {
return pdf;
}
private void storeDummyFile() throws FileNotFoundException {
PrintWriter out = new PrintWriter(new BufferedOutputStream(new FileOutputStream(getHTMLFileName())));
out.println("<html>");
out.println("<body>Download of " + page + " failed</body>");
out.close();
getLog().info("Stored dummy file: " + getHTMLFileName() + " since download of " + page + " failed.");
}
private void storeHTMLFile(String content) throws FileNotFoundException {
PrintWriter out = new PrintWriter(new BufferedOutputStream(new FileOutputStream(getHTMLFileName())));
out.println("<html>");
out.println("<head>");
out.println(" <base href=\"" + page + "\"/>");
if (head != null) {
out.println(head);
}
if (styleSheets != null) {
for (int i = 0; i < styleSheets.length; i++) {
out.println(" <link href=\"" + styleSheets[i] + "\" rel=\"stylesheet\" type=\"text/css\"/>");
}
}
out.println("</head>");
if (replaceToken != null && replaceValue != null) {
content = content.replaceAll(replaceToken, replaceValue);
}
out.println("<body>" + content + "</body>");
out.close();
getLog().info("Stored: " + getHTMLFileName());
}
private String getHTMLFileName() {
String name = getPDFFileName();
if (name.endsWith(".pdf")) {
name = name.substring(0, name.length() - 4);
}
return name + ".html";
}
private String downloadContent() throws MalformedURLException, MojoExecutionException {
String contentTag = "<div class=\"" + contentDivClass + "\"";
getLog().info("Downloading: " + page);
URL url = new URL(page);
try {
TidyMarkupDataFormat dataFormat = new TidyMarkupDataFormat();
dataFormat.setMethod("html");
Node doc = dataFormat.asNodeTidyMarkup(new BufferedInputStream(url.openStream()));
XPath xpath = XPathFactory.newInstance().newXPath();
Node nd = (Node)xpath.evaluate("//div[@class='" + contentDivClass + "']", doc, XPathConstants.NODE);
if (nd != null) {
return new XmlConverter().toString(nd, null);
}
} catch (Throwable e) {
if (errorOnDownloadFailure) {
throw new MojoExecutionException("Download or validation of '" + page + "' failed: " + e);
} else {
getLog().error("Download or validation of '" + page + "' failed: " + e);
return null;
}
}
throw new MojoExecutionException("The '" + page + "' page did not have a " + contentTag + " element.");
}
}