springboot集成itext 9.4.0生成pdf
2025年itext已发展到9.x版本,本文将带你体验最新特性和最佳实践
在现代企业应用开发中,动态生成pdf文档是一个常见需求。无论是生成报表、发票、合同还是其他业务单据,pdf格式因其跨平台、保持布局不变的特性而备受青睐。
本文将详细介绍如何在springboot项目中集成itext 9.4.0——当前最新的版本,来实现高效、专业的pdf生成。
一、itext 9新特性与架构变革
itext 9.x相比旧版本进行了彻底的重构:
- 模块化设计:按功能拆分为多个独立模块
- 性能提升:底层架构优化,处理速度更快
- api现代化:更符合现代java开发习惯
- 功能增强:支持更多pdf标准和特性
二、环境准备与依赖配置
maven依赖配置
在springboot项目的pom.xml中添加以下依赖:
<properties>
<itext.version>9.4.0</itext.version>
</properties>
<dependencies>
<!-- itext 9 核心模块 -->
<dependency>
<groupid>com.itextpdf</groupid>
<artifactid>kernel</artifactid>
<version>${itext.version}</version>
</dependency>
<dependency>
<groupid>com.itextpdf</groupid>
<artifactid>io</artifactid>
<version>${itext.version}</version>
</dependency>
<dependency>
<groupid>com.itextpdf</groupid>
<artifactid>layout</artifactid>
<version>${itext.version}</version>
</dependency>
<dependency>
<groupid>com.itextpdf</groupid>
<artifactid>forms</artifactid>
<version>${itext.version}</version>
</dependency>
<!-- 条形码支持 -->
<dependency>
<groupid>com.itextpdf</groupid>
<artifactid>barcodes</artifactid>
<version>${itext.version}</version>
</dependency>
<!-- 中文支持 -->
<dependency>
<groupid>com.itextpdf</groupid>
<artifactid>font-asian</artifactid>
<version>${itext.version}</version>
</dependency>
</dependencies>
三、完整的service层实现
1. 基础pdf服务
@service
public class pdfservice {
/**
* 创建简单pdf文档
*/
public void createsimplepdf(outputstream outputstream) throws ioexception {
pdfwriter writer = new pdfwriter(outputstream);
pdfdocument pdfdoc = new pdfdocument(writer);
document document = new document(pdfdoc);
// 添加内容
document.add(new paragraph("hello, itext 9.4.0!")
.setfontsize(20)
.setbold());
document.add(new paragraph("这是一个使用springboot和itext 9生成的pdf文档"));
document.add(new paragraph("生成时间: " + new date()));
// 关闭文档
document.close();
}
/**
* 创建带表格的pdf
*/
public void createtablepdf(outputstream outputstream) throws ioexception {
pdfwriter writer = new pdfwriter(outputstream);
pdfdocument pdfdoc = new pdfdocument(writer);
document document = new document(pdfdoc);
// 创建3列表格
table table = new table(3);
// 添加表头
table.addheadercell(new cell().add(new paragraph("姓名").setbold()));
table.addheadercell(new cell().add(new paragraph("部门").setbold()));
table.addheadercell(new cell().add(new paragraph("工资").setbold()));
// 添加数据行
table.addcell(new cell().add(new paragraph("张三")));
table.addcell(new cell().add(new paragraph("技术部")));
table.addcell(new cell().add(new paragraph("8000")));
table.addcell(new cell().add(new paragraph("李四")));
table.addcell(new cell().add(new paragraph("市场部")));
table.addcell(new cell().add(new paragraph("7500")));
table.addcell(new cell().add(new paragraph("王五")));
table.addcell(new cell().add(new paragraph("财务部")));
table.addcell(new cell().add(new paragraph("8200")));
document.add(new paragraph("员工信息表").setfontsize(16).setbold());
document.add(table);
document.close();
}
/**
* 生成带条形码的pdf
*/
public void createpdfwithbarcode(outputstream outputstream, string barcodedata) throws ioexception {
pdfwriter writer = new pdfwriter(outputstream);
pdfdocument pdfdoc = new pdfdocument(writer);
document document = new document(pdfdoc);
// 添加标题
document.add(new paragraph("条形码示例文档")
.setfontsize(18)
.setbold()
.settextalignment(textalignment.center));
// 添加条形码
document.add(new paragraph("条形码数据: " + barcodedata));
// 创建code 128条形码
barcode128 barcode = new barcode128(pdfdoc);
barcode.setcode(barcodedata);
barcode.setfont(null); // 不显示文本
// 将条形码转换为图像
image barcodeimage = new image(barcode.createformxobject(pdfdoc))
.setwidth(200)
.setautoscaleheight(true);
document.add(barcodeimage);
document.close();
}
/**
* 生成带图片和条形码的pdf
*/
public void createpdfwithimageandbarcode(outputstream outputstream, string imageurl, string barcodedata) throws ioexception {
pdfwriter writer = new pdfwriter(outputstream);
pdfdocument pdfdoc = new pdfdocument(writer);
document document = new document(pdfdoc);
// 添加标题
document.add(new paragraph("产品信息卡")
.setfontsize(18)
.setbold()
.settextalignment(textalignment.center));
// 添加图片(如果有)
if (imageurl != null && !imageurl.trim().isempty()) {
try {
image image = new image(imagedatafactory.create(imageurl))
.setwidth(200)
.setautoscaleheight(true)
.sethorizontalalignment(horizontalalignment.center);
document.add(image);
} catch (exception e) {
document.add(new paragraph("图片加载失败: " + e.getmessage()));
}
}
// 添加条形码
if (barcodedata != null && !barcodedata.trim().isempty()) {
barcode128 barcode = new barcode128(pdfdoc);
barcode.setcode(barcodedata);
image barcodeimage = new image(barcode.createformxobject(pdfdoc))
.setwidth(250)
.setautoscaleheight(true)
.sethorizontalalignment(horizontalalignment.center);
document.add(new paragraph("产品条形码:").setbold());
document.add(barcodeimage);
}
document.close();
}
/**
* 创建报告pdf
*/
public void createreportpdf(outputstream outputstream) throws ioexception {
pdfwriter writer = new pdfwriter(outputstream);
pdfdocument pdfdoc = new pdfdocument(writer);
document document = new document(pdfdoc);
// 报告标题
document.add(new paragraph("月度销售报告")
.setfontsize(24)
.setbold()
.settextalignment(textalignment.center));
// 报告信息
document.add(new paragraph("报告周期: " + new date()));
document.add(new paragraph("生成部门: 销售部"));
// 销售数据表格
table salestable = new table(4);
salestable.addheadercell(new cell().add(new paragraph("产品").setbold()));
salestable.addheadercell(new cell().add(new paragraph("季度").setbold()));
salestable.addheadercell(new cell().add(new paragraph("销售额").setbold()));
salestable.addheadercell(new cell().add(new paragraph("增长率").setbold()));
// 示例数据
string[][] salesdata = {
{"产品a", "q1", "¥120,000", "15%"},
{"产品a", "q2", "¥138,000", "15%"},
{"产品b", "q1", "¥85,000", "8%"},
{"产品b", "q2", "¥92,000", "8%"}
};
for (string[] row : salesdata) {
for (string cell : row) {
salestable.addcell(new cell().add(new paragraph(cell)));
}
}
document.add(salestable);
document.close();
}
/**
* 创建发票pdf
*/
public void createinvoicepdf(outputstream outputstream) throws ioexception {
pdfwriter writer = new pdfwriter(outputstream);
pdfdocument pdfdoc = new pdfdocument(writer);
document document = new document(pdfdoc);
// 发票标题
document.add(new paragraph("商业发票")
.setfontsize(20)
.setbold()
.settextalignment(textalignment.center));
// 发票信息表格
table invoicetable = new table(2);
invoicetable.addcell(new cell().add(new paragraph("发票号码:").setbold()));
invoicetable.addcell(new cell().add(new paragraph("inv-2024-001")));
invoicetable.addcell(new cell().add(new paragraph("开票日期:").setbold()));
invoicetable.addcell(new cell().add(new paragraph(new date().tostring())));
invoicetable.addcell(new cell().add(new paragraph("客户名称:").setbold()));
invoicetable.addcell(new cell().add(new paragraph("某某科技有限公司")));
document.add(invoicetable);
// 商品明细表格
table itemstable = new table(4);
itemstable.addheadercell(new cell().add(new paragraph("商品名称").setbold()));
itemstable.addheadercell(new cell().add(new paragraph("数量").setbold()));
itemstable.addheadercell(new cell().add(new paragraph("单价").setbold()));
itemstable.addheadercell(new cell().add(new paragraph("金额").setbold()));
itemstable.addcell(new cell().add(new paragraph("笔记本电脑")));
itemstable.addcell(new cell().add(new paragraph("2")));
itemstable.addcell(new cell().add(new paragraph("¥5,999")));
itemstable.addcell(new cell().add(new paragraph("¥11,998")));
itemstable.addcell(new cell().add(new paragraph("无线鼠标")));
itemstable.addcell(new cell().add(new paragraph("5")));
itemstable.addcell(new cell().add(new paragraph("¥89")));
itemstable.addcell(new cell().add(new paragraph("¥445")));
document.add(new paragraph("商品明细:").setbold());
document.add(itemstable);
// 总计
document.add(new paragraph("总计: ¥12,443").setbold().setfontsize(16));
document.close();
}
}
2. 中文pdf服务
@service
public class chinesepdfservice {
/**
* 创建支持中文的pdf文档
*/
public void createchinesepdf(outputstream outputstream) throws ioexception {
pdfwriter writer = new pdfwriter(outputstream);
pdfdocument pdfdoc = new pdfdocument(writer);
document document = new document(pdfdoc);
// 使用中文字体
pdffont chinesefont = pdffontfactory.createfont("stsong-light", "unigb-ucs2-h", true);
// 添加中文内容
document.add(new paragraph("中文pdf文档示例")
.setfont(chinesefont)
.setfontsize(20)
.setbold()
.settextalignment(textalignment.center));
document.add(new paragraph("企业名称:某某科技有限公司")
.setfont(chinesefont));
document.add(new paragraph("公司地址:北京市海淀区中关村大街1号")
.setfont(chinesefont));
document.add(new paragraph("联系电话:010-12345678")
.setfont(chinesefont));
// 中文表格
table table = new table(3);
table.addheadercell(new cell().add(new paragraph("姓名").setfont(chinesefont).setbold()));
table.addheadercell(new cell().add(new paragraph("职位").setfont(chinesefont).setbold()));
table.addheadercell(new cell().add(new paragraph("部门").setfont(chinesefont).setbold()));
table.addcell(new cell().add(new paragraph("张三").setfont(chinesefont)));
table.addcell(new cell().add(new paragraph("软件工程师").setfont(chinesefont)));
table.addcell(new cell().add(new paragraph("技术部").setfont(chinesefont)));
table.addcell(new cell().add(new paragraph("李四").setfont(chinesefont)));
table.addcell(new cell().add(new paragraph("产品经理").setfont(chinesefont)));
table.addcell(new cell().add(new paragraph("产品部").setfont(chinesefont)));
document.add(table);
document.close();
}
}
3. 模板pdf服务
@service
public class templatepdfservice {
/**
* 基于模板填充pdf表单
*/
public void fillpdftemplate(outputstream outputstream, map<string, string> formdata) throws ioexception {
// 创建临时模板(实际项目中应该从文件系统或数据库读取模板)
byte[] templatebytes = createtemplatepdf();
// 读取模板
pdfreader reader = new pdfreader(new bytearrayinputstream(templatebytes));
pdfwriter writer = new pdfwriter(outputstream);
pdfdocument pdfdoc = new pdfdocument(reader, writer);
// 获取表单
pdfacroform form = pdfacroform.getacroform(pdfdoc, true);
// 填充数据
for (map.entry<string, string> entry : formdata.entryset()) {
string fieldname = entry.getkey();
string fieldvalue = entry.getvalue();
pdfformfield field = form.getfield(fieldname);
if (field != null) {
field.setvalue(fieldvalue);
}
}
// 扁平化表单(使字段不可编辑)
form.flattenfields();
pdfdoc.close();
}
/**
* 创建示例模板pdf(实际项目中应使用现有的pdf模板)
*/
private byte[] createtemplatepdf() throws ioexception {
bytearrayoutputstream baos = new bytearrayoutputstream();
pdfwriter writer = new pdfwriter(baos);
pdfdocument pdfdoc = new pdfdocument(writer);
// 创建表单
pdfacroform form = pdfacroform.getacroform(pdfdoc, true);
// 添加表单字段
rectangle rect = new rectangle(100, 700, 200, 20);
pdftextformfield namefield = pdftextformfield.createtext(pdfdoc, rect, "name", "");
form.addfield(namefield);
rect = new rectangle(100, 650, 200, 20);
pdftextformfield emailfield = pdftextformfield.createtext(pdfdoc, rect, "email", "");
form.addfield(emailfield);
rect = new rectangle(100, 600, 200, 20);
pdftextformfield phonefield = pdftextformfield.createtext(pdfdoc, rect, "phone", "");
form.addfield(phonefield);
// 添加标签
document document = new document(pdfdoc);
document.add(new paragraph("姓名:").setfixedposition(50, 700, 50));
document.add(new paragraph("邮箱:").setfixedposition(50, 650, 50));
document.add(new paragraph("电话:").setfixedposition(50, 600, 50));
document.close();
return baos.tobytearray();
}
}
四、完整的pdf生成controller
@restcontroller
@requestmapping("/api/pdf")
@crossorigin(origins = "*")
public class pdfgeneratorcontroller {
@autowired
private pdfservice pdfservice;
@autowired
private chinesepdfservice chinesepdfservice;
@autowired
private templatepdfservice templatepdfservice;
/**
* 1. 基础pdf生成接口
* 生成包含简单文本的pdf文档
*/
@getmapping("/basic")
public responseentity<byte[]> generatebasicpdf() {
try {
bytearrayoutputstream outputstream = new bytearrayoutputstream();
pdfservice.createsimplepdf(outputstream);
return responseentity.ok()
.contenttype(mediatype.application_pdf)
.header("content-disposition", "inline; filename=\"basic-document.pdf\"")
.body(outputstream.tobytearray());
} catch (ioexception e) {
return responseentity.status(httpstatus.internal_server_error)
.body(("pdf生成失败: " + e.getmessage()).getbytes());
}
}
/**
* 2. 中文pdf生成接口
* 演示中文字体处理和中文内容支持
*/
@getmapping("/chinese")
public responseentity<byte[]> generatechinesepdf() {
try {
bytearrayoutputstream outputstream = new bytearrayoutputstream();
chinesepdfservice.createchinesepdf(outputstream);
return responseentity.ok()
.contenttype(mediatype.application_pdf)
.header("content-disposition", "inline; filename=\"chinese-document.pdf\"")
.body(outputstream.tobytearray());
} catch (ioexception e) {
return responseentity.status(httpstatus.internal_server_error)
.body(("中文pdf生成失败: " + e.getmessage()).getbytes());
}
}
/**
* 3. 表格pdf生成接口
* 展示表格创建和数据展示功能
*/
@getmapping("/table")
public responseentity<byte[]> generatetablepdf() {
try {
bytearrayoutputstream outputstream = new bytearrayoutputstream();
pdfservice.createtablepdf(outputstream);
return responseentity.ok()
.contenttype(mediatype.application_pdf)
.header("content-disposition", "inline; filename=\"table-document.pdf\"")
.body(outputstream.tobytearray());
} catch (ioexception e) {
return responseentity.status(httpstatus.internal_server_error)
.body(("表格pdf生成失败: " + e.getmessage()).getbytes());
}
}
/**
* 4. 条形码pdf生成接口
* 演示条形码生成功能(需要barcodes模块)
*/
@postmapping("/barcode")
public responseentity<byte[]> generatebarcodepdf(@requestparam string barcodedata) {
try {
bytearrayoutputstream outputstream = new bytearrayoutputstream();
pdfservice.createpdfwithbarcode(outputstream, barcodedata);
return responseentity.ok()
.contenttype(mediatype.application_pdf)
.header("content-disposition", "inline; filename=\"barcode-document.pdf\"")
.body(outputstream.tobytearray());
} catch (ioexception e) {
return responseentity.status(httpstatus.internal_server_error)
.body(("条形码pdf生成失败: " + e.getmessage()).getbytes());
}
}
/**
* 5. 图片pdf生成接口
* 展示图片和条形码的混合文档
*/
@postmapping("/image-barcode")
public responseentity<byte[]> generateimagebarcodepdf(
@requestparam(required = false) string imageurl,
@requestparam string barcodedata) {
try {
bytearrayoutputstream outputstream = new bytearrayoutputstream();
pdfservice.createpdfwithimageandbarcode(outputstream, imageurl, barcodedata);
return responseentity.ok()
.contenttype(mediatype.application_pdf)
.header("content-disposition", "inline; filename=\"image-barcode-document.pdf\"")
.body(outputstream.tobytearray());
} catch (ioexception e) {
return responseentity.status(httpstatus.internal_server_error)
.body(("图片条形码pdf生成失败: " + e.getmessage()).getbytes());
}
}
/**
* 6. 模板pdf生成接口
* 基于现有pdf模板填充数据
*/
@postmapping("/template")
public responseentity<byte[]> generatetemplatepdf(@requestbody map<string, string> formdata) {
try {
bytearrayoutputstream outputstream = new bytearrayoutputstream();
templatepdfservice.fillpdftemplate(outputstream, formdata);
return responseentity.ok()
.contenttype(mediatype.application_pdf)
.header("content-disposition", "inline; filename=\"template-document.pdf\"")
.body(outputstream.tobytearray());
} catch (ioexception e) {
return responseentity.status(httpstatus.internal_server_error)
.body(("模板pdf生成失败: " + e.getmessage()).getbytes());
}
}
/**
* 7. 报告pdf下载接口
*/
@getmapping("/download/report")
public responseentity<byte[]> downloadreportpdf() {
try {
bytearrayoutputstream outputstream = new bytearrayoutputstream();
pdfservice.createreportpdf(outputstream);
return responseentity.ok()
.contenttype(mediatype.application_pdf)
.header("content-disposition", "attachment; filename=\"sales-report.pdf\"")
.body(outputstream.tobytearray());
} catch (ioexception e) {
return responseentity.status(httpstatus.internal_server_error)
.body(("报告pdf下载失败: " + e.getmessage()).getbytes());
}
}
/**
* 8. 发票pdf下载接口
*/
@getmapping("/download/invoice")
public responseentity<byte[]> downloadinvoicepdf() {
try {
bytearrayoutputstream outputstream = new bytearrayoutputstream();
pdfservice.createinvoicepdf(outputstream);
return responseentity.ok()
.contenttype(mediatype.application_pdf)
.header("content-disposition", "attachment; filename=\"invoice.pdf\"")
.body(outputstream.tobytearray());
} catch (ioexception e) {
return responseentity.status(httpstatus.internal_server_error)
.body(("发票pdf下载失败: " + e.getmessage()).getbytes());
}
}
}
五、itext框架优劣分析
优势
- 功能全面:支持文本、表格、图片、条形码、表单等几乎所有pdf功能
- 性能优秀:处理大型文档时性能表现良好
- 标准兼容:完美支持pdf/a、pdf/ua等国际标准
- 文档完善:官方文档详细,社区活跃
- 企业级支持:提供商业许可证和技术支持
劣势
- 学习曲线:api较为复杂,新手需要时间适应
- 许可证限制:agpl协议对商业使用有限制
- 内存占用:处理大型文档时内存消耗较高
- 配置复杂:模块化设计增加了依赖管理的复杂度
六、不同版本差异对比
| 特性 | itext 5.x | itext 7.x/9.x | 说明 |
|---|---|---|---|
| 架构设计 | 单体架构 | 模块化设计 | itext 9按功能拆分为多个jar |
| api设计 | 传统api | 现代化api | itext 9 api更清晰一致 |
| 条形码支持 | 内置核心 | 独立模块 | itext 9需要单独引入barcodes |
| 性能表现 | 一般 | 优化提升 | itext 9底层重构,性能更好 |
| 学习资源 | 丰富 | 相对较少 | itext 5教程更多,itext 9较新 |
| 维护状态 | 维护模式 | 积极开发 | itext 9是未来发展方向 |
七、版本选择建议
选择itext 5.x的情况
- 维护遗留项目
- 需要大量现有代码示例
- 项目对性能要求不高
- 快速原型开发
选择itext 7.x/9.x的情况
- 新项目开发
- 需要最佳性能
- 长期维护考虑
- 需要使用最新pdf标准特性
八、最佳实践总结
- 依赖管理:仔细选择所需模块,避免引入不必要的依赖
- 资源清理:使用try-with-resources确保pdf文档正确关闭
- 异常处理:妥善处理io异常和itext特定异常
- 内存管理:对于大文档,考虑分块处理和流式输出
- 字体优化:预加载和复用字体对象提升性能
总结
通过本文的完整示例和详细分析,你应该能够在springboot项目中顺利集成itext 9.4.0,并根据具体需求选择合适的版本和功能模块。
itext虽然有一定的学习成本,但其强大的功能和稳定性使其成为企业级pdf处理的优选方案。
itext官方文档:https://itextpdf.com/
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论