将xml文件转为pdf 以及xsl取值问题 语法问题
前言
一、转化思路
需要创建一个.xsl文件作为pdf模板,同时xsl文件中读取xml中的数据
二、使用步骤
1.引入maven依赖
代码如下(示例):
<dependency>
<groupId>javax.xml.ws</groupId>
<artifactId>jaxws-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>fop</artifactId>
<version>2.1</version>
</dependency>
2.创建xsl文件
xls模板生成的图的大致是这样的,我做了部分截取,同时贴上的xsl文件也只是取了一部分
fo:table标签理解为table就好,fo:table-row就是行, 根据自己的需求加就行这个是简单
含有一些隐私信息我做了简化:
<?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="InvoiceXML">
<fo:root language="EN">
<fo:layout-master-set>
<fo:simple-page-master master-name="A4-portrail" page-height="297mm" page-width="210mm" 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:region-after region-name="xsl-region-after" 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:block border-top-width="2pt" border-top-style="solid" border-top-color="#d6d6d6"
text-align="center">
</fo:block>
</fo:static-content>
<fo:flow flow-name="xsl-region-body" border-collapse="collapse" reference-orientation="0">
<fo:list-block>
<fo:list-item relative-align="before">
<fo:list-item-label end-indent="label-end()">
<fo:block>
<fo:external-graphic src="file:///C:/Users/test/Downloads/logo.png" width="10mm"
height="10mm" content-width="scale-to-fit"/>
</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="body-start()">
<fo:block margin-left="2mm" margin-top="4mm" font-size="15pt">Invoice</fo:block>
</fo:list-item-body>
</fo:list-item>
</fo:list-block>
<fo:table table-layout="fixed" width="100%" font-size="8pt">
<fo:table-column column-width="proportional-column-width(20)"/>
<fo:table-column column-width="proportional-column-width(25)"/>
<fo:table-column column-width="proportional-column-width(20)"/>
<fo:table-column column-width="proportional-column-width(35)"/>
<fo:table-body>
<fo:table-row>
<fo:table-cell text-align="left" display-align="center" padding-left="2mm">
<fo:block>
ID
</fo:block>
</fo:table-cell>
<fo:table-cell text-align="left" display-align="center" padding-left="2mm">
<fo:block>
<!-- 取值的意思就是按照元素从父元素到子元素-->
<xsl:value-of select="invoiceDetails/id"/>
</fo:block>
</fo:table-cell>
<fo:table-cell text-align="left" display-align="center" padding-left="2mm">
<fo:block>
Buyer
</fo:block>
</fo:table-cell>
<fo:table-cell text-align="left" display-align="center" padding-right="2mm">
<fo:block>
<xsl:value-of select="buyer/details/buyerName"/>
</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell text-align="left" display-align="center" padding-left="2mm">
<fo:block>
End Date
</fo:block>
</fo:table-cell>
<fo:table-cell text-align="left" display-align="center" padding-left="2mm">
<fo:block>
<xsl:value-of select="concat(substring-after(translate(invoiceDetails/worker/endDate,'-','/'),'/'),concat('/',substring-before(translate(invoiceDetails/worker/endDate,'-','/'),'/')))"/>
</fo:block>
</fo:table-cell>
<fo:table-cell text-align="left" display-align="center" padding-left="2mm">
<fo:block>
Submitted By
</fo:block>
</fo:table-cell>
<fo:table-cell text-align="left" display-align="center" padding-right="2mm">
<fo:block>
<xsl:value-of select="costCenterAmount[@type='adjustment']/amount"/>
</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-body>
</fo:table>
<fo:table table-layout="fixed" width="100%" font-size="8pt" margin-top="10mm">
<fo:table-column column-width="proportional-column-width(15)"/>
<fo:table-column column-width="proportional-column-width(10)"/>
<fo:table-column column-width="proportional-column-width(10)"/>
<fo:table-column column-width="proportional-column-width(10)"/>
<fo:table-body>
<fo:table-row font-size="10pt" background-color="#d6d6d6" border-bottom-width="0.2mm"
border-bottom-style="solid" border-bottom-color="black">
<fo:table-cell text-align="left" display-align="center" padding-left="2mm">
<fo:block>
Cost Allocation (EUR)
</fo:block>
</fo:table-cell>
<fo:table-cell text-align="left" display-align="center" padding-left="2mm">
<fo:block>
</fo:block>
</fo:table-cell>
<fo:table-cell text-align="left" display-align="center" padding-left="2mm">
<fo:block>
</fo:block>
</fo:table-cell>
<fo:table-cell text-align="left" display-align="center" padding-left="2mm">
<fo:block>
</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row border-bottom-width="0.001mm" border-bottom-style="solid"
border-bottom-color="white">
<fo:table-cell text-align="left" display-align="center" padding-left="2mm">
<fo:block>
Cost Center
</fo:block>
</fo:table-cell>
<fo:table-cell text-align="right" display-align="center" padding-left="2mm">
<fo:block>
Detail Amount
</fo:block>
</fo:table-cell>
<fo:table-cell text-align="right" display-align="center" padding-left="2mm">
<fo:block>
Adjustment
</fo:block>
</fo:table-cell>
<fo:table-cell text-align="right" display-align="center" padding-left="2mm">
<fo:block>
Amount
</fo:block>
</fo:table-cell>
</fo:table-row>
<xsl:for-each select="accounting/costCenterAllocation/costCenter">
<fo:table-row border-bottom-width="0.1mm" border-bottom-style="solid"
border-bottom-color="black">
<fo:table-cell text-align="left" display-align="center" padding-left="2mm">
<fo:block>
<xsl:value-of
select="//lineItemDetails/lineItemDetails/timeSheets/timeSheetHoursWorked/billable/costCenterName"/>
</fo:block>
</fo:table-cell>
<fo:table-cell text-align="right" display-align="center" padding-left="2mm">
<fo:block>
<!-- <costCenterAmount type="detailAmount" fgLabel="Detail Amount">-->
<xsl:value-of select="costCenterAmount[@type='detailAmount']/amount"/>
</fo:block>
</fo:table-cell>
<fo:table-cell text-align="right" display-align="center" padding-left="2mm">
<fo:block>
<!-- <costCenterAmount type="adjustment" fgLabel="Adjustment">-->
<xsl:value-of select="costCenterAmount[@type='adjustment']/amount"/>
</fo:block>
</fo:table-cell>
<fo:table-cell text-align="right" display-align="center" padding-left="2mm">
<fo:block>
<!-- <costCenterAmount type="amount" fgLabel="Amount">-->
<xsl:value-of select="costCenterAmount[@type='amount']/amount"/>
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:for-each>
</fo:table-body>
</fo:flow>
</fo:page-sequence>
</fo:root>
</xsl:template>
</xsl:stylesheet>
java的代码如下
package com.demo.invoice;
import org.apache.fop.apps.*;
import javax.xml.transform.*;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamSource;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
/**
* @author shenyuan
* @date 2022-03-08 5:38
*/
public class SwissDataConvertPdf {
private static String INPUT_PATH="src/main/resources/input";
private static String OUTPUT_PATH="src/main/resources/output";
public static void main(String [] args){
try{
convertPdf();
}catch (Exception e){
e.printStackTrace();
}
}
public static void convertPdf () throws IOException, FOPException, TransformerException {
// 这个定义好的模板文件
File xsltFile = new File( INPUT_PATH+"/invoice_template_Swiss.xsl");
long length = xsltFile.length();
// 这个就是那个xml数据文件
StreamSource xmlSource = new StreamSource(new File(INPUT_PATH + "/Swiss_data.xml"));
FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI());
FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
// 输出的pdf
OutputStream out;
out = new java.io.FileOutputStream(OUTPUT_PATH + "/ebay_invoice_Swiss.pdf");
try {
Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, out);
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer(new StreamSource(xsltFile));
Result res = new SAXResult(fop.getDefaultHandler());
transformer.transform(xmlSource, res);
} finally {
out.close();
}
}
}
总结
可能大家对xls取值会有一些疑问我举例子说名
//这是xsl中取值的 可以去我贴的xls代码中找
<xsl:value-of select="invoiceDetails/id"/>
//对应的 xml元素是这样的
<invoiceDetails label="Invoice Details">
<id label="ID">123455</id>
</invoiceDetails>
//大家又有一个问题假如这样的层级有好多个呢
<invoiceDetails label="Invoice Details">
<id label="ID">123455</id>
<id label="IDS">23444</id>
</invoiceDetails>
//xsl提供了这样的语法,以此类推
<xsl:value-of select="invoiceDetails/id[@label='IDS']"/>
//可能又有这样的问题,表格中的行数是动态的 我需要查到所有的元素拼接到table上
//没关系 xsl提供了 <xsl:for-each> 去我贴的xsl代码中查找
//accounting/costCenterAllocation/costCenter 是外层元素
<xsl:for-each select="accounting/costCenterAllocation/costCenter">
//这是接着for-foreach元素的子元素 意思就是遍历每个 accounting/costCenterAllocation/costCenter元素中的 每个costCenterAmount
<xsl:value-of select="costCenterAmount[@type='detailAmount']/amount"/>
//如果有的行数据不是在for-each遍历的子元素下呢
// 看以下的代码 lineItemDetail和accounting平级的 //lineItemDetail就能跳出到 accounting从lineItemDetail元素开始查找
<xsl:value-of select="//lineItemDetails/lineItemDetails/timeSheets/timeSheetHoursWorked/billable/costCenterName"/>
//关于对取到的值进行处理 xsl数值函数与字符串函数 我只举一个例子
<xsl:value-of select="invoiceDetails/worker/endDate"/>
//把取到的值中的-转换为/,用字符串转换函数这样写,支持函数嵌套
<xsl:value-of select="translate(invoiceDetails/worker/endDate,'-','/')"/>
//最后一个问题 xsl配置 例如
<xsl:template match="InvoiceXML">这个别忘了
InvoiceXML对应xml的根元素