glm2.5 优化,暂存

This commit is contained in:
崮生(子虚) 2026-06-13 20:19:57 +08:00
parent 13787dfd22
commit 4a6c7e64e1
9 changed files with 650 additions and 192 deletions

View File

@ -21,6 +21,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
* @return {Object} ttfObject对象
*/
function otf2ttfobject(otfBuffer, options) {
var __t0 = process.hrtime.bigint();
var otfObject;
if (otfBuffer instanceof ArrayBuffer) {
var otfReader = new _otfreader.default(options);
@ -56,5 +57,7 @@ function otf2ttfobject(otfBuffer, options) {
/** 优化245: delete → null 赋值,避免 V8 隐藏类转换 */
otfObject.CFF = null;
otfObject.VORG = null;
var __t1 = process.hrtime.bigint();
console.error('otf2ttfobject: ' + Number(__t1 - __t0) / 1e6 + 'ms');
return otfObject;
}

View File

@ -41,6 +41,7 @@ var OTFReader = exports.default = /*#__PURE__*/function () {
return _createClass(OTFReader, [{
key: "readBuffer",
value: function readBuffer(buffer) {
var __t0 = process.hrtime.bigint();
var reader = new _reader.default(buffer, 0, buffer.byteLength, false);
var font = {};
@ -84,6 +85,8 @@ var OTFReader = exports.default = /*#__PURE__*/function () {
_error.default.raise(10303);
}
reader.dispose();
var __t1 = process.hrtime.bigint();
console.error('OTFREADER.readBuffer: ' + Number(__t1 - __t0) / 1e6 + 'ms');
return font;
}
@ -95,25 +98,42 @@ var OTFReader = exports.default = /*#__PURE__*/function () {
}, {
key: "resolveGlyf",
value: function resolveGlyf(font) {
var __t0 = process.hrtime.bigint();
var codes = font.cmap;
var glyf = font.CFF.glyf;
var subsetMap = font.readOptions.subset ? font.subsetMap : null;
/** 优化290: subsetMap 检查提到循环外,消除每次迭代的分支判断 */
var cmapKeys = Object.keys(codes);
if (subsetMap) {
for (var ki = 0, kl = cmapKeys.length; ki < kl; ki++) {
var c = cmapKeys[ki];
var i = codes[c];
if (!subsetMap[i]) continue;
if (!glyf[i].unicode) glyf[i].unicode = [];
glyf[i].unicode.push(+c);
/**
* 优化298: subset 模式下只遍历 subset unicode 列表O(S)避免全 cmap 遍历O(U)
* 思源等大 CID 字体 cmap 有数万映射 Object.keys + 全量循环开销显著
*/
if (subsetMap && font.readOptions.subset && font.readOptions.subset.length > 0) {
var subsetList = font.readOptions.subset;
for (var si = 0, sl = subsetList.length; si < sl; si++) {
var cp = subsetList[si];
var gid = codes[cp];
if (gid === undefined) continue;
if (!subsetMap[gid]) continue;
if (!glyf[gid].unicode) glyf[gid].unicode = [];
glyf[gid].unicode.push(cp);
}
} else {
for (var ki = 0, kl = cmapKeys.length; ki < kl; ki++) {
var c = cmapKeys[ki];
var i = codes[c];
if (!glyf[i].unicode) glyf[i].unicode = [];
glyf[i].unicode.push(+c);
/** 优化290: subsetMap 检查提到循环外,消除每次迭代的分支判断 */
var cmapKeys = Object.keys(codes);
if (subsetMap) {
for (var ki = 0, kl = cmapKeys.length; ki < kl; ki++) {
var c = cmapKeys[ki];
var i = codes[c];
if (!subsetMap[i]) continue;
if (!glyf[i].unicode) glyf[i].unicode = [];
glyf[i].unicode.push(+c);
}
} else {
for (var ki2 = 0, kl2 = cmapKeys.length; ki2 < kl2; ki2++) {
var c2 = cmapKeys[ki2];
var i2 = codes[c2];
if (!glyf[i2].unicode) glyf[i2].unicode = [];
glyf[i2].unicode.push(+c2);
}
}
}
@ -121,8 +141,25 @@ var OTFReader = exports.default = /*#__PURE__*/function () {
var hmtxData = font.hmtx;
var isFlat = hmtxData instanceof Int32Array;
var hLen = isFlat ? hmtxData.length / 2 : hmtxData.length;
/** 优化290: subsetMap 检查提到循环外,消除每次迭代的分支判断 */
if (subsetMap) {
/**
* 优化298: subset 模式下遍历 subsetGidsO(S)避免全 hmtx 遍历O(U)
*/
if (subsetMap && font.subsetGids) {
var sGids = font.subsetGids;
if (isFlat) {
for (var gi = 0, gl = sGids.length; gi < gl; gi++) {
var gid2 = sGids[gi];
glyf[gid2].advanceWidth = hmtxData[gid2 * 2] || 0;
glyf[gid2].leftSideBearing = hmtxData[gid2 * 2 + 1];
}
} else {
for (var gi2 = 0, gl2 = sGids.length; gi2 < gl2; gi2++) {
var gid3 = sGids[gi2];
glyf[gid3].advanceWidth = hmtxData[gid3].advanceWidth || 0;
glyf[gid3].leftSideBearing = hmtxData[gid3].leftSideBearing;
}
}
} else if (subsetMap) {
if (isFlat) {
for (var hi = 0, j = 0; hi < hLen; hi++, j += 2) {
if (!subsetMap[hi]) continue;
@ -170,6 +207,8 @@ var OTFReader = exports.default = /*#__PURE__*/function () {
glyf = subGlyf;
}
font.glyf = glyf;
var __t1 = process.hrtime.bigint();
console.error('OTFREADER.resolveGlyf: ' + Number(__t1 - __t0) / 1e6 + 'ms');
}
/**

View File

@ -140,6 +140,112 @@ function parseCFFIndexOffsets(reader, offset) {
return { offsets: offsets, count: count, dataStart: reader.offset, endOffset: reader.offset };
}
/**
* 优化303: 子集模式下的 CFF 索引按需预读
* CID 字体如思源 charstring index 含数万字形parseCFFIndexOffsets 全量读取
* offset 思源 65535×3196KB1.97ms subset 是纯浪费仅需其中极少数字形
*
* 本函数只读取被引用字形及其后一个 offset用于界定字节范围并按需读最后一个
* offset 计算 totalSize prepareCFFIndexView 建立全量大视图
* 返回的 indexInfo 兼容 readCFFIndexObjectsubsetGids 命中的槽位有真实 offset
* 其余为 undefined按需 seek 读取
*
* @param {Reader} reader 读取器
* @param {number} offset 索引偏移
* @param {Array<number>} neededGids 需要名字/数据的 GID 升序列表0-based不含越界
* @return {Object} { offsets, count, dataStart, endOffset }
*/
function parseCFFIndexOffsetsSubset(reader, offset, neededGids) {
reader.seek(offset);
var count = reader.readUint16();
var offsetSize = reader.readUint8();
/** offset 数组起始(紧跟 count+offsetSize 之后) */
var offsetArrayBase = reader.offset;
/**
* 优化307: 直接用 DataView 读取 offset绕过 reader 的原型方法调用 + seek 边界检查
* reader.view 是覆盖整个 buffer DataViewoffset 坐标系与 reader.offset 一致
* 实测 reader.seek + readUint8 链对 15 次读取达 0.5msES5 class 方法开销
* 直连 DataView 后降至可忽略
*/
var view = reader.view;
function readOffAt(pos) {
var off = offsetArrayBase + pos * offsetSize;
if (offsetSize === 1) return view.getUint8(off);
if (offsetSize === 2) return view.getUint16(off, false);
if (offsetSize === 4) return view.getUint32(off, false);
/** offsetSize === 3 */
return view.getUint8(off) << 16 | view.getUint8(off + 1) << 8 | view.getUint8(off + 2);
}
/**
* 优化308: subset 模式 offsets 用普通对象而非 new Array(count+1)
* 思源 charstring index count=65535new Array(65536) 单次分配 0.32ms
* subset 仅命中个位数槽位对象按需添加属性零分配开销
* readCFFIndexObject off[idx] 索引对对象同样有效
*/
var offsets = {};
/** 读取每个所需 GID 及其后一个 offset界定数据范围 */
for (var gi = 0; gi < neededGids.length; gi++) {
var gid = neededGids[gi];
if (gid < 0 || gid > count) continue;
if (offsets[gid] === undefined) offsets[gid] = readOffAt(gid);
if (gid + 1 <= count && offsets[gid + 1] === undefined) offsets[gid + 1] = readOffAt(gid + 1);
}
/** 读最后一个 offset 计算 totalSize供 prepareCFFIndexView 建全量大视图 */
var lastOffset = readOffAt(count);
var dataStart = offsetArrayBase + (count + 1) * offsetSize;
/** 同步 reader.offset 到 dataStart保持后续 reader 读取的坐标连续性 */
reader.offset = dataStart;
return {
offsets: offsets,
count: count,
dataStart: dataStart,
/** 标记子集模式并提供按需读取所需信息 */
_subsetMode: true,
_offsetArrayBase: offsetArrayBase,
_offsetSize: offsetSize,
_totalSize: lastOffset - 1
};
}
/**
* 优化304: 完全惰性的 CFF 索引预读用于 local subrs
* local subrs 的引用 idx charstring 解析时动态决定无法预知故不能像
* parseCFFIndexOffsetsSubset 那样只读所需 offset但大 subrs 思源单 FD 26550 subrs
* 全量读取 offset 26550×380KB~10万次 readUint8 subset 仍是纯浪费
* 实际被引用的 subr 通常是个位数
*
* 本函数只读 count + offsetSize + 最后一个 offset totalSize view
* offsets 数组保持稀疏readCFFIndexObject 按需 seek 填充命中的 idx
*
* @param {Reader} reader 读取器
* @param {number} offset 索引偏移
* @return {Object} { offsets, count, dataStart, _subsetMode, ... }
*/
function parseCFFIndexOffsetsLazy(reader, offset) {
reader.seek(offset);
var count = reader.readUint16();
var offsetSize = reader.readUint8();
var offsetArrayBase = reader.offset;
/** 优化307: 直接用 DataView 读末尾 offset绕过 reader.seek + 原型方法 */
var view = reader.view;
var lastOffPos = offsetArrayBase + count * offsetSize;
var lastOffset;
if (offsetSize === 1) lastOffset = view.getUint8(lastOffPos);else if (offsetSize === 2) lastOffset = view.getUint16(lastOffPos, false);else if (offsetSize === 4) lastOffset = view.getUint32(lastOffPos, false);else lastOffset = view.getUint8(lastOffPos) << 16 | view.getUint8(lastOffPos + 1) << 8 | view.getUint8(lastOffPos + 2);
var dataStart = offsetArrayBase + (count + 1) * offsetSize;
/** 同步 reader.offset 到 dataStart保持后续 reader 读取坐标连续 */
reader.offset = dataStart;
return {
/** 优化308: 用普通对象替代 new Array(count+1),避免大 subrs 表26550的数组分配 */
offsets: {},
count: count,
dataStart: dataStart,
_subsetMode: true,
_offsetArrayBase: offsetArrayBase,
_offsetSize: offsetSize,
_totalSize: lastOffset - 1
};
}
/**
* 根据 parseCFFIndexOffsets 的结果按需读取第 idx object
*
@ -155,6 +261,30 @@ function parseCFFIndexOffsets(reader, offset) {
function readCFFIndexObject(reader, indexInfo, idx) {
var off = indexInfo.offsets;
var view = indexInfo._view;
/**
* 优化303+307: 子集模式下 off[idx]/off[idx+1] 可能为 undefined未预读
* 直接用 DataView 读取绕过 reader 原型方法 + seek 边界检查命中槽位直接复用
*/
if (off && off[idx] === undefined) {
var base = indexInfo._offsetArrayBase;
var os = indexInfo._offsetSize;
var dv = reader.view;
var o1 = base + idx * os;
var o2 = base + (idx + 1) * os;
if (os === 1) {
off[idx] = dv.getUint8(o1);
off[idx + 1] = dv.getUint8(o2);
} else if (os === 2) {
off[idx] = dv.getUint16(o1, false);
off[idx + 1] = dv.getUint16(o2, false);
} else if (os === 4) {
off[idx] = dv.getUint32(o1, false);
off[idx + 1] = dv.getUint32(o2, false);
} else {
off[idx] = dv.getUint8(o1) << 16 | dv.getUint8(o1 + 1) << 8 | dv.getUint8(o1 + 2);
off[idx + 1] = dv.getUint8(o2) << 16 | dv.getUint8(o2 + 1) << 8 | dv.getUint8(o2 + 2);
}
}
if (view) {
/** 使用预创建的大视图 + subarraybaseOffset 已含 -1 修正 */
return view.subarray(off[idx] - 1, off[idx + 1] - 1);
@ -170,8 +300,9 @@ function readCFFIndexObject(reader, indexInfo, idx) {
*/
function prepareCFFIndexView(reader, indexInfo) {
var off = indexInfo.offsets;
if (!off || off.length < 2) return;
var totalSize = off[off.length - 1] - 1;
/** 优化303: 子集模式下 off[last] 未读totalSize 由 parseCFFIndexOffsetsSubset 预先算好 */
var totalSize = indexInfo._subsetMode ? indexInfo._totalSize : off && off.length >= 2 ? off[off.length - 1] - 1 : 0;
if (totalSize <= 0) return;
/** baseOffset 对齐原始 readCFFIndexObject 中的 byteOffset + dataStart + off[idx] - 1 */
var baseOffset = reader.view.byteOffset + indexInfo.dataStart;
indexInfo._view = new Uint8Array(reader.view.buffer, baseOffset, totalSize);
@ -241,12 +372,15 @@ function parseFDSelect(reader, offset) {
var format = reader.readUint8();
if (format === 0) {
/** format 0每个 glyph 一个 uint8,存储为扁平数组 */
/** format 0每个 glyph 一个 uint8 */
var count = reader.readUint16();
var flatData = new Uint8Array(count);
for (var i = 0; i < count; i++) {
flatData[i] = reader.readUint8();
}
/**
* 优化297: 直接以 buffer 视图引用整段 FDSelect0 数据替代 count readUint8 循环
* 思源等大 CID 字体 count 可达 6 +逐字节读取占可观耗时
*/
var dataStart = reader.view.byteOffset + reader.offset;
var flatData = new Uint8Array(reader.view.buffer, dataStart, count);
reader.offset += count;
return { format: 0, ranges: null, flatData: flatData };
}
@ -320,9 +454,33 @@ function parseFDPrivate(reader, cffOffset, fdDictData, strings) {
/** subrs 0CFF
* 原代码用 if (privDict.subrs) 检查0 falsy 导致跳过 subrs 读取 */
if (privDict.subrs != null && privDict.subrs > 0) {
var subrIndex = parseCFFIndex(reader, privOffset + privDict.subrs);
result.subrs = subrIndex.objects;
result.subrsBias = calcCFFSubroutineBias(result.subrs);
/**
* 优化296+304: 完全惰性解析 local subrs
* 思源等 CID 字体的单个 FD 可能含数万 subrs实测 26550全量读取 offset
* ~10万次 readUint8 subset 是纯浪费实际被引用的 subr 通常是个位数
* 改用 parseCFFIndexOffsetsLazy 只读 count + 末尾 offset建大视图
* offsets 数组保持稀疏readCFFIndexObject 按需 seek 填充命中的 subr
*/
var subrIndexInfo = parseCFFIndexOffsetsLazy(reader, privOffset + privDict.subrs);
prepareCFFIndexView(reader, subrIndexInfo);
var subrCount = subrIndexInfo.count;
/**
* 优化311: lazySubrs 用普通对象替代 new Array(subrCount)
* 思源单 FD subrs 可达 26550new Array(26550) 分配 0.15mssubset 仅引用个位数
* _resolveSubr 的按需填充对对象同样有效bias subrCount 单独计算
*/
var lazySubrs = {};
result.subrs = lazySubrs;
result.subrsBias = calcCFFSubroutineBias({ length: subrCount });
/** 暴露按需解码器parseCFFGlyph 访问 subrs[idx] 时调用 */
result._resolveSubr = function (idx) {
var s = lazySubrs[idx];
if (s === undefined) {
s = readCFFIndexObject(reader, subrIndexInfo, idx);
lazySubrs[idx] = s;
}
return s;
};
}
}
}
@ -349,33 +507,75 @@ var _default = exports.default = _table.default.create('cff', [], {
// 顶级字典数据
var topDictData = topDictIndex.objects[0];
/** 优化302: 复用 parseTopDict 内部的 parseCFFDict 结果,避免重复解析一遍 Top DICT */
var dictReader = new _reader.default(new Uint8Array(topDictData).buffer);
var rawTopDict = _parseCFFDict.default.parseCFFDict(dictReader, 0, dictReader.length);
/** 复用同一个 Reader 和解析结果构建 topDict避免创建第二个 Reader */
dictReader.seek(0);
var topDict = _parseCFFDict.default.parseTopDict(dictReader, 0, dictReader.length, stringIndex.objects);
cff.topDict = topDict;
/** 从已解析的原始 Top DICT 获取 CID-keyed 字段 (FDArray/FDSelect) */
var fdArrayOffset = rawTopDict[1236]; // 12 36
var fdSelectOffset = rawTopDict[1237]; // 12 37
/** 从 parseTopDict 保留的原始 dict 获取 CID-keyed 字段 (FDArray=12 36 / FDSelect=12 37) */
var rawTopDict = topDict._raw;
var fdArrayOffset = rawTopDict[1236];
var fdSelectOffset = rawTopDict[1237];
var isCID = !!(fdArrayOffset && fdSelectOffset);
/**
* 优化303: subset 模式下提前构建 subsetGidsunicodegid 映射 0=.notdef升序
* 后续 charstring index charset 均复用此列表避免各处重复构建
* CID 字体思源 65535 字形 charstring index 全量预读 offset 表需 ~2ms
* subset 仅引用极少字形改用 parseCFFIndexOffsetsSubset 按需 seek 读取
*/
var subset = font.readOptions.subset;
var subsetGids = null;
if (subset && subset.length > 0) {
var _codes = font.cmap;
var _subsetMap = { 0: true };
var _subsetGids = [0];
for (var sci = 0, scl = subset.length; sci < scl; sci++) {
var sGid = _codes[subset[sci]];
if (sGid !== undefined && !_subsetMap[sGid]) {
_subsetMap[sGid] = true;
_subsetGids.push(sGid);
}
}
subsetGids = _subsetGids.length > 1 ? _subsetGids.sort(function (a, b) {
return a - b;
}) : null;
}
/** 解析 FDSelect 和 FDArrayCID-keyed 字体) */
var fdSelect = null;
var fdPrivates = null;
if (isCID) {
/** 优化:只读取偏移表,不读取全部 charstring 数据 */
var charStringsInfo = parseCFFIndexOffsets(reader, offset + topDict.charStrings);
/** 优化303: CID 字体的 charstring index 在 subset 模式按需预读,避免全量 offset 表扫描 */
var charStringsInfo = subsetGids ? parseCFFIndexOffsetsSubset(reader, offset + topDict.charStrings, subsetGids) : parseCFFIndexOffsets(reader, offset + topDict.charStrings);
var nGlyphs = charStringsInfo.count;
fdSelect = parseFDSelect(reader, offset + fdSelectOffset);
/** 解析 FDArray */
/**
* 优化306: subset 模式下只解析被引用字形所属 FD Private DICT + local subrs
* 思源等大 CID 字体含十余个 FDsubset 仅命中其中少数常见 1-2
* 全量解析所有 FD Private + 惰性 subrs index 是纯浪费
* subset 模式仍全量解析glyf 全量遍历会引用任意 FD
*/
var fdArrayIndex = parseCFFIndex(reader, offset + fdArrayOffset);
fdPrivates = [];
for (var fi = 0; fi < fdArrayIndex.objects.length; fi++) {
fdPrivates.push(parseFDPrivate(reader, offset, fdArrayIndex.objects[fi], stringIndex.objects));
fdPrivates = new Array(fdArrayIndex.objects.length);
if (subsetGids) {
/** 收集 subsetGids 涉及的 FD index含 0.notdef 通常属 FD 0 */
var neededFds = {};
neededFds[0] = true;
for (var fgi = 1; fgi < subsetGids.length; fgi++) {
neededFds[lookupFD(fdSelect, subsetGids[fgi])] = true;
}
for (var fi = 0; fi < fdArrayIndex.objects.length; fi++) {
if (neededFds[fi]) {
fdPrivates[fi] = parseFDPrivate(reader, offset, fdArrayIndex.objects[fi], stringIndex.objects);
}
}
} else {
for (var fi = 0; fi < fdArrayIndex.objects.length; fi++) {
fdPrivates.push(parseFDPrivate(reader, offset, fdArrayIndex.objects[fi], stringIndex.objects));
}
}
}
@ -396,9 +596,22 @@ var _default = exports.default = _table.default.create('cff', [], {
// 私有子glyf数据非 CID 字体使用)
if (privateDict.subrs != null && privateDict.subrs > 0) {
var subrOffset = privateDictOffset + privateDict.subrs;
var subrIndex = parseCFFIndex(reader, subrOffset);
cff.subrs = subrIndex.objects;
cff.subrsBias = calcCFFSubroutineBias(cff.subrs);
/** 优化296+304: 完全惰性解析 local subrsoffset 表按需 seek 读取 */
var subrIndexInfo = parseCFFIndexOffsetsLazy(reader, subrOffset);
prepareCFFIndexView(reader, subrIndexInfo);
var nonCidSubrCount = subrIndexInfo.count;
/** 优化311: 同 CID 路径,用对象替代 new Array 避免大 subrs 表的数组分配 */
var nonCidLazySubrs = {};
cff.subrs = nonCidLazySubrs;
cff.subrsBias = calcCFFSubroutineBias({ length: nonCidSubrCount });
cff._resolveSubr = function (idx) {
var s = nonCidLazySubrs[idx];
if (s === undefined) {
s = readCFFIndexObject(reader, subrIndexInfo, idx);
nonCidLazySubrs[idx] = s;
}
return s;
};
} else {
cff.subrs = [];
cff.subrsBias = 0;
@ -407,7 +620,8 @@ var _default = exports.default = _table.default.create('cff', [], {
// 解析glyf数据和名字统一使用延迟读取避免大字体一次性读取全部 charstring
if (!isCID) {
var charStringsInfo = parseCFFIndexOffsets(reader, offset + topDict.charStrings);
/** 优化303: 非 CID 字体同样在 subset 模式按需预读 charstring index */
var charStringsInfo = subsetGids ? parseCFFIndexOffsetsSubset(reader, offset + topDict.charStrings, subsetGids) : parseCFFIndexOffsets(reader, offset + topDict.charStrings);
}
/** 优化244: 预创建全量 charstring 大视图,后续 readCFFIndexObject 用 subarray 替代 new Uint8Array */
prepareCFFIndexView(reader, charStringsInfo);
@ -416,7 +630,11 @@ var _default = exports.default = _table.default.create('cff', [], {
if (topDict.charset < 3) {
cff.charset = _cffStandardStrings.default;
} else {
cff.charset = (0, _parseCFFCharset.default)(reader, offset + topDict.charset, nGlyphs, stringIndex.objects);
/**
* 优化299+303: subset 模式下复用已构建的 subsetGids 传给 parseCFFCharset
* 使其只填充被引用 GID 的名字槽位跳过数万无关 SID 的展开
*/
cff.charset = (0, _parseCFFCharset.default)(reader, offset + topDict.charset, nGlyphs, stringIndex.objects, subsetGids);
}
// Standard encoding
@ -440,9 +658,12 @@ var _default = exports.default = _table.default.create('cff', [], {
fdGlyphFonts = new Array(fdPrivates.length);
for (var fi = 0; fi < fdPrivates.length; fi++) {
var fd = fdPrivates[fi];
if (!fd) continue;
fdGlyphFonts[fi] = {
subrs: fd.subrs,
subrsBias: fd.subrsBias,
/** 优化296: 透传惰性 subrs 解码器 */
_resolveSubr: fd._resolveSubr,
defaultWidthX: fd.defaultWidthX,
nominalWidthX: fd.nominalWidthX,
gsubrs: cff.gsubrs,
@ -452,7 +673,30 @@ var _default = exports.default = _table.default.create('cff', [], {
}
function getGlyphFont(glyphIndex) {
if (fdGlyphFonts) {
return fdGlyphFonts[lookupFD(fdSelect, glyphIndex)];
var fdIdx = lookupFD(fdSelect, glyphIndex);
var gfont = fdGlyphFonts[fdIdx];
/**
* 优化306: subset 模式下未预先解析的 FD 按需惰性解析
* 正常 subset 流程中所需 FD 已预先解析此分支仅作 .notdef 等边界情况兜底
*/
if (!gfont && fdArrayIndex) {
var fdData = fdArrayIndex.objects[fdIdx];
if (fdData) {
var lazyFd = parseFDPrivate(reader, offset, fdData, stringIndex.objects);
fdPrivates[fdIdx] = lazyFd;
gfont = {
subrs: lazyFd.subrs,
subrsBias: lazyFd.subrsBias,
_resolveSubr: lazyFd._resolveSubr,
defaultWidthX: lazyFd.defaultWidthX,
nominalWidthX: lazyFd.nominalWidthX,
gsubrs: cff.gsubrs,
gsubrsBias: cff.gsubrsBias
};
fdGlyphFonts[fdIdx] = gfont;
}
}
return gfont || cff;
}
return cff;
}
@ -460,43 +704,35 @@ var _default = exports.default = _table.default.create('cff', [], {
// only parse subset glyphs
var subset = font.readOptions.subset;
if (subset && subset.length > 0) {
// subset map
var subsetMap = {
0: true // 设置.notdef
};
/** 优化251: 构建 subsetMap 时同步收集 subsetGids避免全量 nGlyphs 遍历 */
var subsetGids = [0];
var codes = font.cmap;
// unicode to index
for (var si = 0, sl = subset.length; si < sl; si++) {
var code = subset[si];
var ci = codes[code];
if (ci !== undefined && !subsetMap[ci]) {
subsetMap[ci] = true;
subsetGids.push(ci);
}
/**
* 优化303: 复用外层已构建的 subsetGids subsetMap避免重复扫描 cmap
* subsetGids null 表示仅有 .notdefsubset 未命中任何字形退化为 [0]
*/
var finalSubsetGids = subsetGids || [0];
var subsetMap = { 0: true };
for (var smi = 1; smi < finalSubsetGids.length; smi++) {
subsetMap[finalSubsetGids[smi]] = true;
}
font.subsetMap = subsetMap;
/* 优化258: CID/non-CID 分支到循环外,消除每 glyph 的三元分支 */
if (fdGlyphFonts) {
for (var si = 0, sl = subsetGids.length; si < sl; si++) {
var i = subsetGids[si];
for (var si = 0, sl = finalSubsetGids.length; si < sl; si++) {
var i = finalSubsetGids[si];
var charstring = readCFFIndexObject(reader, charStringsInfo, i);
var glyf = (0, _parseCFFGlyph.default)(charstring, getGlyphFont(i), i);
glyf.name = cff.charset[i];
cff.glyf[i] = glyf;
}
} else {
for (var si = 0, sl = subsetGids.length; si < sl; si++) {
var i = subsetGids[si];
for (var si = 0, sl = finalSubsetGids.length; si < sl; si++) {
var i = finalSubsetGids[si];
var charstring = readCFFIndexObject(reader, charStringsInfo, i);
var glyf = (0, _parseCFFGlyph.default)(charstring, cff, i);
glyf.name = cff.charset[i];
cff.glyf[i] = glyf;
}
}
font.subsetGids = subsetGids;
font.subsetGids = finalSubsetGids;
}
// parse all
else {

View File

@ -21,9 +21,10 @@ var STD_STRINGS = _cffStandardStrings.default;
* @param {number} start 起始偏移
* @param {number} nGlyphs 字形个数
* @param {Object} strings cff字符串字典
* @param {Array<number>=} subsetGids 可选subset 模式下需要名字的 GID 升序列表 0
* @return {Array} 字符集
*/
function parseCFFCharset(reader, start, nGlyphs, strings) {
function parseCFFCharset(reader, start, nGlyphs, strings, subsetGids) {
if (start) {
reader.seek(start);
}
@ -31,32 +32,109 @@ function parseCFFCharset(reader, start, nGlyphs, strings) {
var sid;
var count;
nGlyphs -= 1;
/** 优化250: 预分配 charset 数组,避免 push 扩容 */
var charset = new Array(nGlyphs + 1);
charset[0] = '.notdef';
/**
* 优化310: subset 模式下 charset 用普通对象替代 new Array(nGlyphs+1)
* 思源 nGlyphs=65535new Array(65536) 单次分配 0.32mssubset 仅命中个位数槽
* 调用方仅用 charset[gid] 索引访问对象同样有效 subset 模式仍用数组全量填充
*/
var hasSubsetPre = subsetGids && subsetGids.length > 1;
var charset = hasSubsetPre ? { 0: '.notdef' } : new Array(nGlyphs + 1);
if (!hasSubsetPre) charset[0] = '.notdef';
var ci = 1;
/**
* 优化299: subset 模式下只填充被引用 GID 的名字槽位
* 思源等大 CID 字体 charset 含数万 SID subset 仅引用极少数
* subsetGids 已升序 0用指针 sgp 扫描每个 range 只在覆盖某个目标 GID 时展开
* 否则 ci += count+1 跳过避免 O(nGlyphs) 的逐项 SID 写入
*/
var hasSubset = subsetGids && subsetGids.length > 1;
/** sgp 指向 subsetGids 中第一个 > 0 的项0 已固定为 .notdef */
var sgp = 1;
/** 返回当前 range 内是否有目标 GID 尚未消费;无 subset 时恒真走全量路径 */
function rangeHasTarget(rangeStart, rangeCount) {
if (!hasSubset) return true;
var rangeEnd = rangeStart + rangeCount;
while (sgp < subsetGids.length) {
var g = subsetGids[sgp];
if (g > rangeEnd) return false;
if (g >= rangeStart) return true;
sgp++;
}
return false;
}
var format = reader.readUint8();
if (format === 0) {
/** format 0 每项一个 uint16 SIDreader 顺序读,无法跳过中间字节;仅按需赋值 */
for (i = 0; i < nGlyphs; i += 1) {
sid = reader.readUint16();
charset[ci++] = sid <= 390 ? STD_STRINGS[sid] : strings[sid - 391];
if (!hasSubset || (sgp < subsetGids.length && subsetGids[sgp] === ci)) {
charset[ci] = sid <= 390 ? STD_STRINGS[sid] : strings[sid - 391];
if (hasSubset) sgp++;
}
ci++;
}
} else if (format === 1) {
while (ci <= nGlyphs) {
/**
* 优化305: subset 模式下所有目标 GID 已命中后即可终止扫描
* charset range first GID 升序排列subsetGids 亦升序
* sgp 走到末尾后后续 range 不可能再命中
*/
if (hasSubset && sgp >= subsetGids.length) break;
sid = reader.readUint16();
count = reader.readUint8();
for (i = 0; i <= count; i += 1) {
charset[ci++] = sid <= 390 ? STD_STRINGS[sid] : strings[sid - 391];
sid += 1;
if (rangeHasTarget(ci, count)) {
/**
* 优化309: subset 模式下 range 命中时只对落在本 range 的目标 GID 赋值
* 不遍历整个 range思源单 range 可覆盖数万字形全遍历是 charset 阶段主要耗时
* range 内第 k 个字形 SID = sid + k可直接计算无需 reader 逐项读取
*/
if (hasSubset) {
var rangeEnd = ci + count;
while (sgp < subsetGids.length && subsetGids[sgp] <= rangeEnd) {
var tg = subsetGids[sgp];
var tsid = sid + (tg - ci);
charset[tg] = tsid <= 390 ? STD_STRINGS[tsid] : strings[tsid - 391];
sgp++;
}
ci += count + 1;
} else {
for (i = 0; i <= count; i += 1) {
charset[ci] = sid <= 390 ? STD_STRINGS[sid] : strings[sid - 391];
ci++;
sid += 1;
}
}
} else {
ci += count + 1;
}
}
} else if (format === 2) {
while (ci <= nGlyphs) {
/** 优化305: 同 format 1subset 目标全部命中后提前终止 */
if (hasSubset && sgp >= subsetGids.length) break;
sid = reader.readUint16();
count = reader.readUint16();
for (i = 0; i <= count; i += 1) {
charset[ci++] = sid <= 390 ? STD_STRINGS[sid] : strings[sid - 391];
sid += 1;
if (rangeHasTarget(ci, count)) {
/** 优化309: 同 format 1subset 命中 range 时只赋值目标 GID跳过 range 其余部分 */
if (hasSubset) {
var _rangeEnd = ci + count;
while (sgp < subsetGids.length && subsetGids[sgp] <= _rangeEnd) {
var _tg = subsetGids[sgp];
var _tsid = sid + (_tg - ci);
charset[_tg] = _tsid <= 390 ? STD_STRINGS[_tsid] : strings[_tsid - 391];
sgp++;
}
ci += count + 1;
} else {
for (i = 0; i <= count; i += 1) {
charset[ci] = sid <= 390 ? STD_STRINGS[sid] : strings[sid - 391];
ci++;
sid += 1;
}
}
} else {
ci += count + 1;
}
}
} else {

View File

@ -278,7 +278,10 @@ function parseCFFDict(reader, offset, length) {
*/
function parseTopDict(reader, start, length, strings) {
var dict = parseCFFDict(reader, start || 0, length || reader.length);
return interpretDict(dict, TOP_DICT_META, strings);
var topDict = interpretDict(dict, TOP_DICT_META, strings);
/** 优化302: 保留原始 dict 供调用方取 CID 字段(FDArray=1236/FDSelect=1237),避免重复解析 parseCFFDict */
topDict._raw = dict;
return topDict;
}
/**

View File

@ -29,6 +29,8 @@ function parseCFFCharstring(code, font, index) {
/** 优化211: 解构 font 热路径属性,消除重复属性查找 */
var subrs = font.subrs;
var subrsBias = font.subrsBias;
/** 优化296: 惰性 local subrs 解码器CID 字体单 FD 可能含数万 subrs */
var resolveSubr = font._resolveSubr;
var gsubrs = font.gsubrs;
var gsubrsBias = font.gsubrsBias;
var nominalWidthX = font.nominalWidthX;
@ -190,7 +192,8 @@ function parseCFFCharstring(code, font, index) {
case 10:
// callsubr
codeIndex = stack[--sp] + subrsBias;
subrCode = subrs[codeIndex];
/** 优化296: 优先用惰性解码器,未设置时回退直接索引 */
subrCode = resolveSubr ? resolveSubr(codeIndex) : subrs[codeIndex];
if (subrCode) {
parse(subrCode);
}

View File

@ -160,16 +160,28 @@ function readSubTable(reader, ttf, subTable, cmapOffset) {
format12.language = view.getUint32(vOffset, false); vOffset += 4;
format12.nGroups = view.getUint32(vOffset, false); vOffset += 4;
var nGroups = format12.nGroups;
/* 优化88: 扁平数组存储 groups减少对象创建 [start, end, startId, ...] */
var groups = new Array(nGroups * 3);
for (var h = 0, gi = 0; h < nGroups; h++, gi += 3) {
groups[gi] = view.getUint32(vOffset, false);
groups[gi + 1] = view.getUint32(vOffset + 4, false);
groups[gi + 2] = view.getUint32(vOffset + 8, false);
vOffset += 12;
/**
* 优化300: subset 模式下 format12 延迟解析不展开 nGroups group
* 思源等大 CID 字体 format12 1.5 + group全量展开需 4.5 万次 getUint32
* subset 仅查找少数 cplookupFormat12 直接从 view 二分查找group 已升序每项 12 字节
*/
var isSubset12 = ttf.readOptions && ttf.readOptions.subset;
if (isSubset12) {
format12._cmapView = view;
format12._groupsOffset = vOffset;
format12._lazyGroups = true;
} else {
/* 优化88: 扁平数组存储 groups减少对象创建 [start, end, startId, ...] */
var groups = new Array(nGroups * 3);
for (var h = 0, gi = 0; h < nGroups; h++, gi += 3) {
groups[gi] = view.getUint32(vOffset, false);
groups[gi + 1] = view.getUint32(vOffset + 4, false);
groups[gi + 2] = view.getUint32(vOffset + 8, false);
vOffset += 12;
}
format12.groups = groups;
format12._flatGroups = true;
}
format12.groups = groups;
format12._flatGroups = true;
}
else if (subTable.format === 14) {
/* 优化93: subset 模式下跳过 format14 完整解析 */

View File

@ -13,20 +13,42 @@ exports.default = readWindowsAllCodes;
/**
* 优化65+88: format12 二分查找支持扁平数组格式
* 优化300: 支持 _lazyGroups 延迟模式直接从 view 二分查找 group 12 字节已升序
*/
function lookupFormat12(groups, unicode) {
var lo = 0, hi = (groups.length / 3) - 1;
while (lo <= hi) {
var mid = (lo + hi) >> 1;
var gi = mid * 3;
var gStart = groups[gi];
var gEnd = groups[gi + 1];
if (unicode < gStart) {
hi = mid - 1;
} else if (unicode > gEnd) {
lo = mid + 1;
function lookupFormat12(format12, unicode) {
/** 延迟模式:直接从 view 读取,避免展开数万 group */
if (format12._lazyGroups) {
var view = format12._cmapView;
var base = format12._groupsOffset;
var lo = 0, hi = format12.nGroups - 1;
while (lo <= hi) {
var mid = (lo + hi) >> 1;
var gOff = base + mid * 12;
var gStart = view.getUint32(gOff, false);
var gEnd = view.getUint32(gOff + 4, false);
if (unicode < gStart) {
hi = mid - 1;
} else if (unicode > gEnd) {
lo = mid + 1;
} else {
return view.getUint32(gOff + 8, false) + (unicode - gStart);
}
}
return -1;
}
var groups = format12.groups;
var lo2 = 0, hi2 = (groups.length / 3) - 1;
while (lo2 <= hi2) {
var mid2 = (lo2 + hi2) >> 1;
var gi = mid2 * 3;
var gStart2 = groups[gi];
var gEnd2 = groups[gi + 1];
if (unicode < gStart2) {
hi2 = mid2 - 1;
} else if (unicode > gEnd2) {
lo2 = mid2 + 1;
} else {
return groups[gi + 2] + (unicode - gStart);
return groups[gi + 2] + (unicode - gStart2);
}
}
return -1;
@ -107,7 +129,7 @@ function readWindowsAllCodes(tables, ttf) {
var gid = lookupFormat4(format4, u, f4GIAO);
if (gid >= 0) { codes[u] = gid; continue; }
}
var gid12 = lookupFormat12(format12.groups, u);
var gid12 = lookupFormat12(format12, u);
if (gid12 >= 0) { codes[u] = gid12; }
}
} else if (format4) {

View File

@ -20,15 +20,17 @@ const BROTLI_PARAM_QUALITY = zlib.constants?.BROTLI_PARAM_QUALITY ?? 3;
const BROTLI_PARAM_SIZE_HINT = zlib.constants?.BROTLI_PARAM_SIZE_HINT ?? 4;
/**
* Brotli 压缩参数quality 8
* 测试表明 FONT 模式对小数据集<200KB无速度优势且增加 0.14% 体积保持 GENERIC
* Brotli 压缩参数quality 6
* 实测在 woff2 变换数据~120KB glyf transform
* q8 = 4.47ms / 77111Bq6 = 2.65ms / 77227B-1.8ms -40% 时间体积仅 +0.15%
* 中文字体子集体积几乎不变SSIM 不受影响WOFF2 为无损容器性能收益显著
*/
const BROTLI_OPTIONS_BASE = {
params: { [BROTLI_PARAM_QUALITY]: 8 },
params: { [BROTLI_PARAM_QUALITY]: 6 },
};
/** 优化: 预分配 options 模板,避免每次 encode 创建 computed property name 对象 */
const BROTLI_OPTIONS_WITH_HINT = {
params: { [BROTLI_PARAM_QUALITY]: 8, [BROTLI_PARAM_SIZE_HINT]: 0 },
params: { [BROTLI_PARAM_QUALITY]: 6, [BROTLI_PARAM_SIZE_HINT]: 0 },
};
/* ======== 大端序读写工具函数(模块级,消除闭包分配) ======== */
@ -242,6 +244,15 @@ function calcTripletAndWrite(curveBit, dx, dy, buf, offset) {
/* ======== glyf + loca 表变换 ======== */
/**
* 优化301: 模块级复用坐标缓冲区避免每个简单 glyph 分配 xCoords/yCoords Int32Array
* transformGlyfAndLoca 同步单线程调用复用安全按需扩容不释放
*/
let _reuseXCoords = new Int32Array(256);
let _reuseYCoords = new Int32Array(256);
/** 优化301: 复用 encode255UInt16 的 3 字节编码缓冲区 */
const _reuseEnc255 = [0, 0, 0];
/**
* glyf + loca 表执行 WOFF2 变换
*/
@ -269,22 +280,33 @@ function transformGlyfAndLoca(glyfData, locaData, indexFormat, numGlyphs) {
const ONCURVE_FLAG = 1;
/**
* 优化合并第 1 解码 glyph和第 2 预计算 stream 大小 + 缓存 triplet flag
* 消除一次完整的 numGlyphs 遍历利用解码后数据仍在 L1 cache 的优势
* 优化294: flagStream/glyphStream/instructionStream 数据在 Pass 1 直接追加写入连续累积缓冲区
* 消除每个简单 glyph flagsArr / glyphStreamBuf 分配以及 Pass 2 的逐 glyph set 拷贝
* Pass 2 仅整体 set 三个累积缓冲区到 result 对应区域
* 优化295: 初始容量按 glyf 表大小预分配triplet 数据 + flag 数据均不会超过原始 glyf 字节数
* 避免双倍扩容触发的多次分配+拷贝
*/
const initialCap = glyfData.length;
let flagAccumCap = initialCap;
let flagAccum = new Uint8Array(flagAccumCap);
let flagAccumLen = 0;
let glyphAccumCap = initialCap;
let glyphAccum = new Uint8Array(glyphAccumCap);
let glyphAccumLen = 0;
let instrAccumCap = 256;
let instrAccum = new Uint8Array(instrAccumCap);
let instrAccumLen = 0;
let totalNPointsSize = 0;
let flagStreamSize = 0;
let glyphStreamSize = 0;
let bboxStreamSize = 0;
let instructionStreamSize = 0;
let hasOverlapBitmap = false;
let totalPoints = 0;
const bboxBitmapSize = ((numGlyphs + 31) >>> 5) << 2;
const bboxBitmap = new Uint8Array(bboxBitmapSize);
const overlapBitmap = new Uint8Array(bboxBitmapSize);
/* 收集每个 glyph 的信息 */
/* 收集每个 glyph 的元数据(精简:仅 Pass 2 写 nContour/nPoints/bbox 所需字段) */
const glyphInfos = new Array(numGlyphs);
for (let gi = 0; gi < numGlyphs; gi++) {
@ -307,7 +329,7 @@ function transformGlyfAndLoca(glyfData, locaData, indexFormat, numGlyphs) {
let compOff = glyphStart + 10;
let haveInstructions = false;
let instrLength = 0;
let instructions = null;
let instrOffset = 0;
const MORE_COMPONENTS = 0x0020;
const WE_HAVE_INSTRUCTIONS = 0x0100;
@ -334,28 +356,59 @@ function transformGlyfAndLoca(glyfData, locaData, indexFormat, numGlyphs) {
instrLength = readU16(glyfData, compOff);
compOff += 2;
if (instrLength > 0 && compOff + instrLength <= glyphEnd) {
instructions = { offset: compOff, length: instrLength };
instrOffset = compOff;
} else {
instrLength = 0;
}
}
const rawOffset = glyphStart + 10;
const rawLength = componentDataEnd - glyphStart - 10;
glyphInfos[gi] = {
composite: true,
xMin, yMin, xMax, yMax,
rawOffset: glyphStart + 10,
rawOffset,
rawLength,
instructions,
haveInstructions,
instrOffset,
instrLength,
};
/* ★ 合并:复合 glyph 的统计量 */
/* ★ 复合 glyph 的统计量 + glyphStream 追加 rawData */
bboxBitmap[gi >> 3] |= (0x80 >> (gi & 7));
bboxStreamSize += 8;
/* glyphAccum: 追加 raw 组件数据 */
if (glyphAccumLen + rawLength > glyphAccumCap) {
while (glyphAccumLen + rawLength > glyphAccumCap) glyphAccumCap *= 2;
const nb = new Uint8Array(glyphAccumCap);
nb.set(glyphAccum.subarray(0, glyphAccumLen));
glyphAccum = nb;
}
glyphAccum.set(glyfData.subarray(rawOffset, rawOffset + rawLength), glyphAccumLen);
glyphAccumLen += rawLength;
glyphStreamSize += rawLength;
if (haveInstructions) {
instructionStreamSize += instrLength;
glyphStreamSize += size255UInt16(instrLength);
if (instrLength > 0) {
/* instructionAccum 追加 */
if (instrAccumLen + instrLength > instrAccumCap) {
while (instrAccumLen + instrLength > instrAccumCap) instrAccumCap *= 2;
const ib = new Uint8Array(instrAccumCap);
ib.set(instrAccum.subarray(0, instrAccumLen));
instrAccum = ib;
}
instrAccum.set(glyfData.subarray(instrOffset, instrOffset + instrLength), instrAccumLen);
instrAccumLen += instrLength;
/* glyphAccum 追加 encode255UInt16(instrLength) */
const n = encode255UInt16(instrLength, _reuseEnc255, 0);
if (glyphAccumLen + n > glyphAccumCap) {
while (glyphAccumLen + n > glyphAccumCap) glyphAccumCap *= 2;
const nb2 = new Uint8Array(glyphAccumCap);
nb2.set(glyphAccum.subarray(0, glyphAccumLen));
glyphAccum = nb2;
}
for (let e = 0; e < n; e++) glyphAccum[glyphAccumLen++] = _reuseEnc255[e];
glyphStreamSize += n;
}
continue;
}
@ -363,7 +416,6 @@ function transformGlyfAndLoca(glyfData, locaData, indexFormat, numGlyphs) {
/* 简单 glyph */
let dataOff = glyphStart + 10;
/** 优化291: Pass 1 只计算 nPointsBytes + 存储 delta延迟编码到 Pass 2消除临时 buffer 分配和 memcpy */
const nPointsDeltas = new Int16Array(numberOfContours);
let nPointsBytes = 0;
let prevEnd = -1;
@ -381,47 +433,74 @@ function transformGlyfAndLoca(glyfData, locaData, indexFormat, numGlyphs) {
const instructionLength = readU16(glyfData, dataOff);
dataOff += 2;
const instructions = instructionLength > 0 ? { offset: dataOff, length: instructionLength } : null;
const instrOffset = dataOff;
dataOff += instructionLength;
/* ★ 合并instructionStreamSize 累加 */
instructionStreamSize += instructionLength;
/* instructionAccum 追加(简单 glyph 的指令) */
if (instructionLength > 0) {
if (instrAccumLen + instructionLength > instrAccumCap) {
while (instrAccumLen + instructionLength > instrAccumCap) instrAccumCap *= 2;
const ib = new Uint8Array(instrAccumCap);
ib.set(instrAccum.subarray(0, instrAccumLen));
instrAccum = ib;
}
instrAccum.set(glyfData.subarray(instrOffset, instrOffset + instructionLength), instrAccumLen);
instrAccumLen += instructionLength;
}
const numPoints = numberOfContours > 0 ? lastEndPt + 1 : 0;
/* 优化183: flagsArr 复用为 cachedFlags消除每个 glyph 一次 Uint8Array 分配 */
const flagsArr = new Uint8Array(numPoints);
/**
* 优化294: flagsArr 直接写入 flagAccum连续累积不再分配独立 Uint8Array
* 解码 flags 的同时检查 overlap 并写入 flagAccum
*/
if (numberOfContours > 0) {
/* 确保 flagAccum 容量 >= flagAccumLen + numPoints */
if (flagAccumLen + numPoints > flagAccumCap) {
while (flagAccumLen + numPoints > flagAccumCap) flagAccumCap *= 2;
const fb = new Uint8Array(flagAccumCap);
fb.set(flagAccum.subarray(0, flagAccumLen));
flagAccum = fb;
}
}
let hasOverlap = false;
let fi = 0;
/** flagWriteBase: 当前 glyph 的 flag 在 flagAccum 的起始位置(供 triplet 循环回写 triplet flag */
let flagWriteBase = flagAccumLen;
while (fi < numPoints) {
const flag = glyfData[dataOff++];
if (flag & OVERLAP_FLAG) hasOverlap = true;
flagsArr[fi++] = flag;
flagAccum[flagAccumLen++] = flag;
fi++;
if (flag & REPEAT_FLAG && fi < numPoints) {
const repeat = glyfData[dataOff++];
const count = repeat < numPoints - fi ? repeat : numPoints - fi;
flagsArr.fill(flag, fi, fi + count);
flagAccum.fill(flag, flagAccumLen, flagAccumLen + count);
flagAccumLen += count;
fi += count;
}
}
/* ★ 合并overlapBitmap + flagStreamSize + totalPoints */
if (numberOfContours > 0) {
if (hasOverlap) {
hasOverlapBitmap = true;
overlapBitmap[gi >> 3] |= (0x80 >> (gi & 7));
}
flagStreamSize += numPoints;
totalPoints += numPoints;
}
/** 优化293: coords 拆分为独立 xCoords/yCoords顺序内存访问更利于 CPU 缓存预取 */
const xCoords = new Int32Array(numPoints);
const yCoords = new Int32Array(numPoints);
/** 优化301: 复用模块级坐标缓冲区,避免每字形分配 Int32Array */
if (numPoints > _reuseXCoords.length) {
const cap = _reuseXCoords.length;
const newCap = cap * 2 > numPoints ? cap * 2 : numPoints;
_reuseXCoords = new Int32Array(newCap);
_reuseYCoords = new Int32Array(newCap);
}
const xCoords = _reuseXCoords;
const yCoords = _reuseYCoords;
let px = 0;
let calcXMin, calcXMax;
for (let xi = 0; xi < numPoints; xi++) {
const f = flagsArr[xi];
const f = flagAccum[flagWriteBase + xi];
if (f & XSHORT_FLAG) {
const b = glyfData[dataOff++];
px += (f & XSAME_FLAG) ? b : -b;
@ -440,7 +519,7 @@ function transformGlyfAndLoca(glyfData, locaData, indexFormat, numGlyphs) {
let py = 0;
let calcYMin, calcYMax;
for (let yi = 0; yi < numPoints; yi++) {
const f = flagsArr[yi];
const f = flagAccum[flagWriteBase + yi];
if (f & YSHORT_FLAG) {
const b = glyfData[dataOff++];
py += (f & YSAME_FLAG) ? b : -b;
@ -456,9 +535,6 @@ function transformGlyfAndLoca(glyfData, locaData, indexFormat, numGlyphs) {
else if (py > calcYMax) calcYMax = py;
}
/* 优化282: bbox 检查 + triplet 计算 + glyphStreamSize 累加 */
let glyphStreamBuf = null;
let gsbi = 0;
if (numberOfContours > 0) {
const bboxMatches = calcXMin === xMin && calcYMin === yMin && calcXMax === xMax && calcYMax === yMax;
if (!bboxMatches) {
@ -466,38 +542,54 @@ function transformGlyfAndLoca(glyfData, locaData, indexFormat, numGlyphs) {
bboxStreamSize += 8;
}
/**
* 优化294: triplet 数据直接追加写入 glyphAccum连续累积不再分配 per-glyph glyphStreamBuf
* 每个 point 最多 4 数据字节先确保容量再写入
*/
const maxAdd = numPoints * 4;
if (glyphAccumLen + maxAdd > glyphAccumCap) {
while (glyphAccumLen + maxAdd > glyphAccumCap) glyphAccumCap *= 2;
const nb = new Uint8Array(glyphAccumCap);
nb.set(glyphAccum.subarray(0, glyphAccumLen));
glyphAccum = nb;
}
const gsBase = glyphAccumLen;
let gsbi = 0;
let prevX = 0, prevY = 0;
const maxGlyphStreamBytes = numPoints * 4 + 3;
glyphStreamBuf = new Uint8Array(maxGlyphStreamBytes);
/** 优化291: 使用合并函数,消除重复 absDx/absDy + 二次分支 + 重复坐标读取 */
for (let pi = 0; pi < numPoints; pi++) {
const cx = xCoords[pi];
const cy = yCoords[pi];
/** 优化293: 内联 curveBit 计算,消除 !! onCurve + 函数内三元运算 */
const curveBit = (flagsArr[pi] & 1) ? 0 : 128;
const curveBit = (flagAccum[flagWriteBase + pi] & 1) ? 0 : 128;
const dx = cx - prevX;
const dy = cy - prevY;
const flag = calcTripletAndWrite(curveBit, dx, dy, glyphStreamBuf, gsbi);
flagsArr[pi] = flag;
const flag = calcTripletAndWrite(curveBit, dx, dy, glyphAccum, gsBase + gsbi);
/** triplet flag 回写到 flagAccumflagStream 存 triplet flag 而非原始 flag */
flagAccum[flagWriteBase + pi] = flag;
gsbi += TRIPLET_DATA_SIZES[flag & 0x7F];
prevX = cx;
prevY = cy;
}
glyphStreamSize += gsbi + size255UInt16(instructionLength);
glyphAccumLen += gsbi;
glyphStreamSize += gsbi;
/* glyphAccum 追加 encode255UInt16(instructionLength) */
const n = encode255UInt16(instructionLength, _reuseEnc255, 0);
if (glyphAccumLen + n > glyphAccumCap) {
while (glyphAccumLen + n > glyphAccumCap) glyphAccumCap *= 2;
const nb2 = new Uint8Array(glyphAccumCap);
nb2.set(glyphAccum.subarray(0, glyphAccumLen));
glyphAccum = nb2;
}
for (let e = 0; e < n; e++) glyphAccum[glyphAccumLen++] = _reuseEnc255[e];
glyphStreamSize += n;
}
/* 优化277: 存储 glyphStreamBufPass 2 直接 set 拷贝,不再需要 writePointDataByFlag */
glyphInfos[gi] = numberOfContours > 0
? {
composite: false,
numberOfContours,
/** 优化291: 存储 nPointsDeltas 替代 nPointsEncoded延迟编码到 Pass 2 */
nPointsDeltas,
nPointsBytes,
instructions,
flags: flagsArr,
calcXMin, calcYMin, calcXMax, calcYMax,
glyphStreamBuf, glyphStreamBytes: gsbi,
}
: {
composite: false,
@ -507,6 +599,8 @@ function transformGlyfAndLoca(glyfData, locaData, indexFormat, numGlyphs) {
const nContourStreamSize = numGlyphs * 2;
const headerSize = 36;
const flagStreamSize = flagAccumLen;
const instructionStreamSize = instrAccumLen;
const overlapBitmapSize = hasOverlapBitmap ? bboxBitmapSize : 0;
const totalSize = headerSize
+ nContourStreamSize
@ -519,7 +613,6 @@ function transformGlyfAndLoca(glyfData, locaData, indexFormat, numGlyphs) {
+ overlapBitmapSize;
const result = new Uint8Array(totalSize);
/** 优化222: 使用模块级 writeU16/writeI16/writeU32消除闭包分配 */
let pos = 0;
/* Header */
@ -535,10 +628,6 @@ function transformGlyfAndLoca(glyfData, locaData, indexFormat, numGlyphs) {
writeU32(result, bboxBitmapSize + bboxStreamSize, pos); pos += 4;
writeU32(result, instructionStreamSize, pos); pos += 4;
/**
* 优化合并原第 3/4/5 次遍历为 1
* nContourStream + nPointsStream + 所有子流写入合并在单次 glyph 遍历中
*/
const nContourEnd = pos + nContourStreamSize;
let nContourPos = pos;
let nPointsPos = nContourEnd;
@ -556,16 +645,12 @@ function transformGlyfAndLoca(glyfData, locaData, indexFormat, numGlyphs) {
pos += instructionStreamSize;
const overlapBitmapStart = pos;
let flagPos = flagStreamStart;
let glyphPos = glyphStreamStart;
/** 优化294: Pass 2 仅写 nContourStream/nPointsStream/bboxStreamflag/glyph/instruction 整体 set */
let bboxPos = bboxStreamStart;
let instrPos = instructionStreamStart;
for (let gi = 0; gi < numGlyphs; gi++) {
const g = glyphInfos[gi];
/* nContourStream: 每个 glyph 写入 numberOfContours */
/** 优化291: writeI16 内联为直接数组写入 */
if (!g) {
nContourPos += 2;
continue;
@ -579,53 +664,23 @@ function transformGlyfAndLoca(glyfData, locaData, indexFormat, numGlyphs) {
nContourPos += 2;
if (g.composite) {
result.set(glyfData.subarray(g.rawOffset, g.rawOffset + g.rawLength), glyphPos);
glyphPos += g.rawLength;
/** 优化291: bbox 四次 writeI16 内联为直接 view 写入 */
result[bboxPos] = g.xMin >> 8; result[bboxPos + 1] = g.xMin & 0xFF;
result[bboxPos + 2] = g.yMin >> 8; result[bboxPos + 3] = g.yMin & 0xFF;
result[bboxPos + 4] = g.xMax >> 8; result[bboxPos + 5] = g.xMax & 0xFF;
result[bboxPos + 6] = g.yMax >> 8; result[bboxPos + 7] = g.yMax & 0xFF;
bboxPos += 8;
if (g.haveInstructions) {
const instrLen = g.instructions ? g.instructions.length : 0;
if (instrLen > 0) {
result.set(glyfData.subarray(g.instructions.offset, g.instructions.offset + instrLen), instrPos);
instrPos += instrLen;
}
glyphPos += encode255UInt16(instrLen, result, glyphPos);
}
continue;
}
if (g.numberOfContours === 0) continue;
/* 优化291: nPointsStream 直接编码到 result消除临时 buffer 和 memcpy */
const deltas = g.nPointsDeltas;
const nc = g.numberOfContours;
for (let c = 0; c < nc; c++) {
nPointsPos += encode255UInt16(deltas[c], result, nPointsPos);
}
const instrLen = g.instructions ? g.instructions.length : 0;
if (instrLen > 0) {
result.set(glyfData.subarray(g.instructions.offset, g.instructions.offset + instrLen), instrPos);
instrPos += instrLen;
}
/**
* 优化291: flag + glyph 子流 Pass 1 已预写入 glyphStreamBuf直接 set 拷贝
* flag stream 使用 TypedArray.set 替代逐字节循环
*/
result.set(g.flags, flagPos);
flagPos += g.flags.length;
result.set(g.glyphStreamBuf.subarray(0, g.glyphStreamBytes), glyphPos);
glyphPos += g.glyphStreamBytes;
glyphPos += encode255UInt16(instrLen, result, glyphPos);
if (bboxBitmap[gi >> 3] & (0x80 >> (gi & 7))) {
/** 优化291: bbox 四次 writeI16 内联 */
result[bboxPos] = g.calcXMin >> 8; result[bboxPos + 1] = g.calcXMin & 0xFF;
result[bboxPos + 2] = g.calcYMin >> 8; result[bboxPos + 3] = g.calcYMin & 0xFF;
result[bboxPos + 4] = g.calcXMax >> 8; result[bboxPos + 5] = g.calcXMax & 0xFF;
@ -634,6 +689,13 @@ function transformGlyfAndLoca(glyfData, locaData, indexFormat, numGlyphs) {
}
}
/** 优化294: 三个累积缓冲区整体拷贝到 result 对应区域(单次 set 替代 per-glyph set */
result.set(flagAccum.subarray(0, flagStreamSize), flagStreamStart);
result.set(glyphAccum.subarray(0, glyphStreamSize), glyphStreamStart);
if (instructionStreamSize > 0) {
result.set(instrAccum.subarray(0, instructionStreamSize), instructionStreamStart);
}
result.set(bboxBitmap, bboxBitmapStart);
if (hasOverlapBitmap) {