如何使用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 输出。
相关文章:
如何使用XSL-FO生成PDF格式的电子发票的技术博文示例
目录 使用 XSL-FO 生成电子发票 PDF:从布局设计到优化为什么选择 XSL-FO?1. 初始设置2. 标题区块3. 买卖方信息4. 商品明细表格5. 合计信息6. 优化代码结构与布局7. 生成 PDF 文件8. 示例总结 使用 XSL-FO 生成电子发票 PDF:从布局设计到优化…...
TDengine 签约山东港,赋能港口数字化转型
随着全球港口物流数字化进程的加速,港口运营面临日益复杂的数据管理挑战,从能源管理、设备监控到运营安全保障,各类数据需要及时存储并高效分析。山东港在信息化建设过程中,数字化综合管理平台的性能和查询功能一度受到瓶颈制约。…...

基于YOLO11/v10/v8/v5深度学习的煤矿传送带异物检测系统设计与实现【python源码+Pyqt5界面+数据集+训练代码】
《------往期经典推荐------》 一、AI应用软件开发实战专栏【链接】 项目名称项目名称1.【人脸识别与管理系统开发】2.【车牌识别与自动收费管理系统开发】3.【手势识别系统开发】4.【人脸面部活体检测系统开发】5.【图片风格快速迁移软件开发】6.【人脸表表情识别系统】7.【…...

mysql-workbench 导入csv格式数据报错:Unhandled exception: Could not determine delimiter
xlsx文件中第二行某个单元格有换行符,csv文件中用双引号包起来了,但是python 在采样的时候,只读了前两行,readline可不认识csv的规则。csv文件可以识别双引号包起来的换行符是单元格内部的换行,python的readline识别不…...
使用Python简单实现客户端界面
服务端实现 import threading import timeimport wx from socket import socket, AF_INET, SOCK_STREAMclass LServer(wx.Frame):def __init__(self):wx.Frame.__init__(self, None, id1002, titleL服务器端界面, poswx.DefaultPosition, size(400, 450))# 窗口中添加面板pl …...
15分钟学 Go 第 43 天:前端与Go的结合
第43天:前端与Go的结合 目标:了解Go如何与前端交互,前端使用Vue.js 在现代Web开发中,Go语言常用于后端开发,而Vue.js是一个流行的前端框架,用于构建用户界面。结合二者,可以构建高效、可维护的…...
解决SRS推送webrtc流卡顿问题
目录 1.问题描述2.原因分析3.ffmpeg去掉B帧的方法3.1 命令行推流3.2 ffmpeg源码推流 1.问题描述 使用ffmpeg通过rtmp协议推流给SRS,然后浏览器通过webrtc拉取播放流,经多次测试发现webrtc播放流总是卡顿,而拉取rtmp流是正常的。 2.原因分析…...

GDPU Andriod移动应用 Broadcast Receiver
聆听广播,跟着节拍吧。 计时器 新建一个名为PhoneStateMonitor的工程; 实现一个应用运行时长的计时器,并在界面上刷新计数器,要求包括: (1)在Layout中包含两个TextView控件,横向分…...

CSP/信奥赛C++刷题训练:经典例题 - 栈(1):洛谷P3056 :[USACO12NOV] Clumsy Cows S
CSP/信奥赛C刷题训练:经典例题 - 栈(1):洛谷P3056 :[USACO12NOV] Clumsy Cows S 题目描述 Bessie the cow is trying to type a balanced string of parentheses into her new laptop, but she is sufficiently clums…...

WiFi一直获取不到IP地址是怎么回事?
在当今这个信息化时代,WiFi已成为我们日常生活中不可或缺的一部分。无论是家庭、办公室还是公共场所,WiFi都为我们提供了便捷的无线互联网接入。然而,有时我们可能会遇到WiFi连接后无法获取IP地址的问题,这不仅影响了我们的网络使…...
蓝牙BLE开发——iOS 每次写入数据超过200字节报错?
iOS 写入数据超过200字节报错 文章目录 iOS 写入数据超过200字节报错官方建议:报错问题解决 writeblecharacteristicvalue 官方建议: 并行调用多次会存在写失败的可能性。APP不会对写入数据包大小做限制,但系统与蓝牙设备会限制蓝牙4.0单次…...

