java实现图片添加水印工具类

字体设置和解决中文乱码

可以动态设置水印文字内容,透明度,旋转角度,字体颜色,可以在全局变量里面配置字体类型,默认使用电脑上的微软雅黑,考虑到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);
	}

效果展示

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值