4

我已经按照这篇文章使用 FlyingSaucer 将 XHTML 转换为 PDF,它非常棒,但有一个主要缺点……它非常慢!

我发现从 XHTML 呈现 PDF 需要 1 到 2 分钟,无论该页面多么简单。

基本代码:

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import org.xhtmlrenderer.pdf.ITextRenderer;
import com.lowagie.text.DocumentException;

public class FirstDoc {

    public static void main(String[] args) throws IOException, DocumentException {

        String inputFile = "firstdoc.xhtml";
        String url = new File(inputFile).toURI().toURL().toString();
        String outputFile = "firstdoc.pdf";
        OutputStream os = new FileOutputStream(outputFile);

        ITextRenderer renderer = new ITextRenderer();
        renderer.setDocument(url);
        renderer.layout();
        renderer.createPDF(os);

        os.close();
    }
}

示例 XHTML:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title>My First Document</title>
        <style type="text/css"> b { color: green; } </style>
    </head>
    <body>
        <p>
            <b>Greetings Earthlings!</b>
            We've come for your Java.
        </p>
    </body>
</html>

有谁知道如何提高飞碟的性能?

如果做不到这一点,是否有人能够推荐一个替代 Java 库,它可以有效地将 PDF 从 URL 呈现为具有外部 CSS 和从 URL 生成的图像的 (X)HTML 文档?

4

5 回答 5

15

我面临与 Edd 相同的问题。

遗憾的是,下一个方法不起作用Java DocumentBuilder:xml 解析很慢?Marek Piechut完全为我设计 - 我的 HTML 实体在途中迷路了。

DocumentBuilderFactory fac = DocumentBuilderFactory.newInstance();
fac.setNamespaceAware(false);
fac.setValidating(false);
fac.setFeature("http://xml.org/sax/features/namespaces", false);
fac.setFeature("http://xml.org/sax/features/validation", false);
fac.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false);
fac.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
DocumentBuilder builder = fac.newDocumentBuilder();

最终的诀窍是这些行:

DocumentBuilderFactory fac = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = fac.newDocumentBuilder();
builder.setEntityResolver(FSEntityResolver.instance());

通过使用内置的 Java EntityResolver 来解析 DTD,它变得更快了。

于 2011-08-05T13:47:29.117 回答
4

问题是,您可能正在使用链接文章中的这段代码:

DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = builder.parse(new StringBufferInputStream(buf.toString()));

这样,构建器将尝试加载引用的 DTD。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

加载和解析 DTD 需要很长时间。

如果您正在使用

ITextRenderer renderer = new ITextRenderer();
renderer.setDocument(url); // not setDocument(document)

飞碟不会解决DTD。如果你想加载一个Document,而不是设置一个 url,请参阅

于 2011-03-27T10:34:17.590 回答
2

我会提出 2 条建议:

  1. 剖析它。

  2. 包裹OutputStream在一个BufferedOutputStream

  3. 剖析它。(哎呀......我在重复自己。好吧,你明白了。)

于 2011-03-25T11:19:57.007 回答
2

首先让我说我使用了您的示例代码和示例 xhtml,它“在 2675 毫秒内运行”。

我下载了飞碟R8。并将三个罐子放入我的类路径中。

核心渲染器.jar、iText-2.0.8.jar、xml-apis-xerces-2.9.1.jar

我通过使用仪器修改您的代码来测量运行时间......

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import org.xhtmlrenderer.pdf.ITextRenderer;
import com.lowagie.text.DocumentException;

public class FirstDoc {

    public static void main(String[] args) throws IOException, DocumentException {
        long start = System.currentTimeMillis();
        String inputFile = "firstdoc.xhtml";
        String url = new File(inputFile).toURI().toURL().toString();
        String outputFile = "firstdoc.pdf";
        OutputStream os = new FileOutputStream(outputFile);

        ITextRenderer renderer = new ITextRenderer();
        renderer.setDocument(url);
        renderer.layout();
        renderer.createPDF(os);

        os.close();
        long end = System.currentTimeMillis();
        System.out.println("Ran in " + (end-start) + "ms");
    }
}

现在这个库并不是很快,但似乎也不需要 1-2 分钟。所以现在我们需要弄清楚为什么它对你来说运行如此缓慢。您能否告诉我们您使用的是哪个 JDK 以及在什么平台上?还有你用的是哪个版本的飞碟?

于 2011-03-25T12:22:04.037 回答
0

我们也面临着巨大的性能问题。生成第一个 PDF 花了将近一分钟。如果在第一代仍在运行时触发另一代,它们几乎会同时完成。生成第一个 PDF 后,后续请求的执行速度要快得多。

经过一些分析后,我发现瓶颈是在ITextFontResolver初始化时实例化ITextRenderer的。这是由于解析器加载了它需要的所有字体,com.lowagie.text.pdf.BaseFont从而导致了巨大的延迟。BaseFont缓存其生成的字体,这解释了并行请求的同时完成和后续请求的加速。

我们的解决方案是在应用程序初始化时加载所需的字体。这会稍微增加启动时间(但不会增加一分钟,因为它似乎与其他初始化内容并行执行),但允许第一个 PDF 的生成速度与其他任何 PDF 一样快。为了触发字体加载,我们刚刚初始化了一个ITextFontResolver. 使用 spring 解决方案就像这样简单:

@Component
public class FontLoader  implements InitializingBean {

    @Override
    public void afterPropertiesSet() {
        ITextFontResolver fontResolver = new ITextFontResolver(null);
    }
}
于 2021-07-23T12:40:05.093 回答