Ascend Extension for PyTorch是个what?
1 Ascend Extension for PyTorch Ascend Extension for PyTorch 插件是基于昇腾的深度学习适配框架,使昇腾NPU可以支持PyTorch框架,为PyTorch框架的使用者提供昇腾AI处理器的超强算力。 项目源码地址请参见Ascend/Pytorch。 昇腾为基于昇腾处理器和软…...
学习docker第五弹-----高级篇start-Dockerfile
docker目录 1 Dockerfile是什么2 Dockerfile能干嘛3 如何书写Dockerfile3.1 Dockerfile构建过程解析3.2 小总结3.3 Dockerfile的基本知识3.5 保留字FROMMAINTAINERRUN 有两种方式EXPOSEWORKDIRENVUSERVOLUMEADDCMDENTRYPOINT 4 后记 1 Dockerfile是什么 Dockerfile顾名思义就是…...
【Elasticsearch】Elasticsearch集成Spring Boot
Elasticsearch集成Spring Boot 概述 Spring Data Elasticsearch 介绍一、环境初始化二、实战入门1、定义数据实体类2、定义Dao层3、框架集成-SpringData-集成测试-索引操作4、框架集成-SpringData-集成测试-文档操作5、框架集成-SpringData-集成测试-文档搜索 概述 Spring Data…...
HarmonyOS 移
什么是HarmonyOS HarmonyOS 中文名字是 鸿蒙操作系统 中国神话传说盘古在昆仑山开天辟地之前,世界是一团混沌状的元气,这种自然的元气叫做鸿蒙,那个时代成为鸿蒙时代华为公司的操作系统以鸿蒙取名,是不是有开天辟地之寓意&#x…...

跨子网的WinCC客户机/服务器如何实现通讯?
为了更有效地利用有限的IP地址,为了减少广播对网络带宽的占用从而提高带宽,为了实现在不同子网中应用不同的安全策略从而提高网络安全性,现场通常要求划分子网,将安全等级要求不同的计算机安置在不同的子网中,分开管理…...

java 面向对象高级
1.final关键字 class Demo{public static void main(String[] args) {final int[] anew int[]{1,2,3};// anew int[]{4,5,6}; 报错a[0]5;//可以,解释了final修饰引用性变量,变量存储的地址不能被改变,但地址所指向的对象的内容可以改变} }什…...
递推经典例题 - 爬楼梯
一、题目阅读 题目描述 一段楼梯有n级台阶。你每次可以跨一个、两个或者三个台阶。 请问走上n级台阶有几种方案?答案对998244353取模。 输入格式 一行一个数n。 输出格式 一行一个数,表示方案数。 样例 Input 1 3 Output 1 4 样例解释 1 1 1 3 1 2 …...

OpenCV视觉分析之目标跟踪(12)找到局部的最大值函数meanShift()的使用
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 在反向投影图像上找到一个对象。 meanShift 是一种用于图像处理和计算机视觉领域的算法,特别适用于目标跟踪、图像分割等任务。该算…...

《数据治理精选案例集2.0(2024版)》592页PDF(已授权分享)
《亿信华辰数据治理精选案例集2.0》是北京亿信华辰软件有限责任公司倾力打造的专业数据治理案例集,汇集了100个一线政企数据治理实践案例,覆盖13大行业和500业务场景,通过深入剖析数据治理难题,提供了新思路和实战经验,…...

网络六边形受到攻击
大家读完觉得有帮助记得关注和点赞!!! 抽象 现代智能交通系统 (ITS) 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 (…...
IGP(Interior Gateway Protocol,内部网关协议)
IGP(Interior Gateway Protocol,内部网关协议) 是一种用于在一个自治系统(AS)内部传递路由信息的路由协议,主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢
随着互联网技术的飞速发展,消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁,不仅优化了客户体验,还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用,并…...
Robots.txt 文件
什么是robots.txt? robots.txt 是一个位于网站根目录下的文本文件(如:https://example.com/robots.txt),它用于指导网络爬虫(如搜索引擎的蜘蛛程序)如何抓取该网站的内容。这个文件遵循 Robots…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...

LLMs 系列实操科普(1)
写在前面: 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容,原视频时长 ~130 分钟,以实操演示主流的一些 LLMs 的使用,由于涉及到实操,实际上并不适合以文字整理,但还是决定尽量整理一份笔…...
【JavaSE】多线程基础学习笔记
多线程基础 -线程相关概念 程序(Program) 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存…...
MinIO Docker 部署:仅开放一个端口
MinIO Docker 部署:仅开放一个端口 在实际的服务器部署中,出于安全和管理的考虑,我们可能只能开放一个端口。MinIO 是一个高性能的对象存储服务,支持 Docker 部署,但默认情况下它需要两个端口:一个是 API 端口(用于存储和访问数据),另一个是控制台端口(用于管理界面…...

Linux部署私有文件管理系统MinIO
最近需要用到一个文件管理服务,但是又不想花钱,所以就想着自己搭建一个,刚好我们用的一个开源框架已经集成了MinIO,所以就选了这个 我这边对文件服务性能要求不是太高,单机版就可以 安装非常简单,几个命令就…...