最新消息: USBMI致力于为网友们分享Windows、安卓、IOS等主流手机系统相关的资讯以及评测、同时提供相关教程、应用、软件下载等服务。

自定义复杂图片水印

IT圈 admin 1浏览 0评论

自定义复杂图片水印

我的社交能力还不如5岁儿童和狗。

文章目录

  • 前言
  • 一、主要工具类
  • 总结


前言

之前写过一些简单的图片压缩和图片加水印:JAVA实现图片质量压缩和加水印

本次主要是针对图片加水印进行一个升级,图片水印自定义,自适应大小。

来,先看几张不同分辨率的图片加过水印的效果

分辨率:1702 x 1276

分辨率:4000 x 3000

分辨率:1080 x 1440

其实大家看右下角,csdn自动给我图片加的水印就能发现,4000*3000的图片几乎都看不到右下角的水印,说明他这个水印不是自适应的。但是我左下角加的,三张图片都是这么大,根据图片自动变化大小。


提示:以下是本篇文章正文内容,下面案例可供参考

一、主要工具类

其中主要是进行了坐标点的计算,通过拼接计算来确定各个水印的坐标点,从而进行绘制。

其中进行了一部分封装

 /*** 添加图片文字水印(此方法不在考虑文字过长需要换行等情况)** @param targetImg 原图片路径,需要是本地路径,如是网络url需要单独更改* @param x         水印x轴坐标* @param y         水印y轴坐标* @param fontSize  水印文字大小 英镑,会根据图片分辨率自动放大和缩小,只需要指定标准350像素的文字标准值即可* @param c         水印文字颜色 例如:Color.WHITE* @param fontName  水印文字字体 例如:宋体,微软雅黑* @param fontStyle 水印文字字体风格,例如:Font.PLAIN正常字体/BOLD加粗/ITALIC斜体.* @param alpha     水印文字透明度,例如: 1.0F,范围 0-1F* @param text      水印文字*/public static void pressText(String targetImg, int x, int y, int fontSize, Color c, String fontName, int fontStyle, float alpha, String text) {try {// 读取原图片BufferedImage src = ImageIO.read(new File(targetImg));// 获取原图的宽和高int width = src.getWidth(null);int height = src.getHeight(null);// 动态设置字体大小fontSize = width < height ? width / 350 * fontSize : height / 350 * fontSize;// 创建空图片,指定宽高BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);// 准备绘制图片Graphics2D g = image.createGraphics();// 将原图绘制到此空图片上,左上角为坐标起点,(0,0),宽和高为原图大小g.drawImage(src, 0, 0, width, height, null);// 设置水印字体颜色.g.setColor(c);// 设置水印字体,Font.PLAIN正常字体/BOLD加粗/ITALIC斜体.g.setFont(new Font(fontName, fontStyle, fontSize));// 透明度g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));/* 消除java.awt.Font字体的锯齿 */g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_ON);g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,RenderingHints.VALUE_FRACTIONALMETRICS_ON);// 添加文字水印g.drawString(text, x, y);// 输出图像g.dispose();File f = new File(targetImg);// 将加过水印的图片保存到指定文件ImageIO.write(image, targetImg.substring(targetImg.lastIndexOf(".") + 1), f);} catch (Exception e) {log.error("水印添加失败:{}", e.getMessage());}}

中间还有一部分的坐标点计算,感觉写的有点乱了,没有梳理好,仅供参考。

下面是整个工具类的代码:

package com.example.demo.util;import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.Week;
import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.json.JSONObject;
import lombok.extern.slf4j.Slf4j;import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.Date;/*** @author GMaya* @create 2023/2/22 9:44* @Description 类描述: 图片相关的工具类*/
@Slf4j
public class ImageUtils {/*** 图片加水印** @param json      相关设置* @param targetImg 要加水印的图片路径* @param staticUrl 静态地址*/public static void pressTexts(JSONObject json, String targetImg, String staticUrl) {try {long l = System.currentTimeMillis();// 读取原图片,获取宽度和高度 TODO 使用BufferedImage中没有exif信息,导致有exif的图片宽和高会读取错误,需要进行修正。目前本地图片修正暂时没有处理。线上图片来源为阿里oss,在链接后拼接自动修正参数即可:?x-oss-process=image/auto-orient,1
//            String uuu = ".jpg";
//            String uuu = ".jpg?x-oss-process=image/auto-orient,1";
//            URL url = new URL(uuu);
//            BufferedImage src = ImgUtil.read(url);BufferedImage src = ImgUtil.read(targetImg);int width = src.getWidth(null);int height = src.getHeight(null);log.info("原图宽:{},高:{}",width,height);// 动态设置字体大小 , 以350像素9的基数大小设置int fontSize = width < height ? width / 350 * 9 : height / 350 * 9;BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);Graphics2D g = image.createGraphics();// 将原图绘制g.drawImage(src, 0, 0, width, height, null);// 设置水印字体颜色.g.setColor(Color.WHITE);// 设置水印字体,Font.PLAIN正常字体/BOLD加粗/ITALIC斜体.g.setFont(new Font("微软雅黑", Font.PLAIN, fontSize));// 透明度
//            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.9f));// 消除java.awt.Font字体的锯齿g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_ON);g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,RenderingHints.VALUE_FRACTIONALMETRICS_ON);// 比例int rat = width < height ? width / 350 : height / 350;BufferedImage readAddress = ImgUtil.read(staticUrl + "address.png");BufferedImage readUserName = ImgUtil.read(staticUrl + "userName.png");BufferedImage readCompanyName = ImgUtil.read(staticUrl + "companyName.png");BufferedImage readFloor = ImgUtil.read(staticUrl + "floor.png");int imageWidth = rat * 6;int imageHeight = rat * 7;// 计算各个坐标点int xx1 = 0; // 姓名图标int yy1 = 0; // 姓名图标int xx2 = 0; // 姓名int yy2 = 0; // 姓名int xx3 = 0; // 公司图标int yy3 = 0; // 公司图标int xx4 = 0; // 公司int yy4 = 0; // 公司int xx5 = 0; // 地址图标int yy5 = 0; // 地址图标int xx6 = 0; // 地址int yy6 = 0; // 地址int xx7 = 0; // 左侧竖线int yy7 = 0; // 左侧竖线int xx8 = 0; // 拍照标签int yy8 = 0; // 拍照标签int xx9 = 0; // logo图标int yy9 = 0; // logo图标int xx10 = 32; // 年月日坐标int yy10 = 0; // 年月日坐标int xx11 = 32; // 时分秒坐标int yy11 = 0; // 时分秒坐标int w12 = 0; // 底板宽 底板坐标点无需计算,但是底板的宽高需要计算int h12 = 0; // 底板高int sd = 0; // 标准差// 公共坐标计算点 xx为图标,xxx为文字int xx = 0;int yy = 0;int xxx = 0;int yyy = 0;// 提前计算出各个坐标点for (int i = 0; i < 7; i++) {if (i == 0) {// 用户名称String userNameIsShow = json.getStr("userNameIsShow");if ("0".equals(userNameIsShow)) {// x轴默认距离左边16*比例xx1 = 10 * rat;yy1 = height - (10 * rat) - imageHeight;xx = xx1;yy = yy1;xx2 = 10 * rat + imageWidth + (4 * rat);yy2 = height - (10 * rat);xxx = xx2;yyy = yy2;}continue;}if (i == 1) {// 公司名称String companyNameIsShow = json.getStr("companyNameIsShow");if ("0".equals(companyNameIsShow)) {// 如果公司展示,则需要判断上一个用户字段是否展示,如果展示和不展示,x,y轴坐标将不一样if (xx == 0) {xx3 = 10 * rat;yy3 = height - (10 * rat) - imageHeight;xx = xx3;yy = yy3;xx4 = 10 * rat + imageWidth + (4 * rat);yy4 = height - (10 * rat);xxx = xx4;yyy = yy4;} else {xx3 = xx;yy3 = yy - (5 * rat) - imageHeight;yy = yy3;xx4 = xxx;yy4 = yyy - (5 * rat) - fontSize;yyy = yy4;}}continue;}if (i == 2) {// 全部默认0是,1否String addressIsShow = json.getStr("addressIsShow");if ("0".equals(addressIsShow)) {// 此处定义最高位置的地址即可,换行的在真正生成时进行处理String address = json.getStr("address");int maxLength = 13;// 计算出地址的占用行数int i2 = address.length() / maxLength + (address.length() % maxLength == 0 ? 0 : 1);if (xx == 0) {// 公司和姓名都不展示xx5 = 10 * rat;yy5 = height - 12 - (fontSize * i2);xx6 = 10 * rat + imageWidth + 10;yy6 = height - (12 * (i2 + 1)) - (fontSize * (i2 + 1));xxx  = xx6;yyy = yy6;continue;} else {xx5 = xx;yy5 = yy - 12 - (fontSize * i2);yy = yy5;xx6 = xxx;yy6 = yyy - (12 * (i2 + 1)) - (fontSize * (i2 + 1));yyy = yy6;}}}if (i == 5) {// 时间控制String timeIsShow = json.getStr("timeIsShow");if ("0".equals(timeIsShow)) {xx10 = xx;xx11 = xx;if (xx == 0) {yy10 = height - (10 * rat);yy11 = yy10 - fontSize - 8 * rat;}else{yy10 = yyy - (8 * rat);yy11 = yy10 - fontSize - 8 * rat;}yyy = yy11;yy = yy11;}continue;}if (i == 6) {// 计算地图蒙版,需要在所有坐标计算后,在计算地图大小// 缩放比例int i1 = width < height ? width / 350 : height / 350;w12 = readFloor.getWidth() * i1 / 3 * 2 ;String timeIsShow = json.getStr("timeIsShow");if ("0".equals(timeIsShow)) {h12 = height - yyy  + (i1 * 18);continue;}String addressIsShow = json.getStr("addressIsShow");String companyNameIsShow = json.getStr("companyNameIsShow");String userNameIsShow = json.getStr("userNameIsShow");if("0".equals(addressIsShow) || "0".equals(companyNameIsShow) ||"0".equals(userNameIsShow)){h12 = height - yyy - 6 + 30;}System.out.println(h12);continue;}}for (int i = 0; i < 6; i++) {if (i == 1) {// 优先绘制底板String addressIsShow = json.getStr("addressIsShow");String companyNameIsShow = json.getStr("companyNameIsShow");String userNameIsShow = json.getStr("userNameIsShow");String timeIsShow = json.getStr("timeIsShow");if (!"0".equals(addressIsShow) && !"0".equals(companyNameIsShow) && !"0".equals(userNameIsShow) &&!"0".equals(timeIsShow)) {// 如果所有的都关闭,则无需添加底板和水印,直接跳出循环,结束水印绘制。break;}g.drawImage(readFloor, 6 * rat, height - h12, w12, h12 - 6 * rat, null);continue;}if (i == 2) {// 全部默认0是,1否String userNameIsShow = json.getStr("userNameIsShow");if ("0".equals(userNameIsShow)) {String userName = json.getStr("userName");g.drawImage(readUserName, xx1, yy1,imageWidth,imageHeight, null);g.drawString(userName, xx2, yy2);}continue;}if (i == 3) {// 全部默认0是,1否String companyNameIsShow = json.getStr("companyNameIsShow");if ("0".equals(companyNameIsShow)) {String companyName = json.getStr("companyName");g.drawImage(readCompanyName, xx3, yy3,imageWidth,imageHeight, null);g.drawString(companyName, xx4, yy4);}continue;}if (i == 4) {// 全部默认0是,1否String addressIsShow = json.getStr("addressIsShow");if ("0".equals(addressIsShow)) {String address = json.getStr("address");int maxLength = 13;// 计算出地址的占用行数int i2 = address.length() / maxLength + (address.length() % maxLength == 0 ? 0 : 1);int len = 0;for (int j = i2 - 1; j >= 0; j--) {int mlen = maxLength * (j + 1);len = j * maxLength;if (mlen < address.length()) {g.drawString(address.substring(len, mlen), xx6, yy6 + (fontSize * (j + 1)));} else {g.drawString(address.substring(len), xx6, yy6 + (fontSize * (j + 1)));}}// 图标g.drawImage(readAddress, xx5, yy5,imageWidth,imageHeight, null);}continue;}}// 输出图像g.dispose();File f = new File(targetImg);ImageIO.write(image, targetImg.substring(targetImg.lastIndexOf(".") + 1), f);// 如果年月日展示,则进行坐标计算String timeIsShow = json.getStr("timeIsShow");if ("0".equals(timeIsShow)) {String s = DateUtil.formatDate(new Date());Week week = DateUtil.dayOfWeekEnum(new Date());// 文字水印为:2023-02-22  星期三pressText(targetImg, xx10, yy10, 12, Color.WHITE, "微软雅黑", Font.PLAIN, 0.9F, s + "  " + week.toChinese());String time = DateUtil.format(new Date(), "HH:mm:ss");pressText(targetImg, xx11, yy11, 18, Color.WHITE, "微软雅黑", Font.BOLD, 0.9F, time);}//花费毫秒数long ll = System.currentTimeMillis();log.info("图片加水印耗时:{}{}", ll - l, "ms");} catch (Exception e) {e.printStackTrace();}}/*** 添加图片文字水印(此方法不在考虑文字过长需要换行等情况)** @param targetImg 原图片路径,需要是本地路径,如是网络url需要单独更改* @param x         水印x轴坐标* @param y         水印y轴坐标* @param fontSize  水印文字大小 英镑,会根据图片分辨率自动放大和缩小,只需要指定标准350像素的文字标准值即可* @param c         水印文字颜色 例如:Color.WHITE* @param fontName  水印文字字体 例如:宋体,微软雅黑* @param fontStyle 水印文字字体风格,例如:Font.PLAIN正常字体/BOLD加粗/ITALIC斜体.* @param alpha     水印文字透明度,例如: 1.0F,范围 0-1F* @param text      水印文字*/public static void pressText(String targetImg, int x, int y, int fontSize, Color c, String fontName, int fontStyle, float alpha, String text) {try {// 读取原图片BufferedImage src = ImageIO.read(new File(targetImg));// 获取原图的宽和高int width = src.getWidth(null);int height = src.getHeight(null);// 动态设置字体大小fontSize = width < height ? width / 350 * fontSize : height / 350 * fontSize;// 创建空图片,指定宽高BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);// 准备绘制图片Graphics2D g = image.createGraphics();// 将原图绘制到此空图片上,左上角为坐标起点,(0,0),宽和高为原图大小g.drawImage(src, 0, 0, width, height, null);// 设置水印字体颜色.g.setColor(c);// 设置水印字体,Font.PLAIN正常字体/BOLD加粗/ITALIC斜体.g.setFont(new Font(fontName, fontStyle, fontSize));// 透明度g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));/* 消除java.awt.Font字体的锯齿 */g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_ON);g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,RenderingHints.VALUE_FRACTIONALMETRICS_ON);// 添加文字水印g.drawString(text, x, y);// 输出图像g.dispose();File f = new File(targetImg);// 将加过水印的图片保存到指定文件ImageIO.write(image, targetImg.substring(targetImg.lastIndexOf(".") + 1), f);} catch (Exception e) {log.error("水印添加失败:{}", e.getMessage());}}/*** 计算文字长度,一个中文按两个长度,英文按一个长度计算** @param text 文字* @return*/public static int getLength(String text) {int length = 0;for (int i = 0; i < text.length(); i++) {if (new String(text.charAt(i) + "").getBytes().length > 1) {length += 2;} else {length += 1;}}return length / 2;}/*** 导出zip压缩包** @param srcPath  要压缩的文件夹路径* @param response 输出流*/public static void downloadZip(String srcPath, HttpServletResponse response) {// 将此目录打包成zipFile file = ZipUtil.zip(srcPath);OutputStream toClient = null;try {// 以流的形式下载文件。BufferedInputStream fis = new BufferedInputStream(new FileInputStream(file.getPath()));byte[] buffer = new byte[fis.available()];fis.read(buffer);fis.close();// 清空responseresponse.reset();toClient = new BufferedOutputStream(response.getOutputStream());response.setCharacterEncoding("UTF-8");response.setContentType("application/zip");response.setHeader("Content-Disposition", "attachment;filename=" + file.getName());toClient.write(buffer);toClient.flush();} catch (Exception e) {log.error("下载zip压缩包过程发生异常:", e);} finally {if (toClient != null) {try {toClient.close();} catch (IOException e) {log.error("zip包下载关流失败:", e);}}//删除改临时zip包(此zip包任何时候都不需要保留,因为源文件随时可以再次进行压缩生成zip包)file.delete();}}}

总结

其中,通过ImageIO.read读取的图片,是没有exif信息的,这样就会导致有exif的图片,读取后宽和高可能不准确,最终生成的图片就是未旋转的,看起来就是颠倒的。如果有谁有办法处理本地图片exif问题,可以回复下。
让我学习学习。
如果使用的是阿里云oss地址,在后面拼接参数即可:
例如:

String uuu = ".jpg";
String uuu = ".jpg?x-oss-process=image/auto-orient,1";

在oss地址后面拼接?x-oss-process=image/auto-orient,1 即可。阿里云oss自适应传送门

自定义复杂图片水印

我的社交能力还不如5岁儿童和狗。

文章目录

  • 前言
  • 一、主要工具类
  • 总结


前言

之前写过一些简单的图片压缩和图片加水印:JAVA实现图片质量压缩和加水印

本次主要是针对图片加水印进行一个升级,图片水印自定义,自适应大小。

来,先看几张不同分辨率的图片加过水印的效果

分辨率:1702 x 1276

分辨率:4000 x 3000

分辨率:1080 x 1440

其实大家看右下角,csdn自动给我图片加的水印就能发现,4000*3000的图片几乎都看不到右下角的水印,说明他这个水印不是自适应的。但是我左下角加的,三张图片都是这么大,根据图片自动变化大小。


提示:以下是本篇文章正文内容,下面案例可供参考

一、主要工具类

其中主要是进行了坐标点的计算,通过拼接计算来确定各个水印的坐标点,从而进行绘制。

其中进行了一部分封装

 /*** 添加图片文字水印(此方法不在考虑文字过长需要换行等情况)** @param targetImg 原图片路径,需要是本地路径,如是网络url需要单独更改* @param x         水印x轴坐标* @param y         水印y轴坐标* @param fontSize  水印文字大小 英镑,会根据图片分辨率自动放大和缩小,只需要指定标准350像素的文字标准值即可* @param c         水印文字颜色 例如:Color.WHITE* @param fontName  水印文字字体 例如:宋体,微软雅黑* @param fontStyle 水印文字字体风格,例如:Font.PLAIN正常字体/BOLD加粗/ITALIC斜体.* @param alpha     水印文字透明度,例如: 1.0F,范围 0-1F* @param text      水印文字*/public static void pressText(String targetImg, int x, int y, int fontSize, Color c, String fontName, int fontStyle, float alpha, String text) {try {// 读取原图片BufferedImage src = ImageIO.read(new File(targetImg));// 获取原图的宽和高int width = src.getWidth(null);int height = src.getHeight(null);// 动态设置字体大小fontSize = width < height ? width / 350 * fontSize : height / 350 * fontSize;// 创建空图片,指定宽高BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);// 准备绘制图片Graphics2D g = image.createGraphics();// 将原图绘制到此空图片上,左上角为坐标起点,(0,0),宽和高为原图大小g.drawImage(src, 0, 0, width, height, null);// 设置水印字体颜色.g.setColor(c);// 设置水印字体,Font.PLAIN正常字体/BOLD加粗/ITALIC斜体.g.setFont(new Font(fontName, fontStyle, fontSize));// 透明度g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));/* 消除java.awt.Font字体的锯齿 */g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_ON);g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,RenderingHints.VALUE_FRACTIONALMETRICS_ON);// 添加文字水印g.drawString(text, x, y);// 输出图像g.dispose();File f = new File(targetImg);// 将加过水印的图片保存到指定文件ImageIO.write(image, targetImg.substring(targetImg.lastIndexOf(".") + 1), f);} catch (Exception e) {log.error("水印添加失败:{}", e.getMessage());}}

中间还有一部分的坐标点计算,感觉写的有点乱了,没有梳理好,仅供参考。

下面是整个工具类的代码:

package com.example.demo.util;import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.Week;
import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.json.JSONObject;
import lombok.extern.slf4j.Slf4j;import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.Date;/*** @author GMaya* @create 2023/2/22 9:44* @Description 类描述: 图片相关的工具类*/
@Slf4j
public class ImageUtils {/*** 图片加水印** @param json      相关设置* @param targetImg 要加水印的图片路径* @param staticUrl 静态地址*/public static void pressTexts(JSONObject json, String targetImg, String staticUrl) {try {long l = System.currentTimeMillis();// 读取原图片,获取宽度和高度 TODO 使用BufferedImage中没有exif信息,导致有exif的图片宽和高会读取错误,需要进行修正。目前本地图片修正暂时没有处理。线上图片来源为阿里oss,在链接后拼接自动修正参数即可:?x-oss-process=image/auto-orient,1
//            String uuu = ".jpg";
//            String uuu = ".jpg?x-oss-process=image/auto-orient,1";
//            URL url = new URL(uuu);
//            BufferedImage src = ImgUtil.read(url);BufferedImage src = ImgUtil.read(targetImg);int width = src.getWidth(null);int height = src.getHeight(null);log.info("原图宽:{},高:{}",width,height);// 动态设置字体大小 , 以350像素9的基数大小设置int fontSize = width < height ? width / 350 * 9 : height / 350 * 9;BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);Graphics2D g = image.createGraphics();// 将原图绘制g.drawImage(src, 0, 0, width, height, null);// 设置水印字体颜色.g.setColor(Color.WHITE);// 设置水印字体,Font.PLAIN正常字体/BOLD加粗/ITALIC斜体.g.setFont(new Font("微软雅黑", Font.PLAIN, fontSize));// 透明度
//            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.9f));// 消除java.awt.Font字体的锯齿g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_ON);g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,RenderingHints.VALUE_FRACTIONALMETRICS_ON);// 比例int rat = width < height ? width / 350 : height / 350;BufferedImage readAddress = ImgUtil.read(staticUrl + "address.png");BufferedImage readUserName = ImgUtil.read(staticUrl + "userName.png");BufferedImage readCompanyName = ImgUtil.read(staticUrl + "companyName.png");BufferedImage readFloor = ImgUtil.read(staticUrl + "floor.png");int imageWidth = rat * 6;int imageHeight = rat * 7;// 计算各个坐标点int xx1 = 0; // 姓名图标int yy1 = 0; // 姓名图标int xx2 = 0; // 姓名int yy2 = 0; // 姓名int xx3 = 0; // 公司图标int yy3 = 0; // 公司图标int xx4 = 0; // 公司int yy4 = 0; // 公司int xx5 = 0; // 地址图标int yy5 = 0; // 地址图标int xx6 = 0; // 地址int yy6 = 0; // 地址int xx7 = 0; // 左侧竖线int yy7 = 0; // 左侧竖线int xx8 = 0; // 拍照标签int yy8 = 0; // 拍照标签int xx9 = 0; // logo图标int yy9 = 0; // logo图标int xx10 = 32; // 年月日坐标int yy10 = 0; // 年月日坐标int xx11 = 32; // 时分秒坐标int yy11 = 0; // 时分秒坐标int w12 = 0; // 底板宽 底板坐标点无需计算,但是底板的宽高需要计算int h12 = 0; // 底板高int sd = 0; // 标准差// 公共坐标计算点 xx为图标,xxx为文字int xx = 0;int yy = 0;int xxx = 0;int yyy = 0;// 提前计算出各个坐标点for (int i = 0; i < 7; i++) {if (i == 0) {// 用户名称String userNameIsShow = json.getStr("userNameIsShow");if ("0".equals(userNameIsShow)) {// x轴默认距离左边16*比例xx1 = 10 * rat;yy1 = height - (10 * rat) - imageHeight;xx = xx1;yy = yy1;xx2 = 10 * rat + imageWidth + (4 * rat);yy2 = height - (10 * rat);xxx = xx2;yyy = yy2;}continue;}if (i == 1) {// 公司名称String companyNameIsShow = json.getStr("companyNameIsShow");if ("0".equals(companyNameIsShow)) {// 如果公司展示,则需要判断上一个用户字段是否展示,如果展示和不展示,x,y轴坐标将不一样if (xx == 0) {xx3 = 10 * rat;yy3 = height - (10 * rat) - imageHeight;xx = xx3;yy = yy3;xx4 = 10 * rat + imageWidth + (4 * rat);yy4 = height - (10 * rat);xxx = xx4;yyy = yy4;} else {xx3 = xx;yy3 = yy - (5 * rat) - imageHeight;yy = yy3;xx4 = xxx;yy4 = yyy - (5 * rat) - fontSize;yyy = yy4;}}continue;}if (i == 2) {// 全部默认0是,1否String addressIsShow = json.getStr("addressIsShow");if ("0".equals(addressIsShow)) {// 此处定义最高位置的地址即可,换行的在真正生成时进行处理String address = json.getStr("address");int maxLength = 13;// 计算出地址的占用行数int i2 = address.length() / maxLength + (address.length() % maxLength == 0 ? 0 : 1);if (xx == 0) {// 公司和姓名都不展示xx5 = 10 * rat;yy5 = height - 12 - (fontSize * i2);xx6 = 10 * rat + imageWidth + 10;yy6 = height - (12 * (i2 + 1)) - (fontSize * (i2 + 1));xxx  = xx6;yyy = yy6;continue;} else {xx5 = xx;yy5 = yy - 12 - (fontSize * i2);yy = yy5;xx6 = xxx;yy6 = yyy - (12 * (i2 + 1)) - (fontSize * (i2 + 1));yyy = yy6;}}}if (i == 5) {// 时间控制String timeIsShow = json.getStr("timeIsShow");if ("0".equals(timeIsShow)) {xx10 = xx;xx11 = xx;if (xx == 0) {yy10 = height - (10 * rat);yy11 = yy10 - fontSize - 8 * rat;}else{yy10 = yyy - (8 * rat);yy11 = yy10 - fontSize - 8 * rat;}yyy = yy11;yy = yy11;}continue;}if (i == 6) {// 计算地图蒙版,需要在所有坐标计算后,在计算地图大小// 缩放比例int i1 = width < height ? width / 350 : height / 350;w12 = readFloor.getWidth() * i1 / 3 * 2 ;String timeIsShow = json.getStr("timeIsShow");if ("0".equals(timeIsShow)) {h12 = height - yyy  + (i1 * 18);continue;}String addressIsShow = json.getStr("addressIsShow");String companyNameIsShow = json.getStr("companyNameIsShow");String userNameIsShow = json.getStr("userNameIsShow");if("0".equals(addressIsShow) || "0".equals(companyNameIsShow) ||"0".equals(userNameIsShow)){h12 = height - yyy - 6 + 30;}System.out.println(h12);continue;}}for (int i = 0; i < 6; i++) {if (i == 1) {// 优先绘制底板String addressIsShow = json.getStr("addressIsShow");String companyNameIsShow = json.getStr("companyNameIsShow");String userNameIsShow = json.getStr("userNameIsShow");String timeIsShow = json.getStr("timeIsShow");if (!"0".equals(addressIsShow) && !"0".equals(companyNameIsShow) && !"0".equals(userNameIsShow) &&!"0".equals(timeIsShow)) {// 如果所有的都关闭,则无需添加底板和水印,直接跳出循环,结束水印绘制。break;}g.drawImage(readFloor, 6 * rat, height - h12, w12, h12 - 6 * rat, null);continue;}if (i == 2) {// 全部默认0是,1否String userNameIsShow = json.getStr("userNameIsShow");if ("0".equals(userNameIsShow)) {String userName = json.getStr("userName");g.drawImage(readUserName, xx1, yy1,imageWidth,imageHeight, null);g.drawString(userName, xx2, yy2);}continue;}if (i == 3) {// 全部默认0是,1否String companyNameIsShow = json.getStr("companyNameIsShow");if ("0".equals(companyNameIsShow)) {String companyName = json.getStr("companyName");g.drawImage(readCompanyName, xx3, yy3,imageWidth,imageHeight, null);g.drawString(companyName, xx4, yy4);}continue;}if (i == 4) {// 全部默认0是,1否String addressIsShow = json.getStr("addressIsShow");if ("0".equals(addressIsShow)) {String address = json.getStr("address");int maxLength = 13;// 计算出地址的占用行数int i2 = address.length() / maxLength + (address.length() % maxLength == 0 ? 0 : 1);int len = 0;for (int j = i2 - 1; j >= 0; j--) {int mlen = maxLength * (j + 1);len = j * maxLength;if (mlen < address.length()) {g.drawString(address.substring(len, mlen), xx6, yy6 + (fontSize * (j + 1)));} else {g.drawString(address.substring(len), xx6, yy6 + (fontSize * (j + 1)));}}// 图标g.drawImage(readAddress, xx5, yy5,imageWidth,imageHeight, null);}continue;}}// 输出图像g.dispose();File f = new File(targetImg);ImageIO.write(image, targetImg.substring(targetImg.lastIndexOf(".") + 1), f);// 如果年月日展示,则进行坐标计算String timeIsShow = json.getStr("timeIsShow");if ("0".equals(timeIsShow)) {String s = DateUtil.formatDate(new Date());Week week = DateUtil.dayOfWeekEnum(new Date());// 文字水印为:2023-02-22  星期三pressText(targetImg, xx10, yy10, 12, Color.WHITE, "微软雅黑", Font.PLAIN, 0.9F, s + "  " + week.toChinese());String time = DateUtil.format(new Date(), "HH:mm:ss");pressText(targetImg, xx11, yy11, 18, Color.WHITE, "微软雅黑", Font.BOLD, 0.9F, time);}//花费毫秒数long ll = System.currentTimeMillis();log.info("图片加水印耗时:{}{}", ll - l, "ms");} catch (Exception e) {e.printStackTrace();}}/*** 添加图片文字水印(此方法不在考虑文字过长需要换行等情况)** @param targetImg 原图片路径,需要是本地路径,如是网络url需要单独更改* @param x         水印x轴坐标* @param y         水印y轴坐标* @param fontSize  水印文字大小 英镑,会根据图片分辨率自动放大和缩小,只需要指定标准350像素的文字标准值即可* @param c         水印文字颜色 例如:Color.WHITE* @param fontName  水印文字字体 例如:宋体,微软雅黑* @param fontStyle 水印文字字体风格,例如:Font.PLAIN正常字体/BOLD加粗/ITALIC斜体.* @param alpha     水印文字透明度,例如: 1.0F,范围 0-1F* @param text      水印文字*/public static void pressText(String targetImg, int x, int y, int fontSize, Color c, String fontName, int fontStyle, float alpha, String text) {try {// 读取原图片BufferedImage src = ImageIO.read(new File(targetImg));// 获取原图的宽和高int width = src.getWidth(null);int height = src.getHeight(null);// 动态设置字体大小fontSize = width < height ? width / 350 * fontSize : height / 350 * fontSize;// 创建空图片,指定宽高BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);// 准备绘制图片Graphics2D g = image.createGraphics();// 将原图绘制到此空图片上,左上角为坐标起点,(0,0),宽和高为原图大小g.drawImage(src, 0, 0, width, height, null);// 设置水印字体颜色.g.setColor(c);// 设置水印字体,Font.PLAIN正常字体/BOLD加粗/ITALIC斜体.g.setFont(new Font(fontName, fontStyle, fontSize));// 透明度g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));/* 消除java.awt.Font字体的锯齿 */g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_ON);g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,RenderingHints.VALUE_FRACTIONALMETRICS_ON);// 添加文字水印g.drawString(text, x, y);// 输出图像g.dispose();File f = new File(targetImg);// 将加过水印的图片保存到指定文件ImageIO.write(image, targetImg.substring(targetImg.lastIndexOf(".") + 1), f);} catch (Exception e) {log.error("水印添加失败:{}", e.getMessage());}}/*** 计算文字长度,一个中文按两个长度,英文按一个长度计算** @param text 文字* @return*/public static int getLength(String text) {int length = 0;for (int i = 0; i < text.length(); i++) {if (new String(text.charAt(i) + "").getBytes().length > 1) {length += 2;} else {length += 1;}}return length / 2;}/*** 导出zip压缩包** @param srcPath  要压缩的文件夹路径* @param response 输出流*/public static void downloadZip(String srcPath, HttpServletResponse response) {// 将此目录打包成zipFile file = ZipUtil.zip(srcPath);OutputStream toClient = null;try {// 以流的形式下载文件。BufferedInputStream fis = new BufferedInputStream(new FileInputStream(file.getPath()));byte[] buffer = new byte[fis.available()];fis.read(buffer);fis.close();// 清空responseresponse.reset();toClient = new BufferedOutputStream(response.getOutputStream());response.setCharacterEncoding("UTF-8");response.setContentType("application/zip");response.setHeader("Content-Disposition", "attachment;filename=" + file.getName());toClient.write(buffer);toClient.flush();} catch (Exception e) {log.error("下载zip压缩包过程发生异常:", e);} finally {if (toClient != null) {try {toClient.close();} catch (IOException e) {log.error("zip包下载关流失败:", e);}}//删除改临时zip包(此zip包任何时候都不需要保留,因为源文件随时可以再次进行压缩生成zip包)file.delete();}}}

总结

其中,通过ImageIO.read读取的图片,是没有exif信息的,这样就会导致有exif的图片,读取后宽和高可能不准确,最终生成的图片就是未旋转的,看起来就是颠倒的。如果有谁有办法处理本地图片exif问题,可以回复下。
让我学习学习。
如果使用的是阿里云oss地址,在后面拼接参数即可:
例如:

String uuu = ".jpg";
String uuu = ".jpg?x-oss-process=image/auto-orient,1";

在oss地址后面拼接?x-oss-process=image/auto-orient,1 即可。阿里云oss自适应传送门

与本文相关的文章

发布评论

评论列表 (0)

  1. 暂无评论