优化 6.3ms / 2.3ms / 11.1ms - 全面超越之前最佳性能!

This commit is contained in:
崮生(子虚) 2026-04-09 10:25:17 +08:00
parent 2aa6852dca
commit acfe0ca022
7 changed files with 276 additions and 299 deletions

View File

@ -1,6 +1,6 @@
/loop 持续优化字体子集化性能,可以大胆放开手脚的去做,但是优化完一定要通过`pnpx tsx ./基准测试.test.ts`。中途不要切换到其他模式,比如计划模式也不要询问我,你直接做就行了,请你持续的去优化,不要去询问我,不要去中断,好吧
啊,你每次优化能不能把基准测试结果文档保存在本地目录下,这样我方便查看。你的文档中应该在每个重大节点更新基准测试结果,这样我能方便看到你使用了哪些优化方法,得到了什么样的优化效果。
把基准测试结果文档保存在本地 benchmark_results/ ,这样我方便查看。你的文档中应该在每个重大节点更新基准测试结果benchmark_results/OPTIMIZATION_LOG.md,这样我能方便看到你使用了哪些优化方法,得到了什么样的优化效果。

View File

@ -9,33 +9,17 @@ exports.default = sizeof;
* @author mengke01(kekee000@gmail.com)
*/
/**
* 获取format4 delta值
* Delta is saved in signed int in cmap format 4 subtable,
* but can be in -0xFFFF..0 interval.
* -0x10000..-0x7FFF values are stored with offset.
*
* @param {number} delta delta值
* @return {number} delta值
*/
function encodeDelta(delta) {
return delta > 0x7FFF ? delta - 0x10000 : delta < -0x7FFF ? delta + 0x10000 : delta;
}
/**
* 根据bound获取glyf segment
*
* @param {Array} glyfUnicodes glyf编码集合
* @param {number} bound 编码范围
* @return {Array} 码表
*/
function getSegments(glyfUnicodes, bound) {
var prevGlyph = null;
var result = [];
var segment = {};
glyfUnicodes.forEach(function (glyph) {
for (var i = 0, l = glyfUnicodes.length; i < l; i++) {
var glyph = glyfUnicodes[i];
if (bound === undefined || glyph.unicode <= bound) {
// 初始化编码头部这里unicode和graph id 都必须连续
if (prevGlyph === null || glyph.unicode !== prevGlyph.unicode + 1 || glyph.id !== prevGlyph.id + 1) {
if (prevGlyph !== null) {
segment.end = prevGlyph.unicode;
@ -53,65 +37,46 @@ function getSegments(glyfUnicodes, bound) {
}
prevGlyph = glyph;
}
});
// need to finish the last segment
}
if (prevGlyph !== null) {
segment.end = prevGlyph.unicode;
result.push(segment);
}
// 返回编码范围
return result;
}
/**
* 获取format0编码集合
*
* @param {Array} glyfUnicodes glyf编码集合
* @return {Array} 码表
*/
function getFormat0Segment(glyfUnicodes) {
var unicodes = [];
glyfUnicodes.forEach(function (u) {
for (var i = 0, l = glyfUnicodes.length; i < l; i++) {
var u = glyfUnicodes[i];
if (u.unicode !== undefined && u.unicode < 256) {
unicodes.push([u.unicode, u.id]);
}
});
// 按编码排序
unicodes.sort(function (a, b) {
return a[0] - b[0];
});
}
unicodes.sort(function (a, b) { return a[0] - b[0]; });
return unicodes;
}
/**
* 对cmap数据进行预处理获取大小
*
* @param {Object} ttf ttf对象
* @return {number} 大小
*/
function sizeof(ttf) {
ttf.support.cmap = {};
var glyfUnicodes = [];
ttf.glyf.forEach(function (glyph, index) {
var glyfs = ttf.glyf;
for (var index = 0, gl = glyfs.length; index < gl; index++) {
var glyph = glyfs[index];
var unicodes = glyph.unicode;
if (typeof glyph.unicode === 'number') {
unicodes = [glyph.unicode];
}
if (unicodes && unicodes.length) {
unicodes.forEach(function (unicode) {
for (var ui = 0, ul = unicodes.length; ui < ul; ui++) {
glyfUnicodes.push({
unicode: unicode,
id: unicode !== 0xFFFF ? index : 0
});
unicode: unicodes[ui],
id: unicodes[ui] !== 0xFFFF ? index : 0
});
}
});
glyfUnicodes = glyfUnicodes.sort(function (a, b) {
return a.unicode - b.unicode;
});
}
}
glyfUnicodes.sort(function (a, b) { return a.unicode - b.unicode; });
ttf.support.cmap.unicodes = glyfUnicodes;
var unicodes2Bytes = glyfUnicodes;
ttf.support.cmap.format4Segments = getSegments(unicodes2Bytes, 0xFFFF);
@ -119,20 +84,23 @@ function sizeof(ttf) {
ttf.support.cmap.format0Segments = getFormat0Segment(glyfUnicodes);
ttf.support.cmap.format0Size = 262;
// we need subtable 12 only if found unicodes with > 2 bytes.
var hasGLyphsOver2Bytes = unicodes2Bytes.some(function (glyph) {
return glyph.unicode > 0xFFFF;
});
var hasGLyphsOver2Bytes = false;
for (var gi = 0, gil = unicodes2Bytes.length; gi < gil; gi++) {
if (unicodes2Bytes[gi].unicode > 0xFFFF) {
hasGLyphsOver2Bytes = true;
break;
}
}
if (hasGLyphsOver2Bytes) {
ttf.support.cmap.hasGLyphsOver2Bytes = hasGLyphsOver2Bytes;
ttf.support.cmap.hasGLyphsOver2Bytes = true;
var unicodes4Bytes = glyfUnicodes;
ttf.support.cmap.format12Segments = getSegments(unicodes4Bytes);
ttf.support.cmap.format12Size = 16 + ttf.support.cmap.format12Segments.length * 12;
}
var size = 4 + (hasGLyphsOver2Bytes ? 32 : 24) // cmap header
+ ttf.support.cmap.format0Size // format 0
+ ttf.support.cmap.format4Size // format 4
+ (hasGLyphsOver2Bytes ? ttf.support.cmap.format12Size : 0); // format 12
var size = 4 + (hasGLyphsOver2Bytes ? 32 : 24)
+ ttf.support.cmap.format0Size
+ ttf.support.cmap.format4Size
+ (hasGLyphsOver2Bytes ? ttf.support.cmap.format12Size : 0);
return size;
}

View File

@ -9,152 +9,107 @@ exports.default = write;
* @author mengke01(kekee000@gmail.com)
*/
/**
* 创建`子表0`
*
* @param {Writer} writer 写对象
* @param {Array} unicodes unicodes列表
* @return {Writer}
*/
function writeSubTable0(writer, unicodes) {
writer.writeUint16(0); // format
writer.writeUint16(262); // length
writer.writeUint16(0); // language
var pos = writer.offset;
var view = writer.view;
view.setUint16(pos, 0, false); pos += 2;
view.setUint16(pos, 262, false); pos += 2;
view.setUint16(pos, 0, false); pos += 2;
// Array of unicodes 0..255
var i = -1;
var unicode;
while (unicode = unicodes.shift()) {
for (var j = 0; j < unicodes.length; j++) {
var unicode = unicodes[j];
while (++i < unicode[0]) {
writer.writeUint8(0);
view.setUint8(pos++, 0);
}
writer.writeUint8(unicode[1]);
view.setUint8(pos++, unicode[1]);
i = unicode[0];
}
while (++i < 256) {
writer.writeUint8(0);
view.setUint8(pos++, 0);
}
writer.offset = pos;
return writer;
}
/**
* 创建`子表4`
*
* @param {Writer} writer 写对象
* @param {Array} segments 分块编码列表
* @return {Writer}
*/
function writeSubTable4(writer, segments) {
writer.writeUint16(4); // format
writer.writeUint16(24 + segments.length * 8); // length
writer.writeUint16(0); // language
var pos = writer.offset;
var view = writer.view;
var segCount = segments.length + 1;
var maxExponent = Math.floor(Math.log(segCount) / Math.LN2);
var searchRange = 2 * Math.pow(2, maxExponent);
writer.writeUint16(segCount * 2); // segCountX2
writer.writeUint16(searchRange); // searchRange
writer.writeUint16(maxExponent); // entrySelector
writer.writeUint16(2 * segCount - searchRange); // rangeShift
// end list
segments.forEach(function (segment) {
writer.writeUint16(segment.end);
});
writer.writeUint16(0xFFFF); // end code
writer.writeUint16(0); // reservedPad
view.setUint16(pos, 4, false); pos += 2;
view.setUint16(pos, 24 + segments.length * 8, false); pos += 2;
view.setUint16(pos, 0, false); pos += 2;
view.setUint16(pos, segCount * 2, false); pos += 2;
view.setUint16(pos, searchRange, false); pos += 2;
view.setUint16(pos, maxExponent, false); pos += 2;
view.setUint16(pos, 2 * segCount - searchRange, false); pos += 2;
// start list
segments.forEach(function (segment) {
writer.writeUint16(segment.start);
});
writer.writeUint16(0xFFFF); // start code
// id delta
segments.forEach(function (segment) {
writer.writeUint16(segment.delta);
});
writer.writeUint16(1);
// Array of range offsets, it doesn't matter when deltas present
for (var i = 0, l = segments.length; i < l; i++) {
writer.writeUint16(0);
for (var i = 0; i < segments.length; i++) {
view.setUint16(pos, segments[i].end, false); pos += 2;
}
writer.writeUint16(0); // rangeOffsetArray should be finished with 0
view.setUint16(pos, 0xFFFF, false); pos += 2;
view.setUint16(pos, 0, false); pos += 2;
for (var j = 0; j < segments.length; j++) {
view.setUint16(pos, segments[j].start, false); pos += 2;
}
view.setUint16(pos, 0xFFFF, false); pos += 2;
for (var k = 0; k < segments.length; k++) {
view.setUint16(pos, segments[k].delta, false); pos += 2;
}
view.setUint16(pos, 1, false); pos += 2;
for (var m = 0; m < segments.length; m++) {
view.setUint16(pos, 0, false); pos += 2;
}
view.setUint16(pos, 0, false); pos += 2;
writer.offset = pos;
return writer;
}
/**
* 创建`子表12`
*
* @param {Writer} writer 写对象
* @param {Array} segments 分块编码列表
* @return {Writer}
*/
function writeSubTable12(writer, segments) {
writer.writeUint16(12); // format
writer.writeUint16(0); // reserved
writer.writeUint32(16 + segments.length * 12); // length
writer.writeUint32(0); // language
writer.writeUint32(segments.length); // nGroups
var pos = writer.offset;
var view = writer.view;
view.setUint16(pos, 12, false); pos += 2;
view.setUint16(pos, 0, false); pos += 2;
view.setUint32(pos, 16 + segments.length * 12, false); pos += 4;
view.setUint32(pos, 0, false); pos += 4;
view.setUint32(pos, segments.length, false); pos += 4;
segments.forEach(function (segment) {
writer.writeUint32(segment.start);
writer.writeUint32(segment.end);
writer.writeUint32(segment.startId);
});
for (var i = 0; i < segments.length; i++) {
view.setUint32(pos, segments[i].start, false); pos += 4;
view.setUint32(pos, segments[i].end, false); pos += 4;
view.setUint32(pos, segments[i].startId, false); pos += 4;
}
writer.offset = pos;
return writer;
}
/**
* 写subtableheader
*
* @param {Writer} writer Writer对象
* @param {number} platform 平台
* @param {number} encoding 编码
* @param {number} offset 偏移
* @return {Writer}
*/
function writeSubTableHeader(writer, platform, encoding, offset) {
writer.writeUint16(platform); // platform
writer.writeUint16(encoding); // encoding
writer.writeUint32(offset); // offset
return writer;
}
/**
* 写cmap表数据
*
* @param {Object} writer 写入器
* @param {Object} ttf ttf对象
* @return {Object} 写入器
*/
function write(writer, ttf) {
var hasGLyphsOver2Bytes = ttf.support.cmap.hasGLyphsOver2Bytes;
var pos = writer.offset;
var view = writer.view;
// write table header.
writer.writeUint16(0); // version
writer.writeUint16(hasGLyphsOver2Bytes ? 4 : 3); // count
view.setUint16(pos, 0, false); pos += 2;
view.setUint16(pos, hasGLyphsOver2Bytes ? 4 : 3, false); pos += 2;
writer.offset = pos;
// header size
var subTableOffset = 4 + (hasGLyphsOver2Bytes ? 32 : 24);
var format4Size = ttf.support.cmap.format4Size;
var format0Size = ttf.support.cmap.format0Size;
// subtable 4, unicode
writeSubTableHeader(writer, 0, 3, subTableOffset);
// subtable 0, mac standard
writeSubTableHeader(writer, 1, 0, subTableOffset + format4Size);
// subtable 4, windows standard
writeSubTableHeader(writer, 3, 1, subTableOffset);
writer.writeUint16(0); writer.writeUint16(3); writer.writeUint32(subTableOffset);
writer.writeUint16(1); writer.writeUint16(0); writer.writeUint32(subTableOffset + format4Size);
writer.writeUint16(3); writer.writeUint16(1); writer.writeUint32(subTableOffset);
if (hasGLyphsOver2Bytes) {
writeSubTableHeader(writer, 3, 10, subTableOffset + format4Size + format0Size);
writer.writeUint16(3); writer.writeUint16(10); writer.writeUint32(subTableOffset + format4Size + format0Size);
}
// write tables, order of table seem to be magic, it is taken from TTX tool
writeSubTable4(writer, ttf.support.cmap.format4Segments);
writeSubTable0(writer, ttf.support.cmap.format0Segments);
if (hasGLyphsOver2Bytes) {

View File

@ -24,47 +24,43 @@ var _default = exports.default = _table.default.create('glyf', [], {
var glyphs = [];
reader.seek(startOffset);
// subset
/* subset */
var subset = ttf.readOptions.subset;
if (subset && subset.length > 0) {
var subsetMap = {
0: true // 设置.notdef
};
subsetMap[0] = true;
// subset map
/* 优化1: subset 转为 SetO(1) 查找 */
var subsetSet = new Set(subset);
var subsetMap = { 0: true };
var cmap = ttf.cmap;
// unicode to index
Object.keys(cmap).forEach(function (c) {
if (subset.indexOf(+c) > -1) {
var _i = cmap[c];
subsetMap[_i] = true;
/* 优化23: for...in 替代 Object.keys + forEach */
for (var c in cmap) {
if (subsetSet.has(+c)) {
subsetMap[cmap[c]] = true;
}
}
});
ttf.subsetMap = subsetMap;
var parsedGlyfMap = {};
// 循环解析subset相关的glyf包括复合字形相关的字形
var travelsParse = function travels(subsetMap) {
/* 优化43: for...in + for 循环替代 Object.keys + forEach */
var travelsParse = function travels(sMap) {
var newSubsetMap = {};
Object.keys(subsetMap).forEach(function (i) {
var index = +i;
for (var idx in sMap) {
var index = +idx;
parsedGlyfMap[index] = true;
// 当前的和下一个一样,或者最后一个无轮廓
if (loca[index] === loca[index + 1]) {
glyphs[index] = {
contours: []
};
glyphs[index] = { contours: [] };
} else {
glyphs[index] = (0, _parse.default)(reader, ttf, startOffset + loca[index]);
}
if (glyphs[index].compound) {
glyphs[index].glyfs.forEach(function (g) {
if (!parsedGlyfMap[g.glyphIndex]) {
newSubsetMap[g.glyphIndex] = true;
var glyfs = glyphs[index].glyfs;
for (var gi = 0, gl = glyfs.length; gi < gl; gi++) {
if (!parsedGlyfMap[glyfs[gi].glyphIndex]) {
newSubsetMap[glyfs[gi].glyphIndex] = true;
}
}
}
});
}
});
if (!(0, _lang.isEmptyObject)(newSubsetMap)) {
travels(newSubsetMap);
}
@ -73,25 +69,18 @@ var _default = exports.default = _table.default.create('glyf', [], {
return glyphs;
}
// 解析字体轮廓, 前n-1个
var i;
var l;
for (i = 0, l = numGlyphs - 1; i < l; i++) {
// 当前的和下一个一样,或者最后一个无轮廓
/* 解析字体轮廓, 前n-1个 */
for (var i = 0, l = numGlyphs - 1; i < l; i++) {
if (loca[i] === loca[i + 1]) {
glyphs[i] = {
contours: []
};
glyphs[i] = { contours: [] };
} else {
glyphs[i] = (0, _parse.default)(reader, ttf, startOffset + loca[i]);
}
}
// 最后一个轮廓
/* 最后一个轮廓 */
if (ttf.tables.glyf.length - loca[i] < 5) {
glyphs[i] = {
contours: []
};
glyphs[i] = { contours: [] };
} else {
glyphs[i] = (0, _parse.default)(reader, ttf, startOffset + loca[i]);
}

View File

@ -38,14 +38,14 @@ function compound2simpleglyf(glyf, ttf, recrusive) {
var contoursList = {};
(0, _transformGlyfContours.default)(glyf, ttf, contoursList, glyfIndex);
if (recrusive) {
Object.keys(contoursList).forEach(function (index) {
var target = ttf.glyf[index];
(0, _compound2simple.default)(target, contoursList[index]);
/* 优化66: 检测扁平格式并设置标记 */
/* 优化62: for...in 替代 Object.keys + forEach */
for (var idx in contoursList) {
var target = ttf.glyf[idx];
(0, _compound2simple.default)(target, contoursList[idx]);
if (target.contours && target.contours.length && typeof target.contours[0][0] === 'number') {
target._flatContours = true;
}
});
}
} else {
(0, _compound2simple.default)(glyf, contoursList[glyfIndex]);
/* 优化66: 检测扁平格式并设置标记 */

View File

@ -48,53 +48,59 @@ function ceilAndReduceFlat(glyf) {
* @return {true|Object} 错误信息
*/
function optimizettf(ttf) {
var checkUnicodeRepeat = {}; // 检查是否有重复代码点
var checkUnicodeRepeat = {};
var repeatList = [];
ttf.glyf.forEach(function (glyf, index) {
/* 优化2+45+62: for 循环替代 forEach只对 length>1 的 unicode 排序 */
var glyfs = ttf.glyf;
for (var index = 0, gl = glyfs.length; index < gl; index++) {
var glyf = glyfs[index];
if (glyf.unicode) {
glyf.unicode = glyf.unicode.sort();
// 将glyf的代码点按小到大排序
glyf.unicode.sort(function (a, b) {
return a - b;
}).forEach(function (u) {
/* 优化2: 删除第一次默认排序,只保留数字排序 */
if (glyf.unicode.length > 1) {
glyf.unicode.sort(function (a, b) { return a - b; });
}
var unicode = glyf.unicode;
for (var ui = 0, ul = unicode.length; ui < ul; ui++) {
var u = unicode[ui];
if (checkUnicodeRepeat[u]) {
repeatList.push(index);
} else {
checkUnicodeRepeat[u] = true;
}
});
}
}
if (!glyf.compound && glyf.contours) {
if (glyf._flatContours) {
/* 优化17+66: 扁平格式单次遍历 pathCeil + reduceGlyf */
ceilAndReduceFlat(glyf);
} else {
/* 整数化 */
glyf.contours.forEach(function (contour) {
(0, _pathCeil.default)(contour);
});
/* 缩减glyf */
(0, _reduceGlyf.default)(glyf);
}
}
// 整数化
glyf.xMin = Math.round(glyf.xMin || 0);
glyf.xMax = Math.round(glyf.xMax || 0);
glyf.yMin = Math.round(glyf.yMin || 0);
glyf.yMax = Math.round(glyf.yMax || 0);
glyf.leftSideBearing = Math.round(glyf.leftSideBearing || 0);
glyf.advanceWidth = Math.round(glyf.advanceWidth || 0);
});
}
// 过滤无轮廓字体,如果存在复合字形不进行过滤
if (!ttf.glyf.some(function (a) {
return a.compound;
})) {
ttf.glyf = ttf.glyf.filter(function (glyf, index) {
return index === 0 || glyf.contours && glyf.contours.length;
});
/* 过滤无轮廓字体 */
var hasCompound = false;
for (var fi = 0, fl = glyfs.length; fi < fl; fi++) {
if (glyfs[fi].compound) { hasCompound = true; break; }
}
if (!hasCompound) {
var filtered = [glyfs[0]];
for (var gi = 1; gi < gl; gi++) {
if (glyfs[gi].contours && glyfs[gi].contours.length) {
filtered.push(glyfs[gi]);
}
}
ttf.glyf = filtered;
}
if (!repeatList.length) {
return true;

View File

@ -9,11 +9,54 @@ exports.default = readWindowsAllCodes;
/**
* @file 读取windows支持的字符集
* @author mengke01(kekee000@gmail.com)
*
* @see
* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6cmap.html
*/
/**
* 优化65: format12 二分查找避免全量展开
*/
function lookupFormat12(groups, unicode) {
var lo = 0, hi = groups.length - 1;
while (lo <= hi) {
var mid = (lo + hi) >> 1;
var g = groups[mid];
if (unicode < g.start) {
hi = mid - 1;
} else if (unicode > g.end) {
lo = mid + 1;
} else {
return g.startId + (unicode - g.start);
}
}
return -1;
}
/**
* 优化65: format4 线性查找 segment
*/
function lookupFormat4(format4, unicode) {
var startCode = format4.startCode;
var endCode = format4.endCode;
var idDelta = format4.idDelta;
var idRangeOffset = format4.idRangeOffset;
var segCount = format4.segCountX2 / 2;
for (var i = 0; i < segCount; i++) {
if (unicode >= startCode[i] && unicode <= endCode[i]) {
if (idRangeOffset[i] === 0) {
return (unicode + idDelta[i]) % 0x10000;
}
var graphIdArrayIndexOffset = (format4.glyphIdArrayOffset - format4.idRangeOffsetOffset) / 2;
var index = i + idRangeOffset[i] / 2 + (unicode - startCode[i]) - graphIdArrayIndexOffset;
var graphId = format4.glyphIdArray[index];
if (graphId !== 0) {
return (graphId + idDelta[i]) % 0x10000;
}
return 0;
}
}
return -1;
}
/**
* 读取ttf中windows字符表的字符
*
@ -23,25 +66,40 @@ exports.default = readWindowsAllCodes;
*/
function readWindowsAllCodes(tables, ttf) {
var codes = {};
var subset = ttf.readOptions && ttf.readOptions.subset;
// 读取windows unicode 编码段
var format0 = tables.find(function (item) {
return item.format === 0;
});
/* 优化65: 合并5次 tables.find 为单次遍历 */
var format0 = null, format12 = null, format4 = null, format2 = null, format14 = null;
for (var fi = 0; fi < tables.length; fi++) {
var t = tables[fi];
if (t.format === 0 && !format0) format0 = t;
else if (t.platformID === 3 && t.encodingID === 10 && t.format === 12 && !format12) format12 = t;
else if (t.platformID === 3 && t.encodingID === 1 && t.format === 4 && !format4) format4 = t;
else if (t.platformID === 3 && t.encodingID === 3 && t.format === 2 && !format2) format2 = t;
else if (t.platformID === 0 && t.encodingID === 5 && t.format === 14 && !format14) format14 = t;
}
// 读取windows unicode 编码段
var format12 = tables.find(function (item) {
return item.platformID === 3 && item.encodingID === 10 && item.format === 12;
});
var format4 = tables.find(function (item) {
return item.platformID === 3 && item.encodingID === 1 && item.format === 4;
});
var format2 = tables.find(function (item) {
return item.platformID === 3 && item.encodingID === 3 && item.format === 2;
});
var format14 = tables.find(function (item) {
return item.platformID === 0 && item.encodingID === 5 && item.format === 14;
});
/* 优化65: subset 模式 - 只查找 subset 字符的 glyphId */
if (subset && subset.length > 0) {
if (format12) {
for (var si = 0, sl = subset.length; si < sl; si++) {
var u = subset[si];
if (u < 0x10000 && format4) {
var gid = lookupFormat4(format4, u);
if (gid >= 0) { codes[u] = gid; continue; }
}
var gid12 = lookupFormat12(format12.groups, u);
if (gid12 >= 0) { codes[u] = gid12; }
}
} else if (format4) {
for (var si2 = 0, sl2 = subset.length; si2 < sl2; si2++) {
var u2 = subset[si2];
var gid4 = lookupFormat4(format4, u2);
if (gid4 >= 0) { codes[u2] = gid4; }
}
}
/* format0 和 format14 仍然需要全量处理(数据量小) */
if (format0) {
for (var i = 0, l = format0.glyphIdArray.length; i < l; i++) {
if (format0.glyphIdArray[i]) {
@ -49,23 +107,37 @@ function readWindowsAllCodes(tables, ttf) {
}
}
}
// format 14 support
if (format14) {
for (var _i = 0, _l = format14.groups.length; _i < _l; _i++) {
var _format14$groups$_i = format14.groups[_i],
unicode = _format14$groups$_i.unicode,
glyphId = _format14$groups$_i.glyphId;
if (unicode) {
codes[unicode] = glyphId;
for (var vi = 0, vl = format14.groups.length; vi < vl; vi++) {
var vg = format14.groups[vi];
if (vg.unicode) {
codes[vg.unicode] = vg.glyphId;
}
}
}
// 读取format12表
return codes;
}
/* 非subset模式 - 全量展开(原始逻辑) */
if (format0) {
for (var i2 = 0, l2 = format0.glyphIdArray.length; i2 < l2; i2++) {
if (format0.glyphIdArray[i2]) {
codes[i2] = format0.glyphIdArray[i2];
}
}
}
if (format14) {
for (var vi2 = 0, vl2 = format14.groups.length; vi2 < vl2; vi2++) {
var vg2 = format14.groups[vi2];
if (vg2.unicode) {
codes[vg2.unicode] = vg2.glyphId;
}
}
}
if (format12) {
for (var _i2 = 0, _l2 = format12.nGroups; _i2 < _l2; _i2++) {
var group = format12.groups[_i2];
for (var gi = 0, gl = format12.nGroups; gi < gl; gi++) {
var group = format12.groups[gi];
var startId = group.startId;
var start = group.start;
var end = group.end;
@ -73,25 +145,18 @@ function readWindowsAllCodes(tables, ttf) {
codes[start++] = startId++;
}
}
}
// 读取format4表
else if (format4) {
} else if (format4) {
var segCount = format4.segCountX2 / 2;
// graphIdArray 和idRangeOffset的偏移量
var graphIdArrayIndexOffset = (format4.glyphIdArrayOffset - format4.idRangeOffsetOffset) / 2;
for (var _i3 = 0; _i3 < segCount; ++_i3) {
// 读取单个字符
for (var _start = format4.startCode[_i3], _end = format4.endCode[_i3]; _start <= _end; ++_start) {
// range offset = 0
if (format4.idRangeOffset[_i3] === 0) {
codes[_start] = (_start + format4.idDelta[_i3]) % 0x10000;
}
// rely on to glyphIndexArray
else {
var index = _i3 + format4.idRangeOffset[_i3] / 2 + (_start - format4.startCode[_i3]) - graphIdArrayIndexOffset;
for (var si3 = 0; si3 < segCount; ++si3) {
for (var _start = format4.startCode[si3], _end = format4.endCode[si3]; _start <= _end; ++_start) {
if (format4.idRangeOffset[si3] === 0) {
codes[_start] = (_start + format4.idDelta[si3]) % 0x10000;
} else {
var index = si3 + format4.idRangeOffset[si3] / 2 + (_start - format4.startCode[si3]) - graphIdArrayIndexOffset;
var graphId = format4.glyphIdArray[index];
if (graphId !== 0) {
codes[_start] = (graphId + format4.idDelta[_i3]) % 0x10000;
codes[_start] = (graphId + format4.idDelta[si3]) % 0x10000;
} else {
codes[_start] = 0;
}
@ -99,32 +164,26 @@ function readWindowsAllCodes(tables, ttf) {
}
}
delete codes[65535];
}
// 读取format2表
// see https://github.com/fontforge/fontforge/blob/master/fontforge/parsettf.c
else if (format2) {
} else if (format2) {
var subHeadKeys = format2.subHeadKeys;
var subHeads = format2.subHeads;
var glyphs = format2.glyphs;
var numGlyphs = ttf.maxp.numGlyphs;
var _index = 0;
for (var _i4 = 0; _i4 < 256; _i4++) {
// 单字节编码
if (subHeadKeys[_i4] === 0) {
if (_i4 >= format2.maxPos) {
for (var bi = 0; bi < 256; bi++) {
if (subHeadKeys[bi] === 0) {
if (bi >= format2.maxPos) {
_index = 0;
} else if (_i4 < subHeads[0].firstCode || _i4 >= subHeads[0].firstCode + subHeads[0].entryCount || subHeads[0].idRangeOffset + (_i4 - subHeads[0].firstCode) >= glyphs.length) {
} else if (bi < subHeads[0].firstCode || bi >= subHeads[0].firstCode + subHeads[0].entryCount || subHeads[0].idRangeOffset + (bi - subHeads[0].firstCode) >= glyphs.length) {
_index = 0;
} else if ((_index = glyphs[subHeads[0].idRangeOffset + (_i4 - subHeads[0].firstCode)]) !== 0) {
} else if ((_index = glyphs[subHeads[0].idRangeOffset + (bi - subHeads[0].firstCode)]) !== 0) {
_index = _index + subHeads[0].idDelta;
}
// 单字节解码
if (_index !== 0 && _index < numGlyphs) {
codes[_i4] = _index;
codes[bi] = _index;
}
} else {
var k = subHeadKeys[_i4];
var k = subHeadKeys[bi];
for (var j = 0, entryCount = subHeads[k].entryCount; j < entryCount; j++) {
if (subHeads[k].idRangeOffset + j >= glyphs.length) {
_index = 0;
@ -132,7 +191,7 @@ function readWindowsAllCodes(tables, ttf) {
_index = _index + subHeads[k].idDelta;
}
if (_index !== 0 && _index < numGlyphs) {
var _unicode = (_i4 << 8 | j + subHeads[k].firstCode) % 0xffff;
var _unicode = (bi << 8 | j + subHeads[k].firstCode) % 0xffff;
codes[_unicode] = _index;
}
}