如何在 Java 中从 XML 创建 PDF?

Posted

技术标签:

【中文标题】如何在 Java 中从 XML 创建 PDF?【英文标题】:How do you create a PDF from XML in Java? 【发布时间】:2010-09-17 18:35:51 【问题描述】:

目前,我正在用 Java 创建一个 XML 文件,并通过使用 XSL/XSLT 对其进行转换,将其显示在 JSP 页面中。现在我需要获取该 XML 文件并在 PDF 中显示相同的信息。有没有办法通过使用某种 XSL 文件来做到这一点?

我已经看到iText Java-PDF 库,但我找不到任何将它与 XML 和样式表一起使用的方法。

任何帮助将不胜感激。提前致谢!

【问题讨论】:

现在有了 iText® XMLWorker,默认实现是 html/CSS to pdf 最好考虑使用Apache-FOP框架。我在下面添加了一个答案,使用 apache-fop。 【参考方案1】:

A - 解释

您应该使用 Apache FOP 框架来生成 pdf 输出。只需提供 xml 格式的数据并使用 xsl-fo 文件呈现页面并指定 margin页面布局等参数 在这个 xsl-fo 文件中。

我将提供一个简单的演示,我使用 ma​​ven 构建工具来收集所需的 jar 文件。请注意,在页面的末尾,有一个嵌入 pdf 的 svg 图形。我还想证明你可以在 pdf 中嵌入 svg 图形。

B - XML 输入数据示例

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="application/xml"?>
<users-data>
    <header-section>
        <data-type id="019">User Bill Data</data-type>
        <process-date>Thursday December 9 2016 00:04:29</process-date>
    </header-section>
    <user-bill-data>
        <full-name>John Doe</full-name>
        <postal-code>34239</postal-code>
        <national-id>123AD329248</national-id>
        <price>17.84</price>
    </user-bill-data>
    <user-bill-data>
        <full-name>Michael Doe</full-name>
        <postal-code>54823</postal-code>
        <national-id>942KFDSCW322</national-id>
        <price>34.50</price>
    </user-bill-data>
    <user-bill-data>
        <full-name>Jane Brown</full-name>
        <postal-code>66742</postal-code>
        <national-id>ABDD324KKD8</national-id>
        <price>69.36</price>
    </user-bill-data>
</users-data>

C - XSL-FO 模板

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" version="1.0">
    <xsl:output encoding="UTF-8" indent="yes" method="xml" standalone="no" omit-xml-declaration="no"/>
    <xsl:template match="users-data">
        <fo:root language="EN">
            <fo:layout-master-set>
                <fo:simple-page-master master-name="A4-portrail" page- page- margin-top="5mm" margin-bottom="5mm" margin-left="5mm" margin-right="5mm">
                    <fo:region-body margin-top="25mm" margin-bottom="20mm"/>
                    <fo:region-before region-name="xsl-region-before" extent="25mm" display-align="before" precedence="true"/>
                </fo:simple-page-master>
            </fo:layout-master-set>
            <fo:page-sequence master-reference="A4-portrail">
                <fo:static-content flow-name="xsl-region-before">
                    <fo:table table-layout="fixed"  font-size="10pt" border-color="black" border- border-style="solid">
                        <fo:table-column column-/>
                        <fo:table-column column-/>
                        <fo:table-column column-/>
                        <fo:table-body>
                            <fo:table-row>
                                <fo:table-cell text-align="left" display-align="center" padding-left="2mm">
                                    <fo:block>
                                        Bill Id:<xsl:value-of select="header-section/data-type/@id"/>
                                        , Date: <xsl:value-of select="header-section/process-date"/>
                                    </fo:block>
                                </fo:table-cell>
                                <fo:table-cell text-align="center" display-align="center">
                                    <fo:block font-size="150%">
                                        <fo:basic-link external-destination="http://www.example.com">XXX COMPANY</fo:basic-link>
                                    </fo:block>
                                    <fo:block space-before="3mm"/>
                                </fo:table-cell>
                                <fo:table-cell text-align="right" display-align="center" padding-right="2mm">
                                    <fo:block>
                                        <xsl:value-of select="data-type"/>
                                    </fo:block>
                                    <fo:block display-align="before" space-before="6mm">Page <fo:page-number/> of <fo:page-number-citation ref-id="end-of-document"/>
                                    </fo:block>
                                </fo:table-cell>
                            </fo:table-row>
                        </fo:table-body>
                    </fo:table>
                </fo:static-content>
                <fo:flow flow-name="xsl-region-body" border-collapse="collapse" reference-orientation="0">
                    <fo:block>MONTHLY BILL REPORT</fo:block>
                    <fo:table table-layout="fixed"  font-size="10pt" border-color="black" border- border-style="solid" text-align="center" display-align="center" space-after="5mm">
                        <fo:table-column column-/>
                        <fo:table-column column-/>
                        <fo:table-column column-/>
                        <fo:table-column column-/>
                        <fo:table-body font-size="95%">
                            <fo:table-row >
                                <fo:table-cell>
                                    <fo:block>Full Name</fo:block>
                                </fo:table-cell>
                                <fo:table-cell>
                                    <fo:block>Postal Code</fo:block>
                                </fo:table-cell>
                                <fo:table-cell>
                                    <fo:block>National ID</fo:block>
                                </fo:table-cell>
                                <fo:table-cell>
                                    <fo:block>Payment</fo:block>
                                </fo:table-cell>
                            </fo:table-row>
                            <xsl:for-each select="user-bill-data">
                                <fo:table-row>
                                    <fo:table-cell>
                                        <fo:block>
                                            <xsl:value-of select="full-name"/>
                                        </fo:block>
                                    </fo:table-cell>
                                    <fo:table-cell>
                                        <fo:block>
                                            <xsl:value-of select="postal-code"/>
                                        </fo:block>
                                    </fo:table-cell>
                                    <fo:table-cell>
                                        <fo:block>
                                            <xsl:value-of select="national-id"/>
                                        </fo:block>
                                    </fo:table-cell>
                                    <fo:table-cell>
                                        <fo:block>
                                            <xsl:value-of select="price"/>
                                        </fo:block>
                                    </fo:table-cell>
                                </fo:table-row>
                            </xsl:for-each>
                        </fo:table-body>
                    </fo:table>
                    <fo:block id="end-of-document">
                        <fo:instream-foreign-object>
                            <svg   version="1.1" xmlns="http://www.w3.org/2000/svg">
                                <path d="M153 334
