package com.doumee.core.utils; import lombok.extern.slf4j.Slf4j; import javax.imageio.ImageIO; import java.awt.*; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.text.SimpleDateFormat; import java.util.Date; /** * 图片水印工具类 * 在图片左下角叠加时间 + 地址水印 * * @author rk * @date 2026/04/21 */ @Slf4j public class ImageWatermarkUtil { /** * 为图片添加水印(时间 + 地址 + 经纬度) * * @param imageInput 原图输入流 * @param timeText 时间文案(如 "2026-04-21 14:30:00") * @param addressText 地址文案(如 "北京市海淀区xxx路") * @param latitude 纬度 * @param longitude 经度 * @return 添加水印后的输入流,水印失败时返回原图 */ public static InputStream addWatermark(InputStream imageInput, String timeText, String addressText, Double latitude, Double longitude) { try { BufferedImage srcImage = ImageIO.read(imageInput); if (srcImage == null) { log.warn("水印: 无法读取图片,跳过水印处理"); imageInput.reset(); return imageInput; } int srcWidth = srcImage.getWidth(); int srcHeight = srcImage.getHeight(); // 字体大小按图片宽度自适应(约3%),最小16px int fontSize = Math.max(16, srcWidth * 3 / 100); int padding = fontSize; int lineHeight = fontSize + fontSize / 3; // 经纬度行 String locationText = ""; if (latitude != null && longitude != null) { locationText = String.format("经度:%.6f 纬度:%.6f", longitude, latitude); } int lineCount = 2 + (locationText.isEmpty() ? 0 : 1); // 计算水印区域背景高度 int bgHeight = lineHeight * lineCount + padding; // 创建图片 BufferedImage destImage = new BufferedImage(srcWidth, srcHeight, BufferedImage.TYPE_INT_RGB); Graphics2D g2d = destImage.createGraphics(); // 绘制原图 g2d.drawImage(srcImage, 0, 0, null); // 绘制半透明背景(左下角) g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f)); g2d.setColor(Color.BLACK); g2d.fillRect(0, srcHeight - bgHeight, srcWidth, bgHeight); // 绘制水印文字 g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.9f)); g2d.setColor(Color.WHITE); Font font = new Font(Font.SANS_SERIF, Font.PLAIN, fontSize); g2d.setFont(font); // 开启抗锯齿 g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); int textX = padding; int textY = srcHeight - bgHeight + padding + fontSize; FontMetrics fm = g2d.getFontMetrics(); int maxTextWidth = srcWidth - padding * 2; // 第一行:时间 g2d.drawString(timeText, textX, textY); // 第二行:地址(地址过长时截断) String displayAddr = addressText; while (fm.stringWidth(displayAddr) > maxTextWidth && displayAddr.length() > 0) { displayAddr = displayAddr.substring(0, displayAddr.length() - 1); } g2d.drawString(displayAddr, textX, textY + lineHeight); // 第三行:经纬度 if (!locationText.isEmpty()) { g2d.drawString(locationText, textX, textY + lineHeight * 2); } g2d.dispose(); // 输出为字节流 ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageIO.write(destImage, "jpg", baos); return new ByteArrayInputStream(baos.toByteArray()); } catch (Exception e) { log.error("添加水印失败,返回原图", e); try { imageInput.reset(); } catch (Exception ignored) {} return imageInput; } } }