1 说明
本文主要针对图片和PDF的水印操作设计了四种场景,下图为几个示例
2 实现
针对上述四个场景分别实现如下,首先,引入依赖,因为对图片的水印操作只需要使用JDK自带的功能即可,主要引入的依赖是iText的依赖包。其次,设计一个参数类方便调用本服务时参数传递与接收。之后设计了一个文件属性类方便提取MultipartFile对象传递过来的文件名和文件类型。
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.13</version>
</dependency>
水印参数类
import com.itextpdf.text.BaseColor;
import lombok.Data;
import java.awt.*;
/**
* 文字水印参数
*
* @author 一朝风月
* @date 2021/8/3 14:23
*/
@Data
public class WaterMarkerParams {
/**
* 水印内容
*/
private String mark;
/**
* 水印颜色
*/
private Color color;
/**
* 字体
*/
private String font;
/**
* 水印字体大小
*/
private int fontSize;
/**
* 水印位置 0:右下角,1:居中,9:自定义坐标
*/
private int position;
/**
* 文字样式, 1:居中,2:铺满
*/
private int align;
/**
* 文字倾斜角度
*/
private int angle;
/**
* x坐标
*/
private int x;
/**
* y坐标
*/
private int y;
/**
* 旋转角度
*/
private Integer degree;
/**
* 透明度
*/
private float transparency;
/**
* 不透明度
*/
private float opacity;
/**
* PDF水印颜色
*/
private BaseColor baseColor;
/**
* 视图宽
*/
private float fitWidth;
/**
* 视图高
*/
private float fitHeight;
/**
* pdf图片放置X轴
*/
private float pdfX;
/**
* pdf图片放置Y轴
*/
private float pdfY;
public WaterMarkerParams() {
this.mark = "";
this.color = Color.white;
this.fontSize = 9;
this.font = "宋体";
this.position = 0;
this.transparency = 0.3f;
this.opacity = 0.2f;
this.baseColor = BaseColor.BLACK;
this.fitWidth = 200;
this.fitHeight = 200;
this.pdfX = 200;
this.pdfY = 206;
this.align = 1;
this.angle = 30;
}
}
文件属性类
import lombok.Data;
/**
* 文件属性
*
* @author 一朝风月
* @date 2021/8/4 16:34
*/
@Data
public class FileProps {
private String fileName;
private String fileType;
public FileProps(String fileSrcName) {
if (fileSrcName.contains(".") && fileSrcName != null) {
int location = fileSrcName.lastIndexOf(".");
this.fileName = fileSrcName.substring(0, location);
this.fileType = fileSrcName.substring(location + 1);
} else {
this.fileName = "";
this.fileType = "";
}
}
}
2.1 为图片添加文字水印
/**
* 文字水印
*
* @param file 图片
* @param params 水印参数
* @param httpServletResponse 响应
* @return 加文字后的图片
* @throws IOException IO异常
*/
@PostMapping("text")
public Result mText(MultipartFile file, WaterMarkerParams params, HttpServletResponse httpServletResponse) throws IOException {
FileProps fileProps = new FileProps(file.getOriginalFilename());
if (!"pic".equalsIgnoreCase(ExtService.getCategory(fileProps.getFileType()))) {
return Result.error("请传入图片格式的文件!");
}
Image srcImg = ImageIO.read(file.getInputStream());
int srcImgWidth = srcImg.getWidth(null);
int srcImgHeight = srcImg.getHeight(null);
BufferedImage bufferedImage = new BufferedImage(srcImgWidth, srcImgHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D g = bufferedImage.createGraphics();
g.drawImage(srcImg, 0, 0, srcImgWidth, srcImgHeight, null);
Font font = new Font(params.getFont(), Font.PLAIN, params.getFontSize());
g.setColor(params.getColor());
g.setFont(font);
if (params.getX() > srcImgWidth || params.getY() > srcImgHeight) {
return Result.error("传入x,y坐标超出图片尺寸!");
}
if (params.getPosition() == 0) {
params.setX(srcImgWidth - getWatermarkLength(params.getMark(), g) - 3);
params.setY(srcImgHeight - 3);
}
if (params.getPosition() == 1) {
params.setX((srcImgWidth - getWatermarkLength(params.getMark(), g)) / 2);
params.setY(srcImgHeight / 2);
}
g.drawString(params.getMark(), params.getX(), params.getY());
g.dispose();
// 输出图片
httpServletResponse.setContentType("application/*");
httpServletResponse.setHeader("Content-Disposition", "attachment;filename=\"" + fileProps.getFileName() + "\"");
ImageIO.write(bufferedImage, fileProps.getFileType(), httpServletResponse.getOutputStream());
//测试图片代码
// FileOutputStream outImgStream = new FileOutputStream("D:\\test\\" + UuidUtil.getUuid() + "." + fileType);
// ImageIO.write(bufferedImage, fileType, outImgStream);
// outImgStream.flush();
// outImgStream.close();
return null;
}
2.2 为图片添加图片水印
/**
* 图片水印
*
* @param srcImage 图片
* @param iconImage 水印图片
* @param params 水印参数
* @param httpServletResponse 响应
* @return 加水印的图片
* @throws IOException IO异常
*/
@PostMapping("picture")
public Result mPicture(MultipartFile srcImage, MultipartFile iconImage, WaterMarkerParams params,
HttpServletResponse httpServletResponse) throws IOException {
FileProps fileProps = new FileProps(srcImage.getOriginalFilename());
Image srcImg = ImageIO.read(srcImage.getInputStream());
Image iconImg = ImageIO.read(iconImage.getInputStream());
BufferedImage buffImg = new BufferedImage(srcImg.getWidth(null), srcImg.getHeight(null),
BufferedImage.TYPE_INT_RGB);
Graphics2D g = buffImg.createGraphics();
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
int srcImgWidth = srcImg.getWidth(null);
int srcImgHeight = srcImg.getHeight(null);
int iconImgWidth = iconImg.getWidth(null);
int iconImgHeight = iconImg.getHeight(null);
if (params.getX() > srcImgWidth || params.getY() > srcImgHeight) {
return Result.error("传入x,y坐标超出图片尺寸!");
}
if (params.getPosition() == 0) {
params.setX(srcImgWidth - iconImgWidth);
params.setY(srcImgHeight - iconImgHeight);
}
if (params.getPosition() == 1) {
params.setX(srcImgWidth / 2 - iconImgWidth / 2);
params.setY(srcImgHeight / 2 - iconImgHeight / 2);
}
if (params.getPosition() == 2) {
params.setX(0);
params.setY(0);
}
g.drawImage(srcImg.getScaledInstance(srcImgWidth, srcImgHeight, Image.SCALE_SMOOTH), 0,
0, null);
if (null != params.getDegree()) {
g.rotate(Math.toRadians(params.getDegree()), (double) buffImg.getWidth() / 2, (double) buffImg.getHeight() / 2);
}
ImageIcon imgIcon = new ImageIcon(iconImage.getBytes());
Image img = imgIcon.getImage();
// 透明度
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, params.getTransparency()));
g.drawImage(img, params.getX(), params.getY(), null);
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
g.dispose();
httpServletResponse.setContentType("application/*");
httpServletResponse.setHeader("Content-Disposition", "attachment;filename=\"" + fileProps.getFileName() + "\"");
ImageIO.write(buffImg, fileProps.getFileType(), httpServletResponse.getOutputStream());
// 生成图片
// FileOutputStream outImgStream = new FileOutputStream("D:\\test\\" + UuidUtil.getUuid() + "." + fileType);
// ImageIO.write(buffImg, fileType, outImgStream);
// outImgStream.flush();
// outImgStream.close();
return null;
}
2.3 PDF添加文字水印
/**
* PDF增加水印
*
* @return 加文字后的PDF
*/
@PostMapping("pdfText")
public Result mPdfText(MultipartFile file, WaterMarkerParams params,
HttpServletResponse httpServletResponse) throws IOException, DocumentException {
FileProps fileProps = new FileProps(file.getOriginalFilename());
PdfReader reader = new PdfReader(file.getInputStream());
// FileOutputStream outImgStream = new FileOutputStream("D:\\test\\" + UuidUtil.getUuid() + "."
// + fileProps.getFileType());
httpServletResponse.setContentType("application/*");
httpServletResponse.setHeader("Content-Disposition", "attachment;filename=\"" + fileProps.getFileName() + "\"");
PdfStamper stamper = new PdfStamper(reader, httpServletResponse.getOutputStream());
int total = reader.getNumberOfPages() + 1;
PdfContentByte content;
BaseFont base = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);
PdfGState gs = new PdfGState();
for (int i = 1; i < total; i++) {
content = stamper.getOverContent(i);
gs.setFillOpacity(params.getOpacity());
content.beginText();
content.setColorFill(BaseColor.LIGHT_GRAY);
content.setFontAndSize(base, params.getFontSize());
if (params.getAlign() == 1) {
content.setTextMatrix(70, 200);
content.showTextAligned(Element.ALIGN_CENTER, params.getMark(), 300, 350, params.getAngle());
}
if (params.getAlign() == 2) {
// 间隔
int interval = -5;
// 获取水印文字的高度和宽度
int textH, textW;
JLabel label = new JLabel();
label.setText(params.getMark());
FontMetrics metrics = label.getFontMetrics(label.getFont());
textH = metrics.getHeight();
textW = metrics.stringWidth(label.getText());
// 获取每一页的高度、宽度
com.itextpdf.text.Rectangle pageSizeWithRotation = reader.getPageSizeWithRotation(i);
float pageHeight = pageSizeWithRotation.getHeight();
float pageWidth = pageSizeWithRotation.getWidth();
// 根据纸张大小多次添加, 水印文字成30度角倾斜
for (int height = interval + textH; height < pageHeight; height = height + textH * 3) {
for (int width = interval + textW; width < pageWidth + textW; width = width + textW * 2) {
content.showTextAligned(Element.ALIGN_LEFT, params.getMark(), width - textW, height - textH, 30);
}
}
}
content.endText();
}
stamper.close();
return null;
}
2.4 PDF添加图片水印
/**
* PDF增加图片水印
*
* @return 加图片水印后的PDF
*/
@PostMapping("pdfPicture")
public Result mPdfPicture(MultipartFile file, MultipartFile iconImage, WaterMarkerParams params,
HttpServletResponse httpServletResponse) throws IOException, DocumentException {
FileProps fileProps = new FileProps(file.getOriginalFilename());
PdfReader reader = new PdfReader(file.getInputStream());
httpServletResponse.setContentType("application/*");
httpServletResponse.setHeader("Content-Disposition", "attachment;filename=\"" + fileProps.getFileName() + "\"");
PdfStamper stamper = new PdfStamper(reader, httpServletResponse.getOutputStream());
int total = reader.getNumberOfPages() + 1;
PdfContentByte content;
PdfGState gs = new PdfGState();
for (int i = 1; i < total; i++) {
content = stamper.getOverContent(i);
gs.setFillOpacity(params.getOpacity());
content.beginText();
com.itextpdf.text.Image image = com.itextpdf.text.Image.getInstance(iconImage.getBytes());
image.setAbsolutePosition(params.getPdfX(), params.getPdfY());
image.scaleToFit(params.getFitWidth(), params.getFitHeight());
content.addImage(image);
content.endText();
}
stamper.close();
return null;
}
2.5 如何有既加文字又加图片的情况,那就调用两次接口吧
3 测试
测试后的图和说明中的那幅图一样。