需求背景
一块固定宽高的区域需要打印一段文字,这段文字尽可能的大,且数据展示完整
分析
根据字数的不同,调整字体的大小,找到满足n(一行的字数)*m(多少列) < 文字字数的最大字体大小
n = Math.ceil(区域width / 文字width)
m = Math.ceil(区域height / 文字lineHeight)
字体、字体大小、文字宽高对应表CPCL指令集
方案一:
假设几种特定的字体和字体大小,计算出区域内各自n行分别展示的文字个数,写成不同的方法,根据文字个数区间调用不同的方法
缺点: 区域特定的情况下,使用情况较为特殊,适合使用场景不多的需求
方案二:
从大到小遍历CPCL字体和字体大小,获取对应的宽高,找到符合要求的m(列数)* lineHeight (行高)<= height
缺点:字体、字体大小、文字宽高是二维数组,遍历麻烦,对算法要求高
方案三
找到一个宽高适合的字体、字体大小,固定该字体和字体大小,使用指令放大该文字,找到满足m(列数)* lineHeight (行高)*scale(倍数)<= height
放大指令: SETMAG widthScale heightScale; // 为了保证字不变形,所以宽高放大同一倍数,基本的字体、字体大小选择 55 0(中文字体,个别数字英文可能会模糊);放大倍数取值0-16,0和1不放大
/**
* 自适应文字大小
*
* @param data 字符串数据
* @param x x位移
* @param y y位移
* @param width 区域宽度
* @param height 区域高度
* @return
*/
private String coverSelfAdapt(String data, int x, int y, int width, int height) {
int scale = 16,
// 默认文字宽高以 TEXT 55 0 x y 模式计算,不同字体、字体大小对应的基础宽高不一样
baseWidth = 8, // 文字基础宽度,
baseHeight = 16, // 文字基础高度
widthNum = 0, // 宽度可以展示多少文字
num = 0; // 高度可以展示多少列
boolean isFlag = false;
while (scale > 0 && scale < 17 && !isFlag) {
// 当字体大小满足在最小和最大之间,寻找最符合要求的最大值
widthNum = (int) Math.ceil((double) width / (scale * baseWidth)); // 当前字体大小一行最多的字数个数
num = (int) Math.ceil(((double) data.length()) / widthNum); // 可以打几行
if (num == 0) {
num = 1;
}
// 只能展示一行
Log.d(TAG, "coverSelfAdapt: (" + scale + ")num: " + num + " widthNum:" + widthNum + " 最多:" + num * widthNum + " 文字:" + data.length());
if (num * baseHeight * scale <= height) {
// 满足最大放大倍数条件内可以放下所有文字,跳出循环
isFlag = true;
} else {
scale--;
}
}
Log.e(TAG, "coverSelfAdapt: 高度差:" + (num * baseHeight * scale - height) + " 宽度差:" + (num * baseWidth * scale));
Log.d(TAG, "coverSelfAdapt: 是否有最佳字体大小:" + isFlag + " 字体大小倍数为:" + scale + " 一行最多:" + widthNum);
Log.d(TAG, "coverSelfAdapt: " + " 打印指令串:" + converMultiLine(data, x, y, scale, widthNum, baseHeight * scale, 4,0));
if (isFlag && scale > 0) {
return converMultiLine(data, x, y, scale, widthNum, baseHeight * scale,2,0);
} else {
Log.e(TAG, "coverSelfAdapt: no fontSize: ");
// 不存在则最小展示,尽可能的展示多
widthNum = (int) Math.floor(width / baseWidth);
return converMultiLine(data, x, y, 1, widthNum, baseHeight,2,0);
}
}
/**
* 根据字体等返回多行打印指令
*
* @param data 文字
* @param x x位移
* @param y y位移
* @param scale 放大倍数
* @param num 一行的文字数量
* @param lineHeight 文字的lineHeight
* @return
*/
private String converMultiLine(String data, int x, int y, int scale, int num, int lineHeight, int font, int fontSize) {
int startY = y, startXNum = 0, endXNum = 0;
String info = String.format("SETMAG %d %d\r\n", scale, scale);
int index = (int) Math.ceil(((double) data.length()) / num);
int i = 0;
while (i < index) {
startXNum = i * num;
endXNum = startXNum + num;
if (endXNum >= data.length()) {
endXNum = data.length();
}
info += String.format("TEXT %d %d %d %d %s\r\n",font,fontSize, x, startY, data.substring(startXNum, endXNum));
startY += lineHeight;
i++;
}
info += "SETMAG 1 1\r\n"; // 不影响后面的文字打印
return info;
}
写在最后
更加自适应的方式是方案二和方案三的结合,动态计算font和fontSize再对应的找放大倍数