【问题标题】:Convert malformed HTML to PDF using Flying Saucer PDF Rendering使用飞碟 PDF 渲染将格式错误的 HTML 转换为 PDF
【发布时间】:2019-02-04 18:09:38
【问题描述】:

在一个项目中 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(如果您使用 ok 变量运行 GitHub 项目,它将在项目文件夹中创建一个文件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

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

重要提示:

  • 这里分配给变量okhtml 的值只是问题的占位符。真实的是here
  • 在实际项目中,html字符串是来自用户的输入。是的,他/她必须知道该放什么,但是,当然,他/她可以在 html 构造中犯一些错误,所以我必须处理这个。

问题

  • 我有什么办法可以“告诉”Flying Saucer PDF Rendering忽略/自动完成/自行清理 em>/或任何其他,这些“无效”部分并继续创建 PDF 文件(首选)
  • 有没有更好的方法可以用来克服这个问题。

【问题讨论】:

  • 您必须小心使用过的标签。示例:“
    ”。检查此问题的第二个答案

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


【解决方案1】:

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

https://jsoup.org/

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

https://try.jsoup.org/

【讨论】:

    【解决方案2】:

    由于我在使用 Flying Saucer 从 HTML 生成 PDF 时遇到了同样的问题,因此我使用 HtmlCleaner 库(请参阅 maven link)在解析到 Flying Saucer 库之前清理 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);
    // ....
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-11-15
      • 2013-05-14
      • 2012-07-13
      • 2015-05-18
      • 2013-05-05
      • 1970-01-01
      • 1970-01-01
      • 2014-08-13
      相关资源
      最近更新 更多