1.前言
最近遇到要给pdf
,excel
,word
,html
文件加水印的需求,网上大部分实现的只是其中一种,并没有一个完整的实现方式,因此对现有的资料进行整合
支持pdf
,excel
,word
,html
文件多水印支持
2.依赖
文件水印是基于apach-poi
和itex
来实现,因此需要导入一下依赖
org.apache.poi
poi-ooxml
4.1.2
org.apache.poi
ooxml-schemas
1.4
com.itextpdf
itext-asian
5.2.0
com.itextpdf
itextpdf
5.5.13.1
3.代码
文件加水印代码如下:
package com.tomato.util;
import com.itextpdf.text.Element;
import com.itextpdf.text.pdf.*;
import com.microsoft.schemas.office.office.CTLock;
import com.microsoft.schemas.vml.*;
import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.openxml4j.opc.PackagePartName;
import org.apache.poi.openxml4j.opc.PackageRelationship;
import org.apache.poi.openxml4j.opc.TargetMode;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.wp.usermodel.HeaderFooterType;
import org.apache.poi.xssf.usermodel.XSSFRelation;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFHeader;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.stream.Stream;
/**
*
* Copyright: (C), 2022/3/27 10:47
*
* Company: tomato
*
* @author 秃头猿猿
* @version 1.0
*/
public class WaterMarkUtils {
private static final String fontName = "microsoft-yahei";
private static final String fontSize = "0.1pt";
private static final String fontColor = "#d0d0d0";
private static final Integer widthPerWord = 5;
private static final String styleRotation = "-45";
private static Integer styleTop = 0;
/**
* pdf 增加水印
*
* @param srcFile
* @param destFile
* @param waterMarkContent
* @throws Exception
*/
public static void addPDFWaterMark(File srcFile, File destFile, String... waterMarkContent) throws Exception {
addPDFWaterMark(new FileInputStream(srcFile), new FileOutputStream(destFile), waterMarkContent);
}
public static void addPDFWaterMark(File srcFile, OutputStream outputStream, String... waterMarkContent) throws Exception {
addPDFWaterMark(new FileInputStream(srcFile), outputStream, waterMarkContent);
}
public static void addPDFWaterMark(InputStream inputStream, File destFile, String... waterMarkContent) throws Exception {
addPDFWaterMark(inputStream, new FileOutputStream(destFile), waterMarkContent);
}
/**
* pdf 增加水印
*
* @param inputStream 源文件流
* @param outputStream 目的文件流
* @param waterMarkContent 水印内容
* @throws Exception
*/
private static void addPDFWaterMark(InputStream inputStream, OutputStream outputStream, String... waterMarkContent) throws Exception {
// 原PDF文件
PdfReader reader = new PdfReader(inputStream);
// 输出的PDF文件内容
PdfStamper stamper = new PdfStamper(reader, outputStream);
try {
// 字体 来源于 itext-asian JAR包
BaseFont baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
PdfGState gs = new PdfGState();
// 设置透明度
gs.setFillOpacity(0.3f);
gs.setStrokeOpacity(0.4f);
int totalPage = reader.getNumberOfPages() + 1;
for (int i = 1; i < totalPage; i++) {
// 内容上层
float x = reader.getPageSize(i).getWidth();
float y = reader.getPageSize(i).getHeight();
PdfContentByte content = stamper.getOverContent(i);
content.beginText();
// 字体添加透明度
content.setGState(gs);
// 添加字体大小等
content.setFontAndSize(baseFont, 15);
// 一共七行,每行三个
for (int j = 0; j < 3; j++) {
for (int k = 0; k < 7; k++) {
int ji = 0; // 多个水印之间的间隔
for (String water : waterMarkContent) {
//showTextAligned 方法的参数(文字对齐方式,位置内容,输出水印X轴位置,Y轴位置,旋转角度)
content.showTextAligned(Element.ALIGN_CENTER, water, x / 3 * j + 90, y / 7 * k - ji, 25);
ji = 20;
}
}
}
content.endText();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭
if (stamper != null) {
stamper.close();
}
if (reader != null) {
reader.close();
}
}
}
/**
* excel 加水印
*
* @param srcFile
* @param destFile
* @param waterMarkContent
* @throws Exception
*/
public static void addExcelWaterMark(File srcFile, File destFile, String... waterMarkContent) throws Exception {
addExcelWaterMark(new FileInputStream(srcFile), new FileOutputStream(destFile), waterMarkContent);
}
public static void addExcelWaterMark(File srcFile, OutputStream outputStream, String... waterMarkContent) throws Exception {
addExcelWaterMark(new FileInputStream(srcFile), outputStream, waterMarkContent);
}
public static void addExcelWaterMark(InputStream inputStream, File destFile, String... waterMarkContent) throws Exception {
addExcelWaterMark(inputStream, new FileOutputStream(destFile), waterMarkContent);
}
/**
* excel添加水印
*
* @param inputStream 原始文件流
* @param outputStream 生成文件流
* @param waterMarkName 水印内容
*/
public static void addExcelWaterMark(InputStream inputStream, OutputStream outputStream, String... waterMarkName) throws Exception {
BufferedImage image = createWaterMarkImage(waterMarkName);
// 导出到字节流B
ByteArrayOutputStream os = new ByteArrayOutputStream();
ImageIO.write(image, "png", os);
XSSFWorkbook workbook = new XSSFWorkbook(inputStream);
int pictureIdx = workbook.addPicture(os.toByteArray(), Workbook.PICTURE_TYPE_PNG);
POIXMLDocumentPart poixmlDocumentPart = workbook.getAllPictures().get(pictureIdx);
for (int i = 0; i < workbook.getNumberOfSheets(); i++) {//获取每个Sheet表
XSSFSheet sheet = workbook.getSheetAt(i);
PackagePartName ppn = poixmlDocumentPart.getPackagePart().getPartName();
String relType = XSSFRelation.IMAGES.getRelation();
PackageRelationship pr = sheet.getPackagePart().addRelationship(ppn, TargetMode.INTERNAL, relType, null);
sheet.getCTWorksheet().addNewPicture().setId(pr.getId());
}
workbook.write(outputStream);
}
public static void addWordWaterMark(File srcFile, File destFile, String... waterMarkContent) throws Exception {
addWordWaterMark(new FileInputStream(srcFile), new FileOutputStream(destFile), waterMarkContent);
}
public static void addWordWaterMark(File srcFile, OutputStream outputStream, String... waterMarkContent) throws Exception {
addWordWaterMark(new FileInputStream(srcFile), outputStream, waterMarkContent);
}
public static void addWordWaterMark(InputStream inputStream, File destFile, String... waterMarkContent) throws Exception {
addWordWaterMark(inputStream, new FileOutputStream(destFile), waterMarkContent);
}
/**
* word加水印
*
* @param inputStream
* @param outputStream
* @param customText
* @throws Exception
*/
public static void addWordWaterMark(InputStream inputStream, OutputStream outputStream, String... customText) throws Exception {
XWPFDocument doc = new XWPFDocument(inputStream);
// 把整页都打上水印
for (int lineIndex = -5; lineIndex < 20; lineIndex++) {
styleTop = 100 * lineIndex;
waterMarkDocXDocument_0(doc, customText);
}
doc.write(outputStream);
}
public static void addHtmlWaterMark(File srcFile, File destFile, String... waterMarkContent) throws Exception {
addHtmlWaterMark(new FileInputStream(srcFile), new FileOutputStream(destFile, Boolean.TRUE), waterMarkContent);
}
public static void addHtmlWaterMark(File srcFile, OutputStream outputStream, String... waterMarkContent) throws Exception {
addHtmlWaterMark(new FileInputStream(srcFile), outputStream, waterMarkContent);
}
public static void addHtmlWaterMark(InputStream inputStream, File destFile, String... waterMarkContent) throws Exception {
addHtmlWaterMark(inputStream, new FileOutputStream(destFile, Boolean.TRUE), waterMarkContent);
}
public static void addHtmlWaterMark(InputStream inputStream, OutputStream outputStream, String... customText) throws Exception {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
PrintWriter pw = new PrintWriter(outputStream);
String line = null;
while ((line = bufferedReader.readLine()) != null) {
pw.append(line);
}
String waterMarkCode = "";
int y = 20;
for (String text : customText) {
String replace = waterMarkCode.replace("{0}", text).replace("{1}", "watermark_y:" + y);
pw.println(replace);
y += 20;
}
pw.flush();
pw.close();
bufferedReader.close();
pw.close();
}
/**
* 为文档添加水印
*
* @param doc 需要被处理的docx文档对象
* @param customText 需要添加的水印文字
*/
private static void waterMarkDocXDocument_0(XWPFDocument doc, String... customText) {
// 如果之前已经创建过 DEFAULT 的Header,将会复用
XWPFHeader header = doc.createHeader(HeaderFooterType.DEFAULT);
int size = header.getParagraphs().size();
if (size == 0) {
header.createParagraph();
}
CTP ctp = header.getParagraphArray(0).getCTP();
byte[] rsidr = doc.getDocument().getBody().getPArray(0).getRsidR();
byte[] rsidrdefault = doc.getDocument().getBody().getPArray(0).getRsidRDefault();
ctp.setRsidP(rsidr);
ctp.setRsidRDefault(rsidrdefault);
CTPPr ppr = ctp.addNewPPr();
ppr.addNewPStyle().setVal("Header");
// 开始加水印
CTR ctr = ctp.addNewR();
CTRPr ctrpr = ctr.addNewRPr();
ctrpr.addNewNoProof();
CTGroup group = CTGroup.Factory.newInstance();
CTShapetype shapetype = group.addNewShapetype();
CTTextPath shapeTypeTextPath = shapetype.addNewTextpath();
shapeTypeTextPath.setOn(STTrueFalse.T);
shapeTypeTextPath.setFitshape(STTrueFalse.T);
CTLock lock = shapetype.addNewLock();
lock.setExt(STExt.VIEW);
CTShape shape = group.addNewShape();
shape.setId("PowerPlusWaterMarkObject");
shape.setSpid("_x0000_s102");
shape.setType("#_x0000_t136");
// 设置形状样式(旋转,位置,相对路径等参数)
//shape.setStyle(getShapeStyle(customText));
shape.setFillcolor(fontColor);
// 字体设置为实心
shape.setStroked(STTrueFalse.FALSE);
// 绘制文本的路径
CTTextPath shapeTextPath = shape.addNewTextpath();
// 设置文本字体与大小
shapeTextPath.setStyle("font-family:" + fontName + ";font-size:" + fontSize);
//shapeTextPath.setString(customText);
//CTPicture pict = ctr.addNewPict();
// pict.set(group);
int position = 0;
for (String s : customText) {
// 水印文字之间使用50个空格分隔
s = s + repeatString("\t", 45);
// 一行水印重复水印文字次数
s = repeatString(s, 5);
// 设置形状样式(旋转,位置,相对路径等参数)
shape.setStyle(getShapeStyle(s, position));
shapeTextPath.setString(s);
CTPicture pict = ctr.addNewPict();
pict.set(group);
position += 30;
}
}
/**
* 构建Shape的样式参数
*
* @param customText
* @return
*/
private static String getShapeStyle(String customText, Integer position) {
StringBuilder sb = new StringBuilder();
// 文本path绘制的定位方式
sb.append("position: ").append("absolute");
// 计算文本占用的长度(文本总个数*单字长度)
sb.append(";width: ").append(customText.length() * widthPerWord).append("pt");
// 字体高度
sb.append(";height: ").append("10pt");
sb.append(";z-index: ").append("-251654144");
sb.append(";mso-wrap-edited: ").append("f");
// 设置水印的间隔,这是一个大坑,不能用top,必须要margin-top。
sb.append(";margin-top: ").append(styleTop + position);
sb.append(";mso-position-horizontal-relative: ").append("page");
sb.append(";mso-position-vertical-relative: ").append("page");
sb.append(";mso-position-vertical: ").append("left");
sb.append(";mso-position-horizontal: ").append("center");
sb.append(";rotation: ").append(styleRotation);
return sb.toString();
}
/**
* 将指定的字符串重复repeats次.
*/
private static String repeatString(String pattern, int repeats) {
StringBuilder buffer = new StringBuilder(pattern.length() * repeats);
Stream.generate(() -> pattern).limit(repeats).forEach(buffer::append);
return new String(buffer);
}
/**
* 创建水印图片
* excel
*
* @param waterMark 水印内容
* @return
*/
public static BufferedImage createWaterMarkImage(String... waterMark) {
String[] textArray = waterMark;
Font font = new Font(fontName, Font.PLAIN, 20);
Integer width = 300;
Integer height = 100;
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// 背景透明 开始
Graphics2D g = image.createGraphics();
image = g.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
g.dispose();
// 背景透明 结束
g = image.createGraphics();
g.setColor(new Color(Integer.parseInt("#C5CBCF".substring(1), 16)));// 设定画笔颜色
g.setFont(font);// 设置画笔字体
g.shear(0.1, -0.26);// 设定倾斜度
// 设置字体平滑
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
int y = 50;
for (int i = 0; i < textArray.length; i++) {
g.drawString(textArray[i], 0, y);// 画出字符串
y = y + font.getSize();
}
g.dispose();// 释放画笔
return image;
}
}
4.演示
4.1 pdf
代码如下:
WaterMarkUtils.addPDFWaterMark(new File("D:\\Desktop\\watermark.pdf"),new File("D:\\Desktop\\watermark1.pdf"),"秃头猿猿","hello");
效果如下:
4.2 word
代码如下:
WaterMarkUtils.addExcelWaterMark(new File("D:\\Desktop\\excel.xlsx"),new File("D:\\Desktop\\watermark1.xlsx"),"秃头猿猿","hello");
效果如下:
4.3 word
代码如下:
WaterMarkUtils.addWordWaterMark(new File("D:\\Desktop\\word.docx"),new File("D:\\Desktop\\watermark1.docx"),"秃头猿猿","hello");
效果如下:
4.4 html
代码如下:
WaterMarkUtils.addHtmlWaterMark(new File("D:\\Desktop\\watermark.html"),new File("D:\\Desktop\\watermark1.html"),"秃头猿猿","hello");
效果如下: