1. 开源框架支持
iText,生成PDF文档,还支持将XML、Html文件转化为PDF文件;(简单但是得下载软件)
Apache PDFBox,生成、合并PDF文档;(类似于itext)
docx4j,生成docx、pptx、xlsx文档,支持转换为PDF格式。(需要一直转文件格式,麻烦,不过生成的PDF看不出生成的痕迹,非常标准)
注:由于个人喜好,这里还是用itext(之前用过而且简单)
2. 代码实现
工具类(包含将文件转为byte[]、生成PDF文档与PDF加水印方法)
点击查看代码
package com.example.springboot.common;
import com.example.springboot.domain.Company;
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;
import org.springframework.stereotype.Component;
import java.io.*;
import java.util.ArrayList;
import java.util.Map;
import java.util.Objects;
import static com.itextpdf.commons.utils.FileUtil.deleteFile;
@Component
public class GeneratePDF {
public static byte[] getBytes(String filePath){
byte[] buffer = null;
try {
File file = new File(filePath);
FileInputStream fis = new FileInputStream(file);
ByteArrayOutputStream bos = new ByteArrayOutputStream(8192);
byte[] b = new byte[8192];
int n;
while ((n = fis.read(b)) != -1) {
bos.write(b, 0, n);
}
fis.close();
bos.close();
buffer = bos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return buffer;
}
public static String generetePdf(String temp,String storepath,Map<String,String> map,Map<String,byte[]> images) throws IOException, DocumentException {
/*创建一个pdf读取对象*/
PdfReader reader = new PdfReader(temp);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
/*创建pdf模板,参数reader bos*/
PdfStamper ps = new PdfStamper(reader, bos);
/*定义字体*/
// BaseFont baseFont = BaseFont.createFont("/usr/share/fonts/SIMSUN.TTC,1",BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
BaseFont baseFont = BaseFont.createFont("c://windows//fonts//simsun.ttc,1",BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
ArrayList<BaseFont> fontArrayList = new ArrayList<>();
fontArrayList.add(baseFont);
/*读取模板文本域并封装数据(注意名字应与文本域中的名字一致)*/
AcroFields s = ps.getAcroFields();
/*定义字体,若不定义,会使用模板中的文本域设置的字体*/
s.setSubstitutionFonts(fontArrayList);
for (String a:map.keySet()
) {
s.setField(a, map.get(a));
}
for (String b :images.keySet()
) {
/*插入图片*/
int pageNo = s.getFieldPositions(b).get(0).page;
Rectangle signRect = s.getFieldPositions(b).get(0).position;
float x = signRect.getLeft();
float y = signRect.getBottom();
Image image = Image.getInstance(images.get(b));
/*获取操作的页面*/
PdfContentByte under = ps.getOverContent(pageNo);
/*根据域的大小缩放图片*/
image.scaleToFit(signRect.getWidth(), signRect.getHeight());
/*添加图片*/
image.setAbsolutePosition(x, y);
under.addImage(image);
}
/*这里true表示pdf可编辑*/
ps.setFormFlattening(false);
/*关闭PdfStamper*/
ps.close();
FileOutputStream file=new FileOutputStream(storepath);
file.write(bos.toByteArray());
return storepath;
}
/**
* pdf生成水印
*
* @param srcPdfPath 插入前的文件路径
* @param tarPdfPath 插入后的文件路径
* @param WaterMarkContent 水印文案
* @param numberOfPage 每页需要插入的条数
* @throws Exception
*/
public static void addWaterMark(String srcPdfPath, String tarPdfPath, String WaterMarkContent, int numberOfPage) throws Exception {
PdfReader reader = new PdfReader(srcPdfPath);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(tarPdfPath));
PdfGState gs = new PdfGState();
System.out.println("adksjskalfklsdk");
//设置字体
BaseFont font = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
// 设置透明度
gs.setFillOpacity(0.4f);
int total = reader.getNumberOfPages() + 1;
PdfContentByte content;
for (int i = 1; i < total; i++) {
content = stamper.getOverContent(i);
content.beginText();
content.setGState(gs);
//水印颜色
content.setColorFill(BaseColor.DARK_GRAY);
//水印字体样式和大小
content.setFontAndSize(font, 35);
//插入水印 循环每页插入的条数
for (int j = 0; j < numberOfPage; j++) {
content.showTextAligned(Element.ALIGN_CENTER, WaterMarkContent, 150, 100 * (j + 1), 30);
content.showTextAligned(Element.ALIGN_CENTER, WaterMarkContent, 450, 100 * (j + 1), 30);
content.showTextAligned(Element.ALIGN_CENTER, WaterMarkContent, 750, 100 * (j + 1), 30);
}
content.endText();
}
stamper.close();
reader.close();
boolean b = deleteFile(new File(srcPdfPath));
System.out.println("PDF水印添加完成!");
}
}
实体类(放进PDF中的实体类)
点击查看代码
package com.example.springboot.domain;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class Company {
private String tyxydm;
private String name;
private String fr;
private String zsbh;
private String clrq;
private String jglx;
private String ywfwbg;
private String yxqz;
private byte[] ewm;
private byte[] dzyz;
}
Controller类(生成PDF->加水印->PDF转图片返回图片地址)
点击查看代码
package com.example.springboot.controller;
import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.extra.qrcode.QrCodeUtil;
import cn.hutool.extra.qrcode.QrConfig;
import com.example.springboot.common.ReturnResult;
import com.example.springboot.domain.Company;
import com.example.springboot.domain.Person;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import static com.example.springboot.common.GeneratePDF.*;
@RestController
@Validated
@RequestMapping("/zscx")
public class FindlicenseController {
@Value("E:/桌面/新建文件夹/")
String storepath;
@PostMapping("/zxing")
public ReturnResult a(){
QrConfig config = new QrConfig(300, 300);
// 设置边距,既二维码和背景之间的边距
// config.setMargin(3);
// 设置前景色,既二维码颜色(青色)
// config.setForeColor(Color.yellow.getRGB());
// 设置背景色(灰色)
// config.setBackColor(Color.green.getRGB());
// 高纠错级别
config.setErrorCorrection(ErrorCorrectionLevel.H);
//附带logo
// 生成二维码到文件,也可以到流
// QrCodeUtil.generate("http://hutool.cn/", config, FileUtil.file("e:/qrcode.jpg"));
return ReturnResult.buildSuccessResult(QrCodeUtil.generate("https://home.cnblogs.com/u/beijie/", config.setImg("E:/桌面/新建文件夹/QQ头像.jpg"),FileUtil.file("E:/桌面/新建文件夹/二维码.jpg")));
}
@PostMapping("/createCompanyPdf")
public ReturnResult createCompanyPdf() throws Exception {
/*读取图片地址*/
Company company=Company.builder()
.tyxydm("KKKKKKKKKKKKKKK0111")
.name("辽宁省机动车鉴定评估机构")
.fr("刘***")
.zsbh("(2022)辽鉴评证字第2" +
"1010001号")
.clrq("2019年09月20日")
.jglx("综合类")
.ywfwbg("机动车整车技术状况鉴定和价值评估、事故车辆损失鉴定评估、机动车停运损失评估、二手车认证、报废机动车总成技术状况鉴定和价值评估;机动车技术鉴定、车辆属性鉴定、事故车辆损伤关联性鉴定、事故车辆维修合理性鉴定、水淹事故车辆鉴定、机动车事故成因鉴定")
.yxqz("2021年01月至2021年12月")
.ewm(getBytes("E:/桌面/新建文件夹/二维码.jpg"))
.dzyz(getBytes("E:/桌面/新建文件夹/小北工作室--电子印章.jpg"))
.build();
Map<String, String> map =new HashMap<>();
map.put("tyxydm", company.getTyxydm());
map.put("name", company.getName());
map.put("fr", company.getFr());
map.put("zsbh", company.getZsbh());
map.put("clrq", company.getClrq());
map.put("jglx", company.getJglx());
map.put("ywfwbg", company.getYwfwbg());
map.put("yxqz", company.getYxqz());
Map<String,byte[]> images = new HashMap<>();
images.put("ewm", company.getEwm());
images.put("dzyz", company.getDzyz());
String path = generetePdf("E:/桌面/新建文件夹/备案证书.pdf","E:/桌面/新建文件夹/temp/备案证书.pdf",map,images);
System.out.println(path);
String waterpath = "E:/桌面/新建文件夹/temp/备案证书(加水印).pdf";
try {
addWaterMark(path,waterpath,"小北工作室",10);
} catch (Exception e) {
throw new RuntimeException(e);
}
File file = new File(waterpath);
ArrayList<String> paths = new ArrayList<>();
try {
PDDocument doc = PDDocument.load(file);
PDFRenderer renderer = new PDFRenderer(doc);
int pageCount = doc.getNumberOfPages();
// 循环pdf每个页码,1页pdf转成1张图片,多页pdf会转成多张图片
for (int i = 0; i < pageCount; i++) {
// dpi表示图片清晰度 dpi越大转换后越清晰,相对转换速度越慢
BufferedImage image = renderer.renderImageWithDPI(i, 144);
//亲测 图片格式支持:jpg,png,gif,bmp,jpeg
ImageIO.write(image, "jpg", new File(storepath+"temp/company/"+i+".jpg"));//写入图片
paths.add(storepath+"temp/company/"+i+".jpg");
// ImgUtil.pressText(//
// FileUtil.file(storepath+"temp/"+i+".jpg"), //
// FileUtil.file(finalpath), //
// "小北工作室", Color.lightGray, //文字
// new Font("黑体", Font.BOLD, 100), //字体
// 300, //x坐标修正值。 默认在中间,偏移量相对于中间偏移
// 300 * (i + 1), //y坐标修正值。 默认在中间,偏移量相对于中间偏移
// 0.4f//透明度:alpha 必须是范围 [0.0, 1.0] 之内(包含边界值)的一个浮点数字
// );
}
} catch (IOException e) {
e.printStackTrace();
}
return ReturnResult.buildSuccessResult(paths);
}
@PostMapping("/createPersonPdf")
public ReturnResult createPersonPdf() throws Exception {
/*读取图片地址*/
Person person=Person.builder()
.gsmc("机动车鉴定评估")
.jgmc("鉴定评估机构")
.name("***")
.sex("男")
.djbh("21")
.dwmc("辽宁***机动车鉴定评估有限公司")
.djrq("2022年03月14日")
.njxx("2022年1月-2022年12月")
.szhyzz("辽宁省汽车流通协会")
.dyrq("2022-01-12")
.ewm(getBytes("E:/桌面/新建文件夹/二维码.jpg"))
.cyry(getBytes("E:/桌面/新建文件夹/从业人员.jpg"))
.build();
Map<String, String> map =new HashMap<>();
map.put("gsmc", person.getGsmc());
map.put("jgmc", person.getJgmc());
map.put("name", person.getName());
map.put("sex", person.getSex());
map.put("djbh", person.getDjbh());
map.put("dwmc",person.getDwmc());
map.put("djrq",person.getDjrq());
map.put("njxx", person.getNjxx());
map.put("szhyzz", person.getSzhyzz());
map.put("dyrq", person.getDyrq());
Map<String,byte[]> images = new HashMap<>();
images.put("ewm", person.getEwm());
images.put("cyry", person.getCyry());
String path = generetePdf("E:/桌面/新建文件夹/备案卡从业人员.pdf","E:/桌面/新建文件夹/temp/备案卡从业人员.pdf",map,images);
System.out.println(path);
String waterpath = "E:/桌面/新建文件夹/temp/备案卡从业人员(加水印).pdf";
try {
addWaterMark(path,waterpath,"小北工作室",10);
} catch (Exception e) {
throw new RuntimeException(e);
}
File file = new File(waterpath);
ArrayList<String> paths = new ArrayList<>();
try {
PDDocument doc = PDDocument.load(file);
PDFRenderer renderer = new PDFRenderer(doc);
int pageCount = doc.getNumberOfPages();
// 循环pdf每个页码,1页pdf转成1张图片,多页pdf会转成多张图片
for (int i = 0; i < pageCount; i++) {
// dpi表示图片清晰度 dpi越大转换后越清晰,相对转换速度越慢
BufferedImage image = renderer.renderImageWithDPI(i, 144);
//亲测 图片格式支持:jpg,png,gif,bmp,jpeg
ImageIO.write(image, "jpg", new File(storepath+"temp/person/"+i+".jpg"));//写入图片
paths.add(storepath+"temp/person/"+i+".jpg");
// ImgUtil.pressText(//
// FileUtil.file(storepath+"temp/"+i+".jpg"), //
// FileUtil.file(finalpath), //
// "小北工作室", Color.lightGray, //文字
// new Font("黑体", Font.BOLD, 100), //字体
// 300, //x坐标修正值。 默认在中间,偏移量相对于中间偏移
// 300 * (i + 1), //y坐标修正值。 默认在中间,偏移量相对于中间偏移
// 0.4f//透明度:alpha 必须是范围 [0.0, 1.0] 之内(包含边界值)的一个浮点数字
// );
}
} catch (IOException e) {
e.printStackTrace();
}
return ReturnResult.buildSuccessResult(paths);
}
/**
* 转换全部的pdf
* pdf转图片
*/
@GetMapping("/pdfToImg")
public void pdfToImg(String path) {
// 将pdf转图片 并且自定义图片得格式大小
File file = new File(path);
try {
PDDocument doc = PDDocument.load(file);
PDFRenderer renderer = new PDFRenderer(doc);
int pageCount = doc.getNumberOfPages();
// 循环pdf每个页码,1页pdf转成1张图片,多页pdf会转成多张图片
for (int i = 1; i < pageCount; i++) {
// dpi表示图片清晰度 dpi越大转换后越清晰,相对转换速度越慢
BufferedImage image = renderer.renderImageWithDPI(i, 144);
//亲测 图片格式支持:jpg,png,gif,bmp,jpeg
ImageIO.write(image, "png", new File("E:/桌面/新建文件夹/"+i+".png"));//写入图片
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
参考文档:(主要参考,让人醍醐灌顶)https://blog.csdn.net/a_lllk/article/details/109450972
(辅助参考,对pdf操作介绍比较全面)https://wanghj.blog.csdn.net/article/details/127495462
(辅助参考,itext表单域传值介绍比较全面)https://blog.csdn.net/qq_42130324/article/details/122937657
3. 加水印
方式一 PDF加水印(在工具类里已经包含)
点击查看代码
/**
* pdf生成水印
*
* @param srcPdfPath 插入前的文件路径
* @param tarPdfPath 插入后的文件路径
* @param WaterMarkContent 水印文案
* @param numberOfPage 每页需要插入的条数
* @throws Exception
*/
public static void addWaterMark(String srcPdfPath, String tarPdfPath, String WaterMarkContent, int numberOfPage) throws Exception {
PdfReader reader = new PdfReader(srcPdfPath);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(tarPdfPath));
PdfGState gs = new PdfGState();
System.out.println("adksjskalfklsdk");
//设置字体
BaseFont font = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
// 设置透明度
gs.setFillOpacity(0.4f);
int total = reader.getNumberOfPages() + 1;
PdfContentByte content;
for (int i = 1; i < total; i++) {
content = stamper.getOverContent(i);
content.beginText();
content.setGState(gs);
//水印颜色
content.setColorFill(BaseColor.DARK_GRAY);
//水印字体样式和大小
content.setFontAndSize(font, 35);
//插入水印 循环每页插入的条数
for (int j = 0; j < numberOfPage; j++) {
content.showTextAligned(Element.ALIGN_CENTER, WaterMarkContent, 150, 100 * (j + 1), 30);
content.showTextAligned(Element.ALIGN_CENTER, WaterMarkContent, 450, 100 * (j + 1), 30);
content.showTextAligned(Element.ALIGN_CENTER, WaterMarkContent, 750, 100 * (j + 1), 30);
}
content.endText();
}
stamper.close();
reader.close();
boolean b = deleteFile(new File(srcPdfPath));
System.out.println("PDF水印添加完成!");
}
方式二 图片加水印
ImgUtil.pressText(//
FileUtil.file(storepath+"temp/"+i+".jpg"), //
FileUtil.file(finalpath), //
"小北工作室", Color.lightGray, //文字
new Font("黑体", Font.BOLD, 100), //字体
300, //x坐标修正值。 默认在中间,偏移量相对于中间偏移
300 * (i + 1), //y坐标修正值。 默认在中间,偏移量相对于中间偏移
0.4f//透明度:alpha 必须是范围 [0.0, 1.0] 之内(包含边界值)的一个浮点数字
);
这里用的hutool的图片工具类,链接在这:http://hutool.cn/docs/index.html#/core/图片/图片工具-ImgUtil
PDF转图片
点击查看代码
{
File file = new File(waterpath);
ArrayList<String> paths = new ArrayList<>();
try {
PDDocument doc = PDDocument.load(file);
PDFRenderer renderer = new PDFRenderer(doc);
int pageCount = doc.getNumberOfPages();
// 循环pdf每个页码,1页pdf转成1张图片,多页pdf会转成多张图片
for (int i = 0; i < pageCount; i++) {
// dpi表示图片清晰度 dpi越大转换后越清晰,相对转换速度越慢
BufferedImage image = renderer.renderImageWithDPI(i, 144);
//亲测 图片格式支持:jpg,png,gif,bmp,jpeg
ImageIO.write(image, "jpg", new File(storepath+"temp/company/"+i+".jpg"));//写入图片
paths.add(storepath+"temp/company/"+i+".jpg");
// ImgUtil.pressText(//
// FileUtil.file(storepath+"temp/"+i+".jpg"), //
// FileUtil.file(finalpath), //
// "小北工作室", Color.lightGray, //文字
// new Font("黑体", Font.BOLD, 100), //字体
// 300, //x坐标修正值。 默认在中间,偏移量相对于中间偏移
// 300 * (i + 1), //y坐标修正值。 默认在中间,偏移量相对于中间偏移
// 0.4f//透明度:alpha 必须是范围 [0.0, 1.0] 之内(包含边界值)的一个浮点数字
// );
}
} catch (IOException e) {
e.printStackTrace();
}
return ReturnResult.buildSuccessResult(paths);
}
代码不全,因为是直接在controller里用的,若有需要可以看生成PDF的controller或者参考文档:https://blog.csdn.net/suya2011/article/details/121368330
Maven项目可以用spire,我是特别想用的,因为这个对PDF操作更全,但是这个全是Maven导包,自己在Maven仓库找到依赖还不能用,所以你可以试试,建议Java使用Free Spire.PDF for Java
官网在这:https://www.e-iceblue.cn/Introduce/Free-Spire-PDF-JAVA.html