java给图片、word、ppt、excel、pdf添加水印
使用poi、itextpdf、imageio等技术
只支持高版本的office,即支持docx、pptx、xlsx
给word添加水印遇到的问题:
1、遇到有页眉的word时水印添加失败
2、水印最底层被图片表格覆盖
另一篇见: java使用poi给word添加多个水印
maven配置
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.17</version>
</dependency>
<!--2007 docx文件读取,需要导入poi-ooxml-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.17</version>
</dependency>
<!--2003 doc文件的读取,需要导入poi-scratchpad-->
<!--<dependency>-->
<!--<groupId>org.apache.poi</groupId>-->
<!--<artifactId>poi-scratchpad</artifactId>-->
<!--<version>3.8</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>
<!--twelvemonkeys图片处理-->
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-jpeg</artifactId>
<version>3.6</version>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-tiff</artifactId>
<version>3.6</version>
</dependency>
<!--
Optional dependency. Needed only if you deploy `ImageIO` plugins as part of a web app.
Make sure you add the `IIOProviderContextListener` to your `web.xml`, see above.
-->
<dependency>
<groupId>com.twelvemonkeys.servlet</groupId>
<artifactId>servlet</artifactId>
<version>3.6</version>
</dependency>
代码工具类
package com.feng.util;
import com.feng.bean.ResultCode;
import com.feng.error.MyException;
import com.itextpdf.text.*;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.POIXMLDocument;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.sl.usermodel.PictureData;
import org.apache.poi.sl.usermodel.PictureData.PictureType;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xslf.usermodel.*;
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 javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.Color;
import java.awt.Font;
import java.awt.*;
import java.awt.Image;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.*;
import java.lang.reflect.Field;
/**
* @Author: feng
* @Date: 2020/9/28 2:35 下午
*/
@Slf4j
public class WaterMarkUtils {
// 水印透明度
private static float alpha = 0.5f;
// 水印之间的间隔
private static final int XMOVE = 200;
// 水印之间的间隔
private static final int YMOVE = 200;
public static void main(String[] args) {
addPictureWaterMark("d:/tmp/love.jpg", "d:/tmp/love.png", "我是水印");
setPictureWatermark("d:/tmp/love.jpg", "d:/tmp/love.png", Color.GRAY, "我是水印");
setWordWaterMark("d:/tmp/love.docx", "d:/tmp/love1.docx", "我是水印", "docx");
setPdfWatermark("d:/tmp/love.pdf", "d:/tmp/love1.pdf", "我是水印");
setExcelWaterMark("d:/tmp/love.xlsx", "d:/tmp/love1.xlsx", "我是水印");
setPPTWaterMark("d:/tmp/love.pptx", "d:/tmp/love1.pptx", "我是水印");
}
/**
* @param srcImgPath 源图片路径
* @param tarImgPath 保存的图片路径
* @param waterMarkContent 水印内容
*/
public static void addPictureWaterMark(String srcImgPath, String tarImgPath,
String waterMarkContent) {
FileOutputStream outImgStream = null;
try {
// 读取原图片信息
File srcImgFile = new File(srcImgPath);// 得到文件
Image srcImg = ImageIO.read(srcImgFile);// 文件转化为图片
int srcImgWidth = srcImg.getWidth(null);// 获取图片的宽
int srcImgHeight = srcImg.getHeight(null);// 获取图片的高
// 加水印
BufferedImage bufImg = new BufferedImage(srcImgWidth, srcImgHeight,
BufferedImage.TYPE_INT_RGB);
Graphics2D g = bufImg.createGraphics();
// 设置对线段的锯齿状边缘处理
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.drawImage(srcImg, 0, 0, srcImgWidth, srcImgHeight, null);
// 设置水印旋转
g.rotate(Math.toRadians(-40),
(double) bufImg.getWidth() / 2,
(double) bufImg.getHeight() / 2);
g.setColor(new Color(107, 109, 106)); // 根据图片的背景设置水印颜色
Font font = new Font("宋体", Font.PLAIN, 20);
g.setFont(font); // 设置字体
// 设置水印文字透明度
// g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP,alpha));
//根据不同的像素判断多个水印还是单个水印300*300
if (srcImgWidth > 300 && srcImgHeight > 300) {
//多个水印
// 设置水印的坐标
int x = -srcImgWidth / 2;
int y = -srcImgHeight / 2;
int markWidth = font.getSize() * getTextLength(waterMarkContent);// 字体长度
int markHeight = font.getSize();// 字体高度
// 循环添加水印
while (x < srcImgWidth * 1.5) {
y = -srcImgHeight / 2;
while (y < srcImgHeight * 1.5) {
g.drawString(waterMarkContent, x, y);
y += markHeight + YMOVE;
}
x += markWidth + XMOVE;
}
} else {
//单个水印
int x = (srcImgWidth - getWatermarkLength(waterMarkContent, g)) / 2;
int y = srcImgHeight / 2;
g.drawString(waterMarkContent, x, y);
}
g.dispose();
// 输出图片
outImgStream = new FileOutputStream(tarImgPath);
String formatName = srcImgPath.substring(srcImgPath.lastIndexOf(".") + 1, srcImgPath.length());
ImageIO.write(bufImg, formatName, outImgStream);
outImgStream.flush();
} catch (Exception e) {
log.error("addPictureWatermark fail", e);
throw new MyException(ResultCode.FAILURE, "addPictureWatermark fail");
} finally {
if (outImgStream != null) {
try {
outImgStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 图片添加水印
*
* @param srcImgPath 需要添加水印的图片的路径
* @param outImgPath 添加水印后图片输出路径
* @param markContentColor 水印文字的颜色
* @param waterMarkContent 水印的文字
*/
public static void setPictureWatermark(String srcImgPath, String outImgPath, Color markContentColor, String waterMarkContent) {
FileOutputStream outImgStream = null;
try {
// 读取原图片信息
File srcImgFile = new File(srcImgPath);
Image srcImg = ImageIO.read(srcImgFile);
int srcImgWidth = srcImg.getWidth(null);
int srcImgHeight = srcImg.getHeight(null);
// 加水印
BufferedImage bufImg = new BufferedImage(srcImgWidth, srcImgHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D g = bufImg.createGraphics();
g.drawImage(srcImg, 0, 0, srcImgWidth, srcImgHeight, null);
Font font = new Font("宋体", Font.BOLD, srcImgHeight / 6);
//根据图片的背景设置水印颜色
g.setColor(markContentColor);
//设置旋转角度
g.rotate(Math.toRadians(-45), (double) bufImg.getWidth() / 2, (double) bufImg.getHeight() / 2);
//设置水印透明度
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.2F));
g.setFont(font);
int x = (srcImgWidth - getWatermarkLength(waterMarkContent, g)) / 2;
int y = srcImgHeight / 2;
g.drawString(waterMarkContent, x, y);
g.dispose();
// 输出图片
outImgStream = new FileOutputStream(outImgPath);
ImageIO.write(bufImg, "jpg", outImgStream);
outImgStream.flush();
outImgStream.close();
} catch (Exception e) {
log.error("setPictureWatermark fail", e);
throw new MyException(ResultCode.FAILURE, "setPictureWatermark fail");
} finally {
if (outImgStream != null) {
try {
outImgStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* word文字水印
*
* @param inputPath
* @param outPath
* @param markStr
*/
public static void setWordWaterMark(String inputPath, String outPath, String markStr, String fileType) {
if ("docx".equals(fileType)) {
File inputFile = new File(inputPath);
//2003doc 用HWPFDocument ; 2007doc 用 XWPFDocument
XWPFDocument doc = null;
try {
OPCPackage pack = POIXMLDocument.openPackage(inputPath);
doc = new XWPFDocument(pack);
// doc = new XWPFDocument(new FileInputStream(inputFile));
} catch (FileNotFoundException e) {
log.error("setWordWaterMark fail: 源文件不存在", e);
throw new MyException(ResultCode.FAILURE, "setWordWaterMark fail: 源文件不存在");
} catch (IOException e) {
log.error("setWordWaterMark fail: 读取源文件IO异常", e);
throw new MyException(ResultCode.FAILURE, "setWordWaterMark fail: 读取源文件IO异常");
} catch (Exception e) {
log.error("setWordWaterMark fail: 不支持的文档格式", e);
throw new MyException(ResultCode.FAILURE, "setWordWaterMark fail: 不支持的文档格式");
}
XWPFParagraph paragraph = doc.createParagraph();
XWPFHeaderFooterPolicy headerFooterPolicy = doc.getHeaderFooterPolicy();
if (headerFooterPolicy == null) {
headerFooterPolicy = doc.createHeaderFooterPolicy();
}
// create default Watermark - fill color black and not rotated
headerFooterPolicy.createWatermark(markStr);
// get the default header
// Note: createWatermark also sets FIRST and EVEN headers
// but this code does not updating those other headers
XWPFHeader header = headerFooterPolicy.getHeader(XWPFHeaderFooterPolicy.DEFAULT);
paragraph = header.getParagraphArray(0);
// // get com.microsoft.schemas.vml.CTShape where fill color and rotation is set
// paragraph.getCTP().newCursor();
// System.out.println(paragraph.getCTP().getRArray(0));
// System.out.println(paragraph.getCTP().getRArray(0).getPictArray(0));
org.apache.xmlbeans.XmlObject[] xmlobjects = paragraph.getCTP().getRArray(0).getPictArray(0).selectChildren(
new javax.xml.namespace.QName("urn:schemas-microsoft-com:vml", "shape"));
if (xmlobjects.length > 0) {
com.microsoft.schemas.vml.CTShape ctshape = (com.microsoft.schemas.vml.CTShape) xmlobjects[0];
ctshape.setFillcolor("#D3D3D3");
ctshape.setStyle(ctshape.getStyle() + ";rotation:315" + ";height:60pt;width:360pt");
}
File file = new File(outPath);
if (!file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
log.error("setWordWaterMark fail: 创建输出文件IO异常", e);
throw new MyException(ResultCode.FAILURE, "setWordWaterMark fail: 创建输出文件失败");
}
}
try {
doc.write(new FileOutputStream(outPath));
} catch (FileNotFoundException e) {
log.error("setWordWaterMark fail: 输出文件不存在", e);
throw new MyException(ResultCode.FAILURE, "setWordWaterMark fail: 创建输出文件失败");
} catch (IOException e) {
log.error("setWordWaterMark fail: ", e);
throw new MyException(ResultCode.FAILURE, "setWordWaterMark fail");
} finally {
if (doc != null) {
try {
doc.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
/**
* pdf设置文字水印
*
* @param inputPath
* @param outPath
* @param markStr
*/
public static void setPdfWatermark(String inputPath, String outPath, String markStr) {
File file = new File(outPath);
if (!file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
log.error("setPdfWatermark fail: 创建输出文件IO异常", e);
throw new MyException(ResultCode.FAILURE, "setPdfWatermark fail: 创建输出文件IO异常");
}
}
BufferedOutputStream bufferOut = null;
try {
bufferOut = new BufferedOutputStream(new FileOutputStream(file));
} catch (FileNotFoundException e) {
log.error("setPdfWatermark fail: 源文件不存在", e);
throw new MyException(ResultCode.FAILURE, "setPdfWatermark fail: 源文件不存在");
}
PdfStamper stamper = null;
int total = 0;
PdfContentByte content;
Rectangle pageSizeWithRotation = null;
BaseFont base = null;
PdfReader reader = null;
try {
reader = new PdfReader(inputPath);
//解决PdfReader not opened with owner password
Field f = PdfReader.class.getDeclaredField("ownerPasswordUsed");
f.setAccessible(true);
f.set(reader, Boolean.TRUE);
stamper = new PdfStamper(reader, bufferOut);
total = reader.getNumberOfPages() + 1;
base = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);
} catch (IOException e) {
log.error("setPdfWatermark fail:", e);
throw new MyException(ResultCode.FAILURE, "setPdfWatermark fail");
} catch (DocumentException e) {
log.error("setPdfWatermark fail:", e);
throw new MyException(ResultCode.FAILURE, "setPdfWatermark fail");
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
// 获取水印文字的高度和宽度
int textH = 0, textW = 0;
JLabel label = new JLabel();
label.setText(markStr);
FontMetrics metrics = label.getFontMetrics(label.getFont());
textH = metrics.getHeight();
textW = metrics.stringWidth(label.getText());
PdfGState gs = new PdfGState();
for (int i = 1; i < total; i++) {
//在内容上方加水印
content = stamper.getOverContent(i);
gs.setFillOpacity(0.5f);
content.saveState();
content.setGState(gs);
content.beginText();
// content.setRGBColorFill(0, 0, 0);
content.setFontAndSize(base, 20);
// 获取每一页的高度、宽度
pageSizeWithRotation = reader.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, markStr, width - textW, height - textH, 30);
}
}
content.endText();
}
try {
stamper.close();
bufferOut.flush();
bufferOut.close();
reader.close();
} catch (IOException e) {
log.error("setPdfWatermark fail:", e);
throw new MyException(ResultCode.FAILURE, "setPdfWatermark fail");
} catch (DocumentException e) {
log.error("setPdfWatermark fail:", e);
throw new MyException(ResultCode.FAILURE, "setPdfWatermark fail");
}
}
/**
* excel设置水印
*
* @param inputPath
* @param outPath
* @param markStr
*/
public static void setExcelWaterMark(String inputPath, String outPath, String markStr) {
//读取excel文件
XSSFWorkbook workbook;
try {
workbook = new XSSFWorkbook(new FileInputStream(inputPath));
} catch (FileNotFoundException e) {
log.error("setExcelWaterMark fail: 源文件不存在", e);
throw new MyException(ResultCode.FAILURE, "setExcelWaterMark fail: 源文件不存在");
} catch (IOException e) {
log.error("setExcelWaterMark fail: 读取源文件IO异常", e);
throw new MyException(ResultCode.FAILURE, "setExcelWaterMark fail: 读取源文件IO异常");
}
OutputStream fos = null;
ByteArrayOutputStream os = null;
try {
//获取水印
os = getImage(markStr);
int pictureIdx = workbook.addPicture(os.toByteArray(), Workbook.PICTURE_TYPE_PNG);
for (int i = 0; i < workbook.getNumberOfSheets(); i++) {//获取每个Sheet表
XSSFSheet sheet1 = workbook.getSheetAt(i);
String rID = sheet1.addRelation(null, XSSFRelation.IMAGES, workbook.getAllPictures().get(pictureIdx)).getRelationship().getId();
//set background picture to sheet
sheet1.getCTWorksheet().addNewPicture().setId(rID);
}
// Excel文件生成后存储的位置。
File file = new File(outPath);
fos = new FileOutputStream(file);
workbook.write(fos);
} catch (Exception e) {
log.error("setExcelWaterMark fail: 创建输出文件IO异常", e);
throw new MyException(ResultCode.FAILURE, "setExcelWaterMark 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();
}
}
}
}
/**
* PPT设置水印
*
* @param path
* @param targetpath
* @param markStr
* @throws IOException
*/
public static void setPPTWaterMark(String path, String targetpath, String markStr) {
XMLSlideShow slideShow;
try {
slideShow = new XMLSlideShow(new FileInputStream(path));
} catch (IOException e) {
log.error("setPPTWaterMark fail:", e);
throw new MyException(ResultCode.FAILURE, "setPPTWaterMark fail:获取PPT文件失败");
}
ByteArrayOutputStream os = null;
FileOutputStream out = null;
try {
//获取水印
os = getImage(markStr);
PictureData pictureData1 = slideShow.addPicture(os.toByteArray(), PictureType.PNG);
for (XSLFSlide slide : slideShow.getSlides()) {
XSLFPictureShape pictureShape = slide.createPicture(pictureData1);
// pictureShape.setAnchor(new java.awt.Rectangle(250, 0, 500, 500));
pictureShape.setAnchor(pictureShape.getAnchor());
}
out = new FileOutputStream(targetpath);
slideShow.write(out);
} catch (IOException e) {
log.error("setPPTWaterMark fail:" + e);
throw new MyException(ResultCode.FAILURE, "setPPTWaterMark 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();
}
}
}
}
/**
* 获取水印文字总长度
*
* @param waterMarkContent 水印的文字
* @param g
* @return 水印文字总长度
*/
public static int getWatermarkLength(String waterMarkContent, Graphics2D g) {
return g.getFontMetrics(g.getFont()).charsWidth(waterMarkContent.toCharArray(), 0, waterMarkContent.length());
}
/**
* 获取文本长度。汉字为1:1,英文和数字为2:1
*/
private static int getTextLength(String text) {
int length = text.length();
for (int i = 0; i < text.length(); i++) {
String s = String.valueOf(text.charAt(i));
if (s.getBytes().length > 1) {
length++;
}
}
length = length % 2 == 0 ? length / 2 : length / 2 + 1;
return length;
}
/**
* 获取水印文字图片流
*
* @param text
* @return
*/
private static ByteArrayOutputStream getImage(String text) {
ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
// 导出到字节流B
BufferedImage image = createWaterMarkImageBig(text);
ImageIO.write(image, "png", os);
} catch (IOException e) {
log.error("getImage fail: 创建水印图片IO异常", e);
throw new MyException(ResultCode.FAILURE, "getImage fail: 创建水印图片IO异常");
}
return os;
}
/**
* 根据文字生成水印图片(大号 平铺)
*
* @param text
* @return
*/
public static BufferedImage createWaterMarkImageBig(String text) {
Integer width = 1000;
Integer height = 800;
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);// 获取bufferedImage对象
Font font = new Font("宋体", Font.PLAIN, 70);
Graphics2D g2d = image.createGraphics();
image = g2d.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
g2d.dispose();
g2d = image.createGraphics();
//设置字体颜色和透明度
g2d.setColor(new Color(0, 0, 0, 60));
//设置字体
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;
}
}
问题:
1、要添加字体到/usr/share/fonts下
2、pptx文件中含有嵌入文件,即在ppt中插入外部文件会异常:
java.lang.ArrayStoreException: null
at java.lang.System.arraycopy(Native Method)
at org.apache.xmlbeans.impl.values.XmlObjectBase._typedArray(XmlObjectBase.java:409)
at org.apache.xmlbeans.impl.values.XmlObjectBase.selectPath(XmlObjectBase.java:457)
at org.apache.xmlbeans.impl.values.XmlObjectBase.selectPath(XmlObjectBase.java:415)
at org.apache.poi.xslf.usermodel.XSLFDrawing.<init>(XSLFDrawing.java:44)
at org.apache.poi.xslf.usermodel.XSLFSheet.initDrawingAndShapes(XSLFSheet.java:201)
at org.apache.poi.xslf.usermodel.XSLFSheet.getDrawing(XSLFSheet.java:177)
at org.apache.poi.xslf.usermodel.XSLFSheet.createPicture(XSLFSheet.java:260)
原因是:其他pom引入中含有xmlbeans2.3版本。
解决办法:排除xmlbeans2.3,确保xmlbeans版本是2.6 。