All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.siashan.toolkit.image.painter.TextPainter Maven / Gradle / Ivy
package com.siashan.toolkit.image.painter;
import com.siashan.toolkit.image.element.AbstractElement;
import com.siashan.toolkit.image.element.TextElement;
import sun.font.FontDesignMetrics;
import java.awt.*;
import java.awt.font.TextAttribute;
import java.text.AttributedString;
import java.util.ArrayList;
import java.util.List;
/**
* 文本绘制器
*
* @author siashan
* @since 1.0.8
*/
public class TextPainter implements IPainter {
/**
* 中文字符区间
*/
private static final String CHINESE_CHAR = "[\u4e00-\u9fa5]";
/**
* 绘制
*
* @param g 2D图片
* @param element 元素
* @param canvasWidth 画布宽度
* @throws Exception 异常
*/
@Override
public void draw(Graphics2D g, AbstractElement element, int canvasWidth) {
//强制转成子类
TextElement textElement = (TextElement) element;
//首先计算是否要换行(由于拆行计算比较耗资源,不设置换行则直接用原始对象绘制)
List textLineElements = new ArrayList<>();
textLineElements.add(textElement);
if (textElement.isAutoBreakLine()) {
textLineElements = this.getBreakLineElements(textElement);
}
for (int i = 0; i < textLineElements.size(); i++) {
TextElement textLineElement = textLineElements.get(i);
int textWidth = 0;
//设置字体、颜色
g.setFont(textLineElement.getFont());
g.setColor(textLineElement.getColor());
//设置居中(多行的时候,第一行居中,后续行以第一行的x坐标为准)
if (textLineElement.isCenter()) {
if (i == 0) {
textWidth = this.calcFrontWidth(textLineElement.getText(), textLineElement.getFont());
int centerX = (canvasWidth - textWidth) / 2;
textLineElement.setX(centerX);
} else {
textLineElement.setX(textLineElements.get(0).getX());
}
}
//旋转
if (textLineElement.getRotate() != null) {
if (textWidth == 0) {
textWidth = this.calcFrontWidth(textLineElement.getText(), textLineElement.getFont());
}
g.rotate(Math.toRadians(textLineElement.getRotate()), textLineElement.getX() + textWidth / 2, textLineElement.getY());
}
//设置透明度
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, textLineElement.getAlpha()));
//带删除线样式的文字要特殊处理
if (textLineElement.isStrikeThrough() == true) {
AttributedString as = new AttributedString(textLineElement.getText());
as.addAttribute(TextAttribute.FONT, textLineElement.getFont());
as.addAttribute(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON, 0, textLineElement.getText().length());
g.drawString(as.getIterator(), textLineElement.getX(), textLineElement.getY());
} else {
g.drawString(textLineElement.getText(), textLineElement.getX(), textLineElement.getY());
}
//绘制完后反向旋转,以免影响后续元素
if (textLineElement.getRotate() != null) {
g.rotate(-Math.toRadians(textLineElement.getRotate()), textLineElement.getX() + textWidth / 2, textLineElement.getY());
}
}
}
/**
* 计算文字宽度
*
* @param text 文字
* @param font 字体
* @return 文字宽度
*/
public int calcFrontWidth(String text, Font font) {
FontDesignMetrics metrics = FontDesignMetrics.getMetrics(font);
int width = 0;
for (int i = 0; i < text.length(); i++) {
width += metrics.charWidth(text.charAt(i));
}
return width;
}
/**
* 是否中文字符
*
* @param c 字符
* @return 是/否
*/
private boolean isChineseChar(char c) {
return String.valueOf(c).matches(CHINESE_CHAR);
}
/**
* 计算文字行数
*
* @param text 文字
* @param font 字体
* @param maxLineWidth 最大宽度
* @return 行数
*/
private List computeLines(String text, Font font, int maxLineWidth) {
//最终要返回的多行文本(不超限定宽度)
List computedLines = new ArrayList<>();
String strToComputer = "";
//一个完整单词
String word = "";
//是否获得一个完整单词
boolean hasWord = false;
char[] chars = text.toCharArray();
int count = 0;
//遍历每个字符,拆解单词(一个中文算一个单词,其他字符直到碰到空格算一个单词)
for (int i = 0; i < chars.length; i++) {
if (count++ > 500) {
break; //防止意外情况进入死循环
}
//当前字符
char c = chars[i];
if (isChineseChar(c) || c == ' ' || i == (chars.length - 1)) {
//如果是中文或空格或最后一个字符,一个中文算一个单词, 其他字符遇到空格认为单词结束
word += c;
hasWord = true;
} else {
//英文或其他字符,加入word,待组成单词
word += c;
}
//获得了一个完整单词,加入当前行,并计算限宽
if (hasWord) {
//计算现有文字宽度
int originWidth = calcFrontWidth(strToComputer, font);
//计算单个单词宽度(防止一个单词就超限宽的情况)
int wordWidth = calcFrontWidth(word, font);
//单词加入待计算字符串
strToComputer += word;
//加入了新单词之后的宽度
int newWidth = originWidth + wordWidth;
//一个单词就超限,要暴力换行
if (wordWidth > maxLineWidth) {
//按比例计算要取几个字符(不是特别精准)
//本行剩余宽度所占word宽度比例,乘以字符长度(字符不等宽的时候不太准)
int fetch = (int) ((float) (maxLineWidth - originWidth) / (float) wordWidth * word.length());
//去除最后的word的后半截
strToComputer = strToComputer.substring(0, strToComputer.length() - word.length() + fetch);
//加入计算结果列表
computedLines.add(strToComputer);
strToComputer = "";
//遍历计数器回退word.length()-fetch个
i -= (word.length() - fetch);
}
//行宽度超出限宽,则去除最后word,加入计算结果列表
else if (newWidth > maxLineWidth) {
//去除最后word
strToComputer = strToComputer.substring(0, strToComputer.length() - word.length());
//加入计算结果列表
computedLines.add(strToComputer);
strToComputer = "";
//遍历计数器回退word.length()个
i -= word.length();
}
word = "";
//重置标记
hasWord = false;
}
}
if (strToComputer != "") {
//加入计算结果列表
computedLines.add(strToComputer);
}
return computedLines;
}
public List getBreakLineElements(TextElement textElement) {
List breakLineElements = new ArrayList<>();
List breakLineTexts = computeLines(textElement.getText(), textElement.getFont(), textElement.getMaxLineWidth());
int y = textElement.getY();
for (int i = 0; i < breakLineTexts.size(); i++) {
if (i < textElement.getMaxLineCount()) {
String text = breakLineTexts.get(i);
//如果计该行是要取的最后一行,但不是整体最后一行,则加...
if (i == textElement.getMaxLineCount() - 1 && i < breakLineTexts.size() - 1) {
text = text.substring(0, text.length() - 1) + "...";
}
TextElement combineTextLine = new TextElement(text, textElement.getFont(), textElement.getX(), y);
combineTextLine.setColor(textElement.getColor());
combineTextLine.setStrikeThrough(textElement.isStrikeThrough());
combineTextLine.setCenter(textElement.isCenter());
combineTextLine.setAlpha(textElement.getAlpha());
combineTextLine.setRotate(textElement.getRotate());
breakLineElements.add(combineTextLine);
//累加高度
y += textElement.getLineHeight();
} else {
break;
}
}
return breakLineElements;
}
}