一、pom 文件引用
<dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>4.1.2</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>4.1.2</version></dependency><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.12.0</version></dependency><dependency><groupId>org.jsoup</groupId><artifactId>jsoup</artifactId><version>1.14.3</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>ooxml-schemas</artifactId><version>1.0</version><exclusions><!--解决含嵌入文件ppt转换报错 ArrayStoreException--><exclusion><artifactId>xmlbeans</artifactId><groupId>org.apache.xmlbeans</groupId></exclusion></exclusions></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.16</version></dependency><dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.13</version></dependency>
二、代码展示
2.1,ImageUtil 图片工具类
package cn.piesat.space.watermark.util;import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URL;/*** @author: wangjing* @createTime: 2023-12-05 15:01* @version: 1.0.0* @Description: 图片工具类*/
public class ImageUtil {/*** 调整图片尺寸** @param inputPath 原图片地址* @param outPath 新图片地址* @param newWidth 新图片宽* @param newHeight 新图片高*/public static void resizeImage(String inputPath, String outPath, Integer newWidth, Integer newHeight) {try {BufferedImage bufferedImage = readPicture(inputPath);// 创建新的 BufferedImage,并设置绘制质量BufferedImage resizedImage = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_RGB);Graphics2D g2d = resizedImage.createGraphics();g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);// 绘制原始图像到新的 BufferedImage,并进行缩放Image scaledImage = bufferedImage.getScaledInstance(newWidth, newHeight, Image.SCALE_SMOOTH);g2d.drawImage(scaledImage, 0, 0, null);g2d.dispose();// 保存新的图片ImageIO.write(resizedImage, "jpg", new File(outPath));System.out.println("图片尺寸调整完成!");} catch (IOException e) {e.printStackTrace();}}/*** 读取图片** @param path* @return*/public static BufferedImage readPicture(String path) {try {// 尝试获取本地return readLocalPicture(path);} catch (Exception e) {// 尝试获取网路return readNetworkPicture(path);}}/*** 读取本地图片** @param path* @return*/public static BufferedImage readLocalPicture(String path) {if (null == path) {throw new RuntimeException("本地图片路径不能为空");}// 读取原图片信息 得到文件File srcImgFile = new File(path);try {// 将文件对象转化为图片对象return ImageIO.read(srcImgFile);} catch (IOException e) {throw new RuntimeException(e);}}/*** 读取网络图片** @param path 网络图片地址*/public static BufferedImage readNetworkPicture(String path) {if (null == path) {throw new RuntimeException("网络图片路径不能为空");}try {// 创建一个URL对象,获取网络图片的地址信息URL url = new URL(path);// 将URL对象输入流转化为图片对象 (url.openStream()方法,获得一个输入流)BufferedImage bugImg = ImageIO.read(url.openStream());if (null == bugImg) {throw new RuntimeException("网络图片地址不正确");}return bugImg;} catch (IOException e) {throw new RuntimeException(e);}}
}
2.1,DocumentWaterMarkUtils 文档水印工具类
package cn.piesat.space.watermark.util;import cn.piesat.space.watermark.enums.WatermarkTypeEnum;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.pdf.*;
import com.microsoft.schemas.vml.CTShape;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.sl.usermodel.PictureData;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xslf.usermodel.XMLSlideShow;
import org.apache.poi.xslf.usermodel.XSLFPictureShape;
import org.apache.poi.xslf.usermodel.XSLFSlideMaster;
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.model.XWPFHeaderFooterPolicy;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFHeader;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.xmlbeans.XmlObject;import javax.imageio.ImageIO;
import javax.swing.*;
import javax.xml.namespace.QName;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.*;
import java.lang.reflect.Field;/*** @author: wangjing* @createTime: 2023-12-05 15:01* @version: 1.0.0* @Description: 文档水印工具类*/
@Slf4j
public class DocumentWaterMarkUtils {/*** 水印默认字体*/private static Font defaultFont = new Font("宋体", Font.PLAIN, 70);/*** 水印默认字体颜色*/private static Color defaultColor = new Color(0, 0, 0, 60);/*** 水印透明度*/private static float alpha = 0.5f;/*** 水印之间的间隔*/private static final int xMove = 200;/*** 水印之间的间隔*/private static final int yMove = 200;public static void main(String[] args) {String watermarkText = "测试水印";String watermarkImage = "/Users/wangjing/Desktop/watermark/test.jpeg";// pdfString pdfInputPath = "/Users/wangjing/Desktop/watermark/pdf/test.pdf";String pdfOutPath = "/Users/wangjing/Desktop/watermark/pdf/test2.pdf";addPdfWatermark(pdfInputPath, pdfOutPath, watermarkText);// pptString pptInputPath = "/Users/wangjing/Desktop/watermark/ppt/test.pptx";// excel 文字水印String pptOutPath = "/Users/wangjing/Desktop/watermark/ppt/test2.pptx";addPPTWatermark(pptInputPath, pptOutPath, 1, watermarkText, defaultFont, defaultColor);// excel 图片水印String pptOutPathImage = "/Users/wangjing/Desktop/watermark/ppt/test3.pptx";addPPTWatermark(pptInputPath, pptOutPathImage, 2, watermarkImage, null, null);// excelString watermarkImageExcel = "/Users/wangjing/Desktop/watermark/image/test1.jpg";String excelInputPath = "/Users/wangjing/Desktop/watermark/excel/test.xlsx";// excel 文字水印String excelOutPath = "/Users/wangjing/Desktop/watermark/excel/test2.xlsx";addExcelWatermark(excelInputPath, excelOutPath, 1, watermarkText, defaultFont, defaultColor);// excel 图片水印String excelOutPathImage = "/Users/wangjing/Desktop/watermark/excel/test3.xlsx";addExcelWatermark(excelInputPath, excelOutPathImage, 2, watermarkImageExcel, null, null);//word 添加文字水印String wordInputPath = "/Users/wangjing/Desktop/watermark/word/test.docx";String wordOutPath = "/Users/wangjing/Desktop/watermark/word/test2.docx";addWordWatermark(wordInputPath, wordOutPath, watermarkText, "#D3D3D3", true, false);}/*** pdf设置文字水印** @param inputPath* @param outPath* @param watermark*/public static void addPdfWatermark(String inputPath, String outPath, String watermark) {// 创建输出文件File file = new File(outPath);if (!file.exists()) {try {file.createNewFile();} catch (IOException e) {log.error("addPdfWatermark fail: 创建输出文件IO异常", e);throw new RuntimeException("addPdfWatermark fail: 创建输出文件IO异常");}}BufferedOutputStream outBufferedOutputStream = null;try {outBufferedOutputStream = new BufferedOutputStream(new FileOutputStream(file));} catch (FileNotFoundException e) {log.error("addPdfWatermark fail: 源文件不存在", e);throw new RuntimeException("addPdfWatermark fail: 源文件不存在");}PdfStamper pdfStamper = null;int total = 0;PdfContentByte content;com.itextpdf.text.Rectangle pageSizeWithRotation = null;BaseFont base = null;PdfReader inputPdfReader = null;try {inputPdfReader = new PdfReader(inputPath);// 解决PdfReader not opened with owner passwordField f = PdfReader.class.getDeclaredField("ownerPasswordUsed");f.setAccessible(true);f.set(inputPdfReader, Boolean.TRUE);pdfStamper = new PdfStamper(inputPdfReader, outBufferedOutputStream);total = inputPdfReader.getNumberOfPages() + 1;base = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);} catch (IOException e) {log.error("addPdfWatermark fail:", e);throw new RuntimeException("addPdfWatermark fail:IOException");} catch (DocumentException e) {log.error("addPdfWatermark fail:", e);throw new RuntimeException("addPdfWatermark fail: DocumentException");} catch (IllegalAccessException e) {e.printStackTrace();} catch (NoSuchFieldException e) {e.printStackTrace();}// 获取水印文字的高度和宽度Integer textH = 0;Integer textW = 0;JLabel label = new JLabel();label.setText(watermark);FontMetrics metrics = label.getFontMetrics(label.getFont());textH = metrics.getHeight();textW = metrics.stringWidth(label.getText());PdfGState gs = new PdfGState();for (Integer i = 1; i < total; i++) {//在内容上方加水印content = pdfStamper.getOverContent(i);gs.setFillOpacity(alpha);content.saveState();content.setGState(gs);content.beginText();content.setFontAndSize(base, 20);// 获取每一页的高度、宽度pageSizeWithRotation = inputPdfReader.getPageSizeWithRotation(i);float pageHeight = pageSizeWithRotation.getHeight();float pageWidth = pageSizeWithRotation.getWidth();// 根据纸张大小多次添加, 水印文字成30度角倾斜for (int height = -5 + textH; height < pageHeight; height = height + yMove) {for (int width = -5 + textW; width < pageWidth + textW; width = width + xMove) {content.showTextAligned(Element.ALIGN_LEFT, watermark, width - textW, height - textH, 30);}}content.endText();}// 管理所有流try {pdfStamper.close();outBufferedOutputStream.flush();outBufferedOutputStream.close();inputPdfReader.close();} catch (IOException e) {log.error("addPdfWatermark fail:", e);throw new RuntimeException("addPdfWatermark fail: 关闭流异常,IOException");} catch (DocumentException e) {log.error("addPdfWatermark fail:", e);throw new RuntimeException("addPdfWatermark fail: 关闭流异常,DocumentException");}}/*** PPT 添加水印** @param inputPath 原ppt地址* @param targetpath 新ppt地址* @param watermarkType 水印类型(1:文字水印;2:图片水印)* @param watermark 水印* @param font 字体* @param color 颜色*/public static void addPPTWatermark(String inputPath, String targetpath, Integer watermarkType, String watermark,Font font, Color color) {XMLSlideShow slideShow = null;try {slideShow = new XMLSlideShow(new FileInputStream(inputPath));} catch (IOException e) {log.error("addPPTWatermark fail:", e);throw new RuntimeException("addPPTWatermark fail: 获取PPT文件失败");}ByteArrayOutputStream os = null;FileOutputStream out = null;try {//获取水印os = getWatermarkImage(watermarkType, watermark, font, color, 1280, 720);PictureData pictureData = slideShow.addPicture(os.toByteArray(), PictureData.PictureType.PNG);for (XSLFSlideMaster xslfSlideMaster : slideShow.getSlideMasters()) {XSLFPictureShape pictureShape = xslfSlideMaster.createPicture(pictureData);
// pictureShape.setAnchor(new java.awt.Rectangle(0, 0, -1 , -1));pictureShape.setAnchor(pictureShape.getAnchor());}out = new FileOutputStream(targetpath);slideShow.write(out);} catch (IOException e) {log.error("addPPTWatermark fail:" + e);throw new RuntimeException("addPPTWatermark fail: 生成ppt文件失败");} finally {if (slideShow != null) {try {slideShow.close();} catch (IOException e) {e.printStackTrace();}}if (out != null) {try {out.close();} catch (IOException e) {e.printStackTrace();}}if (os != null) {try {os.close();} catch (IOException e) {e.printStackTrace();}}}}/*** excel 添加水印** @param inputPath 原excel地址* @param outPath 新excel地址* @param watermarkType 水印类型:1:文本水印;2:图片水印;* @param watermark 水印(文字或图片URL)* @param font 字体* @param color 颜色*/public static void addExcelWatermark(String inputPath, String outPath, Integer watermarkType, String watermark,Font font, Color color) {//读取excel文件XSSFWorkbook workbook = null;try {workbook = new XSSFWorkbook(new FileInputStream(inputPath));} catch (FileNotFoundException e) {log.error("addExcelWaterMark fail: 源文件不存在", e);throw new RuntimeException("addExcelWaterMark fail: 源文件不存在");} catch (IOException e) {log.error("addExcelWaterMark fail: 读取源文件IO异常", e);throw new RuntimeException("addExcelWaterMark fail: 读取源文件IO异常");}OutputStream fos = null;ByteArrayOutputStream os = new ByteArrayOutputStream();try {//获取水印os = getWatermarkImage(watermarkType, watermark, font, color, 1000, 800);int pictureIdx = workbook.addPicture(os.toByteArray(), Workbook.PICTURE_TYPE_PNG);// 获取每个Sheet表for (int i = 0; i < workbook.getNumberOfSheets(); i++) {XSSFSheet xssfSheet = workbook.getSheetAt(i);String rID =xssfSheet.addRelation(null, XSSFRelation.IMAGES, workbook.getAllPictures().get(pictureIdx)).getRelationship().getId();xssfSheet.getCTWorksheet().addNewPicture().setId(rID);}// Excel文件生成后存储的位置。File file = new File(outPath);fos = new FileOutputStream(file);workbook.write(fos);} catch (Exception e) {log.error("addExcelWaterMark fail: 创建输出文件IO异常", e);throw new RuntimeException("addExcelWaterMark fail: 创建输出文件IO异常");} finally {if (os != null) {try {os.close();} catch (IOException e) {e.printStackTrace();}}if (fos != null) {try {fos.close();} catch (IOException e) {e.printStackTrace();}}if (workbook != null) {try {workbook.close();} catch (IOException e) {e.printStackTrace();}}}}/*** word 添加文字水印** @param inputPath 原文件地址* @param outPath 新文件地址* @param watermark 水印文字* @param watermarkColor 水印字体颜色* @param incline 是否倾斜(true 倾斜,false 不倾斜)* @param readable 是否设置仅可读(true 仅可读,false 可编辑)*/public static void addWordWatermark(String inputPath, String outPath, String watermark, String watermarkColor,Boolean incline, Boolean readable) {// 根据URL 获取 XWPFDocument 对象XWPFDocument xwpfDocument = null;try {xwpfDocument = new XWPFDocument(new FileInputStream(new File(inputPath)));} catch (FileNotFoundException fileNotFoundException) {log.error("addWordWaterMark fail: ", fileNotFoundException);throw new RuntimeException("addWordWaterMark fail: 源文件不存在");} catch (IOException ioException) {log.error("addWordWaterMark fail: ", ioException);throw new RuntimeException("addWordWaterMark fail: 读取源文件IO异常");} catch (Exception exception) {log.error("addWordWaterMark fail: ", exception);throw new RuntimeException("addWordWaterMark fail: 不支持的文档格式");}XWPFHeaderFooterPolicy headerFooterPolicy = xwpfDocument.getHeaderFooterPolicy();if (headerFooterPolicy == null) {//兼容处理headerFooterPolicy = xwpfDocument.createHeaderFooterPolicy();}//调用API添加水印,效果不好为水平居中headerFooterPolicy.createWatermark(watermark);//处理后续文档更新水印逻辑XWPFHeader xwpfHeader = headerFooterPolicy.getHeader(XWPFHeaderFooterPolicy.DEFAULT);XWPFParagraph xwpfParagraph = xwpfHeader.getParagraphArray(0);//设置水印样式和位置,保持倾斜角度更好看和实用xwpfParagraph.getCTP().newCursor();XmlObject[] xmlobjects = xwpfParagraph.getCTP().getRArray(0).getPictArray(0).selectChildren(new QName("urn:schemas-microsoft-com:vml", "shape"));if (xmlobjects.length > 0) {CTShape ctshape = (CTShape) xmlobjects[0];// 水印字体颜色ctshape.setFillcolor(watermarkColor);// 水印是否倾斜if (incline) {ctshape.setStyle(ctshape.getStyle() + ";rotation:315");}}// 设置文档只读if (readable) {xwpfDocument.enforceReadonlyProtection();}File file = new File(outPath);if (!file.exists()) {try {file.createNewFile();} catch (IOException e) {log.error("addWordWaterMark fail: 创建输出文件IO异常", e);throw new RuntimeException("addWordWaterMark fail: 创建输出文件失败");}}try {xwpfDocument.write(new FileOutputStream(outPath));} catch (FileNotFoundException e) {log.error("addWordWaterMark fail: 输出文件不存在", e);throw new RuntimeException("addWordWaterMark fail: 创建输出文件失败");} catch (IOException e) {log.error("addWordWaterMark fail: ", e);throw new RuntimeException("addWordWaterMark fail");} finally {if (xwpfDocument != null) {try {xwpfDocument.close();} catch (IOException e) {e.printStackTrace();}}}}/*** 获取水印图片文件流** @param watermarkType 水印类型(1:文字水印;2:图片水印)* @param watermark 水印* @param font 字体* @param color 颜色* @param width 图片宽* @param height 图片高* @return*/private static ByteArrayOutputStream getWatermarkImage(Integer watermarkType, String watermark, Font font,Color color, Integer width, Integer height) {ByteArrayOutputStream os = new ByteArrayOutputStream();try {// 导出到字节流BBufferedImage bufferedImage = null;if (WatermarkTypeEnum.TEXT_WATERMARK.getCode().equals(watermarkType)) {bufferedImage = createWaterMarkImageBig(watermark, font, color, width, height);} else {// 图片水印bufferedImage = ImageUtil.readPicture(watermark);}ImageIO.write(bufferedImage, "png", os);} catch (IOException e) {log.error("testToImage fail: 创建水印图片IO异常", e);throw new RuntimeException("testToImage fail: 创建水印图片IO异常");}return os;}/*** 根据文字生成水印图片(大号 平铺)** @param text 文本文字* @param font 字体* @param color 颜色* @return*/public static BufferedImage createWaterMarkImageBig(String text, Font font, Color color, Integer width,Integer height) {// 获取bufferedImage对象BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);Graphics2D g2d = image.createGraphics();image = g2d.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);g2d.dispose();g2d = image.createGraphics();//设置字体颜色和透明度g2d.setColor(color);//设置字体g2d.setStroke(new BasicStroke(1));//设置字体类型 加粗 大小g2d.setFont(font);//设置倾斜度g2d.rotate(Math.toRadians(-30), (double) image.getWidth() / 2, (double) image.getHeight() / 2);FontRenderContext context = g2d.getFontRenderContext();Rectangle2D bounds = font.getStringBounds(text, context);double x = (width - bounds.getWidth()) / 2;double y = (height - bounds.getHeight()) / 2;double ascent = -bounds.getY();double baseY = y + ascent;//写入水印文字原定高度过小,所以累计写水印,增加高度g2d.drawString(text, (int) x, (int) baseY);//设置透明度g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));//释放对象g2d.dispose();return image;}}
三、效果展示
3.1,word 水印
3.2,excel 水印
3.2.1,文字水印
3.2.2,图片水印
3.3,ppt 水印
3.3.1,文字水印
3.3.2,图片水印
3.4,PDF 水印
注:以上内容仅提供参考和交流,请勿用于商业用途,如有侵权联系本人删除!