C153 334 151 334 151 334
C151 339 153 344 156 344
C164 344 171 339 171 334
C171 322 164 314 156 314
C142 314 131 322 131 334
C131 350 142 364 156 364
C175 364 191 350 191 334
C191 311 175 294 156 294
C131 294 111 311 111 334
C111 361 131 384 156 384
C186 384 211 361 211 334
C211 300 186 274 156 274" style="fill:yellow;stroke:red;stroke-width:2"/>
                            </svg>
                        </fo:instream-foreign-object>
                    </fo:block>
                </fo:flow>
            </fo:page-sequence>
        </fo:root>
    </xsl:template>
</xsl:stylesheet>

D - 项目目录结构

E - Pom 文件

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.levent.fopdemo</groupId>
    <artifactId>apache-fop-demo</artifactId>
    <packaging>jar</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>apache-fop-demo</name>
    <url>http://maven.apache.org</url>

    <properties>
        <fop.version>2.1</fop.version>
    </properties>

    <dependencies>      
        <!-- https://mvnrepository.com/artifact/org.apache.xmlgraphics/fop -->
        <dependency>
            <groupId>org.apache.xmlgraphics</groupId>
            <artifactId>fop</artifactId>
            <version>$fop.version</version>
        </dependency>
    </dependencies>

    <build>
        <finalName>Apache Fop Demo</finalName>

        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.5.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

F - 演示代码:PdfGenerationDemo.java

package com.levent.fopdemo;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;

import javax.xml.transform.Result;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamSource;

import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.Fop;
import org.apache.fop.apps.FopFactory;
import org.apache.fop.apps.MimeConstants;

public class PdfGenerationDemo 

    public static final String RESOURCES_DIR;
    public static final String OUTPUT_DIR;

    static 
        RESOURCES_DIR = "src//main//resources//";
        OUTPUT_DIR = "src//main//resources//output//";
    

    public static void main( String[] args )
    
        try 
            convertToPDF();
         catch (FOPException | IOException | TransformerException e) 
            e.printStackTrace();
        
    

    public static void convertToPDF() throws IOException, FOPException, TransformerException 
        // the XSL FO file
        File xsltFile = new File(RESOURCES_DIR + "//template.xsl");
        // the XML file which provides the input
        StreamSource xmlSource = new StreamSource(new File(RESOURCES_DIR + "//data.xml"));
        // create an instance of fop factory
        FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI());
        // a user agent is needed for transformation
        FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
        // Setup output
        OutputStream out;
        out = new java.io.FileOutputStream(OUTPUT_DIR + "//output.pdf");

        try 
            // Construct fop with desired output format
            Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, out);

            // Setup XSLT
            TransformerFactory factory = TransformerFactory.newInstance();
            Transformer transformer = factory.newTransformer(new StreamSource(xsltFile));

            // Resulting SAX events (the generated FO) must be piped through to
            // FOP
            Result res = new SAXResult(fop.getDefaultHandler());

            // Start XSLT transformation and FOP processing
            // That's where the XML is first transformed to XSL-FO and then
            // PDF is created
            transformer.transform(xmlSource, res);
         finally 
            out.close();
        
    

G - 样本输出:output.pdf

