diff --git a/task.md b/task.md index fa96270..cc62381 100644 --- a/task.md +++ b/task.md @@ -1,6 +1,6 @@ /loop 持续优化字体子集化性能,可以大胆放开手脚的去做,但是优化完一定要通过`pnpx tsx ./基准测试.test.ts`。中途不要切换到其他模式,比如计划模式也不要询问我,你直接做就行了,请你持续的去优化,不要去询问我,不要去中断,好吧 -啊,你每次优化能不能把基准测试结果文档保存在本地目录下,这样我方便查看。你的文档中应该在每个重大节点更新基准测试结果,这样我能方便看到你使用了哪些优化方法,得到了什么样的优化效果。 +把基准测试结果文档保存在本地 benchmark_results/ ,这样我方便查看。你的文档中应该在每个重大节点更新基准测试结果(benchmark_results/OPTIMIZATION_LOG.md),这样我能方便看到你使用了哪些优化方法,得到了什么样的优化效果。 diff --git a/vendor/fonteditor-core/lib/ttf/table/cmap/sizeof.js b/vendor/fonteditor-core/lib/ttf/table/cmap/sizeof.js index 5e1fea3..7fa6048 100644 --- a/vendor/fonteditor-core/lib/ttf/table/cmap/sizeof.js +++ b/vendor/fonteditor-core/lib/ttf/table/cmap/sizeof.js @@ -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; -} \ No newline at end of file +} diff --git a/vendor/fonteditor-core/lib/ttf/table/cmap/write.js b/vendor/fonteditor-core/lib/ttf/table/cmap/write.js index 14cc6c0..94d529a 100644 --- a/vendor/fonteditor-core/lib/ttf/table/cmap/write.js +++ b/vendor/fonteditor-core/lib/ttf/table/cmap/write.js @@ -9,156 +9,111 @@ 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) { writeSubTable12(writer, ttf.support.cmap.format12Segments); } return writer; -} \ No newline at end of file +} diff --git a/vendor/fonteditor-core/lib/ttf/table/glyf.js b/vendor/fonteditor-core/lib/ttf/table/glyf.js index 4e10d95..fb1d12e 100644 --- a/vendor/fonteditor-core/lib/ttf/table/glyf.js +++ b/vendor/fonteditor-core/lib/ttf/table/glyf.js @@ -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 转为 Set,O(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]); } diff --git a/vendor/fonteditor-core/lib/ttf/util/compound2simpleglyf.js b/vendor/fonteditor-core/lib/ttf/util/compound2simpleglyf.js index e9d9a7f..505eab7 100644 --- a/vendor/fonteditor-core/lib/ttf/util/compound2simpleglyf.js +++ b/vendor/fonteditor-core/lib/ttf/util/compound2simpleglyf.js @@ -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: 检测扁平格式并设置标记 */ diff --git a/vendor/fonteditor-core/lib/ttf/util/optimizettf.js b/vendor/fonteditor-core/lib/ttf/util/optimizettf.js index d9846c3..9af4caa 100644 --- a/vendor/fonteditor-core/lib/ttf/util/optimizettf.js +++ b/vendor/fonteditor-core/lib/ttf/util/optimizettf.js @@ -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; diff --git a/vendor/fonteditor-core/lib/ttf/util/readWindowsAllCodes.js b/vendor/fonteditor-core/lib/ttf/util/readWindowsAllCodes.js index 2e0b0c9..5db710f 100644 --- a/vendor/fonteditor-core/lib/ttf/util/readWindowsAllCodes.js +++ b/vendor/fonteditor-core/lib/ttf/util/readWindowsAllCodes.js @@ -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,49 +66,78 @@ 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]) { + codes[i] = format0.glyphIdArray[i]; + } + } + } + if (format14) { + for (var vi = 0, vl = format14.groups.length; vi < vl; vi++) { + var vg = format14.groups[vi]; + if (vg.unicode) { + codes[vg.unicode] = vg.glyphId; + } + } + } + + return codes; + } + + /* 非subset模式 - 全量展开(原始逻辑) */ if (format0) { - for (var i = 0, l = format0.glyphIdArray.length; i < l; i++) { - if (format0.glyphIdArray[i]) { - codes[i] = format0.glyphIdArray[i]; + for (var i2 = 0, l2 = format0.glyphIdArray.length; i2 < l2; i2++) { + if (format0.glyphIdArray[i2]) { + codes[i2] = format0.glyphIdArray[i2]; } } } - - // 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 vi2 = 0, vl2 = format14.groups.length; vi2 < vl2; vi2++) { + var vg2 = format14.groups[vi2]; + if (vg2.unicode) { + codes[vg2.unicode] = vg2.glyphId; } } } - - // 读取format12表 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; } } @@ -140,4 +199,4 @@ function readWindowsAllCodes(tables, ttf) { } } return codes; -} \ No newline at end of file +}