java创建包含复杂样式的PDF文件
简介
在项目中我们有时候会有创建复杂PDF的需求,如订单合同,今天我们使用itext工具来实现此功能。
实现
maven依赖
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.13</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
itext是一个可以操作PDF的开源工具包,类似pdfbox,但功能更加强大,官方文档
实现效果
代码实现
点击查看代码
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Font;
import com.itextpdf.text.Image;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import java.io.IOException;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class PdfUtil {
public static BaseColor blackColor = new BaseColor(0, 0, 0);
public static BaseColor redColor = new BaseColor(255, 0, 0);
public static BaseColor lightYellowColor = new BaseColor(255, 255, 153);
public static BaseColor lightBlueColor = new BaseColor(133, 188, 224);
public static BaseColor hardBlueColor = new BaseColor(31, 86, 112);
/**
* 创建cell
*
* @param context 内容
* @param aligin 对齐方式
* @param colspan 跨列数
*/
public static PdfPCell createCell(String context, int aligin, int colspan, int rowspan,
PdfPTable headerTable, Font font) {
return createCell(context, aligin, colspan, rowspan, headerTable, font, 0f, 0);
}
/**
* @param paddingLeft 左边距
* @param border Rectangle.TOP,Rectangle.BOTTOM,Rectangle.LEFT,Rectangle.RIGHT,Rectangle.BOX,Rectangle.NO_BORDER
*/
public static PdfPCell createCell(String context, int aligin, int colspan, int rowspan,
PdfPTable headerTable, Font font, Float paddingLeft, int border) {
return createCell(context, aligin, colspan, rowspan, headerTable, font, paddingLeft, border,
true, BaseColor.WHITE);
}
/**
* @param paddingLeft 左边距
* @param border Rectangle.TOP,Rectangle.BOTTOM,Rectangle.LEFT,Rectangle.RIGHT,Rectangle.BOX,Rectangle.NO_BORDER
*/
public static PdfPCell createCell(String context, int aligin, int colspan, int rowspan,
PdfPTable headerTable, Font font, Float paddingLeft, int border, BaseColor bgColor) {
return createCell(context, aligin, colspan, rowspan, headerTable, font, paddingLeft, border,
true, bgColor);
}
/**
* @param paddingLeft 左边距
* @param border Rectangle.TOP,Rectangle.BOTTOM,Rectangle.LEFT,Rectangle.RIGHT,Rectangle.BOX,Rectangle.NO_BORDER
*/
public static PdfPCell createCell(String context, int aligin, int colspan, int rowspan,
PdfPTable headerTable, Font font, Float paddingLeft, int border, boolean isSet,
BaseColor bgColor) {
if (Objects.isNull(context)) {
context = "";
}
Pattern p = Pattern.compile("[\u4e00-\u9fa5]");
Matcher m = p.matcher(context);
//包含中文,则使用中文字体
if (m.find()) {
font = ChineseFont(font.getSize(), font.getStyle(), font.getColor());
}
Paragraph elements = new Paragraph(context, font);
elements.setAlignment(aligin);
PdfPCell cell = newPdfPCell(bgColor, border, aligin, colspan, rowspan, elements);
if (paddingLeft != null) {
cell.setPaddingLeft(paddingLeft);
}
if (isSet) {
headerTable.addCell(cell);
}
return cell;
}
public static PdfPCell newPdfPCell(BaseColor bgColor, int border, int align, int colspan,
int rowspan, Paragraph paragraph) {
PdfPCell cell = new PdfPCell();
cell.setBorderColor(BaseColor.BLACK);
cell.setBorderColorLeft(BaseColor.BLACK);
cell.setBorderColorRight(BaseColor.BLACK);
cell.setBackgroundColor(bgColor);
cell.setBorder(border);
cell.setHorizontalAlignment(align); //水平居中
cell.setVerticalAlignment(Element.ALIGN_MIDDLE); //垂直居中
cell.setColspan(colspan);
cell.setRowspan(rowspan);
cell.setMinimumHeight(10);//单元格最小高度
cell.addElement(paragraph);
// 设置单元格的边框宽度和颜色
cell.setBorderWidth(0.5f); //边框的宽度
BaseColor blackColor = new BaseColor(139, 134, 130);
cell.setBorderColor(blackColor);
return cell;
}
/**
* 设置空白线
*/
public static void setBlankLine(PdfPTable headerTable, BaseColor bgColor, int columnNumber,
float fixedHeight) {
Paragraph paragraph = new Paragraph("");
PdfPCell pdfPCell = PdfUtil
.newPdfPCell(bgColor, 0, 1, columnNumber, 1, paragraph);
pdfPCell.setFixedHeight(fixedHeight);//单元格固定高度
headerTable.addCell(pdfPCell);
}
/**
* 创建单元格(内容为图片)
*
* @param bgColor 背景
* @param border 边框
* @param align 对齐方式
* @param colspan 所占列数
* @param image 内容(文字或图片对象)
*/
public static PdfPCell newPdfPCellOfImage(BaseColor bgColor, int border, int align, int colspan,
int rowspan, Image image) {
PdfPCell cell = new PdfPCell();
cell.setBorderColor(BaseColor.BLACK);
cell.setBorderColorLeft(BaseColor.BLACK);
cell.setBorderColorRight(BaseColor.BLACK);
cell.setBackgroundColor(bgColor);
cell.setBorder(border);
cell.setUseAscender(Boolean.TRUE);
cell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE);
cell.setHorizontalAlignment(align);
cell.setColspan(colspan);
cell.setRowspan(rowspan);
cell.addElement(image);
return cell;
}
/**
* 创建pdf
*/
public static Document createPDF() {
return new Document(PageSize.A4);
}
/**
* 中文字体
*/
public static Font ChineseFont(float fontSize, int style, BaseColor clolr) {
BaseFont baseFont = null;
try {
String fontDir = "/font";
//包含中文则使用微软雅黑
String fontPath = fontDir + "/msyh.ttc,0";
baseFont = BaseFont.createFont(fontPath, BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
} catch (DocumentException | IOException e) {
}
return new Font(baseFont, fontSize, style, clolr);
}
}
一个工具类,方便的创建单元格及字体,字体颜色等对象。
点击查看代码
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Document;
import com.itextpdf.text.Element;
import com.itextpdf.text.Font;
import com.itextpdf.text.Image;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfDiv;
import com.itextpdf.text.pdf.PdfDiv.PositionType;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.net.URL;
public class PdfService {
private static final int COLUMN_NUMBER = 24;
/**
* 下载pdf
*/
public void downloadPdf(OutputStream outputStream) throws Exception {
//创建字体
BaseFont baseFont = BaseFont.createFont("/font/calibri.ttf", BaseFont.IDENTITY_H,
BaseFont.EMBEDDED); //没有加粗calibrib字体
BaseFont boldFont = BaseFont.createFont("/font/calibrib.ttf", BaseFont.IDENTITY_H,
BaseFont.EMBEDDED);//加粗calibrib字体
Font bold_black_font7 = new Font(boldFont, 7.5f, Font.NORMAL, PdfUtil.blackColor);//加粗-黑色-7号字体
Font normal_hard_blue_font7 = new Font(boldFont, 7.5f, Font.NORMAL,
PdfUtil.hardBlueColor);//加粗-黑色-7号字体
Font bold_red_font7 = new Font(boldFont, 7.5f, Font.NORMAL, PdfUtil.redColor);//加粗-黑色-7号字体
Font normal_black_font5 = new Font(baseFont, 7f, Font.NORMAL, PdfUtil.blackColor);//加粗-黑色-7号字体
Font normal_red_font5 = new Font(baseFont, 7f, Font.NORMAL, PdfUtil.redColor);//加粗-黑色-7号字体
Font bold_black_font5 = new Font(baseFont, 8f, Font.NORMAL, PdfUtil.blackColor);//加粗-黑色-7号字体
Font bold_black_font16 = new Font(boldFont, 16f, Font.NORMAL, PdfUtil.blackColor);//加粗-黑色-17号字体
//创建输出流
Document document = PdfUtil.createPDF();
PdfWriter writer = PdfWriter.getInstance(document, outputStream);
writer.setPdfVersion(PdfWriter.PDF_VERSION_1_7);
// 打开文档对象
document.open();
//头部主要信息表
PdfPTable headerTable = new PdfPTable(COLUMN_NUMBER);
headerTable.setWidthPercentage(100);
Paragraph paragraph = new Paragraph("");
//第1行 公司logo
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
URL logoRes = classLoader.getResource("image/test_logo.jpeg");
URL sealRes = classLoader.getResource("image/test_seal.jpeg");
Image xieldLogoImage = Image.getInstance(logoRes);
Image sealImage = Image.getInstance(sealRes);
PdfPCell leftLogoImageCell = PdfUtil
.newPdfPCellOfImage(BaseColor.WHITE, 0, Element.ALIGN_LEFT, 4, 4, xieldLogoImage);
PdfPCell rightLogoImageCell = PdfUtil
.newPdfPCellOfImage(BaseColor.WHITE, 0, Element.ALIGN_RIGHT, 4, 4, xieldLogoImage);
//添加左边图片logo单元格
headerTable.addCell(leftLogoImageCell);
//添加空白单元格
headerTable
.addCell(PdfUtil.newPdfPCell(BaseColor.WHITE, 0, Element.ALIGN_RIGHT, 16, 4, paragraph));
//添加右边图片logo单元格
headerTable.addCell(rightLogoImageCell);
//添加空白单元格
headerTable
.addCell(PdfUtil.newPdfPCell(BaseColor.WHITE, 0, Element.ALIGN_RIGHT, 6, 2, paragraph));
//添加标题单元格
PdfUtil.createCell("合同", Element.ALIGN_CENTER, 10, 2, headerTable,
bold_black_font16);
//添加空白单元格
headerTable
.addCell(PdfUtil.newPdfPCell(BaseColor.WHITE, 0, Element.ALIGN_RIGHT, 8, 2, paragraph));
//甲方公司和乙方公司信息单元格
PdfUtil.createCell("", Element.ALIGN_LEFT, 16, 1, headerTable,
bold_black_font7);
PdfUtil
.createCell("合同编号:xxxx", Element.ALIGN_LEFT, 8, 1, headerTable, bold_black_font7,
20f, 0);
PdfUtil.createCell("甲方:xxxx有限公司", Element.ALIGN_LEFT, 16, 1, headerTable,
bold_red_font7);
PdfUtil
.createCell("乙方:xxxx有限公司", Element.ALIGN_LEFT, 8, 1, headerTable,
normal_hard_blue_font7, 20f,
0);
PdfUtil.createCell("联络人:xxx", Element.ALIGN_LEFT, 16, 1, headerTable, bold_red_font7);
PdfUtil
.createCell("联系人: xxx", Element.ALIGN_LEFT, 8, 1, headerTable,
normal_hard_blue_font7, 20f,
0);
PdfUtil.createCell("电话:xxxx", Element.ALIGN_LEFT, 16, 1, headerTable, bold_red_font7);
PdfUtil
.createCell("电话:xxxx", Element.ALIGN_LEFT, 8, 1, headerTable,
normal_hard_blue_font7, 20f,
0);
PdfUtil.createCell("E-MAIL: xxxx", Element.ALIGN_LEFT, 16, 1, headerTable,
bold_red_font7);
PdfUtil.createCell("账号:xxxx", Element.ALIGN_LEFT, 8, 1, headerTable,
normal_hard_blue_font7, 20f, 0);
PdfUtil.createCell("地址:xxxx", Element.ALIGN_LEFT, 16, 1, headerTable,
bold_red_font7);
PdfUtil.createCell("E-MAIL: xxxx", Element.ALIGN_LEFT, 8, 1, headerTable,
normal_hard_blue_font7, 20f, 0);
//设备块 上边 空白区域
PdfUtil.setBlankLine(headerTable, BaseColor.WHITE, COLUMN_NUMBER, 10);
//设备块中的明细列表 这部分应该是一个循环体
PdfUtil
.createCell("项次", Element.ALIGN_CENTER, 1, 1, headerTable, bold_black_font5,
1f, Rectangle.BOX, PdfUtil.lightBlueColor);
PdfUtil.createCell("产品名称", Element.ALIGN_CENTER, 5, 1, headerTable,
bold_black_font5, 1f, Rectangle.BOX, PdfUtil.lightBlueColor);
PdfUtil
.createCell("产品编码", Element.ALIGN_CENTER, 2, 1, headerTable, bold_black_font5,
1f, Rectangle.BOX, PdfUtil.lightBlueColor);
PdfUtil
.createCell("种类", Element.ALIGN_CENTER, 2, 1, headerTable, bold_black_font5,
1f, Rectangle.BOX, PdfUtil.lightBlueColor);
PdfUtil
.createCell("原价", Element.ALIGN_CENTER, 2, 1, headerTable, bold_black_font5,
1f, Rectangle.BOX, PdfUtil.lightBlueColor);
PdfUtil
.createCell("折扣", Element.ALIGN_CENTER, 1, 1,
headerTable, bold_black_font5, 1f, Rectangle.BOX, PdfUtil.lightBlueColor);
PdfUtil
.createCell("优惠价", Element.ALIGN_CENTER, 2, 1, headerTable, bold_black_font5,
1f, Rectangle.BOX, PdfUtil.lightBlueColor);
PdfUtil
.createCell("数量", Element.ALIGN_CENTER, 1, 1, headerTable, bold_black_font5,
1f, Rectangle.BOX, PdfUtil.lightBlueColor);
PdfUtil
.createCell("小计", Element.ALIGN_CENTER, 2, 1, headerTable, bold_black_font5,
1f, Rectangle.BOX, PdfUtil.lightBlueColor);
PdfUtil
.createCell("SN", Element.ALIGN_CENTER, 2, 1, headerTable, bold_black_font5,
1f, Rectangle.BOX, PdfUtil.lightBlueColor);
PdfUtil
.createCell("最终用户", Element.ALIGN_CENTER, 4, 1, headerTable, bold_black_font5,
1f, Rectangle.BOX, PdfUtil.lightBlueColor);
for (int i = 0; i < 2; i++) {
PdfUtil
.createCell(String.valueOf(i + 1), Element.ALIGN_CENTER, 1, 1, headerTable,
normal_black_font5,
1f, Rectangle.BOX, PdfUtil.lightYellowColor);
PdfUtil
.createCell("测试商品",
Element.ALIGN_CENTER,
5, 1, headerTable,
normal_red_font5, 1f, Rectangle.BOX, PdfUtil.lightYellowColor);
PdfUtil
.createCell("测试产品编码", Element.ALIGN_CENTER, 2, 1, headerTable, normal_red_font5,
1f, Rectangle.BOX, PdfUtil.lightYellowColor);
PdfUtil
.createCell("测试种类", Element.ALIGN_CENTER, 2, 1, headerTable, normal_red_font5,
1f, Rectangle.BOX, PdfUtil.lightYellowColor);
PdfUtil
.createCell("¥10000.00", Element.ALIGN_CENTER, 2, 1, headerTable, normal_red_font5,
1f, Rectangle.BOX, PdfUtil.lightYellowColor);
PdfUtil
.createCell("100%", Element.ALIGN_CENTER, 1, 1,
headerTable, normal_red_font5, 1f, Rectangle.BOX, PdfUtil.lightYellowColor);
PdfUtil
.createCell("¥10000.00", Element.ALIGN_CENTER, 2, 1, headerTable, normal_red_font5,
1f, Rectangle.BOX, PdfUtil.lightYellowColor);
PdfUtil
.createCell("1", Element.ALIGN_CENTER, 1, 1, headerTable, normal_red_font5,
1f, Rectangle.BOX, PdfUtil.lightYellowColor);
PdfUtil
.createCell("¥10000.00", Element.ALIGN_CENTER, 2, 1, headerTable, normal_red_font5,
1f, Rectangle.BOX, PdfUtil.lightYellowColor);
PdfUtil
.createCell("xxxx", Element.ALIGN_CENTER, 2, 1, headerTable, normal_red_font5,
1f, Rectangle.BOX, PdfUtil.lightYellowColor);
PdfUtil
.createCell("XXXX有限公司", Element.ALIGN_CENTER, 4, 1, headerTable,
normal_red_font5,
1f, Rectangle.BOX, PdfUtil.lightYellowColor);
}
PdfUtil
.createCell("产品合计(含13%增值税):", Element.ALIGN_RIGHT, 16, 1, headerTable, normal_black_font5,
1f, Rectangle.BOX);
PdfUtil
.createCell("¥10000.00", Element.ALIGN_CENTER, 2, 1, headerTable, normal_black_font5,
1f, Rectangle.BOX, PdfUtil.lightBlueColor);
//空白区域
PdfUtil.setBlankLine(headerTable, BaseColor.WHITE, COLUMN_NUMBER, 10);
PdfUtil.createCell("备注", Element.ALIGN_LEFT,
COLUMN_NUMBER, 24, headerTable, normal_hard_blue_font7);
PdfUtil.createCell("1.运费由乙方承担,默认方式为顺丰速运。也可甲方提供运输账号或甲方上门自提。", Element.ALIGN_LEFT,
COLUMN_NUMBER, 24, headerTable, normal_hard_blue_font7);
PdfUtil.createCell("一、甲方向乙方采购 XXXX。",
Element.ALIGN_LEFT,
COLUMN_NUMBER, 24, headerTable, bold_black_font7);
PdfUtil.createCell("二、有效期为30天,本合同必须在2021年11月9日起30天内签署完成,过期作废。", Element.ALIGN_LEFT,
COLUMN_NUMBER, 24, headerTable, bold_black_font7);
PdfUtil.createCell("三、本合同总金额(大写)贰万元整 人民币:¥10000.00", Element.ALIGN_LEFT,
COLUMN_NUMBER, 24, headerTable, bold_black_font7);
PdfUtil.createCell("甲方:XXXX有限公司", Element.ALIGN_LEFT,
COLUMN_NUMBER, 24, headerTable, bold_red_font7);
PdfUtil.createCell("乙方:XXXX有限公司", Element.ALIGN_LEFT,
COLUMN_NUMBER, 24, headerTable, bold_black_font7);
// 2-PDF文档属性
document.addTitle("合同");// 标题(登录id)
document.addAuthor("作者");// 作者(登录人名称)
document.addSubject("合同");// 主题
document.addKeywords("合同");// 关键字
document.addCreator("XXXX有限公司");// 谁创建的
document.add(headerTable);
PdfDiv sealImageDiv = new PdfDiv();
sealImageDiv.setPosition(PositionType.ABSOLUTE);
sealImageDiv.setPaddingLeft(300);
sealImageDiv.setPaddingBottom(700);
sealImageDiv.addElement(sealImage);
sealImage.scaleAbsolute(100f, 100f);
document.add(sealImageDiv);
//关闭
document.close();
}
public static void main(String[] args) throws Exception {
new PdfService().downloadPdf(new FileOutputStream("D:/testjar/test.pdf"));
}
}
用来创建PDF的核心逻辑,主要原理为将整个PDF视为一个大表格,每个元素都是一个单元格,类似于excel的单元格,
有普通文本的单元格,也有包含图片的单元格,每个单元格都可以设置样式,如设置宽,高,背景颜色,字体等。
例子代码中设置的总列宽为24,所以下边每一行的单元格总宽度加起来也要为24。PDF中的公司公章图片是要浮在文字上的,
不能使用单元格这种方式,要使用绝对定位。
文件资源
代码中使用到的字体和图片要放到classpath下,因为我的项目为SpringBoot项目,所以放在了resources目录下
字体下载 提取码(gonn)