Java代码生成PDF2.0(包括文字图片)+PDF加水印+PDF转图片
1. 开源框架支持
iText,生成PDF文档,还支持将XML、Html文件转化为PDF文件;(简单但是得下载软件)
Apache PDFBox,生成、合并PDF文档;(类似于itext)
docx4j,生成docx、pptx、xlsx文档,支持转换为PDF格式。(需要一直转文件格式,麻烦,不过生成的PDF看不出生成的痕迹,非常标准)
注:由于个人喜好,这里还是用itext(之前用过而且简单)
2. 代码实现
导包
implementation 'com.itextpdf:itextpdf:5.5.13.3'
implementation 'com.itextpdf:itext-asian:5.2.0'
implementation 'com.itextpdf:itext7-core:7.2.5'
implementation 'org.apache.pdfbox:pdfbox:2.0.27'
注意:这里pdfbox包用的是老版的,新版的PDDocument中的load方法已经没了或者已经改名被替代(下面的pdftoimage将无法使用),我这里为了省事,就用老版了
工具类(包含将文件转为byte[]、生成PDF文档与PDF加水印方法)
点击查看代码
package com.example.studydemo.common;
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.springframework.stereotype.Component;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.ArrayList;
import java.util.Map;
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水印添加完成!");
}
public static ArrayList<String> pdftoimage(String pdfpath,String temppath,String storepath) {
File file = new File(pdfpath);
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);
String path = storepath+temppath+i+".jpg";
//亲测 图片格式支持:jpg,png,gif,bmp,jpeg
ImageIO.write(image, "jpg", new File(path));//写入图片
paths.add("http://101.42.99.134:8888/jeecg-boot/"+temppath+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 paths;
}
}
实体类(放进PDF中的实体类,这里根据实际情况换成自己的,下面Controller中,我为了比较大众一点就直接将数据写死了,所以理论上这个实体类可以暂时不用了)
点击查看代码
package com.example.springboot.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@Data
@NoArgsConstructor
@TableName("jggl")
public class Jggl {
@TableId(type =IdType.ASSIGN_ID)
private String id;
private String createBy;
private java.sql.Timestamp createTime;
private String updateBy;
private java.sql.Timestamp updateTime;
private String sysOrgCode;
private String name;
private String tyxydm;
private String address;
private String jglx;
private String fr;
private String frdh;
private String frsfzh;
private String dwlxr;
private String lxrdh;
private String gslxyx;
private String sjrxm;
private String sjrdh;
private String sjrdz;
private String ssqy;
private String hysqb;
private String bahzsdjb;
private String jytjb;
private String frsfz;
private String zyfwbg;
private String cns;
private String gstg;
private String xhyz;
private String splx;
private String clrq;
private String zsbh;
private String dzzs;
private String spzt;
private String xydj;
private String thyj;
private String yxqx;
private String ewm;
private String fzrq;
private String yyzz;
private String sndcwbb;
}
Controller类(生成PDF->加水印->PDF转图片返回图片地址)
点击查看代码
package com.example.studydemo.PDF;
import cn.hutool.core.date.DateUtil;
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.studydemo.common.ReturnResult;
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.*;
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.studydemo.common.GeneratePDF.*;
@RestController
@Validated
@RequestMapping("/zscx")
public class FindlicenseController {
@Value("E:/桌面/新建文件夹/")
String storepath;
@Value("${file.location}")
String filelocation;
@Value("${file.path}")
String filepath;
@Value("${file.domainname}")
String domainname;
public String createEwm(String startpath,String endpath){
File file = new File(endpath);
if (!file.exists()) {
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"));
System.out.println(startpath+filelocation+endpath);
QrCodeUtil.generate(startpath, config.setImg(filelocation+"QQ头像.jpg"),FileUtil.file(endpath));
System.out.println("文件已创建");
} else {
System.out.println("文件已存在");
}
return endpath;
}
@PostMapping("/createxyzsPdf")
public ReturnResult createxyzsPdf() throws Exception {
// List<Jggl> list = jgglService.listByMap(message);
// if (list.size() == 1 && Objects.equals(list.get(0).getSpzt(),"通过") && !Objects.equals(list.get(0).getXydj(),"")) {
// Jggl jggl = list.get(0);
/*读取图片地址*/
Map<String, String> map = new HashMap<>();
map.put("name","小北车企");
map.put("tyxydm","12345678910");
map.put("xydj", "三级");
map.put("fzrq", "2024-01-09");
Map<String, byte[]> images = new HashMap<>();
images.put("ewm",getBytes(createEwm("https://home.cnblogs.com/u/beijie/","E:/usr/local/apps/zscx-download/"+System.currentTimeMillis()+"xyzsewm.jpg")));
String path = generetePdf("E:/usr/local/apps/zscx-download/辽宁省汽车流通协会信用证书.pdf", "E:/usr/local/apps/zscx-download"+"/temp/"+"12345678910"+"辽宁省汽车流通协会信用证书.pdf", map, images);
System.out.println(path);
String waterpath = "E:/usr/local/apps/zscx-download"+"/temp/"+"12345678910"+"xyzs.pdf";
try {
addWaterMark(path, waterpath, "小北工作室", 10);
} catch (Exception e) {
throw new RuntimeException(e);
}
return ReturnResult.buildSuccessResult(pdftoimage(waterpath, "temp/"+"12345678910", "E:/usr/local/apps/zscx-download/").get(0));
}
/**
* 转换全部的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转图片
点击查看代码
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("/usr/local/apps/zscx-download/"+i+".png"));//写入图片
}
} catch (IOException e) {
e.printStackTrace();
}
}
代码不全,因为是直接在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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」