修复ttf字体轮廓查询不到的bug

This commit is contained in:
Antecer
2024-04-27 12:49:11 +08:00
parent 00e240615d
commit cb06d149f7
3 changed files with 94 additions and 104 deletions
+2 -1
View File
@@ -178,7 +178,8 @@ let options = {
if(b64){ if(b64){
var f1 = java.queryBase64TTF(b64[1]); var f1 = java.queryBase64TTF(b64[1]);
var f2 = java.queryTTF("https://alanskycn.gitee.io/teachme/assets/font/Source Han Sans CN Regular.ttf"); var f2 = java.queryTTF("https://alanskycn.gitee.io/teachme/assets/font/Source Han Sans CN Regular.ttf");
return java.replaceFont(result, f1, f2); // return java.replaceFont(result, f1, f2);
return java.replaceFont(result, f1, f2, true); // 过滤掉f1中不存在的字形
} }
return result; return result;
})() })()
@@ -799,23 +799,34 @@ interface JsExtensions : JsEncodeUtils {
fun replaceFont( fun replaceFont(
text: String, text: String,
errorQueryTTF: QueryTTF?, errorQueryTTF: QueryTTF?,
correctQueryTTF: QueryTTF? correctQueryTTF: QueryTTF?,
filter: Boolean
): String { ): String {
if (errorQueryTTF == null || correctQueryTTF == null) return text if (errorQueryTTF == null || correctQueryTTF == null) return text
val contentArray = text.toStringArray() //这里不能用toCharArray,因为有些文字占多个字节 val contentArray = text.toStringArray() //这里不能用toCharArray,因为有些文字占多个字节
contentArray.forEachIndexed { index, s -> contentArray.forEachIndexed { index, s ->
val oldCode = s.codePointAt(0) val oldCode = s.codePointAt(0)
if (errorQueryTTF.inLimit(oldCode)) {
val glyf = errorQueryTTF.getGlyfByCode(oldCode) val glyf = errorQueryTTF.getGlyfByCode(oldCode)
val code = correctQueryTTF.getCodeByGlyf(glyf) val code = correctQueryTTF.getCodeByGlyf(glyf)
if (code != 0) { if (code != 0) {
contentArray[index] = code.toChar().toString() contentArray[index] = code.toChar().toString()
} }
if (glyf == "" && filter) {
// 删除轮廓数据为空的字符
contentArray[index] = ""
} }
} }
return contentArray.joinToString("") return contentArray.joinToString("")
} }
fun replaceFont(
text: String,
errorQueryTTF: QueryTTF?,
correctQueryTTF: QueryTTF?
): String {
return replaceFont(text, errorQueryTTF, correctQueryTTF, false)
}
/** /**
* 章节数转数字 * 章节数转数字
@@ -11,7 +11,7 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@SuppressWarnings({"FieldCanBeLocal", "StatementWithEmptyBody", "unused"}) @SuppressWarnings({"FieldCanBeLocal", "unused"})
public class QueryTTF { public class QueryTTF {
private static class Header { private static class Header {
public int majorVersion; public int majorVersion;
@@ -222,7 +222,7 @@ public class QueryTTF {
private final MaxpLayout maxp = new MaxpLayout(); private final MaxpLayout maxp = new MaxpLayout();
private final List<Integer> loca = new LinkedList<>(); private final List<Integer> loca = new LinkedList<>();
private final CmapLayout Cmap = new CmapLayout(); private final CmapLayout Cmap = new CmapLayout();
private final List<GlyfLayout> glyf = new LinkedList<>(); private final List<String> glyf = new LinkedList<>();
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private final Pair<Integer, Integer>[] pps = new Pair[]{ private final Pair<Integer, Integer>[] pps = new Pair[]{
Pair.of(3, 10), Pair.of(3, 10),
@@ -233,10 +233,9 @@ public class QueryTTF {
Pair.of(0, 1) Pair.of(0, 1)
}; };
public final Map<Integer, String> codeToGlyph = new HashMap<>(); public final Map<Integer, String> unicodeToGlyph = new HashMap<>();
public final Map<String, Integer> glyphToCode = new HashMap<>(); public final Map<String, Integer> glyphToUnicode = new HashMap<>();
private int limitMix = Integer.MAX_VALUE; public final Map<Integer, Integer> unicodeToGlyphIndex = new HashMap<>();
private int limitMax = 0;
/** /**
* 构造函数 * 构造函数
@@ -406,83 +405,42 @@ public class QueryTTF {
} }
} }
} }
// 解析表 glyf (字体轮廓数据表) // 读取表 glyf (字体轮廓数据表)
for (Directory Temp : directorys) { for (Directory Temp : directorys) {
if (Temp.tag.equals("glyf")) { if (Temp.tag.equals("glyf")) {
fontReader.index = Temp.offset; int glyfCount = maxp.numGlyphs;
for (int i = 0; i < maxp.numGlyphs; ++i) { for (int i = 0; i < glyfCount; ) {
fontReader.index = Temp.offset + loca.get(i); fontReader.index = Temp.offset + loca.get(i);
++i;
int glyfNextIndex = i < glyfCount ? loca.get(i) : Temp.length;
byte[] glyph;
short numberOfContours = fontReader.ReadInt16(); short numberOfContours = fontReader.ReadInt16();
if (numberOfContours > 0) { if (numberOfContours > 0) {
GlyfLayout g = new GlyfLayout(); short g_xMin = fontReader.ReadInt16();
g.numberOfContours = numberOfContours; short g_yMin = fontReader.ReadInt16();
g.xMin = fontReader.ReadInt16(); short g_xMax = fontReader.ReadInt16();
g.yMin = fontReader.ReadInt16(); short g_yMax = fontReader.ReadInt16();
g.xMax = fontReader.ReadInt16(); int[] endPtsOfContours = fontReader.GetUInt16Array(numberOfContours);
g.yMax = fontReader.ReadInt16(); glyph = fontReader.GetBytes(glyfNextIndex - (fontReader.index - Temp.offset));
g.endPtsOfContours = fontReader.GetUInt16Array(numberOfContours);
g.instructionLength = fontReader.ReadUInt16();
g.instructions = fontReader.GetBytes(g.instructionLength);
int flagLength = g.endPtsOfContours[g.endPtsOfContours.length - 1] + 1;
// 获取轮廓点描述标志
g.flags = new byte[flagLength];
for (int n = 0; n < flagLength; ++n) {
g.flags[n] = fontReader.GetByte();
if ((g.flags[n] & 0x08) != 0x00) {
for (int m = fontReader.ReadUInt8(); m > 0; --m) {
g.flags[++n] = g.flags[n - 1];
}
}
}
// 获取轮廓点描述x轴相对值
g.xCoordinates = new short[flagLength];
for (int n = 0; n < flagLength; ++n) {
short same = (short) ((g.flags[n] & 0x10) != 0 ? 1 : -1);
if ((g.flags[n] & 0x02) != 0) {
g.xCoordinates[n] = (short) (same * fontReader.ReadUInt8());
} else { } else {
g.xCoordinates[n] = same == 1 ? (short) 0 : fontReader.ReadInt16(); glyph = fontReader.GetBytes(glyfNextIndex - (fontReader.index - 2));
}
}
// 获取轮廓点描述y轴相对值
g.yCoordinates = new short[flagLength];
for (int n = 0; n < flagLength; ++n) {
short same = (short) ((g.flags[n] & 0x20) != 0 ? 1 : -1);
if ((g.flags[n] & 0x04) != 0) {
g.yCoordinates[n] = (short) (same * fontReader.ReadUInt8());
} else {
g.yCoordinates[n] = same == 1 ? (short) 0 : fontReader.ReadInt16();
}
}
/* 相对坐标转绝对坐标
for (int n = 1; n < flagLength; ++n) {
xCoordinates[n] += xCoordinates[n - 1];
yCoordinates[n] += yCoordinates[n - 1];
}*/
glyf.add(g);
} else {
// 复合字体暂未使用
} }
glyf.add(getHexFromBytes(glyph));
} }
} }
} }
// 建立Unicode&Glyph双向表 // 建立Unicode&Glyph双向表
for (int key = 0; key < 130000; ++key) { for (int key = 0; key < 130000; ++key) {
if (key == 0xFF) key = 0x3400; // if (key == 0xFF) key = 0x3400;
int gid = getGlyfIndex(key); int gid = queryGlyfIndex(key);
if (gid == 0 || gid >= glyf.size()) continue; if (gid >= glyf.size()) continue;
StringBuilder sb = new StringBuilder(); unicodeToGlyphIndex.put(key, gid);
// 字型数据转String,方便存HashMap var val = glyf.get(gid);
for (short b : glyf.get(gid).xCoordinates) sb.append(b); unicodeToGlyph.put(key, val);
for (short b : glyf.get(gid).yCoordinates) sb.append(b); if (glyphToUnicode.containsKey(val)) continue;
String val = sb.toString(); glyphToUnicode.put(val, key);
if (limitMix > key) limitMix = key;
if (limitMax < key) limitMax = key;
codeToGlyph.put(key, val);
if (glyphToCode.containsKey(val)) continue;
glyphToCode.put(val, key);
} }
} }
@@ -509,11 +467,11 @@ public class QueryTTF {
/** /**
* 使用Unicode值查找轮廓索引 * 使用Unicode值查找轮廓索引
* *
* @param code 传入Unicode十进制值 * @param unicode 传入Unicode值
* @return 返回十进制轮廓索引 * @return 返回十进制轮廓索引
*/ */
private int getGlyfIndex(int code) { public int queryGlyfIndex(int unicode) {
if (code == 0) return 0; if (unicode == 0) return 0;
int fmtKey = 0; int fmtKey = 0;
for (Pair<Integer, Integer> item : pps) { for (Pair<Integer, Integer> item : pps) {
for (CmapRecord record : Cmap.records) { for (CmapRecord record : Cmap.records) {
@@ -531,34 +489,33 @@ public class QueryTTF {
assert table != null; assert table != null;
int fmt = table.format; int fmt = table.format;
if (fmt == 0) { if (fmt == 0) {
if (code < table.glyphIdArray.length) glyfID = table.glyphIdArray[code] & 0xFF; if (unicode < table.glyphIdArray.length) glyfID = table.glyphIdArray[unicode] & 0xFF;
} else if (fmt == 4) { } else if (fmt == 4) {
CmapFormat4 tab = (CmapFormat4) table; CmapFormat4 tab = (CmapFormat4) table;
if (code > tab.endCode[tab.endCode.length - 1]) return 0; if (unicode > tab.endCode[tab.endCode.length - 1]) return 0;
// 二分法查找数值索引 // 二分法查找数值索引
int start = 0, middle, end = tab.endCode.length - 1; int start = 0, middle, end = tab.endCode.length - 1;
while (start + 1 < end) { while (start + 1 < end) {
middle = (start + end) / 2; middle = (start + end) / 2;
if (tab.endCode[middle] <= code) start = middle; if (tab.endCode[middle] <= unicode) start = middle;
else end = middle; else end = middle;
} }
if (tab.endCode[start] < code) ++start; if (tab.endCode[start] < unicode) ++start;
if (code < tab.startCode[start]) return 0; if (unicode < tab.startCode[start]) return 0;
if (tab.idRangeOffset[start] != 0) { if (tab.idRangeOffset[start] != 0) {
glyfID = tab.glyphIdArray[code - tab.startCode[start] + (tab.idRangeOffset[start] >> 1) - (tab.idRangeOffset.length - start)]; glyfID = tab.glyphIdArray[unicode - tab.startCode[start] + (tab.idRangeOffset[start] >> 1) - (tab.idRangeOffset.length - start)];
} else glyfID = code + tab.idDelta[start]; } else glyfID = unicode + tab.idDelta[start];
glyfID &= 0xFFFF; glyfID &= 0xFFFF;
} else if (fmt == 6) { } else if (fmt == 6) {
CmapFormat6 tab = (CmapFormat6) table; CmapFormat6 tab = (CmapFormat6) table;
int index = code - tab.firstCode; int index = unicode - tab.firstCode;
if (index < 0 || index >= tab.glyphIdArray.length) glyfID = 0; if (0 <= index && index < tab.glyphIdArray.length) glyfID = tab.glyphIdArray[index];
else glyfID = tab.glyphIdArray[index];
} else if (fmt == 12) { } else if (fmt == 12) {
CmapFormat12 tab = (CmapFormat12) table; CmapFormat12 tab = (CmapFormat12) table;
if (code > tab.groups.get(tab.numGroups - 1).getMiddle()) return 0; if (unicode > tab.groups.get(tab.numGroups - 1).getMiddle()) return 0;
for (int i = 0; i < tab.numGroups; i++) { for (int i = 0; i < tab.numGroups; i++) {
if (tab.groups.get(i).getLeft() <= code && code <= tab.groups.get(i).getMiddle()) { if (tab.groups.get(i).getLeft() <= unicode && unicode <= tab.groups.get(i).getMiddle()) {
glyfID = tab.groups.get(i).getRight() + code - tab.groups.get(i).getLeft(); glyfID = tab.groups.get(i).getRight() + unicode - tab.groups.get(i).getLeft();
break; break;
} }
} }
@@ -567,33 +524,54 @@ public class QueryTTF {
} }
/** /**
* 判断Unicode值是否在字体范围内 * 使用Unicode值获取轮廓索引
* *
* @param code 传入Unicode十进制值 * @param unicode 传入Unicode值
* @return 返回bool查询结果 * @return 返回十进制轮廓索引
*/ */
public boolean inLimit(int code) { public int getGlyfIndex(int unicode) {
return (limitMix <= code) && (code <= limitMax); var glyfIndex = unicodeToGlyphIndex.get(unicode);
if (glyfIndex == null) return 0;
return glyfIndex;
} }
/** /**
* 使用Unicode值获取轮廓数据 * 使用Unicode值获取轮廓数据
* *
* @param key 传入Unicode十进制值 * @param unicode 传入Unicode值
* @return 返回轮廓数组的String值 * @return 返回轮廓数组的String值
*/ */
public String getGlyfByCode(int key) { public String getGlyfByCode(int unicode) {
return codeToGlyph.getOrDefault(key, ""); return unicodeToGlyph.getOrDefault(unicode, "");
} }
/** /**
* 使用轮廓数据获取Unicode值 * 使用轮廓数据获取Unicode值
* *
* @param val 传入轮廓数组的String值 * @param glyph 传入轮廓数组的String值
* @return 返回Unicode十进制值 * @return 返回Unicode十进制值
*/ */
public int getCodeByGlyf(String val) { public int getCodeByGlyf(String glyph) {
//noinspection ConstantConditions //noinspection ConstantConditions
return glyphToCode.getOrDefault(val, 0); return glyphToUnicode.getOrDefault(glyph, 0);
}
/**
* 字体轮廓数据转Hex字符串
*
* @param glyph 字体轮廓数据
* @return 返回轮廓数组的String值
*/
public String getHexFromBytes(byte[] glyph) {
if (glyph == null) return "";
StringBuilder sb = new StringBuilder();
for (byte b : glyph) {
String hex = Integer.toHexString(b);
if (hex.length() == 1) {
sb.append("0");//当16进制为个位数时,在前面补0
}
sb.append(hex);//将16进制加入字符串
}
return sb.toString().toUpperCase();
} }
} }