如何使用XSL-FO生成PDF格式的电子发票的技术博文示例
目录
- 使用 XSL-FO 生成电子发票 PDF:从布局设计到优化
- 为什么选择 XSL-FO?
- 1. 初始设置
- 2. 标题区块
- 3. 买卖方信息
- 4. 商品明细表格
- 5. 合计信息
- 6. 优化代码结构与布局
- 7. 生成 PDF 文件
- 8. 示例
- 总结
使用 XSL-FO 生成电子发票 PDF:从布局设计到优化
在电子商务和财务系统中,生成规范化的发票 PDF 文件是一个常见需求。利用 XSL-FO(Extensible Stylesheet Language Formatting Objects),我们可以设计和生成格式规范的发票 PDF。本文将介绍如何使用 XSL-FO 构建电子发票的 PDF 文件,并提供一些优化布局的建议,以实现更清晰美观的发票。
为什么选择 XSL-FO?
XSL-FO 是 W3C 的标准,用于将 XML 文档格式化为 PDF、PostScript 等格式的高质量输出文档。它尤其适合结构化数据的排版需求。结合 Apache FOP(Formatting Objects Processor)等工具,我们可以从 XML 模板生成发票 PDF。
1. 初始设置
首先,在根节点 <fo:root>
中设置页面布局:
<fo:layout-master-set>
<fo:simple-page-master master-name="invoice">
<fo:region-body margin="5mm"/>
</fo:simple-page-master>
</fo:layout-master-set>
这里指定了页面的边距和布局,便于后续的内容排版。
2. 标题区块
发票的顶部通常包含发票标题、二维码、公司信息和发票号。可以通过 <fo:table>
元素来布局这些信息,利用列宽度来控制内容的对齐和分布:
<fo:table table-layout="fixed" width="100%">
<fo:table-column column-width="22%"/>
<fo:table-column column-width="56%"/>
<fo:table-column column-width="22%"/>
...
</fo:table>
在这个表格中,我们在左右两侧显示二维码和发票信息,中间展示发票标题。通过设置 column-width
,可以确保不同元素之间的布局合理。
3. 买卖方信息
发票中买方和卖方的信息是关键内容。我们使用 <fo:table>
元素创建一个两列的表格来放置这些信息:
<fo:table-column column-width="5%"/>
<fo:table-column column-width="45%"/>
每个列块中包括公司名称、税号等信息,并使用 writing-mode="tb-rl"
为标签列设置垂直方向书写,从而增强布局的清晰度。
4. 商品明细表格
商品明细表格包含了商品的名称、规格、单位、数量、单价、金额、税率、税额等。可以通过固定列宽设置,确保不同商品数据的显示一致:
<fo:table-column column-width="20%"/>
...
对于每个商品的明细行,我们使用了 fo:table-body
标签,包含具体的内容。可以进一步通过 min-height="50pt"
设置行高度,确保无数据时也有足够的空间。
5. 合计信息
在表格的下方,使用一个新的表格展示“价税合计”信息,包括大小写金额:
<fo:table-body>
<fo:table-row>
<fo:table-cell text-align="center"><fo:block>价税合计(大写)</fo:block></fo:table-cell>
...
</fo:table-row>
</fo:table-body>
通过将列宽和内容对齐调整为居中,可以确保整体布局对齐美观。
6. 优化代码结构与布局
在发票生成过程中,统一样式设置和简化代码结构可以提高代码的可读性和可维护性,以下是几点优化建议:
- 统一表格样式:将表格边框样式、颜色等属性集中设置,避免重复代码。
- 简化内容布局:对于标签文本等内容,使用
writing-mode="tb-rl"
实现垂直对齐,避免复杂的布局调整。 - 表格布局的合理分配:使用
table-layout="fixed"
和精确的column-width
设置,确保表格布局稳定。
7. 生成 PDF 文件
在完成 XSL-FO 模板后,可以使用 Apache FOP 工具将其转换为 PDF 文件:
fop -xml invoice.xml -xsl invoice.xsl -pdf invoice.pdf
这样即可获得一份规范、清晰的电子发票 PDF 文件。
8. 示例
<?xml version="1.0" encoding="utf-8"?>
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:fox="http://xmlgraphics.apache.org/fop/extensions"
xmlns:xe="http://www.x-easypdf.cn/ns"
xmlns:svg="http://www.w3.org/2000/svg">
<fo:layout-master-set>
<fo:simple-page-master master-name="invoice">
<fo:region-body margin="5mm"/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="invoice">
<fo:flow flow-name="xsl-region-body" color="rgb(188, 119, 76)">
<fo:block font-family="SimSun" space-before="10pt">
<fo:table table-layout="fixed" width="100%">
<fo:table-column column-width="22%"/>
<fo:table-column column-width="56%"/>
<fo:table-column column-width="22%"/>
<fo:table-body>
<fo:table-row>
<fo:table-cell>
<fo:block text-align="left">
<!-- 二维码图片所在位置 -->
<fo:external-graphic src="image/qrcode.png" content-width="60pt" content-height="60pt"/>
</fo:block>
</fo:table-cell>
<fo:table-cell display-align="center">
<fo:block text-align="center" font-family="SimSun" font-size="20pt">
电子发票(增值税专用发票)
</fo:block>
<fo:block text-align="center" font-family="SimSun" font-size="14pt">
==========================
</fo:block>
<fo:block text-align="center" space-before="-50pt"> <!-- 使用负值移动位置 -->
<!-- 章图片所在位置 -->
<fo:external-graphic src="image/seal.png" content-width="70pt" content-height="70pt"/>
</fo:block>
</fo:table-cell>
<fo:table-cell font-size="10pt" display-align="center" text-align="left">
<fo:block>发票号码:123456789</fo:block>
<fo:block>开票日期:2024年01月01日</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-body>
</fo:table>
</fo:block>
<!-- Buyer and Seller Information -->
<fo:block font-family="SimSun" font-size="10pt">
<fo:table table-layout="fixed" width="100%" border-style="solid" border-width="0.5pt">
<fo:table-column column-width="5%" border-style="solid" border-color="rgb(188, 119, 76)" />
<fo:table-column column-width="45%" border-style="solid" border-color="rgb(188, 119, 76)" />
<fo:table-column column-width="5%" border-style="solid" border-color="rgb(188, 119, 76)" />
<fo:table-column column-width="45%" border-style="solid" border-color="rgb(188, 119, 76)" />
<fo:table-body>
<fo:table-row>
<fo:table-cell padding="5pt" writing-mode="tb-rl">
<fo:block>购买方信息</fo:block>
</fo:table-cell>
<fo:table-cell display-align="center">
<fo:block>名称:</fo:block>
<fo:block>统一社会信用代码/纳税人识别号:</fo:block>
</fo:table-cell>
<fo:table-cell padding="5pt" writing-mode="tb-rl">
<fo:block>销售方信息</fo:block>
</fo:table-cell>
<fo:table-cell display-align="center">
<fo:block>名称:</fo:block>
<fo:block>统一社会信用代码/纳税人识别号:</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-body>
</fo:table>
</fo:block>
<!-- Item Details Table -->
<fo:block font-family="SimSun" font-size="10pt">
<fo:table table-layout="fixed" width="100%" border-style="solid" border-color="rgb(188, 119, 76)" border-width="0.5pt">
<fo:table-column column-width="20%" border-left-style="solid" border-color="rgb(188, 119, 76)"/>
<fo:table-column column-width="10%" border-color="rgb(188, 119, 76)"/>
<fo:table-column column-width="10%" border-color="rgb(188, 119, 76)"/>
<fo:table-column column-width="10%" border-color="rgb(188, 119, 76)"/>
<fo:table-column column-width="15%" border-color="rgb(188, 119, 76)"/>
<fo:table-column column-width="10%" border-color="rgb(188, 119, 76)"/>
<fo:table-column column-width="15%" border-color="rgb(188, 119, 76)"/>
<fo:table-column column-width="10%" border-right-style="solid" border-color="rgb(188, 119, 76)"/>
<fo:table-header>
<fo:table-row>
<fo:table-cell text-align="center"><fo:block>项目名称</fo:block></fo:table-cell>
<fo:table-cell text-align="center"><fo:block>规格型号</fo:block></fo:table-cell>
<fo:table-cell text-align="center"><fo:block>单位</fo:block></fo:table-cell>
<fo:table-cell text-align="center"><fo:block>数量</fo:block></fo:table-cell>
<fo:table-cell text-align="center"><fo:block>单价</fo:block></fo:table-cell>
<fo:table-cell text-align="center"><fo:block>金额</fo:block></fo:table-cell>
<fo:table-cell text-align="center"><fo:block>税率/征收率</fo:block></fo:table-cell>
<fo:table-cell text-align="center"><fo:block>税额</fo:block></fo:table-cell>
</fo:table-row>
</fo:table-header>
<fo:table-body min-height="50pt">
<fo:table-row>
<fo:table-cell text-align="center" padding-top="5pt"><fo:block>商品A</fo:block></fo:table-cell>
<fo:table-cell text-align="center" padding-top="5pt"><fo:block>型号A</fo:block></fo:table-cell>
<fo:table-cell text-align="center" padding-top="5pt"><fo:block>件</fo:block></fo:table-cell>
<fo:table-cell text-align="center" padding-top="5pt"><fo:block>1</fo:block></fo:table-cell>
<fo:table-cell text-align="center" padding-top="5pt"><fo:block>100.00</fo:block></fo:table-cell>
<fo:table-cell text-align="center" padding-top="5pt"><fo:block>100.00</fo:block></fo:table-cell>
<fo:table-cell text-align="center" padding-top="5pt"><fo:block>10%</fo:block></fo:table-cell>
<fo:table-cell text-align="center" padding-top="5pt"><fo:block>10.00</fo:block></fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell text-align="center" padding-top="5pt"><fo:block>合 计</fo:block></fo:table-cell>
<fo:table-cell text-align="center" padding-top="5pt"><fo:block></fo:block></fo:table-cell>
<fo:table-cell text-align="center" padding-top="5pt"><fo:block></fo:block></fo:table-cell>
<fo:table-cell text-align="center" padding-top="5pt"><fo:block></fo:block></fo:table-cell>
<fo:table-cell text-align="center" padding-top="5pt"><fo:block></fo:block></fo:table-cell>
<fo:table-cell text-align="center" padding-top="5pt"><fo:block>¥100.00</fo:block></fo:table-cell>
<fo:table-cell text-align="center" padding-top="5pt"><fo:block></fo:block></fo:table-cell>
<fo:table-cell text-align="center" padding-top="5pt"><fo:block>¥10.00</fo:block></fo:table-cell>
</fo:table-row>
</fo:table-body>
</fo:table>
</fo:block>
<!-- Summary Section -->
<fo:block font-family="SimSun" font-size="10pt">
<fo:table table-layout="fixed" width="100%" border-style="solid" border-width="0.5pt" border-color="rgb(188, 119, 76)">
<fo:table-column column-width="25%" border-style="solid" border-color="rgb(188, 119, 76)"/>
<fo:table-column column-width="40%" border-top-style="solid" border-color="rgb(188, 119, 76)"/>
<fo:table-column column-width="10%" border-top-style="solid" border-color="rgb(188, 119, 76)"/>
<fo:table-column column-width="25%" border-top-style="solid" border-right-style="solid" border-color="rgb(188, 119, 76)"/>
<fo:table-body>
<fo:table-row>
<fo:table-cell text-align="center" padding-top="5pt" padding-bottom="5pt">
<fo:block>价税合计(大写)</fo:block>
</fo:table-cell>
<fo:table-cell text-align="left" padding-top="5pt" padding-bottom="5pt">
<fo:block>壹佰壹拾元整</fo:block>
</fo:table-cell>
<fo:table-cell text-align="center" padding-top="5pt" padding-bottom="5pt">
<fo:block>(小写)</fo:block>
</fo:table-cell>
<fo:table-cell text-align="left" padding-top="5pt" padding-bottom="5pt">
<fo:block>¥110.00</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-body>
</fo:table>
</fo:block>
<!-- Footer Section -->
<fo:block font-family="SimSun" font-size="10pt" space-after="10pt" border-color="rgb(188, 119, 76)">
<fo:table table-layout="fixed" width="100%" border-style="solid" border-width="0.5pt">
<fo:table-column column-width="5%" border-style="solid" border-color="rgb(188, 119, 76)"/>
<fo:table-column column-width="95%" border-style="solid" border-color="rgb(188, 119, 76)"/>
<fo:table-body>
<fo:table-row>
<fo:table-cell padding="5pt" writing-mode="tb-rl">
<fo:block>备注</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-body>
</fo:table>
<fo:block>开票人:</fo:block>
</fo:block>
</fo:flow>
</fo:page-sequence>
</fo:root>
总结
XSL-FO 提供了一个强大的格式化模型,适合用来生成电子发票等结构化文档。通过灵活的表格布局和样式控制,可以实现专业的 PDF 输出。