使用飞碟PDF渲染将格式错误的HTML转换为PDF

时间:2018-08-29 21:13:42

标签: java html pdf flying-saucer pdf-rendering

在项目 GitHub 中,我试图将任意HTML字符串转换为PDF版本。转换是指解析HTML,并将其呈现为PDF文件。

要实现这一点,我正在使用Flying Saucer PDF Rendering,如下所示:

Main.java

public class Main {

    public static void main(String [] args) {
        final String ok = "<valid html here>: see github rep for real html markup here";
        final String html = "<invalid html here>: see github rep for real html markup here";
        try {
            // final byte[] bytes = generatePDFFrom(ok); // works!
            final byte[] bytes = generatePDFFrom(html); // does NOT work :(
            try(FileOutputStream fos = new FileOutputStream("sample-file.pdf")) {
                fos.write(bytes);
            }

        } catch (IOException | DocumentException e) {
            e.printStackTrace();
        }
    }

    private static byte[] generatePDFFrom(String html) throws IOException, DocumentException {
        final ITextRenderer renderer = new ITextRenderer();
        renderer.setDocumentFromString(html);
        renderer.layout();
        try (ByteArrayOutputStream fos = new ByteArrayOutputStream(html.length())) {
            renderer.createPDF(fos);
            return fos.toByteArray();
        }
    }
}

在上面的代码中,如果我使用存储在ok变量中的html字符串(这是一个“有效” html),它将正确创建PDF(如果您使用{{1}运行GitHub项目, }变量,它将在项目文件夹中创建文件ok,其中包含一些呈现的html)。

现在,如果我使用sample-file.pdf变量中的值(带有无效标记的html,标记可能未正确关闭等),则会引发以下错误(该错误可能因错误的值而异):

html

现在,据我了解,这是因为html字符串的“无效”部分。

重要说明:

  • 这里分配给变量ERROR: 'The markup in the document following the root element must be well-formed.' Exception in thread "main" org.xhtmlrenderer.util.XRRuntimeException: Can't load the XML resource (using TrAX transformer). org.xml.sax.SAXParseException; lineNumber: 22; columnNumber: 9; The markup in the document following the root element must be well-formed. at org.xhtmlrenderer.resource.XMLResource$XMLResourceBuilder.transform(XMLResource.java:222) at org.xhtmlrenderer.resource.XMLResource$XMLResourceBuilder.createXMLResource(XMLResource.java:181) at org.xhtmlrenderer.resource.XMLResource.load(XMLResource.java:84) at org.xhtmlrenderer.pdf.ITextRenderer.setDocumentFromString(ITextRenderer.java:171) at org.xhtmlrenderer.pdf.ITextRenderer.setDocumentFromString(ITextRenderer.java:166) at Main.generatePDFFrom(Main.java:84) at Main.main(Main.java:72) Caused by: javax.xml.transform.TransformerException: org.xml.sax.SAXParseException; lineNumber: 22; columnNumber: 9; The markup in the document following the root element must be well-formed. at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:740) at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:343) at org.xhtmlrenderer.resource.XMLResource$XMLResourceBuilder.transform(XMLResource.java:220) ... 6 more Caused by: org.xml.sax.SAXParseException; lineNumber: 22; columnNumber: 9; The markup in the document following the root element must be well-formed. at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1239) at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transformIdentity(TransformerImpl.java:659) at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:728) ... 8 more ok的值只是问题的占位符。真实的是here
  • 在实际项目中,html字符串是来自用户的输入。是的,他/她必须知道该放在哪里,但是,当然,他/她可以在html格式中犯一些错误,所以我必须处理这个问题。

问题

  • 有什么办法可以让我“讲”飞碟PDF渲染以忽略 / 自动完成 / 清理自身 / 或任何其他,这些“无效”部分,然后继续创建PDF文件 (首选)
  • 有没有更好的方法可以用来克服这个问题。

2 个答案:

答案 0 :(得分:1)

最初的想法是通过另一个可以更好地处理html的库解析输入,然后将该库的结果 toString()解析为PDF渲染器。

https://jsoup.org/

五分钟的谷歌搜索发现这是一个相当合理的库。甚至还有一个测试实用程序,您可以尝试将格式错误的输入放入:

https://try.jsoup.org/

答案 1 :(得分:1)

最近我做了同样的事情。使用飞碟从HTML生成PDF。由于存在相同的问题,因此我在解析为飞碟库之前使用了HtmlCleaner库来清理HTML代码。

// Clean the html to use in the flying saucer converting tool
// get the element you want to serialize
HtmlCleaner cleaner = new HtmlCleaner();
TagNode rootTagNode = cleaner.clean(html);
// set up properties for the serializer (optional, see online docs)
CleanerProperties cleanerProperties = cleaner.getProperties();
// use the getAsString method on an XmlSerializer class
XmlSerializer xmlSerializer = new PrettyXmlSerializer(cleanerProperties);
String cleanedHtml = xmlSerializer.getAsString(rootTagNode);

// use the https://github.com/flyingsaucerproject/flyingsaucer to convert cleaned HTML to PDF
ITextRenderer renderer = new ITextRenderer();
renderer.setDocumentFromString(cleanedHtml);
// ....

https://mvnrepository.com/artifact/net.sourceforge.htmlcleaner/htmlcleaner http://htmlcleaner.sourceforge.net/javause.php