簡介
在項目中我們有時候會有創建復雜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)