一、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 水印
注:以上內容僅提供參考和交流,請勿用于商業用途,如有侵權聯系本人刪除!