【讨论】:

有趣!但我必须在我的依赖项中添加compile group: 'xml-apis', name: 'xml-apis', version: '1.4.01' 才能正常运行。 感谢您的详细解答。我用相同的文件运行了上面的代码,它返回错误:“无法编译样式表。检测到 1 个错误。在 net.sf.saxon.PreparedStylesheet.prepare(PreparedStylesheet.java:176)”。请帮忙!【参考方案2】:

您可以使用 XSL 格式化对象。 以下是一些关于如何做到这一点的好文章:

http://www.xml.com/pub/a/2001/01/17/xsl-fo/index.html http://www.xml.com/pub/a/2001/01/24/xsl-fo/index.html?page=1 http://www.javaworld.com/javaworld/jw-04-2006/jw-0410-html.html

【讨论】:

【参考方案3】:

您也可以查看 Apache FOP 项目here

【讨论】:

【参考方案4】:

BIRT 有一个用于 Eclipse 的 GUI,可让您从 XML、DB、CSV 等定义 PDF。

【讨论】:

【参考方案5】:

您可能希望查看可以将 PDF 创建作为转换的 XSL-FO 库。我会尝试找到一个链接。

【讨论】:

你找到了吗?【参考方案6】:

试试xhtmlrenderer 项目。 见文章“Generating PDFs for Fun and Profit with Flying Saucer and iText”。

【讨论】:

这很有趣,但如果您打算使用 itext,请务必查看许可。【参考方案7】:

您可以将 XSL-Fo 应用到您的 XML 并使用 Java 转换器对其进行转换:

File xmlfile = new File(baseDir, xml);
File xsltfile = new File(baseDir, xsl);
File pdffile = new File(outDir, "ResultXMLPDF.pdf");

FopFactory fopFactory = FopFactory.newInstance();
FOUserAgent foUserAgent = fopFactory.newFOUserAgent();

OutputStream out = new java.io.FileOutputStream(pdffile);
out = new java.io.BufferedOutputStream(out);

try

    Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, out);
    // Setup XSLT
    TransformerFactory factory = TransformerFactory.newInstance();
    Transformer transformer = factory.newTransformer(new StreamSource(xsltfile));

    transformer.setParameter("versionParam", "1.0");

    Source src = new StreamSource(xmlfile);

    Result res = new SAXResult(fop.getDefaultHandler());

    transformer.transform(src, res);

 finally 
    out.close();


System.out.println("Success!");

【讨论】:

【参考方案8】:

使用 JasperReports。您可以从数据库或 XML 中提取数据。 您可以导出为多种格式:pdf、excel、html 等...

【讨论】:

【参考方案9】:

稍后,您可以使用 Adob​​e 的设计器创建具有可编辑字段的静态 PDF,然后创建匹配的 XDP XML 文档。

【讨论】:

【参考方案10】:

有两种方法可以做到这一点。

首先,您可以创建一个普通的 PDF,该 PDF 在回读时不会为您提供原始 XML 文件的层次结构。这在'iText in Action : Edition 2''Section 9.4.2 Parsing XML' 中有非常详细的解释。

其次,您可以创建一个带标签的 PDF,其中包含 XML 的层次结构和数据。这使您能够读回 PDF 文件并从中创建一个 XML 文件(与原始 XML 文件完全匹配)。这个概念在'iText in Action : Edition 2''15.2.3 Adding structure'中也有详细论述。

根据您的要求,您可以使用上述任何一种方法。

【讨论】:

【参考方案11】:

XML、CSS、XHTML 等构成一个开放标准的“活生生的生态系统”,而 XSL-FO 是一个孤立的标准。

...从历史上看,XSL-FO 和 XSLT 是作为孪生兄弟创建的,但只有 XSLT 仍然是“活标准”,XSL-FO 将大量 DNA 集中在专有 (Adobe) 标准中...现在已经过时了。

严格来说,XSL-FO 是一种不会进化的“废弃方式”的一部分,它忽略了 CSS,在“活着的生态系统”中表达布局的“新方式”。

这不是 Java 问题

See this answer 关于在 XML 或 XHTML 中使用 CSS-page

【讨论】:

XSL-FO 为何不属于 XML 生态系统? @TonyGraham 谢谢,我同意并编辑...更好(对不起我的英语)?您可以编辑,现在是 Wiki!

以上是关于如何在 Java 中从 XML 创建 PDF?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 JavaScript 中从 PDF 中提取文本

在golang中从html创建pdf

如何在 Maven 中从 pom.xml 调用 testng.xml 文件

如何在c#中从pdf文档中获取标签

如何在 Flutter 中从 Web 加载和呈现 PDF 文件

如何在 Swift 中从 NSData 显示 PDF - 如何将 PDF 保存到文档文件夹 Swift - 如何在 Swift 中通过 WebView 从保存的 NSData 显示 PDF