From 4990a0f61daca50096905d382b0a7c93472f4cab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B4=AE=E7=94=9F=EF=BC=88=E5=AD=90=E8=99=9A=EF=BC=89?= <2234839456@qq.com> Date: Fri, 10 Apr 2026 08:44:09 +0800 Subject: [PATCH] =?UTF-8?q?=E6=80=A7=E8=83=BD=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/math/bezierCubic2Q2.js | 22 ++-- .../fonteditor-core/lib/ttf/otf2ttfobject.js | 41 ++----- vendor/fonteditor-core/lib/ttf/otfreader.js | 21 ++-- vendor/fonteditor-core/lib/ttf/table/CFF.js | 44 +++++--- vendor/fonteditor-core/lib/ttf/table/OS2.js | 13 +-- .../lib/ttf/table/cmap/sizeof.js | 16 +-- .../lib/ttf/table/glyf/sizeof.js | 1 - .../lib/ttf/table/glyf/write.js | 39 +++---- vendor/fonteditor-core/lib/ttf/table/head.js | 5 +- vendor/fonteditor-core/lib/ttf/table/hhea.js | 5 +- vendor/fonteditor-core/lib/ttf/table/name.js | 19 ++-- vendor/fonteditor-core/lib/ttf/table/table.js | 104 ++++++------------ vendor/fonteditor-core/lib/ttf/ttf.js | 22 ++-- vendor/fonteditor-core/lib/ttf/ttfwriter.js | 8 +- .../lib/ttf/util/optimizettf.js | 47 ++++---- .../lib/ttf/util/otfContours2ttfContours.js | 92 +++++++++++----- vendor/fonteditor-core/lib/ttf/writer.js | 12 +- 17 files changed, 258 insertions(+), 253 deletions(-) diff --git a/vendor/fonteditor-core/lib/math/bezierCubic2Q2.js b/vendor/fonteditor-core/lib/math/bezierCubic2Q2.js index b95661d..ba56844 100644 --- a/vendor/fonteditor-core/lib/math/bezierCubic2Q2.js +++ b/vendor/fonteditor-core/lib/math/bezierCubic2Q2.js @@ -9,6 +9,8 @@ exports.default = bezierCubic2Q2; * @author mengke01(kekee000@gmail.com) * * 改进:递归分割三次贝塞尔直到可精确近似,提高 SSIM + * 优化:返回扁平数组 [control1, endpoint1, control2, endpoint2, ...] + * 减少 3-element 包装数组的分配 */ var MAX_DEPTH = 4; @@ -42,24 +44,30 @@ function cubicToQuads(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, depth, endpoints, cubicToQuads(midx, midy, m123x, m123y, m23x, m23y, p2x, p2y, depth + 1, endpoints, controls); } +/** + * 三次贝塞尔转二次贝塞尔 + * 返回扁平数组: [control1, endpoint1, control2, endpoint2, ...] + * 每对 (control, endpoint) 代表一个二次贝塞尔段 + */ function bezierCubic2Q2(p1, c1, c2, p2) { if (p1.x === c1.x && p1.y === c1.y && c2.x === p2.x && c2.y === p2.y) { - return [[p1, { + return [{ x: (p1.x + p2.x) * 0.5, y: (p1.y + p2.y) * 0.5 - }, p2]]; + }, p2]; } var endpoints = []; var controls = []; cubicToQuads(p1.x, p1.y, c1.x, c1.y, c2.x, c2.y, p2.x, p2.y, 0, endpoints, controls); - var segments = []; - var prev = p1; + var result = new Array(controls.length * 2); + var ri = 0; for (var i = 0, l = controls.length; i < l; i++) { var next = i < endpoints.length ? endpoints[i] : p2; - segments.push([prev, controls[i], next]); - prev = next; + next.onCurve = true; + result[ri++] = controls[i]; + result[ri++] = next; } - return segments; + return result; } diff --git a/vendor/fonteditor-core/lib/ttf/otf2ttfobject.js b/vendor/fonteditor-core/lib/ttf/otf2ttfobject.js index 6f02a74..fed4bb3 100644 --- a/vendor/fonteditor-core/lib/ttf/otf2ttfobject.js +++ b/vendor/fonteditor-core/lib/ttf/otf2ttfobject.js @@ -13,31 +13,6 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de * @author mengke01(kekee000@gmail.com) */ -/** - * 直接遍历 contours 计算包围盒,避免合并数组 - */ -function computeContoursBox(contours) { - var left, right, top, bottom; - var found = false; - for (var ci = 0, cl = contours.length; ci < cl; ci++) { - var contour = contours[ci]; - for (var pi = 0, pl = contour.length; pi < pl; pi++) { - var p = contour[pi]; - if (!found) { - left = right = p.x; - top = bottom = p.y; - found = true; - } else { - if (p.x < left) left = p.x; - else if (p.x > right) right = p.x; - if (p.y < top) top = p.y; - else if (p.y > bottom) bottom = p.y; - } - } - } - return found ? { x: left, y: top, width: right - left, height: bottom - top } : null; -} - /** * otf格式转ttf格式对象 * @@ -57,17 +32,17 @@ function otf2ttfobject(otfBuffer, options) { _error.default.raise(10111); } - // 转换otf轮廓 + // 转换otf轮廓,同时获取包围盒 var glyf = otfObject.glyf; for (var i = 0, l = glyf.length; i < l; i++) { var g = glyf[i]; - g.contours = (0, _otfContours2ttfContours.default)(g.contours); - var box = computeContoursBox(g.contours); - if (box) { - g.xMin = box.x; - g.xMax = box.x + box.width; - g.yMin = box.y; - g.yMax = box.y + box.height; + var result = (0, _otfContours2ttfContours.default)(g.contours); + g.contours = result.contours; + if (result.xMin != null) { + g.xMin = result.xMin; + g.xMax = result.xMax; + g.yMin = result.yMin; + g.yMax = result.yMax; } else { g.xMin = 0; g.xMax = 0; diff --git a/vendor/fonteditor-core/lib/ttf/otfreader.js b/vendor/fonteditor-core/lib/ttf/otfreader.js index 762c118..336ace6 100644 --- a/vendor/fonteditor-core/lib/ttf/otfreader.js +++ b/vendor/fonteditor-core/lib/ttf/otfreader.js @@ -70,13 +70,13 @@ var OTFReader = exports.default = /*#__PURE__*/function () { } font.readOptions = this.options; - // 读取支持的表数据 - Object.keys(_supportOtf.default).forEach(function (tableName) { + /* 优化125: Object.keys+forEach → for...in 循环 */ + for (var tableName in _supportOtf.default) { if (font.tables[tableName]) { var offset = font.tables[tableName].offset; font[tableName] = new _supportOtf.default[tableName](offset).read(reader, font); } - }); + } if (!font.CFF.glyf) { _error.default.raise(10303); } @@ -95,17 +95,17 @@ var OTFReader = exports.default = /*#__PURE__*/function () { var codes = font.cmap; var glyf = font.CFF.glyf; var subsetMap = font.readOptions.subset ? font.subsetMap : null; // 当前ttf的子集列表 - // unicode - Object.keys(codes).forEach(function (c) { + /* 优化125: Object.keys+forEach → for...in 循环 */ + for (var c in codes) { var i = codes[c]; if (subsetMap && !subsetMap[i]) { - return; + continue; } if (!glyf[i].unicode) { glyf[i].unicode = []; } glyf[i].unicode.push(+c); - }); + } /* leftSideBearing / advanceWidth —— 兼容扁平 Int32Array 和对象数组 */ var hmtxData = font.hmtx; @@ -125,11 +125,12 @@ var OTFReader = exports.default = /*#__PURE__*/function () { } // 设置了subsetMap之后需要选取subset中的字形 + /* 优化125: Object.keys+forEach → for...in 循环 */ if (subsetMap) { var subGlyf = []; - Object.keys(subsetMap).forEach(function (i) { - subGlyf.push(glyf[+i]); - }); + for (var si in subsetMap) { + subGlyf.push(glyf[+si]); + } glyf = subGlyf; } font.glyf = glyf; diff --git a/vendor/fonteditor-core/lib/ttf/table/CFF.js b/vendor/fonteditor-core/lib/ttf/table/CFF.js index 366ed7c..27fff92 100644 --- a/vendor/fonteditor-core/lib/ttf/table/CFF.js +++ b/vendor/fonteditor-core/lib/ttf/table/CFF.js @@ -231,8 +231,10 @@ function lookupFD(fdSelect, glyphIndex) { } /** format 3 二分查找 ranges */ var ranges = fdSelect.ranges; + /* 优化154: 缓存 numRanges 避免重复除法 */ + var numRanges = ranges.length / 3; var lo = 0; - var hi = (ranges.length / 3) - 1; + var hi = numRanges - 1; while (lo <= hi) { var mid = (lo + hi) >> 1; var idx = mid * 3; @@ -241,7 +243,7 @@ function lookupFD(fdSelect, glyphIndex) { hi = mid - 1; } else { /** 检查是否在当前 range 内(即 < 下一个 range 的 first) */ - if (mid === (ranges.length / 3) - 1 || glyphIndex < (ranges[idx + 3] | (ranges[idx + 4] << 8))) { + if (mid === numRanges - 1 || glyphIndex < (ranges[idx + 3] | (ranges[idx + 4] << 8))) { return ranges[idx + 2]; } lo = mid + 1; @@ -385,13 +387,14 @@ var _default = exports.default = _table.default.create('cff', [], { /** * 为指定 glyph 构建 per-glyph 的 font 对象 - * CID-keyed 字体使用 FD 对应的 local subrs + * 优化154: CID-keyed 字体预构建 per-FD font 对象缓存,避免每次分配 */ - function getGlyphFont(glyphIndex) { - if (isCID && fdSelect && fdPrivates) { - var fdIdx = lookupFD(fdSelect, glyphIndex); - var fd = fdPrivates[fdIdx]; - return { + var fdGlyphFonts = null; + if (isCID && fdSelect && fdPrivates) { + fdGlyphFonts = new Array(fdPrivates.length); + for (var fi = 0; fi < fdPrivates.length; fi++) { + var fd = fdPrivates[fi]; + fdGlyphFonts[fi] = { subrs: fd.subrs, subrsBias: fd.subrsBias, defaultWidthX: fd.defaultWidthX, @@ -400,6 +403,11 @@ var _default = exports.default = _table.default.create('cff', [], { gsubrsBias: cff.gsubrsBias }; } + } + function getGlyphFont(glyphIndex) { + if (fdGlyphFonts) { + return fdGlyphFonts[lookupFD(fdSelect, glyphIndex)]; + } return cff; } @@ -412,21 +420,25 @@ var _default = exports.default = _table.default.create('cff', [], { }; var codes = font.cmap; - // unicode to index - Object.keys(codes).forEach(function (c) { - if (subset.indexOf(+c) > -1) { - var i = codes[c]; - subsetMap[i] = true; + // unicode to index — 用 Set 替代 indexOf 实现 O(1) 查找 + var subsetSet = {}; + for (var si = 0, sl = subset.length; si < sl; si++) { + subsetSet[subset[si]] = true; + } + for (var c in codes) { + if (subsetSet[c]) { + var ci = codes[c]; + subsetMap[ci] = true; } - }); + } font.subsetMap = subsetMap; - Object.keys(subsetMap).forEach(function (i) { + for (var i in subsetMap) { i = +i; var charstring = readCFFIndexObject(reader, charStringsInfo, i); var glyf = (0, _parseCFFGlyph.default)(charstring, getGlyphFont(i), i); glyf.name = cff.charset[i]; cff.glyf[i] = glyf; - }); + } } // parse all else { diff --git a/vendor/fonteditor-core/lib/ttf/table/OS2.js b/vendor/fonteditor-core/lib/ttf/table/OS2.js index ff76bb3..164be64 100644 --- a/vendor/fonteditor-core/lib/ttf/table/OS2.js +++ b/vendor/fonteditor-core/lib/ttf/table/OS2.js @@ -59,17 +59,6 @@ var _default = exports.default = _table.default.create('OS/2', [['version', _str var hinting = ttf.writeOptions ? ttf.writeOptions.hinting : false; if (metrics) { - var glyfs = ttf.glyf; - /* 计算 minRightSideBearing(需要逐字形遍历 advanceWidth - xMax) */ - var minRightSideBearing = 16384; - for (var ri = 0, rl = glyfs.length; ri < rl; ri++) { - var rg = glyfs[ri]; - if (rg.xMax != null) { - var rsb = rg.advanceWidth - rg.xMax; - if (rsb < minRightSideBearing) minRightSideBearing = rsb; - } - } - ttf['OS/2'].version = 0x4; ttf['OS/2'].achVendID = (ttf['OS/2'].achVendID + ' ').slice(0, 4); ttf['OS/2'].xAvgCharWidth = metrics.xAvgCharWidth; @@ -80,7 +69,7 @@ var _default = exports.default = _table.default.create('OS/2', [['version', _str ttf.hhea.version = ttf.hhea.version || 0x1; ttf.hhea.advanceWidthMax = metrics.advanceWidthMax; ttf.hhea.minLeftSideBearing = metrics.minLeftSideBearing; - ttf.hhea.minRightSideBearing = minRightSideBearing; + ttf.hhea.minRightSideBearing = metrics.minRightSideBearing; ttf.hhea.xMaxExtent = metrics.xMaxExtent; ttf.head.version = ttf.head.version || 0x1; diff --git a/vendor/fonteditor-core/lib/ttf/table/cmap/sizeof.js b/vendor/fonteditor-core/lib/ttf/table/cmap/sizeof.js index 10a0d66..f7501eb 100644 --- a/vendor/fonteditor-core/lib/ttf/table/cmap/sizeof.js +++ b/vendor/fonteditor-core/lib/ttf/table/cmap/sizeof.js @@ -49,11 +49,11 @@ function getFormat0Segment(glyfUnicodes) { var unicodes = []; for (var i = 0, l = glyfUnicodes.length; i < l; i++) { var u = glyfUnicodes[i]; - if (u.unicode !== undefined && u.unicode < 256) { + if (u.unicode < 256) { unicodes.push([u.unicode, u.id]); } } - unicodes.sort(function (a, b) { return a[0] - b[0]; }); + /* 数据已排序,无需再次 sort */ return unicodes; } @@ -85,17 +85,11 @@ function sizeof(ttf) { ttf.support.cmap.hasFormat0 = ttf.support.cmap.format0Segments.length > 0; ttf.support.cmap.format0Size = ttf.support.cmap.hasFormat0 ? 262 : 0; - var hasGLyphsOver2Bytes = false; - for (var gi = 0, gil = unicodes2Bytes.length; gi < gil; gi++) { - if (unicodes2Bytes[gi].unicode > 0xFFFF) { - hasGLyphsOver2Bytes = true; - break; - } - } + /* 优化142: 排序后直接检查最大 unicode,避免单独遍历 */ + var hasGLyphsOver2Bytes = glyfUnicodes.length > 0 && glyfUnicodes[glyfUnicodes.length - 1].unicode > 0xFFFF; if (hasGLyphsOver2Bytes) { ttf.support.cmap.hasGLyphsOver2Bytes = true; - var unicodes4Bytes = glyfUnicodes; - ttf.support.cmap.format12Segments = getSegments(unicodes4Bytes); + ttf.support.cmap.format12Segments = getSegments(glyfUnicodes); ttf.support.cmap.format12Size = 16 + ttf.support.cmap.format12Segments.length * 12; } /** 记录头大小必须动态计算,与 write.js 中的 numRecords 保持一致,否则会导致表偏移错位 */ diff --git a/vendor/fonteditor-core/lib/ttf/table/glyf/sizeof.js b/vendor/fonteditor-core/lib/ttf/table/glyf/sizeof.js index 0287e5d..20fece7 100644 --- a/vendor/fonteditor-core/lib/ttf/table/glyf/sizeof.js +++ b/vendor/fonteditor-core/lib/ttf/table/glyf/sizeof.js @@ -240,7 +240,6 @@ function sizeof(ttf) { glyfSupportArr[i] = glyfSupport; tableSize += size; } - glyfSupportArr.tableSize = tableSize; ttf.head.indexToLocFormat = tableSize > 65536 ? 1 : 0; return tableSize; } diff --git a/vendor/fonteditor-core/lib/ttf/table/glyf/write.js b/vendor/fonteditor-core/lib/ttf/table/glyf/write.js index 73c4dfb..c9fd2d0 100644 --- a/vendor/fonteditor-core/lib/ttf/table/glyf/write.js +++ b/vendor/fonteditor-core/lib/ttf/table/glyf/write.js @@ -22,6 +22,9 @@ function write(writer, ttf) { var glyfSupport = ttf.support.glyf; var glyfs = ttf.glyf; var view = writer.view; + /** 优化141: 复用 Uint8Array 视图,避免每次 set 创建临时视图 */ + var buf = view.buffer; + var vbo = view.byteOffset; var ARG_1_AND_2_ARE_WORDS = _componentFlag.default.ARG_1_AND_2_ARE_WORDS; var ROUND_XY_TO_GRID = _componentFlag.default.ROUND_XY_TO_GRID; var WE_HAVE_A_SCALE = _componentFlag.default.WE_HAVE_A_SCALE; @@ -133,21 +136,18 @@ function write(writer, ttf) { pos += 2; } - /* 优化11+79: flags 使用 Uint8Array.set 批量写入 */ + /* 优化11+79+135: flags 直接 view 写入,避免临时 TypedArray */ var flags = gSupport.flags || []; - if (flags.length > 0) { - var flagsArr = flags instanceof Uint8Array ? flags : new Uint8Array(flags); - new Uint8Array(view.buffer, view.byteOffset + pos, flagsArr.length).set(flagsArr); + for (var fi = 0, fl = flags.length; fi < fl; fi++) { + view.setUint8(pos++, flags[fi]); } - pos += flags.length; - /* 优化21+98+119: xCoord 预编码 Uint8Array 直接 set,或逐个写入 */ - var support = gSupport; - if (support.xEncoded) { - new Uint8Array(view.buffer, view.byteOffset + pos, support.xEncoded.length).set(support.xEncoded); - pos += support.xEncoded.length; + /* 优化21+98+119+141: xCoord 预编码 Uint8Array 直接 set,使用缓存引用 */ + if (gSupport.xEncoded) { + new Uint8Array(buf, vbo + pos, gSupport.xEncoded.length).set(gSupport.xEncoded); + pos += gSupport.xEncoded.length; } else { - var xCoord = support.xCoord || []; + var xCoord = gSupport.xCoord || []; for (var xi = 0, xl = xCoord.length; xi < xl; xi++) { var xv = xCoord[xi]; if (0 <= xv && xv <= 0xFF) { @@ -160,12 +160,12 @@ function write(writer, ttf) { } } - /* 优化21+58+98+119: yCoord 预编码 Uint8Array 直接 set,或逐个写入 */ - if (support.yEncoded) { - new Uint8Array(view.buffer, view.byteOffset + pos, support.yEncoded.length).set(support.yEncoded); - pos += support.yEncoded.length; + /* 优化21+58+98+119+141: yCoord 预编码 Uint8Array 直接 set,使用缓存引用 */ + if (gSupport.yEncoded) { + new Uint8Array(buf, vbo + pos, gSupport.yEncoded.length).set(gSupport.yEncoded); + pos += gSupport.yEncoded.length; } else { - var yCoord = support.yCoord || []; + var yCoord = gSupport.yCoord || []; for (var yi = 0, yl = yCoord.length; yi < yl; yi++) { var yv = yCoord[yi]; if (0 <= yv && yv <= 0xFF) { @@ -179,12 +179,13 @@ function write(writer, ttf) { } } - /* 优化81: 4字节对齐使用 fill(0) 批量填充 */ + /* 优化81: 4字节对齐直接 view 写入,避免临时 TypedArray */ var glyfSize = gSupport.glyfSize; if (glyfSize % 4) { var pad = 4 - glyfSize % 4; - new Uint8Array(view.buffer, view.byteOffset + pos, pad).fill(0); - pos += pad; + if (pad >= 1) view.setUint8(pos++, 0); + if (pad >= 2) view.setUint8(pos++, 0); + if (pad >= 3) view.setUint8(pos++, 0); } writer.offset = pos; diff --git a/vendor/fonteditor-core/lib/ttf/table/head.js b/vendor/fonteditor-core/lib/ttf/table/head.js index cf36d12..fd7ab71 100644 --- a/vendor/fonteditor-core/lib/ttf/table/head.js +++ b/vendor/fonteditor-core/lib/ttf/table/head.js @@ -11,4 +11,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de * @file head表 * @author mengke01(kekee000@gmail.com) */ -var _default = exports.default = _table.default.create('head', [['version', _struct.default.Fixed], ['fontRevision', _struct.default.Fixed], ['checkSumAdjustment', _struct.default.Uint32], ['magickNumber', _struct.default.Uint32], ['flags', _struct.default.Uint16], ['unitsPerEm', _struct.default.Uint16], ['created', _struct.default.LongDateTime], ['modified', _struct.default.LongDateTime], ['xMin', _struct.default.Int16], ['yMin', _struct.default.Int16], ['xMax', _struct.default.Int16], ['yMax', _struct.default.Int16], ['macStyle', _struct.default.Uint16], ['lowestRecPPEM', _struct.default.Uint16], ['fontDirectionHint', _struct.default.Int16], ['indexToLocFormat', _struct.default.Int16], ['glyphDataFormat', _struct.default.Int16]]); \ No newline at end of file +var _default = exports.default = _table.default.create('head', [['version', _struct.default.Fixed], ['fontRevision', _struct.default.Fixed], ['checkSumAdjustment', _struct.default.Uint32], ['magickNumber', _struct.default.Uint32], ['flags', _struct.default.Uint16], ['unitsPerEm', _struct.default.Uint16], ['created', _struct.default.LongDateTime], ['modified', _struct.default.LongDateTime], ['xMin', _struct.default.Int16], ['yMin', _struct.default.Int16], ['xMax', _struct.default.Int16], ['yMax', _struct.default.Int16], ['macStyle', _struct.default.Uint16], ['lowestRecPPEM', _struct.default.Uint16], ['fontDirectionHint', _struct.default.Int16], ['indexToLocFormat', _struct.default.Int16], ['glyphDataFormat', _struct.default.Int16]], { + /* 优化148: head 表固定 54 字节,跳过 switch 循环 */ + size: function () { return 54; } +}); \ No newline at end of file diff --git a/vendor/fonteditor-core/lib/ttf/table/hhea.js b/vendor/fonteditor-core/lib/ttf/table/hhea.js index 5c6ec6c..418d476 100644 --- a/vendor/fonteditor-core/lib/ttf/table/hhea.js +++ b/vendor/fonteditor-core/lib/ttf/table/hhea.js @@ -13,4 +13,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de * * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6hhea.html */ -var _default = exports.default = _table.default.create('hhea', [['version', _struct.default.Fixed], ['ascent', _struct.default.Int16], ['descent', _struct.default.Int16], ['lineGap', _struct.default.Int16], ['advanceWidthMax', _struct.default.Uint16], ['minLeftSideBearing', _struct.default.Int16], ['minRightSideBearing', _struct.default.Int16], ['xMaxExtent', _struct.default.Int16], ['caretSlopeRise', _struct.default.Int16], ['caretSlopeRun', _struct.default.Int16], ['caretOffset', _struct.default.Int16], ['reserved0', _struct.default.Int16], ['reserved1', _struct.default.Int16], ['reserved2', _struct.default.Int16], ['reserved3', _struct.default.Int16], ['metricDataFormat', _struct.default.Int16], ['numOfLongHorMetrics', _struct.default.Uint16]]); \ No newline at end of file +var _default = exports.default = _table.default.create('hhea', [['version', _struct.default.Fixed], ['ascent', _struct.default.Int16], ['descent', _struct.default.Int16], ['lineGap', _struct.default.Int16], ['advanceWidthMax', _struct.default.Uint16], ['minLeftSideBearing', _struct.default.Int16], ['minRightSideBearing', _struct.default.Int16], ['xMaxExtent', _struct.default.Int16], ['caretSlopeRise', _struct.default.Int16], ['caretSlopeRun', _struct.default.Int16], ['caretOffset', _struct.default.Int16], ['reserved0', _struct.default.Int16], ['reserved1', _struct.default.Int16], ['reserved2', _struct.default.Int16], ['reserved3', _struct.default.Int16], ['metricDataFormat', _struct.default.Int16], ['numOfLongHorMetrics', _struct.default.Uint16]], { + /* 优化148: hhea 表固定 36 字节,跳过 switch 循环 */ + size: function () { return 36; } +}); \ No newline at end of file diff --git a/vendor/fonteditor-core/lib/ttf/table/name.js b/vendor/fonteditor-core/lib/ttf/table/name.js index 393ada8..eb795f0 100644 --- a/vendor/fonteditor-core/lib/ttf/table/name.js +++ b/vendor/fonteditor-core/lib/ttf/table/name.js @@ -102,7 +102,7 @@ var _default = exports.default = _table.default.create('name', [], { // 这里为了简化书写,仅支持英文编码字符, // 中文编码字符将被转化成url encode var size = 6; - Object.keys(names).forEach(function (name) { + for (var name in names) { var id = _nameId.default.names[name]; var utf8Bytes = _string.default.toUTF8Bytes(names[name]); var usc2Bytes = _string.default.toUCS2Bytes(names[name]); @@ -128,19 +128,14 @@ var _default = exports.default = _table.default.create('name', [], { // 子表大小 size += 12 * 2 + utf8Bytes.length + usc2Bytes.length; } - }); + } var namingOrder = ['platform', 'encoding', 'language', 'nameId']; nameRecordTbl = nameRecordTbl.sort(function (a, b) { - var l = 0; - namingOrder.some(function (name) { - var o = a[name] - b[name]; - if (o) { - l = o; - return true; - } - return false; - }); - return l; + for (var ni = 0; ni < 4; ni++) { + var o = a[namingOrder[ni]] - b[namingOrder[ni]]; + if (o) return o; + } + return 0; }); // 保存预处理信息 diff --git a/vendor/fonteditor-core/lib/ttf/table/table.js b/vendor/fonteditor-core/lib/ttf/table/table.js index 15d6ce6..560f6af 100644 --- a/vendor/fonteditor-core/lib/ttf/table/table.js +++ b/vendor/fonteditor-core/lib/ttf/table/table.js @@ -29,7 +29,9 @@ function read(reader) { reader.seek(offset); } var me = this; - this.struct.forEach(function (item) { + var struct = this.struct; + for (var si = 0, sl = struct.length; si < sl; si++) { + var item = struct[si]; var name = item[0]; var type = item[1]; var typeName = null; @@ -61,7 +63,7 @@ function read(reader) { default: _error.default.raise(10003, name, type); } - }); + } return this.valueOf(); } @@ -78,39 +80,26 @@ function write(writer, ttf) { if (!table) { _error.default.raise(10203, this.name); } - this.struct.forEach(function (item) { + var struct = this.struct; + /* 优化152: 直接分发到 writer 方法,消除 string 类型名中间层双重 switch */ + for (var si = 0, sl = struct.length; si < sl; si++) { + var item = struct[si]; var name = item[0]; - var type = item[1]; - var typeName = null; - switch (type) { - case _struct.default.Int8: - case _struct.default.Uint8: - case _struct.default.Int16: - case _struct.default.Uint16: - case _struct.default.Int32: - case _struct.default.Uint32: - typeName = _struct.default.names[type]; - writer.write(typeName, table[name]); - break; - case _struct.default.Fixed: - writer.writeFixed(table[name]); - break; - case _struct.default.LongDateTime: - writer.writeLongDateTime(table[name]); - break; - case _struct.default.Bytes: - writer.writeBytes(table[name], item[2] || 0); - break; - case _struct.default.Char: - writer.writeChar(table[name]); - break; - case _struct.default.String: - writer.writeString(table[name], item[2] || 0); - break; - default: - _error.default.raise(10003, name, type); + switch (item[1]) { + case 1: writer.write('Int8', table[name]); break; + case 2: writer.write('Uint8', table[name]); break; + case 3: writer.write('Int16', table[name]); break; + case 4: writer.write('Uint16', table[name]); break; + case 5: writer.write('Int32', table[name]); break; + case 6: writer.write('Uint32', table[name]); break; + case 7: writer.writeFixed(table[name]); break; + case 12: writer.writeLongDateTime(table[name]); break; + case 13: writer.writeChar(table[name]); break; + case 14: writer.writeString(table[name], item[2] || 0); break; + case 15: writer.writeBytes(table[name], item[2] || 0); break; + default: _error.default.raise(10003, name, item[1]); } - }); + } return writer; } @@ -120,40 +109,18 @@ function write(writer, ttf) { * @param {string} name 表名 * @return {number} 表大小 */ +/* 优化152: 类型大小查找表,替代 switch 循环 */ +var TYPE_SIZES = [0, 1, 1, 2, 2, 4, 4, 4, 0, 0, 0, 2, 8, 1, 0, 0, 0, 0, 0, 0, 3]; + function size() { var sz = 0; - this.struct.forEach(function (item) { - var type = item[1]; - switch (type) { - case _struct.default.Int8: - case _struct.default.Uint8: - sz += 1; - break; - case _struct.default.Int16: - case _struct.default.Uint16: - sz += 2; - break; - case _struct.default.Int32: - case _struct.default.Uint32: - case _struct.default.Fixed: - sz += 4; - break; - case _struct.default.LongDateTime: - sz += 8; - break; - case _struct.default.Bytes: - sz += item[2] || 0; - break; - case _struct.default.Char: - sz += 1; - break; - case _struct.default.String: - sz += item[2] || 0; - break; - default: - _error.default.raise(10003, name, type); - } - }); + var struct = this.struct; + for (var si = 0, sl = struct.length; si < sl; si++) { + var item = struct[si]; + var t = item[1]; + /* Bytes/String 使用 item[2] 指定长度,其余查表 */ + sz += (t === 15 || t === 14) ? (item[2] || 0) : TYPE_SIZES[t]; + } return sz; } @@ -165,9 +132,10 @@ function size() { function valueOf() { var val = {}; var me = this; - this.struct.forEach(function (item) { - val[item[0]] = me[item[0]]; - }); + var struct = this.struct; + for (var si = 0, sl = struct.length; si < sl; si++) { + val[struct[si][0]] = me[struct[si][0]]; + } return val; } var _default = exports.default = { diff --git a/vendor/fonteditor-core/lib/ttf/ttf.js b/vendor/fonteditor-core/lib/ttf/ttf.js index ffecc30..b4d2e56 100644 --- a/vendor/fonteditor-core/lib/ttf/ttf.js +++ b/vendor/fonteditor-core/lib/ttf/ttf.js @@ -312,8 +312,10 @@ var TTF = exports.default = /*#__PURE__*/function () { value: function removeGlyf(indexList) { var glyf = this.ttf.glyf; var removed = []; + /* 优化147: 用 Set 替代 indexOf,O(N+M) 替代 O(N*M) */ + var indexSet = new Set(indexList); for (var i = glyf.length - 1; i >= 0; i--) { - if (indexList.indexOf(i) >= 0) { + if (indexSet.has(i)) { removed.push(glyf[i]); glyf.splice(i, 1); } @@ -664,14 +666,16 @@ var TTF = exports.default = /*#__PURE__*/function () { return -2; } var notdef = glyf.shift(); - /* 优化113: unicode 已在 optimizettf 中排序,最小值始终是 unicode[0] */ - glyf.sort(function (a, b) { - var aU = a.unicode; - var bU = b.unicode; - if (!aU || !aU.length) return bU && bU.length ? 1 : 0; - if (!bU || !bU.length) return -1; - return aU[0] - bU[0]; - }); + /* 优化113+146: unicode 已在 optimizettf 中排序,跳过冗余 sort() */ + if (!this.ttf._unicodeSorted) { + glyf.sort(function (a, b) { + var aU = a.unicode; + var bU = b.unicode; + if (!aU || !aU.length) return bU && bU.length ? 1 : 0; + if (!bU || !bU.length) return -1; + return aU[0] - bU[0]; + }); + } glyf.unshift(notdef); return glyf; } diff --git a/vendor/fonteditor-core/lib/ttf/ttfwriter.js b/vendor/fonteditor-core/lib/ttf/ttfwriter.js index bdd2e7c..de2886e 100644 --- a/vendor/fonteditor-core/lib/ttf/ttfwriter.js +++ b/vendor/fonteditor-core/lib/ttf/ttfwriter.js @@ -55,10 +55,9 @@ var TTFWriter = exports.default = /*#__PURE__*/function () { if (!ttf.head.modified) { ttf.head.modified = ttf.head.created; } - var checkUnicodeRepeat = {}; - - /* 优化112: optimizettf 已排序 unicode 并检查重复,跳过冗余工作 */ + /* 优化112+145: optimizettf 已排序 unicode 并检查重复,跳过冗余工作;延迟分配 checkUnicodeRepeat */ if (!ttf._unicodeSorted) { + var checkUnicodeRepeat = {}; var glyfs = ttf.glyf; for (var index = 0, gl = glyfs.length; index < gl; index++) { var glyf = glyfs[index]; @@ -189,7 +188,8 @@ var TTFWriter = exports.default = /*#__PURE__*/function () { ttf.writeOptions.writeZeroContoursGlyfData = !!this.options.writeZeroContoursGlyfData; ttf.writeOptions.hinting = !!this.options.hinting; ttf.writeOptions.kerning = !!this.options.kerning; - ttf.writeOptions.tables = tables.sort(); + /* 优化144: SUPPORT_TABLES 已有序,hint/kern 表名按字母序追加,无需 sort() */ + ttf.writeOptions.tables = tables; } }, { key: "write", diff --git a/vendor/fonteditor-core/lib/ttf/util/optimizettf.js b/vendor/fonteditor-core/lib/ttf/util/optimizettf.js index d39e3fe..5b2edec 100644 --- a/vendor/fonteditor-core/lib/ttf/util/optimizettf.js +++ b/vendor/fonteditor-core/lib/ttf/util/optimizettf.js @@ -6,7 +6,6 @@ Object.defineProperty(exports, "__esModule", { exports.default = optimizettf; exports.ceilReduceAndSizeFlat = ceilReduceAndSizeFlat; var _reduceGlyf = _interopRequireDefault(require("./reduceGlyf")); -var _pathCeil = _interopRequireDefault(require("../../graphics/pathCeil")); var _glyFlag = _interopRequireDefault(require("../enum/glyFlag")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /** @@ -134,12 +133,14 @@ function ceilReduceAndSizeFromTypedArrays(glyf, sharedXBuf, sharedYBuf) { delete glyf._yArr; delete glyf._flags; delete glyf.endPtsOfContours; + + return { sharedXBuf: sharedXBuf, sharedYBuf: sharedYBuf }; } /** - * 优化84+98: 合并 ceil+reduce+flagsAndSize 为单次遍历,预编码 x/y 为 Uint8Array + * 优化84+98+149: 合并 ceil+reduce+flagsAndSize 为单次遍历,使用共享 buffer 池 */ -function ceilReduceAndSizeFlat(glyf) { +function ceilReduceAndSizeFlat(glyf, sharedXBuf, sharedYBuf) { var contours = glyf.contours; /* 优化91: 跳过 reducePathFlat */ for (var j = contours.length - 1; j >= 0; j--) { @@ -149,11 +150,11 @@ function ceilReduceAndSizeFlat(glyf) { } if (0 === contours.length) { delete glyf.contours; - return; + return { sharedXBuf: sharedXBuf, sharedYBuf: sharedYBuf }; } if (glyf._precomputedGlyfSupport) { - return; + return { sharedXBuf: sharedXBuf, sharedYBuf: sharedYBuf }; } var ONCURVE = _glyFlag.default.ONCURVE; @@ -175,9 +176,10 @@ function ceilReduceAndSizeFlat(glyf) { var repeatPoint = -1; var encodedCoordSize = 0; - /* 优化98: 预编码 x/y 坐标 buffer */ - var xCoordBuf = new Uint8Array(totalPoints * 2); - var yCoordBuf = new Uint8Array(totalPoints * 2); + /* 优化149: 复用共享 buffer,仅在 buffer 不够大时才分配新的 */ + var neededSize = totalPoints * 2; + var xCoordBuf = sharedXBuf.length >= neededSize ? sharedXBuf : new Uint8Array(neededSize); + var yCoordBuf = sharedYBuf.length >= neededSize ? sharedYBuf : new Uint8Array(neededSize); var xbi = 0, ybi = 0; for (var j = 0, cl2 = contours.length; j < cl2; j++) { @@ -245,8 +247,13 @@ function ceilReduceAndSizeFlat(glyf) { } flagsC.length = fi; - var xEncoded = xCoordBuf.buffer.slice(0, xbi); - var yEncoded = yCoordBuf.buffer.slice(0, ybi); + /* 优化149: 存储 Uint8Array 视图,与 ceilReduceAndSizeFromTypedArrays 一致 */ + var xEncoded = new Uint8Array(xCoordBuf.buffer.slice(0, xbi)); + var yEncoded = new Uint8Array(yCoordBuf.buffer.slice(0, ybi)); + + /* 更新共享 buffer 引用(如果分配了新的更大的 buffer) */ + if (xCoordBuf !== sharedXBuf) sharedXBuf = xCoordBuf; + if (yCoordBuf !== sharedYBuf) sharedYBuf = yCoordBuf; glyf._precomputedGlyfSupport = { flags: flagsC, @@ -254,6 +261,8 @@ function ceilReduceAndSizeFlat(glyf) { xEncoded: xEncoded, yEncoded: yEncoded }; + + return { sharedXBuf: sharedXBuf, sharedYBuf: sharedYBuf }; } /** @@ -273,15 +282,15 @@ function optimizettf(ttf) { var m_firstChar = 0x10FFFF, m_lastChar = -1; var m_maxPoints = 0, m_maxContours = 0; - /* 优化120: 合并 maxPoints 扫描到主循环 */ + /* 优化120+150: 预扫描 _xArr 最大点数以一次性分配共享 buffer */ var maxBufPoints = 0; for (var pi = 0, pl = glyfs.length; pi < pl; pi++) { if (glyfs[pi]._xArr && glyfs[pi]._xArr.length > maxBufPoints) { maxBufPoints = glyfs[pi]._xArr.length; } } - var sharedXBuf = new Uint8Array(maxBufPoints * 2); - var sharedYBuf = new Uint8Array(maxBufPoints * 2); + var sharedXBuf = new Uint8Array(maxBufPoints * 2 || 256); + var sharedYBuf = new Uint8Array(maxBufPoints * 2 || 256); for (var index = 0, gl = glyfs.length; index < gl; index++) { var glyf = glyfs[index]; @@ -308,9 +317,10 @@ function optimizettf(ttf) { } } if (!glyf.compound) { - /* 优化94+116: 优先从 TypedArray 构建 contour + precompute,使用共享 buffer */ + /* 优化94+116+149+150: 优先从 TypedArray 构建 contour + precompute,使用共享 buffer 池 */ if (glyf._xArr) { - ceilReduceAndSizeFromTypedArrays(glyf, sharedXBuf, sharedYBuf); + var bufResult = ceilReduceAndSizeFromTypedArrays(glyf, sharedXBuf, sharedYBuf); + if (bufResult) { sharedXBuf = bufResult.sharedXBuf; sharedYBuf = bufResult.sharedYBuf; } /* 优化120: 从 _numContours/_totalPoints 收集 metrics */ if (glyf._numContours > 0) { if (glyf._numContours > m_maxContours) m_maxContours = glyf._numContours; @@ -318,7 +328,8 @@ function optimizettf(ttf) { } } else if (glyf.contours) { if (glyf._flatContours) { - ceilReduceAndSizeFlat(glyf); + var flatResult = ceilReduceAndSizeFlat(glyf, sharedXBuf, sharedYBuf); + if (flatResult) { sharedXBuf = flatResult.sharedXBuf; sharedYBuf = flatResult.sharedYBuf; } } else { /* 对象 contours 格式也需要收集 maxPoints/maxContours */ var numC = glyf.contours.length; @@ -330,9 +341,7 @@ function optimizettf(ttf) { } if (totalPts > m_maxPoints) m_maxPoints = totalPts; } - glyf.contours.forEach(function (contour) { - (0, _pathCeil.default)(contour); - }); + /* 优化153: pathCeil 已在 transformContour 中完成,无需重复调用 */ (0, _reduceGlyf.default)(glyf); } } diff --git a/vendor/fonteditor-core/lib/ttf/util/otfContours2ttfContours.js b/vendor/fonteditor-core/lib/ttf/util/otfContours2ttfContours.js index 42bf55b..30b2900 100644 --- a/vendor/fonteditor-core/lib/ttf/util/otfContours2ttfContours.js +++ b/vendor/fonteditor-core/lib/ttf/util/otfContours2ttfContours.js @@ -5,7 +5,6 @@ Object.defineProperty(exports, "__esModule", { }); exports.default = otfContours2ttfContours; var _bezierCubic2Q = _interopRequireDefault(require("../../math/bezierCubic2Q2")); -var _pathCeil = _interopRequireDefault(require("../../graphics/pathCeil")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /** * @file otf轮廓转ttf轮廓 @@ -21,7 +20,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de /** * 将 CFF contour 转换为标准 [onCurve, offCurve, offCurve, onCurve, ...] 序列 * 处理隐含端点和连续 offCurve 点的情况 - * 优化:原地操作 otfContour,减少中间数组分配 + * 优化:构建新数组替代 unshift/splice,避免 O(n^2) */ function normalizeContour(otfContour) { var len = otfContour.length; @@ -47,20 +46,30 @@ function normalizeContour(otfContour) { len = otfContour.length; } - /** 处理连续的 offCurve 点:在它们之间插入隐含端点 */ - for (var i = 0; i < len; i++) { - var p = otfContour[i]; - if (!p.onCurve && i + 1 < len && !otfContour[i + 1].onCurve) { - var next = otfContour[i + 1]; - otfContour.splice(i + 1, 0, { - x: (p.x + next.x) * 0.5, - y: (p.y + next.y) * 0.5, - onCurve: true - }); - len++; - i++; + /** 处理连续的 offCurve 点:在它们之间插入隐含端点 + * 优化:构建新数组替代 splice,将 O(n^2) 降为 O(n) */ + var hasConsecutive = false; + for (var i = 0; i < len - 1; i++) { + if (!otfContour[i].onCurve && !otfContour[i + 1].onCurve) { + hasConsecutive = true; + break; } } + if (hasConsecutive) { + var result = []; + for (var i = 0; i < len; i++) { + result.push(otfContour[i]); + if (!otfContour[i].onCurve && i + 1 < len && !otfContour[i + 1].onCurve) { + var next = otfContour[i + 1]; + result.push({ + x: (otfContour[i].x + next.x) * 0.5, + y: (otfContour[i].y + next.y) * 0.5, + onCurve: true + }); + } + } + return result; + } return otfContour; } @@ -68,20 +77,22 @@ function normalizeContour(otfContour) { /** * 转换已标准化的轮廓(onCurve/offCurve 严格交替) * 模式:onCurve, offCurve, offCurve, onCurve, offCurve, offCurve, ... + * 优化153: 在构建 contour 时同时做 Math.round,消除 pathCeil 的二次遍历 */ function transformContour(otfContour) { var normalized = normalizeContour(otfContour); if (normalized.length < 2) return []; var contour = []; - contour.push(normalized[0]); + var p0 = normalized[0]; + contour.push({ x: Math.round(p0.x), y: Math.round(p0.y), onCurve: true }); var i = 1; while (i < normalized.length) { var cur = normalized[i]; if (cur.onCurve) { /** 线段:直接添加 onCurve 端点 */ - contour.push(cur); + contour.push({ x: Math.round(cur.x), y: Math.round(cur.y), onCurve: true }); i++; } else { /** cubic bezier:offCurve, offCurve, onCurve */ @@ -104,32 +115,61 @@ function transformContour(otfContour) { } var bezierArray = (0, _bezierCubic2Q.default)(contour[contour.length - 1], c1, c2 || c1, end); - for (var bi = 0, bl = bezierArray.length; bi < bl; bi++) { - bezierArray[bi][2].onCurve = true; - contour.push(bezierArray[bi][1]); - contour.push(bezierArray[bi][2]); + for (var bi = 0, bl = bezierArray.length; bi < bl; bi += 2) { + var ctrl = bezierArray[bi]; + var ep = bezierArray[bi + 1]; + ep.x = Math.round(ep.x); + ep.y = Math.round(ep.y); + ep.onCurve = true; + ctrl.x = Math.round(ctrl.x); + ctrl.y = Math.round(ctrl.y); + contour.push(ctrl); + contour.push(ep); } } } - return (0, _pathCeil.default)(contour); + return contour; } /** - * otf轮廓转ttf轮廓 + * otf轮廓转ttf轮廓,同时计算包围盒 * * @param {Array} otfContours otf轮廓数组 - * @return {Array} ttf轮廓 + * @return {{contours: Array, xMin: number, yMin: number, xMax: number, yMax: number}} 转换结果和包围盒 */ function otfContours2ttfContours(otfContours) { if (!otfContours || !otfContours.length) { - return otfContours; + return { contours: otfContours }; } var contours = []; + var left, right, top, bottom; + var found = false; for (var i = 0, l = otfContours.length; i < l; i++) { if (otfContours[i][0]) { - contours.push(transformContour(otfContours[i])); + var contour = transformContour(otfContours[i]); + contours.push(contour); + /** 同时计算包围盒,避免 otf2ttfobject 二次遍历 */ + for (var ci = 0, cl = contour.length; ci < cl; ci++) { + var p = contour[ci]; + if (!found) { + left = right = p.x; + top = bottom = p.y; + found = true; + } else { + if (p.x < left) left = p.x; + else if (p.x > right) right = p.x; + if (p.y < top) top = p.y; + else if (p.y > bottom) bottom = p.y; + } + } } } - return contours; + return { + contours: contours, + xMin: left, + yMin: top, + xMax: right, + yMax: bottom + }; } diff --git a/vendor/fonteditor-core/lib/ttf/writer.js b/vendor/fonteditor-core/lib/ttf/writer.js index cbefa1c..8be97bd 100644 --- a/vendor/fonteditor-core/lib/ttf/writer.js +++ b/vendor/fonteditor-core/lib/ttf/writer.js @@ -111,11 +111,15 @@ var Writer = /*#__PURE__*/function () { if (length < 0 || offset + length > this.length) { _error.default.raise(10002, this.length, offset + length); } - /* 优化5: Uint8Array.set 批量写入,替代逐字节循环 */ + /* 优化151: 缓存 buffer 引用,避免每次创建 Uint8Array 视图 */ + var vBuf = this.view.buffer; + var vOff = this.view.byteOffset + offset; if (value instanceof ArrayBuffer) { - new Uint8Array(this.view.buffer, this.view.byteOffset + offset, length).set(new Uint8Array(value, 0, length)); + new Uint8Array(vBuf, vOff, length).set(new Uint8Array(value, 0, length)); + } else if (value instanceof Uint8Array) { + new Uint8Array(vBuf, vOff, length).set(value); } else { - new Uint8Array(this.view.buffer, this.view.byteOffset + offset, length).set(value instanceof Uint8Array ? value : new Uint8Array(value), 0); + new Uint8Array(vBuf, vOff, length).set(new Uint8Array(value)); } this.offset = offset + length; return this; @@ -169,7 +173,7 @@ var Writer = /*#__PURE__*/function () { /* 优化28: 直接 view 写入,替代逐字节 writeUint8/writeUint16 */ var pos = offset; for (var i = 0, l = str.length, charCode; i < l; ++i) { - charCode = str.charCodeAt(i) || 0; + charCode = str.charCodeAt(i); if (charCode > 127) { this.view.setUint16(pos, charCode, this.littleEndian); pos += 2;