通用包引用:
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import javax.imageio.ImageIO;
import org.apache.commons.io.FileUtils;
1、图片添加文字水印
/**
* 添加图片水印
*
* @param targetImg
* 目标图片路径
* @param waterImg
* 水印图片路径
* @param x
* 水印图片距离目标图片左侧的偏移量,如果x<0, 则在正中间
* @param y
* 水印图片距离目标图片上侧的偏移量,如果y<0, 则在正中间
* @param alpha
* 透明度(0.0 -- 1.0, 0.0为完全透明,1.0为完全不透明)
*/
public final static void addImageWeatermark(String targetImg, String waterImg, int x, int y, float alpha) {
try {
File file = new File(targetImg);
Image image = ImageIO.read(file);
int width = image.getWidth(null);
int height = image.getHeight(null);
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g = bufferedImage.createGraphics();
g.drawImage(image, 0, 0, width, height, null);
Image waterImage = ImageIO.read(new File(waterImg)); // 水印文件
int width_1 = waterImage.getWidth(null);
int height_1 = waterImage.getHeight(null);
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
int widthDiff = width - width_1;
int heightDiff = height - height_1;
if (x < 0) {
x = widthDiff / 2;
} else if (x > widthDiff) {
x = widthDiff;
}
if (y < 0) {
y = heightDiff / 2;
} else if (y > heightDiff) {
y = heightDiff;
}
g.drawImage(waterImage, x, y, width_1, height_1, null); // 水印文件结束
g.dispose();
ImageIO.write(bufferedImage, PICTRUE_FORMATE_PNG, file);
} catch (IOException e) {
e.printStackTrace();
}
}
2、添加文字水印
/**
* 添加文字水印
*
* @param targetImg
* 目标图片路径
* @param pressText
* 水印文字
* @param fontName
* 字体名称, 如:宋体
* @param fontStyle
* 字体样式,如:粗体和斜体(Font.BOLD|Font.ITALIC)
* @param fontSize
* 字体大小,单位为像素
* @param color
* 字体颜色
* @param x
* 水印文字距离目标图片左侧的偏移量,如果x<0, 则在正中间
* @param y
* 水印文字距离目标图片上侧的偏移量,如果y<0, 则在正中间
* @param alpha
* 透明度(0.0 – 1.0, 0.0为完全透明,1.0为完全不透明)
*/
public static void addTextWeatermark(String targetImg, List<ImageDrawTextConfig> textConfig, float alpha, boolean createImg, String newFileUrl) {
try {
File file = new File(targetImg);
Image image = ImageIO.read(file);
int width = image.getWidth(null);
int height = image.getHeight(null);
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
Graphics2D g = bufferedImage.createGraphics();
g.drawImage(image, 0, 0, width, height, null);
//添加文字水印
drawString(g, width, height, textConfig);
g.dispose();
if(createImg){
File newFile = new File(newFileUrl);
FileUtils.forceMkdir(newFile.getParentFile());
ImageIO.write(bufferedImage, PICTRUE_FORMATE_PNG, newFile);
} else {
ImageIO.write(bufferedImage, PICTRUE_FORMATE_PNG, file);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 添加文字水印
* @param g
* @param imageWidth 图片宽度
* @param imageHeight 图片高度
* @param pressText 添加的文字水印
* @param fontName 字体名称
* @param fontStyle 字体样式
* @param fontSize 字体大小的磅值
* @param color 颜色
* @param x 偏离宽
* @param y 偏离高
* @param widthScale 字体大小误差比例
*/
public static void drawString(Graphics2D g, int imageWidth, int imageHeight, List<ImageDrawTextConfig> textConfig){
//font_name,字体的名称。如:“SansSerif”、“Times”、“黑体”、“宋体”等。
//font_style,字体的样式。如:Font.PLAIN(正常体)、Font.BOLD(加粗体)、Font.ITALIC(斜体)等。
//font_size,字体大小的磅值,是一个数字。如:10、12、14、18、20等。
for(ImageDrawTextConfig config: textConfig){
g.setFont(new Font(config.getFontName(), config.getFontStyle(), config.getFontSize()));
g.setColor(config.getColor());
int width_1 = config.getFontSize() * getLength(config.getPressText());
//非正常字体的相应的增加整体的宽度,才不会类似靠最右边会丢失文字
if(config.getFontStyle() != Font.PLAIN){
width_1 += getLength(config.getPressText()) * config.getWidthScale();
}
int height_1 = config.getFontSize();
int widthDiff = imageWidth - width_1;//剩余宽度
int heightDiff = imageHeight - height_1;//剩余高度
if (config.getX() < 0) {
config.setX(widthDiff);
} else if (config.getX() > widthDiff) {
config.setX(widthDiff);
}
if (config.getY() < 0) {
config.setY(heightDiff);
} else if (config.getY() > heightDiff) {
config.setY(heightDiff);
}
g.drawString(config.getPressText(), config.getX(), config.getY() + height_1);
}
}
import java.awt.Color;
/**
- 图片添加文字水印配置
- @author smart_cwx
*/
public class ImageDrawTextConfig {
/** 添加的水印文字 */
String pressText;
/** 字体的名称 */
String fontName;
/** 文字样式 */
int fontStyle;
/** 字体大小的磅值 */
int fontSize;
/** 颜色 */
Color color;
/** 偏离x宽度 */
int x;
/** 偏离y高度 */
int y;
/** 字体大小误差比例 */
float widthScale;
public ImageDrawTextConfig(String pressText, String fontName, int fontStyle, int fontSize, Color color, int x,
int y, float widthScale) {
super();
this.pressText = pressText;
this.fontName = fontName;
this.fontStyle = fontStyle;
this.fontSize = fontSize;
this.color = color;
this.x = x;
this.y = y;
this.widthScale = widthScale;
}
public String getPressText() {
return pressText;
}
public void setPressText(String pressText) {
this.pressText = pressText;
}
public String getFontName() {
return fontName;
}
public void setFontName(String fontName) {
this.fontName = fontName;
}
public int getFontStyle() {
return fontStyle;
}
public void setFontStyle(int fontStyle) {
this.fontStyle = fontStyle;
}
public int getFontSize() {
return fontSize;
}
public void setFontSize(int fontSize) {
this.fontSize = fontSize;
}
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public float getWidthScale() {
return widthScale;
}
public void setWidthScale(float widthScale) {
this.widthScale = widthScale;
}
}
/**
* 获取字符长度,一个汉字作为 1 个字符, 一个英文字母作为 0.5 个字符
*
* @param text
* @return 字符长度,如:text="中国",返回 2;text="test",返回 2;text="中国ABC",返回 4.
*/
public static int getLength(String text) {
int textLength = text.length();
int length = textLength;
for (int i = 0; i < textLength; i++) {
if (String.valueOf(text.charAt(i)).getBytes().length > 1) {
length++;
}
}
return (length % 2 == 0) ? length / 2 : length / 2 + 1;
}
3、图片缩放-高清
以下包jar的引用,直接复制到类中:
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGEncodeParam;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
/**
* 图片缩放
*
* @param sourceFile 需要缩放的图片
* @param savePath 缩放后的图片保存路径
* @param height 高度
* @param width 宽度
* @param bb 比例不对时是否需要补白
*/
@SuppressWarnings("restriction")
public static double resize(String sourceFile, String savePath, int height, int width, boolean bb) {
double scale = 1;
try {
Image image = ImageIO.read(new File(sourceFile));
// 计算比例
if ((image.getHeight(null) > height) || (image.getWidth(null) > width)) {
if (image.getHeight(null) > image.getWidth(null)) {
scale = (new Integer(height)).doubleValue() / image.getHeight(null);
} else {
scale = (new Integer(width)).doubleValue() / image.getWidth(null);
}
}
//设置新画布的大小
int w = (int)(image.getWidth(null)*scale);
int h = (int)(image.getHeight(null)*scale);
Image _img = image.getScaledInstance(w, h, Image.SCALE_SMOOTH);
BufferedImage tmp = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = tmp.createGraphics();
//开启文字抗锯齿
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
//画图
g2.drawImage(_img, 0, 0, null);
g2.dispose();
//生成输出的目标文件
File newFile = new File(savePath);
FileUtils.forceMkdir(newFile.getParentFile());
/*输出到文件流*/
FileOutputStream newImage = new FileOutputStream(savePath);
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(newImage);
JPEGEncodeParam jep = JPEGCodec.getDefaultJPEGEncodeParam(tmp);
/* 压缩质量 */
jep.setQuality(1f, true);
encoder.encode(tmp, jep);
/*近JPEG编码*/
newImage.close();
} catch (IOException e) {
e.printStackTrace();
}
return scale;
}
4、合并图片
/**
* 将集合地址转为字符串
*
* @param list 地址集合
* @return
*/
public static String listToString(List<String> list){
StringBuilder sb = new StringBuilder();
for(String s: list){
sb.append(s);
sb.append(",");
}
return list.isEmpty()? "":sb.toString().substring(0, sb.toString().length()-1);
}
/**
* load image
* */
public static BufferedImage loadImage(String filePath) {
try {
return ImageIO.read(new File(filePath));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* convert to byte array.
* */
public static byte[] convert2bytes(BufferedImage image, String format) {
try {
ByteArrayOutputStream bout = new ByteArrayOutputStream(1024);
ImageIO.write(image, format, bout);
byte[] data = bout.toByteArray();
return data;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 多张图片合并
*
* @param fileFolders 文件存储基础路径,eg: /usr/
* @param nameTypePath 文件储存基础路径下的分支路径 eg: img
* @param imageUrls 要合并的图片基础路径下的地址 eg: /xxxxxxxxxxxx/xxxx.png
* @return
*/
public static String mergeManyImage(String fileFolders, String nameTypePath, List<String> imageUrls, int maxWidth, int maxHeight) {
List<String> newImageUrls = new ArrayList<String>();
try {
double scale = 1;
// 定义暂时存储读取的文件集合
List<File> targetFiels = new ArrayList<File>();
//读取文件
readFiles(fileFolders, imageUrls, targetFiels);
List<Image> images = new ArrayList<Image>();
readImages(targetFiels, images);
//日期层级文件夹名称路径
String dateFolder = "/" + CommonUtils.ymdhmsdfStr.format(new Date()) + (int) (1 + Math.random() * (100 - 1 + 1)) + "/";
String name;
if(images.size() > 0){
int height = 0, index = 0;
double currentHeight = 0;
boolean isResize = false;
List<String> imageTemps = new ArrayList<>();
for(Image image: images){
currentHeight = image.getHeight(null);
height += currentHeight;
//如果图片的宽度超过纸的大小,需要进行缩放
if(image.getWidth(null) > maxWidth){
height -= currentHeight;
name = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 9) + "." + PICTRUE_FORMATE_PNG;
//记录缩放图片地址
List<String> resizeFile = new ArrayList<String>();
resizeFile.add(fileFolders + imageUrls.get(index));
//缩放图片
scale = resize(fileFolders + imageUrls.get(index), fileFolders + nameTypePath + dateFolder + name, image.getHeight(null), maxWidth, false);
//读取缩放后的图片高度
currentHeight = currentHeight * scale;
height += currentHeight;
//用新生成的图片进去合并
imageTemps.add(fileFolders + nameTypePath + dateFolder + name);
isResize = true;
}
//超过纸则需要另起一张图片
if(height > maxHeight){
//创建本次图片
name = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 9) + "." + PICTRUE_FORMATE_PNG;
combinePicture(maxWidth, maxHeight, imageTemps, fileFolders + nameTypePath + dateFolder + name);
newImageUrls.add("/" + nameTypePath + dateFolder + name);
//重置新一张图片
imageTemps.clear();
height = (int) currentHeight;
}
if(!isResize){
imageTemps.add(fileFolders+imageUrls.get(index));
}
index++;
isResize = false;
}
//判断最后一轮是否有刚好剩下的图片,另起一张
if(imageTemps.size() > 0){
name = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 9) + "." + PICTRUE_FORMATE_PNG;
combinePicture(maxWidth, maxHeight, imageTemps, fileFolders + nameTypePath + dateFolder + name);
newImageUrls.add("/" + nameTypePath + dateFolder + name);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return newImageUrls.size() > 0?listToString(newImageUrls):null;
}
/**
* 合并图片
*
* @param width 新生成的图片的宽度
* @param height 新生成的图片的高度
* @param images 需要合并的图片对象集合
* @param targetUrl 生成的图片路径
*/
public static void combinePicture(int width, int height, List<String> files, String targetUrl){
//构造一个类型为预定义图像类型之一的 BufferedImage。 宽度为第一只的宽度,高度为各个图片高度之和
BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
//绘制合成图像
Graphics2D g = tag.createGraphics();
//开启文字抗锯齿
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g.fillRect(0, 0, width, height);
g.setColor(Color.WHITE);
g.drawLine(0, 0, width, height);
int tempHeight = 0;
try {
for(String image: files){
BufferedImage sourceImage = loadImage(image);
byte[] pngData = convert2bytes(sourceImage, PICTRUE_FORMATE_PNG);
ByteArrayInputStream bais = new ByteArrayInputStream(pngData);
BufferedImage bi = ImageIO.read(bais);
g.drawImage(bi, 0, tempHeight, null);
tempHeight += sourceImage.getHeight(null);
bais.close();
}
} catch (IOException e) {
e.printStackTrace();
}
// 释放此图形的上下文以及它使用的所有系统资源。
g.dispose();
try {
File custodyFile = new File(targetUrl);
FileUtils.forceMkdir(custodyFile.getParentFile());
//输出图片
ImageIO.write(tag, PICTRUE_FORMATE_PNG, custodyFile);
}catch(IOException e){
}
}
/**
* 读取图片文件附件
*
* @param targetFiels 图片文件对象
* @param images 暂时存储的图片对象
*/
public static void readImages(List<File> targetFiels, List<Image> images){
try {
for(File f: targetFiels){
images.add(javax.imageio.ImageIO.read(f));
}
} catch (IOException e) {
System.out.println("读取图片文件出错:");
e.printStackTrace();
}
}
/**
* 读取需要合并的图片
*
* @param fileFolders 文件路径前缀
* @param imageUrls 图片地址后缀
* @param targetFiels 存储读取出来的文件集合
*/
public static void readFiles(String fileFolders, List<String> imageUrls, List<File> targetFiels){
for(String url: imageUrls){
File _file = new File(fileFolders + url);
if(_file.exists()){//判断文件是否存在
targetFiels.add(_file);
}
}
}