字体设置和解决中文乱码
可以动态设置水印文字内容,透明度,旋转角度,字体颜色,可以在全局变量里面配置字体类型,默认使用电脑上的微软雅黑,考虑到lindux系统没有Windows上的字体,添加了可以读取字体文件。可以直接复制Windows里面 C:\Windows\Fonts 目录下的字体文件到lindux系统,就可以解决中文乱码。
package org.example.img;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
public class WaterMarkUtils {
private static Font watermarkFont; // 全局字体变量
static {
try {
// 默认设置为微软雅黑字体
watermarkFont = Font.createFont(Font.TRUETYPE_FONT, new File("C:\\Users\\pc\\Desktop\\my-summary\\simfang.ttf")).deriveFont(Font.PLAIN, 20);
} catch (FontFormatException | IOException e) {
System.out.println(e.getMessage());
// 如果加载字体文件失败,使用默认的系统字体(这里依然使用微软雅黑作为兜底,若系统没有可能显示为其他默认字体)
watermarkFont = new Font("微软雅黑", Font.PLAIN, 20);
}
}
/**
* 添加文字水印的方法
*
* @param sourceImgPath 源图片路径
* @param tarImgPath 保存图片的目标文件夹路径,如果为null,则默认保存到源图片所在文件夹
* @param waterMarkContent 水印内容
* @param watermarkColor 水印颜色
* @param watermarkAlpha 水印透明度(取值范围0.0 - 1.0)
* @param degree 水印文字的旋转角度
*/
public static void addWaterMark(String sourceImgPath, String tarImgPath, String waterMarkContent, Color watermarkColor, float watermarkAlpha, Integer degree) throws Exception {
if (degree == null) {
degree = -45;
}
OutputStream outImgStream = null;
try {
File srcImgFile = new File(sourceImgPath); // 得到文件
String fileExt = getFileExtension(srcImgFile); // 获取源图片文件的扩展名(格式)
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);
g.setColor(watermarkColor); // 设置水印颜色
g.setFont(watermarkFont); // 使用全局配置的字体
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, watermarkAlpha)); // 设置水印文字透明度
g.rotate(Math.toRadians(degree));
JLabel label = new JLabel(waterMarkContent);
FontMetrics metrics = label.getFontMetrics(watermarkFont);
int width = metrics.stringWidth(label.getText());
width = width + 60; // 2倍宽度
int rowsNumber = (srcImgHeight + srcImgHeight / 2) / width; // 图片的高
int columnsNumber = (srcImgWidth + srcImgWidth / 2) * 2 / width; // 图片的宽
if (rowsNumber < 1) {
rowsNumber = 1;
}
if (columnsNumber < 1) {
columnsNumber = 1;
}
for (int j = 0; j < rowsNumber + 2; j++) {
for (int i = 0; i < columnsNumber + 2; i++) {
int x = width + i * width - j * width + 80;
int y = -width + i * width + j * width - 30;
g.drawString(waterMarkContent, x, y);
}
}
g.dispose();
// 确定保存的文件路径
String savePath = getSaveFilePath(sourceImgPath, tarImgPath, fileExt);
// 创建保存文件路径中不存在的文件夹
createDirsIfNotExist(savePath);
// 输出图片
outImgStream = Files.newOutputStream(Paths.get(savePath));
ImageIO.write(bufImg, fileExt, outImgStream);
} catch (Exception e) {
System.err.println("图片添加文字水印失败sourceImgPath:" + sourceImgPath);
} finally {
try {
if (outImgStream != null) {
outImgStream.flush();
outImgStream.close();
}
} catch (Exception e) {
System.err.println(e.getMessage());
}
}
}
/**
* 获取文件的扩展名
*
* @param file 文件对象
* @return 文件的扩展名,如果文件名为空或没有扩展名,则抛出异常
*/
private static String getFileExtension(File file) {
// 获取文件名
String fileName = file.getName();
// 找到文件名中最后一个点的位置
int lastIndex = fileName.lastIndexOf('.');
// 如果点的位置在文件名的第一个字符之后,且不是文件名的最后一个字符
if (lastIndex > 0 && lastIndex < fileName.length() - 1) {
// 返回点后面的所有字符,即文件的扩展名,并转换为小写
return fileName.substring(lastIndex + 1).toLowerCase();
}
// 如果文件名不符合要求,抛出异常
throw new IllegalArgumentException("无法识别文件的扩展名");
}
/**
* 获取保存路径
*/
private static String getSaveFilePath(String sourceImgPath, String tarImgPath, String fileExt) {
File sourceFile = new File(sourceImgPath);
String fileName = sourceFile.getName();
int lastIndex = fileName.lastIndexOf('.');
String baseName = fileName.substring(0, lastIndex);
if (tarImgPath == null) {
// 如果目标路径为null,保存到源图片所在文件夹
return sourceFile.getParent() + File.separator + baseName + " 水印." + fileExt;
} else {
// 确保目标文件夹路径以文件分隔符结尾
if (!tarImgPath.endsWith(File.separator)) {
tarImgPath += File.separator;
}
return tarImgPath + baseName + " 水印." + fileExt;
}
}
/**
* 如果文件路径不存在,则创建文件路径
*
* @param filePath 文件路径
*/
private static void createDirsIfNotExist(String filePath) {
File file = new File(filePath);
File parentDir = file.getParentFile();
if (parentDir != null && !parentDir.exists()) {
boolean mkdirs = parentDir.mkdirs();
if (!mkdirs) {
throw new RuntimeException("保存文件路径创建失败" + parentDir.getAbsolutePath());
}
}
}
/**
* 测试代码
*/
public static void main(String[] args) throws Exception {
String sourceImgPath = "C:\\Users\\pc\\Desktop\\my-summary\\1.png";
String tarImgPath = "C:\\Users\\pc\\Desktop\\my-summary"; // 示例传入目标文件夹路径,可根据需求修改,若为null则按默认规则保存
// 文字水印相关参数示例
String waterMarkContent = "阿斯蒂芬该十多个覅";
Color watermarkColor = Color.green;
float watermarkAlpha = 0.2f;
Integer degree = -45;
// 调用添加文字水印方法
WaterMarkUtils.addWaterMark(sourceImgPath, tarImgPath, waterMarkContent, watermarkColor, watermarkAlpha, degree);
}
}
升级版工具类
添加水印工具类升级版,可以根据指定坐标创建水印;以图片左上角为顶点,长为x轴,宽为y轴,一次可以添加多个坐标,可以创建多个水印。
输入图片信息
import lombok.AllArgsConstructor;
import lombok.Data;
import java.awt.image.BufferedImage;
import java.io.File;
@Data
@AllArgsConstructor
public class InputFileInfo {
public File inputFile;
public BufferedImage originalImage;
public Double width;
public Double height;
}
水印坐标
这个水印坐标是根据传入参数number(图片短边水印个数)计算出每个水印中心的间距,然后根据这个间距计算出所有坐标点,这个坐标可以根据自己需求更改定制。
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.List;
/**
* 水印坐标
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class WatermarkCoordinate {
private double x;
private double y;
/**
* 获取所有的水印中心点
*/
public static List<WatermarkCoordinate> getAllCenterPoint(Double width, Double height, int number) {
ArrayList<WatermarkCoordinate> list = new ArrayList<>();
if (number == 1) {
WatermarkCoordinate watermarkCoordinate = new WatermarkCoordinate(width / 2, height / 2);
list.add(watermarkCoordinate);
} else {
double length = Math.min(width, height) / (number - 1);
int widthNumber = (int) Math.ceil(width / length);
int heightNumber = (int) Math.ceil(height / length);
for (int i = 0; i <= widthNumber; i++) {
for (int j = 0; j <= heightNumber; j++) {
WatermarkCoordinate watermarkCoordinate = new WatermarkCoordinate(i * length, j * length);
list.add(watermarkCoordinate);
}
}
}
return list;
}
}
根据坐标集合批量创建水印工具类
升级后的工具类可以根据传入的坐标集合创建水印,还可以设置字体(可以指定字体文件,或使用电脑自带的字体,如果没有配置字体文件FONT_PATH,会默认使用电脑自带字体DEFAUT_FONT设置的字体类型,如果是Lindux系统,没有配置字体文件FONT_PATH会出现中文乱码),字体大小,字体颜色,字体透明度,字体旋转角度,文字内容。
import com.alibaba.excel.util.StringUtils;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
public class ImageWatermark {
// 全局默认字体,如果没有指定字体文件就使用这个,这里设置为微软雅黑
private static final String FONT_PATH = "C:\\Users\\pc\\Desktop\\my-summary\\1.ttf";
private static final Font DEFAULT_FONT = new Font("微软雅黑", Font.PLAIN, 12);
/**
* 给图片添加水印的方法
*
* @param inputFileInfo 输入文件信息
* @param saveFolderPath 保存文件夹路径
* @param watermarkText 水印文字
* @param watermarkColor 水印颜色
* @param fontSize 字体大小
* @param transparency 透明度
* @param rotateAngle 旋转角度
* @param watermarkCoordinates 水印坐标列表,可传入多个坐标来添加多个水印
*/
public static void addWatermark(InputFileInfo inputFileInfo, String saveFolderPath, String watermarkText, Color watermarkColor,
int fontSize, float transparency, double rotateAngle, List<WatermarkCoordinate> watermarkCoordinates) {
try {
Double width = inputFileInfo.getWidth();
Double height = inputFileInfo.getHeight();
// 如果保存文件夹路径为空,设置保存路径为输入图片所在文件夹,并修改保存文件名
if (saveFolderPath == null || saveFolderPath.isEmpty()) {
saveFolderPath = inputFileInfo.inputFile.getParent();
}
String outputFileName = inputFileInfo.inputFile.getName().substring(0, inputFileInfo.inputFile.getName().lastIndexOf('.')) + "-" + watermarkText
+ inputFileInfo.inputFile.getName().substring(inputFileInfo.inputFile.getName().lastIndexOf('.'));
File outputFile = new File(saveFolderPath + File.separator + outputFileName);
Graphics2D g2d = inputFileInfo.originalImage.createGraphics();
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, transparency));
Font font = null;
if (!StringUtils.isEmpty(FONT_PATH)) {
font = loadFont(fontSize);
if (font != null) {
System.out.println("加载字体文件成功");
}
}
if (font == null) {
font = DEFAULT_FONT.deriveFont((float) fontSize);
}
g2d.setFont(font);
g2d.setColor(watermarkColor);
// 循环遍历每个水印坐标,添加多个水印
for (WatermarkCoordinate coordinate : watermarkCoordinates) {
double xCenter = coordinate.getX();
double yCenter = coordinate.getY();
// 根据传入的中心点坐标和旋转角度来进行变换
AffineTransform affineTransform = new AffineTransform();
affineTransform.rotate(Math.toRadians(rotateAngle), xCenter, yCenter);
g2d.setTransform(affineTransform);
FontMetrics fontMetrics = g2d.getFontMetrics();
int textWidth = fontMetrics.stringWidth(watermarkText);
int textHeight = fontMetrics.getHeight();
// 在指定位置绘制水印文字
g2d.drawString(watermarkText, (float) (xCenter - textWidth / 2.0), (float) (yCenter + textHeight / 4.0));
}
g2d.dispose();
ImageIO.write(inputFileInfo.originalImage, getFileExtension(inputFileInfo.inputFile.getName()), outputFile);
System.out.println("水印添加成功,保存路径: " + outputFile.getAbsolutePath());
} catch (IOException e) {
System.err.println(e.getMessage());
}
}
/**
* 尝试从指定路径加载字体文件,如果加载失败返回null
*/
public static Font loadFont(int fontSize) {
try (InputStream is = Files.newInputStream(Paths.get(FONT_PATH))) {
Font font = Font.createFont(Font.TRUETYPE_FONT, is);
return font.deriveFont((float) fontSize);
} catch (IOException | FontFormatException e) {
System.err.println("加载字体文件失败,将使用默认字体,错误信息: " + e.getMessage());
return null;
}
}
/**
* 读取图片,获取图片宽高
*/
public static InputFileInfo getResult(String imagePath) {
File inputFile = new File(imagePath);
BufferedImage originalImage = null;
try {
originalImage = ImageIO.read(inputFile);
} catch (IOException e) {
throw new RuntimeException(e);
}
// 获取图片的宽度和高度并打印输出
double width = originalImage.getWidth();
double height = originalImage.getHeight();
System.out.println("输入图片的宽度: " + width + ", 高度: " + height + ", 单位(px)");
return new InputFileInfo(inputFile, originalImage, width, height);
}
/**
* 获取文件扩展名的辅助方法
*/
public static String getFileExtension(String fileName) {
int index = fileName.lastIndexOf('.');
if (index > 0) {
return fileName.substring(index + 1);
}
return "";
}
}
测试示例代码
public static void main(String[] args) {
// 示例调用,可根据实际情况修改参数
String imagePath = "C:\\Users\\pc\\Desktop\\my-summary\\1.png";
String saveFolderPath = null;
String watermarkText = "波涛后的宁静";
Color watermarkColor = Color.RED;
int fontSize = 30;
float transparency = 0.2f;
double rotateAngle = 45;
InputFileInfo inputFileInfo = getResult(imagePath);
List<WatermarkCoordinate> allCenterPoint = WatermarkCoordinate.getAllCenterPoint(inputFileInfo.getWidth(), inputFileInfo.getHeight(), 4);
addWatermark(inputFileInfo, saveFolderPath, watermarkText, watermarkColor, fontSize, transparency, rotateAngle, allCenterPoint);
}