有没有办法提高FlyingSaucer的性能?

时间:2011-03-25 11:11:31

标签: java performance pdf xhtml flying-saucer

我跟着this article使用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>

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

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

5 个答案:

答案 0 :(得分:15)

我遇到了和Edd一样的问题。

可悲的是,下一个方法对我来说完全不起作用Java DocumentBuilder: xml parsing is very slow? by 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,它获得了更快的速度。

答案 1 :(得分: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,而不是设置网址,请参阅

答案 2 :(得分:2)

我会提出2条建议:

  1. 简介。

  2. OutputStream包裹在BufferedOutputStream

  3. 简介。 (哎呀......我正在重复自己。好吧,你得到了照片。)

答案 3 :(得分:2)

首先我要说的是,我使用了您的示例代码和示例xhtml,并且“在2675ms内跑了”。

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

core-renderer.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以及使用的平台吗?您还使用哪个版本的flyingsaucer?

答案 4 :(得分: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);
    }
}