【问题标题】:Is there any way improve the performance of FlyingSaucer?有什么方法可以提高飞碟的性能吗?
【发布时间】:2021-07-23 20:40:05
【问题描述】:

我已经关注 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>

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

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

【问题讨论】:

    标签: java performance pdf xhtml flying-saucer


    【解决方案1】:

    我遇到了和 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,它变得更快了。

    【讨论】:

      【解决方案2】:

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

      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,见

      【讨论】:

        【解决方案3】:

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

        我下载了飞碟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 以及在什么平台上?还有你用的是哪个版本的飞碟?

        【讨论】:

        • 我使用的是相同的库和 jdk 1.5。
        • 我刚刚运行了你的代码,它运行得非常好......我现在很困惑,因为我的代码也运行良好。非常感谢,如果浪费了您的时间,我们深表歉意
        • 没问题。不是浪费,因为我从未尝试过飞碟,它给了我一个尝试的好借口。我建议您尝试使用 JDK 6,看看是否有性能提升。此外,如果您开始处理更大的文档,事情可能会变慢。如果是这样,您可能需要考虑尝试不同的 Dom 解析器。
        • 虽然这不再是问题,但我添加我的评论以防其他人遇到同样的情况。我尝试以与您在此处所做的完全相同的方式使用飞碟,只是我必须解析的 HTML 文件要大得多,这使我在 118000 毫秒的范围内运行整个程序。当我对代码的各个部分进行计时时,我发现延迟来自在渲染和生成 PDF 之前解析 HTML。使用下面的 Henson 先生的 cmets 代码就可以了。 builder.setEntityResolver(FSEntityResolver.instance())解析时间从 118000 毫秒下降到 190 毫秒!
        【解决方案4】:

        我会提出 2 条建议:

        1. 对其进行分析。

        2. OutputStream 包裹在BufferedOutputStream

        3. 对其进行分析。 (糟糕......我在重复自己。好吧,你明白了。)

        【讨论】:

        • 这可能是个愚蠢的问题,但你所说的“profile it”是什么意思?
        • 使用以下部分代码替换:InputStream is = new FileInputStream(new File(inputFile)); DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); Document doc = builder.parse(is); ITextRenderer renderer = new ITextRenderer(); renderer.setDocument(doc, null); 看起来问题实际上是在将 xhtml 文件解析为 dom 对象而不是在渲染过程中
        • 我的意思是,使用 Java 分析器运行您的应用程序。例如:hprof - java.sun.com/developer/technicalArticles/Programming/HPROF.html
        • 另外,尝试将 FileInputStream 包装在 BufferedInputStream 中。可能是您正在读取/写入文件系统,该文件系统在小(例如 1 字节)读取/写入时性能特别差。
        【解决方案5】:

        我们也面临着巨大的性能问题。生成第一个 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);
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-09-13
          • 1970-01-01
          • 2012-02-11
          • 2010-11-23
          • 2014-11-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多