本人由于工作需要开发将文字转为图片的功能,所以其中也遇到坑,也找了很多的解决方法,但是不是每个方法对你都是有用,因为每个环境都不一样,下面将讲述这些经历。
从设计的时候对于文字大小是随着图片的大小自适应居中,所以在计算文字大小和居中的时候遇到了一些问题。
首先自适应居中,这个时候对于文字的基准线也就是x、y的位置没有了解清楚,认为就是在文字的中间,所以计算位置展示在图片上的时候会存现很大的偏移,经过很多次的测试,都无法找到准确的位置算法,所以最后查就找这种计算方式:
// BufferedImage类是具有缓冲区的Image类,Image类是用于描述图像信息的类
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
//产生Image对象的Graphics对象,改对象可以在图像上进行各种绘制操作
Graphics2D g = image.createGraphics();
// 设置背景颜色
g.setColor(Color.black);
// 背景色填充,这里设置为全部
g.fillRect(0, 0, width, height);
//字体大小
Font font = new Font(fontType, Font.PLAIN, fontSize);
g.setFont(font);
//字体颜色
g.setColor(fontColor);
FontDesignMetrics metrics = FontDesignMetrics.getMetrics(font);
int startWidth = metrics.stringWidth(text);
int startHeight = metrics.getHeight();
//左边位置
int left = (width - startWidth) / 2;
// 顶边位置+上升距离(原本字体基线位置对准画布的y坐标导致字体偏上ascent距离,加上ascent后下移刚好顶边吻合)
int top = (height - startHeight) / 2 + metrics.getAscent();
g.drawString(text, left, top);
上面的方法解决了单行文字的位置,但是这里需要设置多行文字,单行位移为0,两行的话就需要位移-0.5、0.5,三行就是-1、0、1这样以此类推,所以会计算位置就是行数为奇数中间为0,偶数的时候两边对称,所以偏移时候的算法:当前行数 -(行数/2.0 + 0.5)注意这里都是 double 类型的算法,所以改造后:
// BufferedImage类是具有缓冲区的Image类,Image类是用于描述图像信息的类
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
//产生Image对象的Graphics对象,改对象可以在图像上进行各种绘制操作
Graphics2D g = image.createGraphics();
// 设置背景颜色
g.setColor(Color.black);
// 背景色填充,这里设置为全部
g.fillRect(0, 0, width, height);
double median = content.size()/2.0 + 0.5;
for (int i = 0; i < content.size(); i++) {
TextMessage textMessage = content.get(i);
//字体大小
Font font = new Font(textMessage.getFont(), Font.PLAIN, fontSize);
g.setFont(font);
//字体颜色
g.setColor(getColor(textMessage.getFontColor()));
FontDesignMetrics metrics = FontDesignMetrics.getMetrics(font);
String text = textMessage.getText();
int strWidth = metrics.stringWidth(text);
int strHeight = metrics.getHeight();
//左边位置
int left = (width - strWidth) / 2;
// 顶边位置+上升距离(原本字体基线位置对准画布的y坐标导致字体偏上ascent距离,加上ascent后下移刚好顶边吻合)
int top = (height - strHeight) / 2 + metrics.getAscent();
top = (int) (top + (Double.parseDouble(String.valueOf(i + 1)) - median)* fontSize);
g.drawString(text, left, top);
}
上面字体居中的问题解决后,下面解决字体大小的问题,也遇到了一些问题,由于自适应大小,所以是根据画布大小来做的,会出现字体在不同大小的画布上,字体超出画布或者字体随着画布的变化,距离两边的边框越远,以至于后来的多行文字也是这样,当时查了很多的资料,各种字体的长宽比、字体大小和像素的比,但是都没有找到好的方法,最后查到一个说是5:4,这个使用测试还是可以的,同时由于字体中汉字、英文和数字它们占的位置也是不一样的,所以就按照字节来计算位置,两个字节算是一个位置,所以算法就是先找到行数最长的,然后再按照字体的长宽比为5:4(这里按照1.2来进行计算的),获取到字体的值:
// TextMessage 中有 text 文字属性
int length = 0;
for (TextMessage message : content) {
int size = message.getText().getBytes().length/2;
if (length < size) {
length = size;
}
}
int fontSize = Math.min((int)(width / length * 1.2), height/content.size());
总算是将上面的基础的功能开发完成了,idea 基本测试也正常,下面提交代码,进行打包发布到测试环境,本以为会一切顺利,可是意外情况还是出现了,真是防不胜防啊,测试生成的图片汉字为口,数字和英文正常,这里补充下测试环境使用的是docker,在centos的环境中,网上查询了下,原来是系统中缺少字体库,找了很多种方法,比如:向系统中字体、jvm中家字体,但是我们这边开发人员无法操作系统环境,我这边进行了很多种尝试,当然这时候的尝试其实还有坑没有发现后面会讲到,比如直接将我本地盘中的字体放到项目的resource下建的font文件夹里,然后项目启动自动同步到系统字体的文件夹、也测试同步到jvm字体文件夹,但都是失败告终。
通过上面的尝试,已经感到很无力的时候,这个时候想到是不是可以 java 加载字体,这里在测试读取字体库创建字体,就去网上查找资料,别说还真有,这时候我就测试读取字体文件创建字体,但是发现都会报错,这时候一查报错的原因是因为字体库文件损坏读取异常,这时候就查询为什么字体库会异常,在stackoverfloew 找到这方面的问题,原来是maven搞的鬼,项目编译过后编译文件里的字体库会比原来的字体库大,这里讲下我们的项目以springboot作为框架,maven 作为项目的jar引用管理和打包编译,所以这里就将添加了如下的maven配置:
<resources>
<resource>
<directory>${project.basedir}/src/main/resources</directory>
<filtering>false</filtering>
<includes>
<include>**/jasperreports_extension.properties</include>
<include>**/tecsofti-fonts.xml</include>
<include>**/*.ttf</include>
<include>**/*.TTF</include>
<include>**/*.ttc</include>
<include>**/*.fon</include>
<include>**/*.yml</include>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.1</version>
<configuration>
<encoding>UTF-8</encoding>
<nonFilteredFileExtensions>
<nonFilteredFileExtension>ttf</nonFilteredFileExtension>
<nonFilteredFileExtension>TTF</nonFilteredFileExtension>
<nonFilteredFileExtension>ttc</nonFilteredFileExtension>
</nonFilteredFileExtensions>
</configuration>
</plugin>
</plugins>
这里解决了这个编译打包的问题,这时候进行测试,基本上没有问题,打包发布到测试环境也都可以了,可能上面的设计还是没有很严谨,但是对于基本的还是可以的。
后面附赠一个计算颜色的代码,这里是颜色的编码转为java颜色对象(没有前面的#):
/**
* 颜色编码转颜色对象
*
* @param rgb 颜色编码
* @return 转换后的对象
*/
private static Color getColor(String rgb) {
if (rgb == null || rgb.length() != 6) {
return Color.red;
}
int red = Integer.parseInt(rgb.substring(0, 2), 16);
int green = Integer.parseInt(rgb.substring(2, 4), 16);
int blue = Integer.parseInt(rgb.substring(4), 16);
int alpha = (int) (255 * 1.0);
int colour = (red << 16) | (green << 8) | blue;
colour = colour | (alpha << 24);
return new Color(colour, true);
}