add FontUtil

This commit is contained in:
Looly 2020-05-26 00:15:59 +08:00
parent 7c803950ef
commit 73b2f75de2
5 changed files with 290 additions and 63 deletions

View File

@ -16,6 +16,7 @@
* 【core 】 BeanValuePovider转换失败时返回原数据而非null
* 【core 】 支持BeanUtil.toBean(object, Map.class)转换issue#I1I4HC@Gitee
* 【core 】 MapUtil和CollUtil增加clear方法issue#I1I4HC@Gitee
* 【core 】 增加FontUtil可定义pressText是否从中间issue#I1HSWU@Gitee
### Bug修复
* 【core 】 修复SimpleCache死锁问题issue#I1HOKB@Gitee

View File

@ -0,0 +1,123 @@
package cn.hutool.core.img;
import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.io.IORuntimeException;
import sun.font.FontDesignMetrics;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontFormatException;
import java.awt.FontMetrics;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
/**
* AWT中字体相关工具类
*
* @author looly
* @since 5.3.6
*/
public class FontUtil {
/**
* 创建默认字体
*
* @return 默认字体
*/
public static Font createFont() {
return new Font(null);
}
/**
* 创建SansSerif字体
*
* @param size 字体大小
* @return 字体
*/
public static Font createSansSerifFont(int size) {
return createFont(Font.SANS_SERIF, size);
}
/**
* 创建指定名称的字体
*
* @param name 字体名称
* @param size 字体大小
* @return 字体
*/
public static Font createFont(String name, int size) {
return new Font(name, Font.PLAIN, size);
}
/**
* 根据文件创建字体<br>
* 首先尝试创建{@link Font#TRUETYPE_FONT}字体此类字体无效则创建{@link Font#TYPE1_FONT}
*
* @param fontFile 字体文件
* @return {@link Font}
*/
public static Font createFont(File fontFile) {
try {
return Font.createFont(Font.TRUETYPE_FONT, fontFile);
} catch (FontFormatException e) {
// True Type字体无效时使用Type1字体
try {
return Font.createFont(Font.TYPE1_FONT, fontFile);
} catch (Exception e1) {
throw new UtilException(e);
}
} catch (IOException e) {
throw new IORuntimeException(e);
}
}
/**
* 根据文件创建字体<br>
* 首先尝试创建{@link Font#TRUETYPE_FONT}字体此类字体无效则创建{@link Font#TYPE1_FONT}
*
* @param fontStream 字体流
* @return {@link Font}
*/
public static Font createFont(InputStream fontStream) {
try {
return Font.createFont(Font.TRUETYPE_FONT, fontStream);
} catch (FontFormatException e) {
// True Type字体无效时使用Type1字体
try {
return Font.createFont(Font.TYPE1_FONT, fontStream);
} catch (Exception e1) {
throw new UtilException(e1);
}
} catch (IOException e) {
throw new IORuntimeException(e);
}
}
/**
* 获得字体对应字符串的长宽信息
*
* @param font 字体
* @param str 字符串
* @return 长宽信息
*/
public static Dimension getDimension(Font font, String str) {
final FontMetrics metrics = FontDesignMetrics.getMetrics(font);
return getDimension(FontDesignMetrics.getMetrics(font), str);
}
/**
* 获得字体对应字符串的长宽信息
*
* @param metrics {@link FontMetrics}
* @param str 字符串
* @return 长宽信息
*/
public static Dimension getDimension(FontMetrics metrics, String str) {
final int width = metrics.stringWidth(str);
final int height = metrics.getAscent() - metrics.getLeading() - metrics.getDescent();
return new Dimension(width, height);
}
}

View File

