文件加水印实现
支持文件类型
/**
* 目前可支持加水印的文件类型
*/
private static List<String> FILE_TYPE_WATER_LIST = Arrays.asList(
".jpg", ".jpeg", ".png",".bmp",".JPG",".PNG",".JPEG",".BMP", ".docx", ".xlsx", ".pptx", ".doc", ".xls", ".ppt", ".pdf"
);
maven 依赖
<properties>
<!-- pdf -->
<itext.version>7.0.3</itext.version>
</properties>
<dependencies>
<!-- PDF -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext7-core</artifactId>
<version>${itext.version}</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>kernel</artifactId>
<version>${itext.version}</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>io</artifactId>
<version>${itext.version}</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>layout</artifactId>
<version>${itext.version}</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>forms</artifactId>
<version>${itext.version}</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>pdfa</artifactId>
<version>${itext.version}</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>pdftest</artifactId>
<version>${itext.version}</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>font-asian</artifactId>
<version>${itext.version}</version>
</dependency>
<!-- word -->
<dependency>
<groupId>e-iceblue</groupId>
<artifactId>spire.doc.free</artifactId>
<version>2.7.3</version>
</dependency>
<!-- excel -->
<dependency>
<groupId>e-iceblue</groupId>
<artifactId>spire.xls.free</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>e-iceblue</groupId>
<artifactId>spire.pdf.free</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.15</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.15</version>
</dependency>
<!-- ppt -->
<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.twelvemonkeys.servlet</groupId>
<artifactId>servlet</artifactId>
<version>3.6</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>
</dependencies>
<repositories>
<repository>
<id>com.e-iceblue</id>
<name>e-iceblue</name>
<url>http://repo.e-iceblue.com/nexus/content/groups/public/</url>
</repository>
</repositories>
若无法导入
e-iceblue
下的包可能是因为maven
的setting.xml
配置了私服 无法连接至http://repo.e-iceblue.com/nexus/content/groups/public/
下载jar包
将私服配置注释一下即可
controller测试
@GetMapping("/test")
public void testWaterMark(@RequestParam String watermark,HttpServletResponse response) throws IOException {
response.setCharacterEncoding(Charsets.UTF_8.name());
//response.setContentType("application/ppt"); //文件类型设置
String header = getRequest().getHeader("User-Agent").toUpperCase();
Boolean flag = header.contains("MSIE") || header.contains("TRIDENT") || header.contains("EDGE");
if (flag) {
response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode("Snipaste_2021-09-16_15-18-17.jpg", "UTF-8"));
} else {
response.setHeader("Content-Disposition", "attachment; filename=" + new String("Snipaste_2021-09-16_15-18-17.jpg".getBytes("utf-8"), "ISO8859-1"));
}
String path = "D:\\cloudpivot\\资料\\Snipaste_2021-09-16_15-18-17.jpg";//原文件绝对路径
String savePath = "D:/cloudpivot/资料/水印/";//生成水印文件暂存地址 后续删除该文件
String suffix = path.substring(path.lastIndexOf('.'));
if (FILE_TYPE_WATER_LIST.contains(suffix)) {
FileWaterMarkUtils.generateFile(savePath,path, watermark, response,new File(path));
}else {
System.out.println("不支持添加水印");
}
}
response
头部信息统一设置在controller
了,工具类中未作设置!
加水印工具类
package com.authine.cloudpivot.ext.util;
import com.itextpdf.io.font.PdfEncodings;
import com.itextpdf.kernel.events.Event;
import com.itextpdf.kernel.events.IEventHandler;
import com.itextpdf.kernel.events.PdfDocumentEvent;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.kernel.pdf.extgstate.PdfExtGState;
import com.itextpdf.layout.Canvas;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.property.TextAlignment;
import com.itextpdf.layout.property.VerticalAlignment;
import com.spire.doc.Document;
import com.spire.doc.FileFormat;
import com.spire.doc.Section;
import com.spire.doc.TextWatermark;
import com.spire.doc.documents.WatermarkLayout;
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.XSLFSlide;
import org.apache.poi.xssf.usermodel.XSSFRelation;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.core.io.ClassPathResource;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.UUID;
/**
* @author zhouzhicheng
* @version 1.0 2021/9/14
*/
@Slf4j
public class FileWaterMarkUtils {
private static final String PDF = "pdf";
private static final String DOC = ".doc.docx";
private static final String XLS = ".xls.xlsx";
private static final String PPT = ".ppt.pptx";
private static final String IMG = ".jpg.JPG.png.PNG.jpeg.JPEG.bmp.BMP";
/**
* 生成水印文件
* @param saveFilePath 水印生成文件暂存路径
* @param fileName 原文件绝对路径
* @param watermark 水印内容
* @param response
*/
public static void generateFile(String saveFilePath,String fileName,String watermark, HttpServletResponse response,File file){
log.info("添加水印文件:{}",fileName);
log.info("水印内容:{}",watermark);
//获取文件后缀
String suffix = fileName.substring((fileName.lastIndexOf(".") + 1));
if (PDF.equals(suffix)){
//generatePDF(fileName,watermark,response);
//多行多条水印
generatePDF2(saveFilePath,file,watermark,response);
log.info("生成水印pdf成功!");
}
if (DOC.contains(suffix)){
generateDOC(saveFilePath,fileName,watermark,response);
log.info("生成水印word成功!");
}
if (XLS.contains(suffix)){
generateExcel(saveFilePath,fileName,watermark,response);
log.info("生成水印excel成功!");
}
if (PPT.contains(suffix)){
generatePPT(saveFilePath,fileName,watermark,response);
log.info("生成水印ppt成功!");
}
if (IMG.contains(suffix)){
generateImage(saveFilePath,fileName,watermark,response);
log.info("生成水印image成功!");
}
}
private static void generateImage(String savePath,String fileName,String watermark,HttpServletResponse response){
try {
File fileImage = new File(savePath.concat(new File(fileName).getName()));
log.info("生成水印后的图片地址:{}",fileImage);
setImageWatermark(fileName,fileImage,Color.GRAY,watermark);
outputStream(fileImage,response);
fileImage.delete();
}catch (Exception e){
log.error(e.getMessage(),e);
}
}
private static void generatePPT(String savePath,String fileName,String watermark,HttpServletResponse response){
try {
File file = new File(fileName);
XMLSlideShow slideShow = new XMLSlideShow(new FileInputStream(file));
ByteArrayOutputStream os = getWaterImage(watermark);
PictureData pictureData = slideShow.addPicture(os.toByteArray(), PictureData.PictureType.PNG);
for (XSLFSlide slide : slideShow.getSlides()) {
XSLFPictureShape pictureShape = slide.createPicture(pictureData);
pictureShape.setAnchor(pictureShape.getAnchor());
}
log.info("水印PPT生成成功:{}",savePath.concat(file.getName()));
//生成新的文件
File filePPT = new File(savePath.concat(file.getName()));
FileOutputStream out = new FileOutputStream(filePPT);
slideShow.write(out);
outputStream(filePPT,response);
filePPT.delete();
slideShow.close();
}catch (Exception e){
log.error(e.getMessage(),e);
}
}
private static void generateExcel(String savePath,String fileName,String watermark,HttpServletResponse response){
try {
File file = new File(fileName);
//读取excel文件
XSSFWorkbook workbook = new XSSFWorkbook(new FileInputStream(file));
ByteArrayOutputStream os = getWaterImage(watermark);
int pictureIdx = workbook.addPicture(os.toByteArray(), Workbook.PICTURE_TYPE_PNG);
for (int i = 0; i < workbook.getNumberOfSheets(); i++) {//获取每个Sheet表
XSSFSheet sheet = workbook.getSheetAt(i);
String rID = sheet.addRelation(null, XSSFRelation.IMAGES, workbook.getAllPictures().get(pictureIdx)).getRelationship().getId();
sheet.getCTWorksheet().addNewPicture().setId(rID);
}
log.info("水印excel生成成功:{}",savePath.concat(file.getName()));
// Excel文件生成后存储的位置
File fileExcel = new File(savePath.concat(file.getName()));
OutputStream fos = new FileOutputStream(fileExcel);
workbook.write(fos);
outputStream(fileExcel,response);
fileExcel.delete();
workbook.close();
}catch (Exception e) {
log.error(e.getMessage(), e);
}
}
private static void generateDOC(String savePath,String fileName,String watermark,HttpServletResponse response){
File file = new File(fileName);
Document document = new Document();
document.loadFromFile(file.toString());
//添加水印
//InsertTextWatermark(document.getSections().get(0), watermark);
InsertManyTextWatermark(document,watermark);
log.info("水印doc生成成功:{}",savePath.concat(file.getName()));
String suffix = fileName.substring(fileName.lastIndexOf('.'));
//存储生成水印的文件
document.saveToFile(savePath.concat(file.getName()), suffix.equals("doc")? FileFormat.Doc: FileFormat.Docx);
File fileS = new File(savePath, file.getName());
//写出文件
outputStream(fileS,response);
fileS.delete();
document.close();
}
private static void generatePDF(String fileName,String watermark,HttpServletResponse response){
try {
File file = new File(fileName);
FileInputStream fis = new FileInputStream(file);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PdfReader readerInfo = new PdfReader(fis);
PdfDocument docInfo = new PdfDocument(readerInfo, new PdfWriter(baos));
//中文字体
File tempFile = File.createTempFile(UUID.randomUUID().toString().replaceAll("-", ""), ".otf");
InputStream input = new ClassPathResource("NotoSerifCJKsc.otf").getInputStream();
Files.copy(input, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
String fontPath = tempFile.getAbsolutePath();
//加水印
addWater(docInfo, fontPath,watermark);
//关闭文档
docInfo.close();
readerInfo.close();
ServletOutputStream fos = response.getOutputStream();
fos.write(baos.toByteArray());
fos.flush();
fos.close();
baos.close();
fis.close();
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
/**
* 图片添加水印
*
* @param srcImgPath 需要添加水印的图片的路径
* @param fileImage 添加水印后图片
* @param markContentColor 水印文字的颜色
* @param waterMarkContent 水印的文字
*/
private static void setImageWatermark(String srcImgPath, File fileImage, Color markContentColor, String waterMarkContent) {
FileOutputStream outImgStream = null;
String suffix = srcImgPath.substring(srcImgPath.lastIndexOf('.')+1);
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(fileImage);
ImageIO.write(bufImg, suffix, outImgStream);
outImgStream.flush();
outImgStream.close();
} catch (Exception e) {
log.error(e.getMessage(),e);
} finally {
if (outImgStream != null) {
try {
outImgStream.close();
} catch (IOException e) {
log.error(e.getMessage(),e);
}
}
}
}
/**
* 文件写出流
* @param file
* @param response
*/
private static void outputStream(File file, HttpServletResponse response){
try {
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream());
byte[] buf = new byte[4096];
int length = bis.read(buf);
//保存文件
while(length != -1)
{
bos.write(buf, 0, length);
length = bis.read(buf);
}
bos.flush();
bos.close();
bis.close();
}catch (Exception e){
log.error(e.getMessage(),e);
}
}
/**
* 获取水印文字总长度
*
* @param waterMarkContent 水印的文字
* @param g
* @return 水印文字总长度
*/
private static int getWatermarkLength(String waterMarkContent, Graphics2D g) {
return g.getFontMetrics(g.getFont()).charsWidth(waterMarkContent.toCharArray(), 0, waterMarkContent.length());
}
/**
* 获取水印文字图片流
*
* @param text
* @return
*/
private static ByteArrayOutputStream getWaterImage(String text) {
ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
// 导出到字节流B
BufferedImage image = createWaterMarkImageBig(text);
ImageIO.write(image, "png", os);
} catch (IOException e) {
log.info(e.getMessage(),e);
}
return os;
}
/**
* 根据文字生成水印图片(大号 平铺)
*
* @param text
* @return
*/
private 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;
}
/**
* 自定义方法插入文本水印并格式水印效果
* word
*/
private static void InsertTextWatermark(Section section,String watermark){
TextWatermark txtWatermark = new TextWatermark();
txtWatermark.setText(watermark);
txtWatermark.setFontSize(40);
txtWatermark.setColor(Color.LIGHT_GRAY);
txtWatermark.setLayout(WatermarkLayout.Diagonal);
section.getDocument().setWatermark(txtWatermark);
}
/**
* word 通过页眉生成多条文字水印
* @param document
* @param watermark
*/
private static void InsertManyTextWatermark(Document document,String watermark){
ShapeObject shape = new ShapeObject(document, ShapeType.Text_Plain_Text);
shape.setWidth(50);
shape.setHeight(15);
//设置艺术字文本内容、位置及样式
shape.setVerticalPosition(10);
shape.setHorizontalPosition(5);
shape.setRotation(330);
shape.getWordArt().setText(watermark);
shape.setFillColor(Color.GRAY);
shape.setLineStyle(ShapeLineStyle.Single);
shape.setStrokeColor(new Color(192, 192, 192, 255));
shape.setStrokeWeight(1);
Section section = document.getSections().get(0);
//获取section的页眉
HeaderFooter header = section.getHeadersFooters().getHeader();
com.spire.doc.documents.Paragraph paragraph;
for (int i = 0; i < 3; i++) {
//添加段落到页眉
paragraph = header.addParagraph();
for (int j = 0; j < 3; j++) {
//复制艺术字并设置多行多列位置
shape = (ShapeObject) shape.deepClone();
shape.setVerticalPosition(150 + 180 * i);
shape.setHorizontalPosition(40 + 150 * j);
paragraph.getChildObjects().add(shape);
}
}
}
/**
* PDF
* @param docInfo
*/
private static void addWater(PdfDocument docInfo,String path,String watermark){
docInfo.addEventHandler(PdfDocumentEvent.END_PAGE, new IEventHandler() {
@Override
public void handleEvent(Event event) {
PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
PdfDocument pdfDoc = docEvent.getDocument();
PdfPage page = docEvent.getPage();
PdfFont font = null;
try {
font = PdfFontFactory.createFont(path, PdfEncodings.IDENTITY_H, false);
} catch (IOException e) {
log.error(e.getMessage(),e);
}
PdfCanvas canvas = new PdfCanvas(page);
PdfExtGState gs1 = new PdfExtGState();
gs1.setFillOpacity(0.10f); // 水印透明度
canvas.setExtGState(gs1);
new Canvas(canvas, pdfDoc, page.getPageSize()).setFontSize(60)
.setFont(font).showTextAligned(new Paragraph(watermark), 298, 421,
pdfDoc.getPageNumber(page), TextAlignment.CENTER, VerticalAlignment.MIDDLE, 45);
}
});
}
/**
* PDF多行多条水印
*/
static void generatePDF2(String savePath,File file,String watermark,HttpServletResponse response){
//创建PdfDocument类的对象
com.spire.pdf.PdfDocument pdf = new com.spire.pdf.PdfDocument();
//加载测试文档
pdf.loadFromFile(file.toString());
//添加一个空白页,目的为了删除jar包添加的水印,后面再移除这一页
pdf.getPages().add();
//获取测试文档中的第一页
//遍历文档中的页
for (int i = 0; i < pdf.getPages().getCount(); i++) {
PdfPageBase page = pdf.getPages().get(i);
//添加文本水印
addPDFWatermark(page, watermark);
}
//移除第一个页
pdf.getPages().remove(pdf.getPages().get(pdf.getPages().getCount()-1));
//保存文档
pdf.saveToFile(savePath.concat(file.getName()));
File fileS = new File(savePath, file.getName());
//写出文件
outputStream(fileS,response);
fileS.delete();
pdf.close();
}
static void addPDFWatermark(PdfPageBase page, String watermark) {
Dimension2D dimension2D = new Dimension();
//水印数量
dimension2D.setSize(page.getCanvas().getClientSize().getWidth() / 5, page.getCanvas().getClientSize().getHeight() / 5);
PdfTilingBrush brush = new PdfTilingBrush(dimension2D);
//透明度
brush.getGraphics().setTransparency(0.4F);
brush.getGraphics().save();
//浮动
brush.getGraphics().translateTransform((float) brush.getSize().getWidth() / 5, (float) brush.getSize().getHeight() / 5);
//倾斜度(负:上倾斜,正:下倾斜)
brush.getGraphics().rotateTransform(-45);
//设置字体颜色
PdfBrush brush_color = new PdfSolidBrush(new PdfRGBColor(Color.GRAY));
//水印内容和水印坐标
brush.getGraphics().drawString(watermark, new PdfTrueTypeFont(new Font("宋体", Font.PLAIN,15),true), brush_color, 25, 30 , new PdfStringFormat(PdfTextAlignment.Center));
brush.getGraphics().restore();
brush.getGraphics().setTransparency(1);
Rectangle2D loRect = new Rectangle2D.Float();
loRect.setFrame(new Point2D.Float(0, 0), page.getCanvas().getClientSize());
page.getCanvas().drawRectangle(brush, loRect);
}
}
生成多行多条水印pdf
时linux环境水印中文字体不显示或乱码处理:
fc-list :lang=zh
检查linux服务器中文字体
若没有中文字体则执行以下操作
将windows的字体复制下来 路径:
C:\Windows\Fonts
丢到linux服务器上 路径:/usr/share/fonts
可以自建一个文件夹放复制过来的字体文件
winfont为自建文件夹 字体则是windows里的一些中文字体包(微软雅黑、宋体)
再刷新一下linux的字体缓存fc-cache -fv
命令
最后重启自己的web应用程序即可!
resources 两个字体附件(生成单条水印pdf时需要)
链接:https://pan.baidu.com/s/1uLIbRpx4K8SXkxWxViFCeA
提取码:42l5
水印内容不传便可下载不加水印文件!!