java生成带水印和公章的文档
一、使用场景
需要在线生成合同文件,并加盖公章和水印,如 第三方服务平台提供服务时,给自动生成合同文件等。
本文涉及了 HTML、PDF、PNG的生成,在日常合同使用类型中基本都包括了。废话不多说,上代码
二、添加依赖
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>kernel</artifactId>
<version>7.2.5</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>io</artifactId>
<version>7.2.5</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>layout</artifactId>
<version>7.2.5</version>
</dependency>
三、代码
package cn.com.juejin.utils;
import com.itextpdf.io.font.PdfEncodings;
import com.itextpdf.io.image.ImageData;
import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.Image;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.properties.TextAlignment;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.Base64;
/**
* 文档工具类,用于生成指定格式文档
* */
public class DocUtil {
//水印文字
private static final String WATER_MARK = "掘金社区屌丝俱乐部";
//公章图片,todo:需要先准备好
private static final String sealPath = "src/main/resources/scripts/seal.png";
//默认文档名称
private static final String DEFAULT_DOC_NAME = "合同文件";
//临时文件路径,可用于本地下载或者上传到oss
private static final String TEMP_FILE_PATH = "src/main/resources/scripts/";
//字体(宋体)
private static final String FONT_PATH = "src/main/resources/font/simsun.ttc,0";
/**
* 创建带水印和公章的合同文件,采用默认文件名
*
* @param docType 文档类型(1:PDF,2:HTML, 3:PNG)
* @param content 文档内容
*
* @return 临时文件全路径,
* 根据实际情况可将该文件上传至oss或者返回给调用者
*/
public static String createDoc(int docType, String content){
return createDoc(docType, content, DEFAULT_DOC_NAME);
}
/**
* 创建带水印和公章的合同文件
*
* @param docType 文档类型(1:PDF,2:HTML, 3:PNG)
* @param content 文档内容
* @param docName 文档名称,不带后缀 (可选) 默认值:合同文件
*
* @return 临时文件全路径,
* 根据实际情况可将该文件上传至oss或者返回给调用者
*/
public static String createDoc(int docType, String content, String docName){
String fileName = "";
if (docType == 1){
fileName = TEMP_FILE_PATH+docName+".pdf";
try (FileOutputStream fos = new FileOutputStream(fileName)) {
DocUtil.generatePdfWithSeal(content, fos, sealPath, WATER_MARK);
System.out.println("PDF文档生成成功!");
} catch (IOException e) {
System.err.println("生成PDF文档失败: " + e.getMessage());
}
}
if (docType == 2){
fileName = TEMP_FILE_PATH+docName+".html";
//方式一:通过公章图片路径生成
// generateHTMLWithSeal(content, sealPath, null, WATER_MARK, fileName);
//方式二:通过公章图片Base64编码生成
String sealBase64 = encodeImageToBase64(sealPath);
generateHTMLWithSeal(content, null, sealBase64, WATER_MARK, fileName);
}
if (docType == 3){
try {
fileName = TEMP_FILE_PATH+docName+".png";
generateImageDocWithSeal(content, sealPath, fileName, WATER_MARK);
}catch (Exception e){
System.err.println("生成图片文档失败: " + e.getMessage());
}
}
return fileName;
}
private static String encodeImageToBase64(String imagePath) {
try {
File imageFile = new File(imagePath);
byte[] imageData = java.nio.file.Files.readAllBytes(imageFile.toPath());
return Base64.getEncoder().encodeToString(imageData);
} catch (IOException e) {
return null;
}
}
/**
* 生成带公章的HTML文档
* @param content 文档内容
* @param sealPath 公章图片路径(可选)
* @param sealBase64 公章图片Base64编码(可选)
* sealBase64 和 sealPath 不能同时为空
* 如果水印文件为本地文件,可以采用base64编码
* 如果是云端文件,公网可访问,则可以用sealPath
* @param waterMarkText 水印文字
* @param fileName 带路径的文件名
*/
private static void generateHTMLWithSeal(String content, String sealPath, String sealBase64, String waterMarkText, String fileName) {
StringBuilder html = new StringBuilder();
html.append("<html><head><meta charset='UTF-8'><title>Document</title></head><body>");
// 添加水印样式
if (waterMarkText != null && !waterMarkText.isEmpty()) {
html.append("<style>");
html.append(".watermark {");
html.append("position: fixed;");
html.append("top: 50%;");
html.append("left: 50%;");
html.append("transform: translate(-50%, -50%) rotate(-45deg);");
html.append("font-size: 50px;");
html.append("color: rgba(0, 0, 0, 0.1);");
html.append("pointer-events: none;");
html.append("z-index: 1000;");
html.append("}");
html.append("</style>");
}
html.append("</head><body>");
// 添加水印
if (waterMarkText != null && !waterMarkText.isEmpty()) {
html.append("<div class='watermark'>").append(waterMarkText).append("</div>");
}
html.append("<div style='position: relative; min-height: 800px;'>");
content = content.replace("\\n", "\n");
html.append("<div>").append(content).append("</div>");
// 添加公章
if (sealPath != null && !sealPath.isEmpty()) {
html.append("<div style='position: absolute; bottom: 100px; right: 100px;'>");
html.append("<img src='").append(sealPath).append("' style='width: 150px; height: 150px; opacity: 0.8;' />");
html.append("</div>");
} else if (sealBase64 != null && !sealBase64.isEmpty()) {
html.append("<div style='position: absolute; bottom: 100px; right: 100px;'>");
html.append("<img src='data:image/png;base64,").append(sealBase64).append("' style='width: 150px; height: 150px; opacity: 0.8;' />");
html.append("</div>");
}
html.append("</div></body></html>");
try (FileOutputStream fos = new FileOutputStream(fileName)) {
fos.write(html.toString().getBytes());
fos.flush();
fos.close();
System.out.println("PDF文档生成成功!");
} catch (IOException e) {
System.err.println("生成PDF文档失败: " + e.getMessage());
}
}
/**
* 生成带公章的PDF文档(需要额外的PDF库,如iText)
* @param content 文档内容
* @param outputStream 输出流
* @param sealPath 公章图片路径
* @param waterMarkText 水印文字
*/
private static void generatePdfWithSeal(String content, OutputStream outputStream, String sealPath, String waterMarkText) throws IOException {
try {
// 创建PDF文档
PdfWriter writer = new PdfWriter(outputStream);
PdfDocument pdfDoc = new PdfDocument(writer);
Document document = new Document(pdfDoc);
PdfFont font = PdfFontFactory.createFont(FONT_PATH, PdfEncodings.IDENTITY_H, PdfFontFactory.EmbeddingStrategy.PREFER_EMBEDDED);
// 添加文档内容
if (content != null && !content.isEmpty()) {
// 处理字面量换行符
content = content.replace("\\n", "\n");
// 处理HTML标签,如果内容是HTML格式
// String plainText = content.replaceAll("<[^>]*>", ""); // 简单去除HTML标签
Paragraph paragraph = new Paragraph(content)
.setFont(font)
.setFontSize(12)
.setTextAlignment(TextAlignment.LEFT)
.setMargin(20);
document.add(paragraph);
}
// 添加水印(在内容添加之后处理)
if (waterMarkText != null && !waterMarkText.isEmpty()) {
// 遍历所有页面添加水印
for (int i = 1; i <= pdfDoc.getNumberOfPages(); i++) {
PdfPage page = pdfDoc.getPage(i);
Rectangle pageSize = page.getPageSize();
// 创建水印文本
Paragraph watermark = new Paragraph(waterMarkText)
.setFontColor(com.itextpdf.kernel.colors.ColorConstants.LIGHT_GRAY)
.setFont(font)
.setFontSize(60)
.setOpacity(0.3f);
// 添加水印到页面背景(使用canvas直接绘制)
com.itextpdf.kernel.pdf.canvas.PdfCanvas canvas = new com.itextpdf.kernel.pdf.canvas.PdfCanvas(page.newContentStreamBefore(), page.getResources(), pdfDoc);
com.itextpdf.layout.Canvas canvasLayer = new com.itextpdf.layout.Canvas(canvas, pageSize);
canvasLayer.showTextAligned(watermark,
pageSize.getWidth() / 2,
pageSize.getHeight() / 2,
i,
TextAlignment.CENTER,
com.itextpdf.layout.properties.VerticalAlignment.MIDDLE,
45); // 旋转45度
canvasLayer.close();
}
}
// 如果提供了公章路径,则添加公章
if (sealPath != null && !sealPath.isEmpty()) {
try {
// 读取公章图片
ImageData imageData = ImageDataFactory.create(sealPath);
Image sealImage = new Image(imageData);
// 设置公章大小和位置
sealImage.setWidth(100);
sealImage.setHeight(100);
// 将公章定位在页面右下角
PdfPage lastPage = pdfDoc.getLastPage();
Rectangle pageSize = lastPage.getPageSize();
int number = pdfDoc.getPageNumber(lastPage);
// 设置绝对定位
sealImage.setFixedPosition(number,
pageSize.getWidth() - 120, // 距离右边120单位
100 // 距离底部100单位
);
// 设置透明度
sealImage.setOpacity(0.8f);
document.add(sealImage);
} catch (Exception e) {
// 如果公章加载失败,仅记录日志但不中断文档生成
System.err.println("警告:无法加载公章图片: " + e.getMessage());
}
}
// 关闭文档
document.close();
} catch (Exception e) {
throw new IOException("生成PDF文档时发生错误", e);
}
}
/**
* 生成带水印公章的图片文档
* @param content 文档内容
* @param sealPath 公章图片路径
* @param outputPath 输出图片路径
* @param waterMarkText 水印文字
*/
private static void generateImageDocWithSeal(String content, String sealPath, String outputPath, String waterMarkText) throws Exception {
// 创建一个BufferedImage用于绘制文档
BufferedImage image = new BufferedImage(800, 1000, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = image.createGraphics();
// 设置抗锯齿
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// 绘制白色背景
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, 800, 1000);
// 添加水印
if (waterMarkText != null && !waterMarkText.isEmpty()) {
g2d.setColor(new Color(0, 0, 0, 30)); // 半透明黑色
Font watermarkFont = new Font("宋体", Font.BOLD, 40);
g2d.setFont(watermarkFont);
// 设置旋转
FontMetrics fm = g2d.getFontMetrics();
int watermarkWidth = fm.stringWidth(waterMarkText);
// 保存当前变换
AffineTransform old = g2d.getTransform();
// 设置旋转中心为图片中心
g2d.rotate(Math.toRadians(-45), 400, 500);
g2d.drawString(waterMarkText, 400 - watermarkWidth/2, 500);
// 恢复变换
g2d.setTransform(old);
}
// 绘制文本内容
g2d.setColor(Color.BLACK);
Font font = new Font("宋体", Font.PLAIN, 16);
g2d.setFont(font);
content = content.replace("\\n", "\n");
// 简单的文本绘制(实际应用中需要更复杂的文本布局)
String[] lines = content.split("\n");
int y = 50;
for (String line : lines) {
g2d.drawString(line, 50, y);
y += 25;
}
// 绘制公章
if (sealPath != null && !sealPath.isEmpty()) {
try {
BufferedImage sealImage = ImageIO.read(new File(sealPath));
if (sealImage != null) {
// 使用 AffineTransform 实现高质量缩放
BufferedImage scaledSeal = new BufferedImage(150, 150, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2dSeal = scaledSeal.createGraphics();
g2dSeal.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2dSeal.drawImage(sealImage, 0, 0, 150, 150, null);
g2dSeal.dispose();
// 绘制到主图中
g2d.drawImage(scaledSeal, 600, 800, null);
}
} catch (IOException e) {
System.err.println("生成图片文档失败: " + e.getMessage());
}
}
g2d.dispose();
// 保存图片
ImageIO.write(image, "PNG", new File(outputPath));
System.out.println("图片文档生成成功!");
}
public static void main(String[] args) throws Exception {
String content = "这是文档内容...\\n包含多行文本。\\n这是文档的最后一行。";
createDoc(1,content);
createDoc(2,content);
createDoc(3,content);
}
}