一、依赖
<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>
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>2.0.24</version>
</dependency>
二、枚举
- 内容显示方式
public enum CellAlign {
LEFT(0),
CENTER(1),
RIGHT(2),
TOP(4),
MIDDLE(5),
BOTTOM(6);
private Integer align;
private CellAlign(Integer align) {
this.align = align;
}
public Integer getAlign() {
return this.align;
}
}
- 数据类型
public enum DataType {
TEXT,
IMAGE;
}
- 文字样式
public enum TextAttr {
TEXTFONT,
TEXTCOLOR,
TEXTSIZE,
BGCOLOR,
BORDERCOLOR,;
}
三、异常
public class PdfException extends Exception {
private static final long serialVersionUID = 1L;
public PdfException(String message) {
super(message);
}
public PdfException(Throwable cause) {
super(cause);
}
public PdfException(String message, Throwable cause) {
super(message, cause);
}
}
四、相关配置类
方式一:
使用 Adobe Acrobat DC 制作 pdf 模版,根据制作 pdf 模板生成 pdf 使用到的类
- 模版配置
import org.springframework.core.io.ClassPathResource;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
public class PdfTemplate {
/**
* pdf 名称
*/
private String title;
/**
* pdf 模板
*/
private InputStream template;
/**
* 需要填入 pdf 文件中的数据
*/
private Map<String, PdfData> values;
/**
* 用于获取 pdf 文件中某些字段的位置信息
*/
private Map<String, Position> positions;
public PdfTemplate() {
}
public PdfTemplate(String title, InputStream template) {
this.title = title;
this.template = template;
}
public PdfTemplate(String title, String templatePath) {
this.title = title;
try {
this.template = new ClassPathResource(templatePath).getInputStream();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public PdfTemplate addValue(String key,PdfData value){
if(this.values == null){
this.values = new HashMap<>();
}
this.values.put(key,value);
return this;
}
public PdfTemplate addPositions(String key,Position value){
if(this.positions == null){
this.positions = new HashMap<>();
}
this.positions.put(key,value);
return this;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public InputStream getTemplate() {
return template;
}
public void setTemplate(InputStream template) {
this.template = template;
}
public Map<String, PdfData> getValues() {
return values;
}
public void setValues(Map<String, PdfData> values) {
this.values = values;
}
public Map<String, Position> getPositions() {
return positions;
}
public void setPositions(Map<String, Position> positions) {
this.positions = positions;
}
}
- 数据信息类
import com.itextpdf.text.Image;
import com.kuroshiro.pdf.enums.DataType;
import com.kuroshiro.pdf.enums.TextAttr;
import java.util.HashMap;
import java.util.Map;
public class PdfData {
private DataType type = DataType.TEXT;
/**
* pdf 对应 key 需要填入的值
*/
private String value;
/**
* pdf 对应 key 需要填入的值
*/
private Image image;
/**
* 设置字体样式。有效的属性名称是:
* textfont - 设置文本字体。该条目的值是一个 BaseFont。
* textcolor - 设置文本颜色。该条目的值是一个 BaseColor。
* textsize - 设置文本大小。该表项的值为 Float 类型。
* bgcolor - 设置背景颜色。该条目的值是一个 BaseColor。如果为null则删除背景。
* bordercolor - 设置边框颜色。该条目的值是一个 BaseColor。如果为null则删除边框。
*/
private Map<TextAttr, Object> textStyles;
public PdfData(String value) {
this.value = value;
}
public PdfData(Image image) {
this.type = DataType.IMAGE;
this.image = image;
}
public PdfData addTextStyle(TextAttr key,Object value){
if(this.textStyles == null){
this.textStyles = new HashMap<>();
}
this.textStyles.put(key,value);
return this;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public DataType getType() {
return type;
}
public void setType(DataType type) {
this.type = type;
}
public Image getImage() {
return image;
}
public void setImage(Image image) {
this.image = image;
}
public Map<TextAttr, Object> getTextStyles() {
return textStyles;
}
public void setTextStyles(Map<TextAttr, Object> textStyles) {
this.textStyles = textStyles;
}
}
- 位置信息类
import java.math.BigDecimal;
import java.util.Objects;
public class Position {
private BigDecimal page;
private BigDecimal positionX;
private BigDecimal positionY;
public Position() {
}
public Position(BigDecimal page, BigDecimal positionX, BigDecimal positionY) {
this.page = page;
this.positionX = positionX;
this.positionY = positionY;
}
public Position(int page, int positionX, int positionY) {
this.page = BigDecimal.valueOf((long)page);
this.positionX = BigDecimal.valueOf((long)positionX);
this.positionY = BigDecimal.valueOf((long)positionY);
}
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o != null && this.getClass() == o.getClass()) {
Position postion = (Position)o;
return Objects.equals(this.page.intValue(), postion.page.intValue()) && Objects.equals(this.positionX.intValue(), postion.positionX.intValue()) && Objects.equals(this.positionY.intValue(), postion.positionY.intValue());
} else {
return false;
}
}
public int hashCode() {
return Objects.hash(new Object[]{this.page, this.positionX, this.positionY});
}
public BigDecimal getPage() {
return page;
}
public void setPage(BigDecimal page) {
this.page = page;
}
public BigDecimal getPositionX() {
return positionX;
}
public void setPositionX(BigDecimal positionX) {
this.positionX = positionX;
}
public BigDecimal getPositionY() {
return positionY;
}
public void setPositionY(BigDecimal positionY) {
this.positionY = positionY;
}
}
方式二:
创建简单的 table 形式的 pdf
- Table 配置
import java.util.ArrayList;
import java.util.List;
public class PdfTable {
/**
* 标题
*/
private String title;
/**
* 标题字体大小
*/
private Float fontSize = 21.0F;
/**
* table 列数
*/
private int columns = 1;
/**
* table cell 信息
*/
private List<PdfTableCell> cells;
/**
* 标题是否需要边框
*/
private boolean haveBorder = false;
/**
* 列宽比例
*/
private float[] columnWidths;
public PdfTable(String title) {
this.title = title;
}
public PdfTable(String title, boolean haveBorder) {
this.title = title;
this.haveBorder = haveBorder;
}
public PdfTable(String title,int columns) {
this.title = title;
this.columns = columns<1?1:columns;
}
public PdfTable(String title,int columns, boolean haveBorder) {
this.title = title;
this.columns = columns<1?1:columns;
this.haveBorder = haveBorder;
}
public PdfTable(String title, Float fontSize, int columns) {
this.title = title;
this.fontSize = fontSize;
this.columns = columns<1?1:columns;
}
public PdfTable(String title, Float fontSize, int columns, boolean haveBorder) {
this.title = title;
this.fontSize = fontSize;
this.columns = columns<1?1:columns;
this.haveBorder = haveBorder;
}
public PdfTable addCell(PdfTableCell cell){
if(cells == null){
cells = new ArrayList(){{
add(cell);
}};
}else{
cells.add(cell);
}
return this;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Float getFontSize() {
return fontSize;
}
public void setFontSize(Float fontSize) {
this.fontSize = fontSize;
}
public int getColumns() {
return columns;
}
public void setColumns(int columns) {
this.columns = columns;
}
public List<PdfTableCell> getCells() {
return cells;
}
public void setCells(List<PdfTableCell> cells) {
this.cells = cells;
}
public boolean isHaveBorder() {
return haveBorder;
}
public void setHaveBorder(boolean haveBorder) {
this.haveBorder = haveBorder;
}
public float[] getColumnWidths() {
return columnWidths;
}
public void setColumnWidths(float[] columnWidths) {
this.columnWidths = columnWidths;
}
}
- Cell 配置
import com.itextpdf.text.Image;
import com.kuroshiro.pdf.enums.CellAlign;
import com.kuroshiro.pdf.enums.DataType;
public class PdfTableCell {
private DataType type;
private Image image;
private String info;
private Float fontSize = 16.0F;
private CellAlign horizontalAlign = CellAlign.CENTER;
private CellAlign verticalAlign = CellAlign.MIDDLE;
private int colspan = 1;
private boolean haveBorder = true;
public PdfTableCell(String info) {
this.type = DataType.TEXT;
this.info = info;
}
public PdfTableCell(String info,Float fontSize) {
this.type = DataType.TEXT;;
this.info = info;
this.fontSize = fontSize;
}
public PdfTableCell(String info,int colspan) {
this.type = DataType.TEXT;
this.info = info;
this.colspan = colspan<1?1:colspan;
}
public PdfTableCell(String info,int colspan,Float fontSize) {
this.type = DataType.TEXT;;
this.info = info;
this.fontSize = fontSize;
this.colspan = colspan<1?1:colspan;
}
public PdfTableCell(String info,int colspan,boolean haveBorder,Float fontSize, CellAlign horizontalAlign, CellAlign verticalAlign) {
this.type = DataType.TEXT;;
this.info = info;
this.fontSize = fontSize==null?16F:fontSize;
this.horizontalAlign = horizontalAlign==null?CellAlign.CENTER:horizontalAlign;
this.verticalAlign = verticalAlign==null?CellAlign.MIDDLE:verticalAlign;
this.colspan = colspan<1?1:colspan;
this.haveBorder = haveBorder;
}
public PdfTableCell(Image image) {
this.type = DataType.IMAGE;
this.image = image;
}
public PdfTableCell(Image image,int colspan) {
this.type = DataType.IMAGE;
this.image = image;
this.colspan = colspan<1?1:colspan;
}
public PdfTableCell(Image image,int colspan,boolean haveBorder) {
this.type = DataType.IMAGE;
this.image = image;
this.colspan = colspan<1?1:colspan;
this.haveBorder = haveBorder;
}
public Float getFontSize() {
return fontSize;
}
public void setFontSize(Float fontSize) {
this.fontSize = fontSize;
}
public DataType getType() {
return type;
}
public void setType(DataType type) {
this.type = type;
}
public Image getImage() {
return image;
}
public void setImage(Image image) {
this.image = image;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
public CellAlign getHorizontalAlign() {
return horizontalAlign;
}
public void setHorizontalAlign(CellAlign horizontalAlign) {
this.horizontalAlign = horizontalAlign;
}
public CellAlign getVerticalAlign() {
return verticalAlign;
}
public void setVerticalAlign(CellAlign verticalAlign) {
this.verticalAlign = verticalAlign;
}
public int getColspan() {
return colspan;
}
public void setColspan(int colspan) {
this.colspan = colspan;
}
public boolean isHaveBorder() {
return haveBorder;
}
public void setHaveBorder(boolean haveBorder) {
this.haveBorder = haveBorder;
}
}
五、工具类
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;
import com.kuroshiro.pdf.enums.DataType;
import com.kuroshiro.pdf.enums.TextAttr;
import com.kuroshiro.pdf.exception.PdfException;
import com.kuroshiro.pdf.model.*;
import com.kuroshiro.pdf.model.PdfTemplate;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDType0Font;
import org.apache.pdfbox.rendering.ImageType;
import org.apache.pdfbox.rendering.PDFRenderer;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
import javax.imageio.stream.MemoryCacheImageOutputStream;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
/**
* pdf 生成工具
*/
public class PDFUtils {
private static Charset charset;
public PDFUtils() {
}
/**
* 计算某个数据在某个位置范围内显示的位置信息
*
* 一个位置需要显示多个数据时计算每个数据的位置
*
* @param positionx 位置范围的 x 坐标
* @param positiony 位置范围的 y 坐标
* @param index 需要计算的数据下标
* @param width 每个数据占用的宽度
* @param height 每个数据占用的高度
* @param lineCount 该位置一行能够显示的数据个数
* @return
*/
public static Position calcPosition(BigDecimal positionx, BigDecimal positiony, int index, BigDecimal width, BigDecimal height, int lineCount) {
int j = 0;
if (index != 0 && index % lineCount == 0) {
++j;
}
Position postionresult = new Position();
int px = index % lineCount;
postionresult.setPositionX(positionx.add((new BigDecimal(px)).multiply(width)));
postionresult.setPositionY(positiony.add((new BigDecimal(j)).multiply(height)));
return postionresult;
}
/**
* 根据pdf模版创建pdf
* @param pdfTemplate
* @return
* @throws PdfException
*/
public static byte[] createPdfByTemplate(PdfTemplate pdfTemplate) throws PdfException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
byte[] res;
try {
BaseFont simsun_font = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
PdfReader reader = new PdfReader(pdfTemplate.getTemplate());
PdfStamper stamper = new PdfStamper(reader, os);
AcroFields form = stamper.getAcroFields();
if(pdfTemplate.getValues() != null && !pdfTemplate.getValues().isEmpty()){
for(String valueKey:pdfTemplate.getValues().keySet()){
int type = form.getFieldType(valueKey);
/**
* FIELD_TYPE_NONE 0 无效字段或字段不存在
* FIELD_TYPE_PUSHBUTTON 1 按钮(PushButton)
* FIELD_TYPE_CHECKBOX 2 复选框(Checkbox)
* FIELD_TYPE_RADIOBUTTON 3 单选按钮(RadioButton)
* FIELD_TYPE_TEXT 4 文本框(Text Field)
* FIELD_TYPE_LIST 5 下拉列表(List Field)
* FIELD_TYPE_COMBO 6 组合框(Combo Box)
* FIELD_TYPE_SIGNATURE 7 数字签名(Signature Field)
*/
if (type != AcroFields.FIELD_TYPE_NONE) {
PdfData pdfData = pdfTemplate.getValues().get(valueKey);
if(DataType.IMAGE.equals(pdfData.getType())){
int pageNo = form.getFieldPositions(valueKey).get(0).page;
Rectangle signRect = form.getFieldPositions(valueKey).get(0).position;
float x = signRect.getLeft();
float y = signRect.getBottom();
//读取图片
Image image = pdfData.getImage();
//获取图片页面
PdfContentByte under = stamper.getOverContent(pageNo);
//图片大小自适应
image.scaleToFit(signRect.getWidth(), signRect.getHeight());
//添加图片
image.setAbsolutePosition(x, y);
under.addImage(image);
continue;
}
switch (type) {
case AcroFields.FIELD_TYPE_CHECKBOX:
/**
* 设置字段属性只读
* inst[] 当表单字段由多个控件(如跨页的单选框)组成时,指定操作哪个实例。
* 常见用法:
* null:操作所有实例。
* new int[]{0}:操作第一个实例(索引从 0 开始)。
*/
form.setFieldProperty(valueKey, "setfflags", PdfFormField.FF_READ_ONLY, null);
form.setField(valueKey, pdfData.getValue().toString());
break;
case AcroFields.FIELD_TYPE_TEXT:
form.setFieldProperty(valueKey, "textfont", simsun_font, null);
if (pdfData.getTextStyles() != null && !pdfData.getTextStyles().isEmpty()) {
for(TextAttr attrKey : pdfData.getTextStyles().keySet()) {
/**
* 设置字段属性。有效的属性名称是:
* textfont - 设置文本字体。该条目的值是一个BaseFont。
* textcolor - 设置文本颜色。该条目的值是一个BaseColor。
* textsize - 设置文本大小。该表项的值为Float类型。
* bgcolor - 设置背景颜色。该条目的值是一个BaseColor。如果为null则删除背景。
* bordercolor - 设置边框颜色。该条目的值是一个BaseColor。如果为null则删除边框。
*/
form.setFieldProperty(valueKey, attrKey.name(), pdfData.getTextStyles().get(attrKey), null);
}
}
form.setField(valueKey, pdfData.getValue().toString());
break;
case AcroFields.FIELD_TYPE_SIGNATURE:
AcroFields.Item item = form.getFieldItem(valueKey);
// 获取该字段的第一个控件(PdfDictionary),表单字段可能有多个控件(如多页表单)
PdfDictionary widget = item.getWidget(0);
// 提取控件的坐标范围(PdfArray),格式为 [左下角X, 左下角Y, 右上角X, 右上角Y]
PdfArray array = widget.getAsArray(PdfName.RECT);
Image image = Image.getInstance(Base64.getDecoder().decode(pdfData.getValue()));
// 获取字段所在页面的 “最上层画布”(PdfContentByte),用于覆盖内容(不会破坏原有表单字段)
PdfContentByte under = stamper.getOverContent(item.getPage(0));
image.setAbsolutePosition((float)(new BigDecimal(array.getAsNumber(0).toString())).intValue(), (float)(new BigDecimal(array.getAsNumber(1).toString())).intValue());
under.addImage(image);
}
}
}
}
if (pdfTemplate.getPositions() != null && !pdfTemplate.getPositions().isEmpty()) {
for(String key : pdfTemplate.getPositions().keySet()) {
if(form.getFieldType(key) != AcroFields.FIELD_TYPE_NONE){
AcroFields.Item item = form.getFieldItem(key);
PdfDictionary widget = item.getWidget(0);
PdfArray array = widget.getAsArray(PdfName.RECT);
(pdfTemplate.getPositions().get(key)).setPage(new BigDecimal(item.getPage(0)));
(pdfTemplate.getPositions().get(key)).setPositionX(new BigDecimal(array.getAsNumber(0).toString()));
(pdfTemplate.getPositions().get(key)).setPositionY(new BigDecimal(array.getAsNumber(1).toString()));
}
}
}
stamper.setFormFlattening(true);
stamper.close();
res = os.toByteArray();
} catch (Exception e) {
throw new PdfException("根据pdf模板生成pdf异常", e);
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return res;
}
/**
* 根据pdf模版创建pdf
* @param pdfTemplate
* @return
* @throws PdfException
*/
public static void createPdfByTemplate(PdfTemplate pdfTemplate, OutputStream out) throws PdfException {
Document doc = new Document();
try {
PdfReader reader = new PdfReader(createPdfByTemplate(pdfTemplate));
PdfCopy copy = new PdfCopy(doc, out);
doc.open();
int page = reader.getNumberOfPages();
for(int j = 1; j <= page; ++j) {
doc.newPage();
PdfImportedPage copypage = copy.getImportedPage(reader, j);
copy.addPage(copypage);
}
doc.close();
} catch (DocumentException e) {
throw new PdfException("根据pdf模板生成pdf异常",e);
} catch (IOException e) {
throw new PdfException("根据pdf模板生成pdf异常",e);
}finally {
if (doc != null) {
try {
doc.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
}
/**
* 根据模版批量生成 pdf 到一个文件中
* @param pdfTemplates 每个 pdf 相关的配置
* @return 文件字节数组
* @throws PdfException
*/
public static byte[] createPdfByTemplates(List<PdfTemplate> pdfTemplates) throws PdfException{
ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
List<PdfReader> list = new ArrayList<>();
for (PdfTemplate pdfTemplate:pdfTemplates) {
list.add(new PdfReader(createPdfByTemplate(pdfTemplate)));
}
// 将ByteArray字节数组中的流输出到out中(即输出到浏览器)
Document doc = new Document();
PdfCopy copy = new PdfCopy(doc, os);
doc.open();
for (PdfReader reader:list) {
int page = reader.getNumberOfPages();
for(int j = 1; j <= page; ++j) {
doc.newPage();
PdfImportedPage copypage = copy.getImportedPage(reader, j);
copy.addPage(copypage);
}
}
doc.close();
return os.toByteArray();
}catch (Exception e){
throw new PdfException("根据pdf模板生成pdf异常",e);
}finally {
if (os != null) {
try {
os.flush();
os.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
}
/**
* 根据模版批量生成 pdf 到一个文件中
* @param pdfTemplates 每个 pdf 相关的配置
* @param out 文件输出位置
*/
public static void createPdfByTemplates(List<PdfTemplate> pdfTemplates,OutputStream out){
try {
List<PdfReader> list = new ArrayList<>();
for (PdfTemplate pdfTemplate:pdfTemplates) {
list.add(new PdfReader(createPdfByTemplate(pdfTemplate)));
}
// 将ByteArray字节数组中的流输出到out中(即输出到浏览器)
Document doc = new Document();
PdfCopy copy = new PdfCopy(doc, out);
doc.open();
for (PdfReader reader:list) {
int page = reader.getNumberOfPages();
for(int j = 1; j <= page; ++j) {
doc.newPage();
PdfImportedPage copypage = copy.getImportedPage(reader, j);
copy.addPage(copypage);
}
}
doc.close();
}catch (Exception e){
e.printStackTrace();
}finally {
try {
if (out != null) {
out.flush();
out.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
/**
* 创建pdf
* @param pdf
* @param target
* @throws PdfException
*/
public static void createSimpleTablePDF(PdfTable pdf, OutputStream target) throws PdfException {
try {
Document document = new Document(PageSize.A4, 50.0F, 50.0F, 50.0F, 50.0F);
PdfWriter writer = PdfWriter.getInstance(document, target);
document.open();
BaseFont simsun_font = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
Font titleFont = new Font(simsun_font, pdf.getFontSize());
PdfPTable table = new PdfPTable(pdf.getColumns());
if(pdf.getColumnWidths() != null){
table.setWidths(pdf.getColumnWidths());
}
table.setWidthPercentage(100.0F);
String title = pdf.getTitle();
PdfPCell titlecell = new PdfPCell();
titlecell.setColspan(pdf.getColumns());
titlecell.setFixedHeight(50);
if(!pdf.isHaveBorder()){
titlecell.setBorder(0);
}
// titlecell.setBorderWidth(2.0F);
// 水平居中
titlecell.setHorizontalAlignment(Element.ALIGN_CENTER);
// 垂直居中
titlecell.setVerticalAlignment(Element.ALIGN_MIDDLE);
titlecell.setPhrase(new Paragraph(title, titleFont));
table.addCell(titlecell);
for(int i = 0; pdf.getCells() != null && i < pdf.getCells().size(); ++i) {
PdfTableCell pdfTableCell = pdf.getCells().get(i);
PdfPCell cell = new PdfPCell();
if(!pdfTableCell.isHaveBorder()){
cell.setBorder(0);
}
cell.setColspan(pdfTableCell.getColspan());
// cell.setBorderWidthTop(i == 0 ? 0.0F : 1.0F);
// cell.setBorderWidthBottom(i == pdf.getCells().size() - 1 ? 2.0F : 1.0F);
// cell.setBorderWidthLeft(2.0F);
// cell.setBorderWidthRight(2.0F);
if (pdfTableCell.getType().equals(DataType.IMAGE)) {
cell.addElement((pdf.getCells().get(i)).getImage());
} else if (pdfTableCell.getType().equals(DataType.TEXT)) {
cell.setHorizontalAlignment(pdfTableCell.getHorizontalAlign().getAlign());
cell.setVerticalAlignment(pdfTableCell.getVerticalAlign().getAlign());
cell.setPhrase(new Paragraph(pdfTableCell.getInfo(), new Font(simsun_font,pdfTableCell.getFontSize())));
}
table.addCell(cell);
}
document.add(table);
document.close();
writer.close();
} catch (Exception e) {
throw new PdfException("pdf创建失败", e);
}
}
/**
* pdf合成
* @param pdfs
* @param target
* @throws PdfException
*/
public static void mergePdf(List<? extends InputStream> pdfs, OutputStream target) throws PdfException {
try {
Document targetdocument = new Document();
PdfCopy copy = new PdfCopy(targetdocument, target);
targetdocument.open();
for(int i = 0; i < pdfs.size(); ++i) {
PdfReader reader = new PdfReader(pdfs.get(i));
int page = reader.getNumberOfPages();
for(int j = 1; j <= page; ++j) {
targetdocument.newPage();
PdfImportedPage copypage = copy.getImportedPage(reader, j);
copy.addPage(copypage);
}
}
targetdocument.close();
} catch (Exception e) {
throw new PdfException("pdf合成异常", e);
}
}
/**
* pdf 转图片
* @param pdf
* @return
* @throws PdfException
*/
public static List<ByteArrayOutputStream> pdfImage(InputStream pdf) throws PdfException {
PDDocument document = null;
List<ByteArrayOutputStream> res;
try {
document = PDDocument.load(pdf);
// PDFont font = PDType0Font.load(document, PDFUtils.class.getResourceAsStream("/static/fonts/STSong-Light.ttf"), true);
PDFRenderer renderer = new PDFRenderer(document);
List<ByteArrayOutputStream> oses = new ArrayList();
for(int i = 0; i < document.getNumberOfPages(); ++i) {
// PDPage page = document.getPage(i);
// PDResources resources = page.getResources();
// String name = resources.add(font).getName();
// page.setResources(resources);
BufferedImage image = renderer.renderImageWithDPI(i, 120.0F, ImageType.RGB);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ImageOutputStream ios = new MemoryCacheImageOutputStream(bos);
ImageWriter jpg_writer = ImageIO.getImageWritersByFormatName("jpg").next();
ImageWriteParam jpg_write_param = jpg_writer.getDefaultWriteParam();
jpg_write_param.setCompressionMode(2);
jpg_write_param.setCompressionQuality(0.7F);
jpg_writer.setOutput(ios);
IIOImage output_image = new IIOImage(image, null, null);
jpg_writer.write(null, output_image, jpg_write_param);
jpg_writer.dispose();
bos.flush();
oses.add(bos);
ios.close();
}
res = oses;
} catch (Exception e) {
throw new PdfException("pdf转换异常", e);
} finally {
if (document != null) {
try {
document.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return res;
}
public static void setCharset(Charset charset) {
PDFUtils.charset = charset;
}
static {
charset = StandardCharsets.UTF_8;
}
}
六、测试
public class PdfTest {
public static void main(String[] args) throws Exception {
pdfTemplate();
pdfTable();
}
public static void pdfTemplate() throws Exception {
List<PdfTemplate> list = new ArrayList<>();
for (int i = 0; i < 3; i++) {
PdfTemplate pdfTemplate = new PdfTemplate("测试","/pdfs/xxk.pdf");
pdfTemplate.addValue("cyjgmc",new PdfData("从业机构"+i))
.addValue("jgmc",new PdfData("从业机构1111"+i))
.addValue("name",new PdfData("张三"+i))
.addValue("xxklx",(new PdfData("经纪人")).addTextStyle(TextAttr.TEXTCOLOR, BaseColor.RED))
.addValue("xxkh",new PdfData("18313226955"))
.addValue("xxkyxq",new PdfData("2026-12-18 止+i"))
.addValue("zjz",new PdfData(Image.getInstance("E:\\zjz.jpg")))
.addValue("xxkyxq",new PdfData("2026-12-18 止+i"));
String content = "http://sys.yn-yf.cn/fdcjj/wx/ryxx/12345";
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
Resource resource = new cn.hutool.core.io.resource.ClassPathResource("/static/img/15620371964200.png");
QRCodeUtil.encode(content,resource.getUrl().getPath(),outStream,true);
pdfTemplate.addValue("qrcode", new PdfData(Image.getInstance(outStream.toByteArray())));
list.add(pdfTemplate);
}
PDFUtils.createPdfByTemplates(list,new FileOutputStream("E:\\xxk1.pdf"));
}
public static void pdfTable() throws Exception {
PdfTable pdfTable = new PdfTable("员工信息表",4);
pdfTable.setColumnWidths(new float[]{1.0f,2.0f,1.0f,2.0f});
// 添加文本单元格
pdfTable.addCell(new PdfTableCell( "姓名"))
.addCell(new PdfTableCell( "测试测试2222"))
.addCell(new PdfTableCell( "电话"))
.addCell(new PdfTableCell( "15912153741"))
.addCell(new PdfTableCell( "地址"))
.addCell(new PdfTableCell( "15912153741",3))
.addCell(new PdfTableCell(Image.getInstance("E:\\zjz.jpg"),3))
.addCell(new PdfTableCell( ""))
.addCell(new PdfTableCell("郑重申明:谨此保证,本表所填报内容及所附证明材料真实、完整",4,false,11.0F, CellAlign.RIGHT,CellAlign.MIDDLE));
// 生成 PDF 到文件
try (OutputStream os = new FileOutputStream("E:\\xxk2.pdf")) {
PDFUtils.createSimpleTablePDF(pdfTable, os);
}
}
}
结果