使用Apache FOP从Java中的XML生成PDF

时间:2020-01-09 10:35:30  来源:igfitidea点击:

这篇文章展示了如何使用Apache FOP从Java中的XML生成PDF。

Apache FOP

Apache FOP(格式化对象处理器)是一种由XSL格式化对象(XSL-FO)和独立于输出的格式化程序驱动的打印格式化程序。它是一个Java应用程序,它读取格式对象(FO)树并将结果页面呈现到指定的输出。当前支持的输出格式包括PDF,PS,PCL,AFP,XML(区域树表示),打印,AWT和PNG,在较小程度上包括RTF和TXT。主要输出目标是PDF。

Maven对Apache FOP的依赖

要在应用程序的类路径中获取与Apache FOP相关的jar,我们需要在pom.xml文件中添加以下依赖项。

<dependency>
  <groupId>org.apache.xmlgraphics</groupId>
  <artifactId>fop</artifactId>
  <version>2.3</version>
  <exclusions>
    <exclusion>
      <groupId>xml-apis</groupId>
      <artifactId>xml-apis</artifactId>
    </exclusion>
  </exclusions>
</dependency>

请注意,xml-apis中的java.xml软件包与JDK 10中的java.xml软件包冲突,这就是为什么将其排除在外的原因。

或者,我们可以从此处https://xmlgraphics.apache.org/fop/download.html下载Apache FOP,然后自己复制所需的jar。

使用Apache FOP从XML创建PDF的步骤

  • 首先,我们需要使用XSLT将XML文件转换为XSL-FO。

  • 然后使用FOP将XSL-FO转换为PDF。

使用Apache FOP从Java用XML创建PDF – Hello World示例

首先,我们使用Apache FOP和Java从XML创建一个hello world PDF,它仅在PDF中显示传递的名称。

本示例中使用的XML是一种非常简单的XML,只有一个元素名称。

名称.xml

<?xml version="1.0" encoding="UTF-8"?>
<name>theitroad</name>

我们还需要一个样式表,该样式表用于将XML转换为XSL-FO。

style.xsl

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:fo="http://www.w3.org/1999/XSL/Format">
  <xsl:template match="/">
    <fo:root>
      <fo:layout-master-set>
        <fo:simple-page-master master-name="simpleA4"
            page-height="29.7cm" page-width="21.0cm" margin="2cm">
          <fo:region-body/>
        </fo:simple-page-master>
      </fo:layout-master-set>
      <fo:page-sequence master-reference="simpleA4">
        <fo:flow flow-name="xsl-region-body">
          <fo:block font-family="Helvetica" color="red" font-size="16pt" font-weight="bold">
            Hello, <xsl:value-of select="name"/>!
          </fo:block>
        </fo:flow>
      </fo:page-sequence>
    </fo:root>
  </xsl:template>
</xsl:stylesheet>

在此XSL中需要注意的几件事是

  • 在XSL- xmlns中添加了fo的命名空间:fo =" http://www.w3.org/1999/XSL/Format"

  • 在这里,匹配使用" /"根本身完成。

  • name元素的值是从XML中提取的,还有字体和文本颜色设置。

Java程序,可以完成从XML到XSL-FO以及从XSL-FO到PDF的转换。

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
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 PDFFromFOP {
  public static void main(String[] args) {
    try {
      File xmlfile = new File("resources\name.xml");
      File xsltfile = new File("resources\style.xsl");
      File pdfDir = new File("./Test");
      pdfDir.mkdirs();
      File pdfFile = new File(pdfDir, "hello.pdf");
      System.out.println(pdfFile.getAbsolutePath());
      // configure fopFactory as desired
      final FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI());		
      FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
      // configure foUserAgent as desired
      // Setup output
      OutputStream out = new FileOutputStream(pdfFile);
      out = new java.io.BufferedOutputStream(out);
      try {
        // Construct fop with desired output format
        Fop fop;      
        fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, out);
				
        // Setup XSLT
        TransformerFactory factory = TransformerFactory.newInstance();
        Transformer transformer = factory.newTransformer(new StreamSource(xsltfile));
					
        // Setup input for XSLT transformation
        Source src = new StreamSource(xmlfile);
        
        // Resulting SAX events (the generated FO) must be piped through to FOP
        Result res = new SAXResult(fop.getDefaultHandler());
		    		
        // Start XSLT transformation and FOP processing
        transformer.transform(src, res);
      } catch (FOPException | TransformerException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      } finally {
          out.close();
      }
    }catch(IOException exp){
      exp.printStackTrace();
    }
  }
}

创建的PDF

使用Apache FOP从XML创建PDF – PDF表示例

这是另一个Java示例,其中XML数据显示为PDF表格。在XSL中,我们需要指定匹配的元素,然后程序会循环遍历该元素以在表中为匹配元素的每次重复创建一行。

Organization.xml

<?xml version="1.0" encoding="UTF-8"?>
<organization>
  <orgname>XYZ Pvt. Ltd.</orgname>
  <branch>
    <name>XYZ software services</name>
    <city>Bangalore</city>
    <startdate>12/05/2002</startdate>
  </branch>
  <branch>
    <name>XYZ financial services</name>
    <city>New York City</city>
    <startdate>10/04/1975</startdate>
  </branch>
  <branch>
    <name>XYZ hardware services</name>
    <city>Taipei</city>
    <startdate>20/10/2004</startdate>
  </branch>