@ -1,16 +1,23 @@
package cn.hutool.core.img;
import cn.hutool.core.util.ObjectUtil;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
/**
* {@link Graphics}相关工具类
*
*
* @author looly
* @since 4.5.2
*/
@ -18,7 +25,7 @@ public class GraphicsUtil {
/**
* 创建{@link Graphics2D}
*
*
* @param image {@link BufferedImage}
* @param color {@link Color}背景颜色以及当前画笔颜色{@code null}表示不设置背景色
* @return {@link Graphics2D}
@ -26,8 +33,8 @@ public class GraphicsUtil {
*/
public static Graphics2D createGraphics(BufferedImage image, Color color) {
final Graphics2D g = image.createGraphics();
if(null != color) {
if (null != color) {
// 填充背景
g.setColor(color);
g.fillRect(0, 0, image.getWidth(), image.getHeight());
@ -39,8 +46,8 @@ public class GraphicsUtil {
/**
* 获取文字居中高度的Y坐标距离上边距距离<br>
* 此方法依赖FontMetrics如果获取失败默认为背景高度的1/3
*
* @param g {@link Graphics2D}画笔
*
* @param g {@link Graphics2D}画笔
* @param backgroundHeight 背景高度
* @return 最小高度-1表示无法获取
* @since 4.5.17
@ -64,11 +71,11 @@ public class GraphicsUtil {
/**
* 绘制字符串使用随机颜色默认抗锯齿
*
* @param g {@link Graphics}画笔
* @param str 字符串
* @param font 字体
* @param width 字符串总宽度
*
* @param g {@link Graphics}画笔
* @param str 字符串
* @param font 字体
* @param width 字符串总宽度
* @param height 字符串背景高度
* @return 画笔对象
* @since 4.5.10
@ -79,12 +86,12 @@ public class GraphicsUtil {
/**
* 绘制字符串默认抗锯齿
*
* @param g {@link Graphics}画笔
* @param str 字符串
* @param font 字体
* @param color 字体颜色{@code null} 表示使用随机颜色每个字符单独随机
* @param width 字符串背景的宽度
*
* @param g {@link Graphics}画笔
* @param str 字符串
* @param font 字体
* @param color 字体颜色{@code null} 表示使用随机颜色每个字符单独随机
* @param width 字符串背景的宽度
* @param height 字符串背景的高度
* @return 画笔对象
* @since 4.5.10
@ -98,7 +105,7 @@ public class GraphicsUtil {
g.setFont(font);
// 文字高度必须在设置字体后调用
int midY = GraphicsUtil.getCenterY(g, height);
int midY = getCenterY(g, height);
if (null != color) {
g.setColor(color);
}
@ -115,4 +122,97 @@ public class GraphicsUtil {
return g;
}
/**
* 绘制字符串默认抗锯齿<br>
* 此方法定义一个矩形区域和坐标文字基于这个区域中间偏移x,y绘制
*
* @param g {@link Graphics}画笔
* @param str 字符串
* @param font 字体字体大小决定了在背景中绘制的大小
* @param color 字体颜色{@code null} 表示使用黑色
* @param rectangle 字符串绘制坐标和大小此对象定义了绘制字符串的区域大小和偏移位置
* @return 画笔对象
* @since 4.5.10
*/
public static Graphics drawString(Graphics g, String str, Font font, Color color, Rectangle rectangle) {
// 背景长宽
final int backgroundWidth = rectangle.width;
final int backgroundHeight = rectangle.height;
//获取字符串本身的长宽
Dimension dimension;
try {
dimension = FontUtil.getDimension(g.getFontMetrics(font), str);
} catch (Exception e) {
// 此处报告bug某些情况下会抛出IndexOutOfBoundsException在此做容错处理
dimension = new Dimension(backgroundWidth / 3, backgroundHeight / 3);
}
rectangle.setSize(dimension.width, dimension.height);
final Point point = ImgUtil.getPointBaseCentre(rectangle, backgroundWidth, backgroundHeight);
return drawString(g, str, font, color, point);
}
/**
* 绘制字符串默认抗锯齿
*
* @param g {@link Graphics}画笔
* @param str 字符串
* @param font 字体字体大小决定了在背景中绘制的大小
* @param color 字体颜色{@code null} 表示使用黑色
* @param point 绘制字符串的位置坐标
* @return 画笔对象
* @since 5.3.6
*/
public static Graphics drawString(Graphics g, String str, Font font, Color color, Point point) {
// 抗锯齿
if (g instanceof Graphics2D) {
((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
}
g.setFont(font);
g.setColor(ObjectUtil.defaultIfNull(color, Color.BLACK));
g.drawString(str, point.x, point.y);
return g;
}
/**
* 绘制图片
*
* @param g 画笔
* @param img 要绘制的图片
* @param point 绘制的位置基于左上角
* @return 画笔对象
*/
public static Graphics drawImg(Graphics g, Image img, Point point) {
return drawImg(g, img,
new Rectangle(point.x, point.y, img.getWidth(null), img.getHeight(null)));
}
/**
* 绘制图片
*
* @param g 画笔
* @param img 要绘制的图片
* @param rectangle 矩形对象表示矩形区域的xywidthheight,基于左上角
* @return 画笔对象
*/
public static Graphics drawImg(Graphics g, Image img, Rectangle rectangle) {
g.drawImage(img, rectangle.x, rectangle.y, rectangle.width, rectangle.height, null); // 绘制切割后的图
return g;
}
/**
* 设置画笔透明度
*
* @param g 画笔
* @param alpha 透明度alpha 必须是范围 [0.0, 1.0] 之内包含边界值的一个浮点数字
* @return 画笔
*/
public static Graphics2D setAlpha(Graphics2D g, float alpha){
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
return g;
}
}

View File

@ -15,9 +15,9 @@ import javax.imageio.stream.ImageOutputStream;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Toolkit;
@ -145,13 +145,13 @@ public class Img implements Serializable {
/**
* 构造
*
* @param srcImage 来源图片
* @param srcImage 来源图片
* @param targetImageType 目标图片类型
* @since 5.0.7
*/
public Img(BufferedImage srcImage, String targetImageType) {
this.srcImage = srcImage;
if(null == targetImageType){
if (null == targetImageType) {
targetImageType = ImgUtil.IMAGE_TYPE_JPG;
}
this.targetImageType = targetImageType;
@ -312,7 +312,7 @@ public class Img implements Serializable {
Graphics2D g = image.createGraphics();
// 设置背景
if(null != fixedColor){
if (null != fixedColor) {
g.setBackground(fixedColor);
g.clearRect(0, 0, width, height);
}
@ -450,20 +450,22 @@ public class Img implements Serializable {
if (null == font) {
// 默认字体
font = new Font("Courier", Font.PLAIN, (int) (targetImage.getHeight() * 0.75));
font = FontUtil.createSansSerifFont((int) (targetImage.getHeight() * 0.75));
}
// 抗锯齿
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(color);
g.setFont(font);
// 透明度
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
// 在指定坐标绘制水印文字
final FontMetrics metrics = g.getFontMetrics(font);
final int textLength = metrics.stringWidth(pressText);
final int textHeight = metrics.getAscent() - metrics.getLeading() - metrics.getDescent();
g.drawString(pressText, Math.abs(targetImage.getWidth() - textLength) / 2 + x, Math.abs(targetImage.getHeight() + textHeight) / 2 + y);
// 绘制
if (positionBaseCentre) {
// 基于中心绘制
GraphicsUtil.drawString(g, pressText, font, color,
new Rectangle(x, y, targetImage.getWidth(), targetImage.getHeight()));
} else {
// 基于左上角绘制
GraphicsUtil.drawString(g, pressText, font, color,
new Point(x, y));
}
g.dispose();
this.targetImage = targetImage;
@ -497,7 +499,6 @@ public class Img implements Serializable {
public Img pressImage(Image pressImg, Rectangle rectangle, float alpha) {
final Image targetImg = getValidSrcImg();
fixRectangle(rectangle, targetImg.getWidth(null), targetImg.getHeight(null));
this.targetImage = draw(ImgUtil.toBufferedImage(targetImg), pressImg, rectangle, alpha);
return this;
}
@ -619,12 +620,21 @@ public class Img implements Serializable {
* @param backgroundImg 背景图片
* @param img 要绘制的图片
* @param rectangle 矩形对象表示矩形区域的xywidthheightx,y从背景图片中心计算
* @param alpha 透明度alpha 必须是范围 [0.0, 1.0] 之内包含边界值的一个浮点数字
* @return 绘制后的背景
*/
private static BufferedImage draw(BufferedImage backgroundImg, Image img, Rectangle rectangle, float alpha) {
private BufferedImage draw(BufferedImage backgroundImg, Image img, Rectangle rectangle, float alpha) {
final Graphics2D g = backgroundImg.createGraphics();
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
g.drawImage(img, rectangle.x, rectangle.y, rectangle.width, rectangle.height, null); // 绘制切割后的图
GraphicsUtil.setAlpha(g, alpha);
Point point;
if (positionBaseCentre) {
point = ImgUtil.getPointBaseCentre(rectangle, backgroundImg.getWidth(), backgroundImg.getHeight());
} else {
point = new Point(rectangle.x, rectangle.y);
}
GraphicsUtil.drawImg(g, img, point);
g.dispose();
return backgroundImg;
}

View File

@ -2,7 +2,6 @@ package cn.hutool.core.img;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
@ -25,10 +24,10 @@ import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontFormatException;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.font.FontRenderContext;
import java.awt.geom.AffineTransform;
@ -1377,18 +1376,7 @@ public class ImgUtil {
* @since 3.0.9
*/
public static Font createFont(File fontFile) {
try {
return Font.createFont(Font.TRUETYPE_FONT, fontFile);
} catch (FontFormatException e) {
// True Type字体无效时使用Type1字体
try {
return Font.createFont(Font.TYPE1_FONT, fontFile);
} catch (Exception e1) {
throw new UtilException(e);
}
} catch (IOException e) {
throw new IORuntimeException(e);
}
return FontUtil.createFont(fontFile);
}
/**
@ -1400,18 +1388,7 @@ public class ImgUtil {
* @since 3.0.9
*/
public static Font createFont(InputStream fontStream) {
try {
return Font.createFont(Font.TRUETYPE_FONT, fontStream);
} catch (FontFormatException e) {
// True Type字体无效时使用Type1字体
try {
return Font.createFont(Font.TYPE1_FONT, fontStream);
} catch (Exception e1) {
throw new UtilException(e1);
}
} catch (IOException e) {
throw new IORuntimeException(e);
}
return FontUtil.createFont(fontStream);
}
/**
@ -1917,4 +1894,20 @@ public class ImgUtil {
}
return new Color(random.nextInt(255), random.nextInt(255), random.nextInt(255));
}
/**
* 获得修正后的矩形坐标位置变为以背景中心为基准坐标即x,y == 0,0时处于背景正中
*
* @param rectangle 矩形
* @param backgroundWidth 参考宽背景宽
* @param backgroundHeight 参考高背景高
* @return 修正后的{@link Point}
* @since 5.3.6
*/
public static Point getPointBaseCentre(Rectangle rectangle, int backgroundWidth, int backgroundHeight) {
return new Point(
rectangle.x + (Math.abs(backgroundWidth - rectangle.width) / 2), //
rectangle.y + (Math.abs(backgroundHeight - rectangle.height) / 2)//
);
}
}