</organization>

在PDF中,每个分支元素需要一行,并在该行中显示子元素的数据。

Organization.xsl

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"    
        xmlns:fo="http://www.w3.org/1999/XSL/Format">
  <!-- Attribute used for table border -->
  <xsl:attribute-set name="tableBorder">
    <xsl:attribute name="border">solid 0.1mm black</xsl:attribute>
  </xsl:attribute-set>
  <xsl:template match="organization">
    <fo:root>
      <fo:layout-master-set>
        <fo:simple-page-master master-name="simpleA4"
              page-height="29.7cm" page-width="21.0cm" margin="1cm">
          <fo:region-body/>
        </fo:simple-page-master>
      </fo:layout-master-set>
      <fo:page-sequence master-reference="simpleA4">
        <fo:flow flow-name="xsl-region-body">
          <fo:block font-size="16pt" font-family="Helvetica" color="blue" font-weight="bold" space-after="5mm">
            Organization Name: <xsl:value-of select="orgname"/>
          </fo:block>
          <fo:block font-size="10pt">
            <fo:table table-layout="fixed" width="100%" border-collapse="separate">    
              <fo:table-column column-width="5cm"/>
              <fo:table-column column-width="5cm"/>
              <fo:table-column column-width="5cm"/>
              <fo:table-header>
                <fo:table-cell xsl:use-attribute-sets="tableBorder">
                  <fo:block font-weight="bold">Name</fo:block>
                </fo:table-cell>
                <fo:table-cell xsl:use-attribute-sets="tableBorder">
                  <fo:block font-weight="bold">City</fo:block>
                </fo:table-cell>
                <fo:table-cell xsl:use-attribute-sets="tableBorder">
                  <fo:block font-weight="bold">Start Date</fo:block>
                </fo:table-cell>
              </fo:table-header>
              <fo:table-body>
                <xsl:apply-templates select="branch"/>
              </fo:table-body>
            </fo:table>
          </fo:block>
        </fo:flow>
      </fo:page-sequence>
    </fo:root>
  </xsl:template>
  <xsl:template match="branch">
    <fo:table-row>   
      <fo:table-cell xsl:use-attribute-sets="tableBorder">
        <fo:block>
          <xsl:value-of select="name"/>
        </fo:block>
      </fo:table-cell>
     
      <fo:table-cell xsl:use-attribute-sets="tableBorder">
        <fo:block>
          <xsl:value-of select="city"/>
        </fo:block>
      </fo:table-cell>   
      <fo:table-cell xsl:use-attribute-sets="tableBorder">
      <fo:block>
        <xsl:value-of select="startdate"/>
      </fo:block>
      </fo:table-cell>
    </fo:table-row>
  </xsl:template>
</xsl:stylesheet>

在XSL中,我们可以看到初始匹配是针对根元素" organization"的,然后再次有针对模板" branch"的模板匹配。对于<branch>元素,子元素的值被提取并显示在PDF的表格单元格中。
Java程序,可以完成从XML到XSL-FO以及从XSL-FO到PDF的转换。

public class PDFFromFOP {
  public static void main(String[] args) {
    try {
      File xmlfile = new File("resources\organization.xml");
      File xsltfile = new File("resources\organization.xsl");
      File pdfDir = new File("./Test");
      pdfDir.mkdirs();
      File pdfFile = new File(pdfDir, "organization.pdf");
      System.out.println(pdfFile.getAbsolutePath());
      // configure fopFactory as desired
      final FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI());        
      FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
      // configure foUserAgent as desired        
      // Setup output
      OutputStream out = new FileOutputStream(pdfFile);
      out = new java.io.BufferedOutputStream(out);
      try {
        // Construct fop with desired output format
        Fop fop;                
        fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, out);    
        // Setup XSLT
        TransformerFactory factory = TransformerFactory.newInstance();
        Transformer transformer = factory.newTransformer(new StreamSource(xsltfile));        
        // Setup input for XSLT transformation
        Source src = new StreamSource(xmlfile);                   
        // Resulting SAX events (the generated FO) must be piped through to FOP
        Result res = new SAXResult(fop.getDefaultHandler());        
        // Start XSLT transformation and FOP processing
        transformer.transform(src, res);
      } catch (FOPException | TransformerException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
      } finally {
        out.close();
      }
    }catch(IOException exp){
      exp.printStackTrace();
    }
  }
}

创建的PDF

使用Apache FOP从XML创建PDF –在Web应用程序中

如果要在Web应用程序的浏览器中呈现生成的PDF,则可以使用类似于以下内容的内容

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {	
  try{
    FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI());
    //Setup a buffer to obtain the content length
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    //Setup FOP
    Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, out);
    TransformerFactory factory = TransformerFactory.newInstance();
    Transformer transformer = factory.newTransformer(new StreamSource(PATH_TO_XSL));
    //Make sure the XSL transformation's result is piped through to FOP
    Result res = new SAXResult(fop.getDefaultHandler());

    //Setup input
    Source src = new StreamSource(new File("PATH_TO_XML"));

    //Start the transformation and rendering process
    transformer.transform(src, res);

    //Prepare response
    response.setContentType("application/pdf");
    response.setContentLength(out.size());

    //Send content to Browser
    response.getOutputStream().write(out.toByteArray());
    response.getOutputStream().flush();
  }catch(Exception e){
    e.printStackTrace();
  }
}