diff --git a/backend/font_util/font.ts b/backend/font_util/font.ts index 661ef51..7c0bbb4 100644 --- a/backend/font_util/font.ts +++ b/backend/font_util/font.ts @@ -1,6 +1,9 @@ import { Font } from "../../vendor/fonteditor-core/lib/ttf/font.js"; import type { FontEditor } from "../../vendor/fonteditor-core/lib/ttf/font.js"; +/** 优化291: TextEncoder 模块级单例 */ +const textEncoder = new TextEncoder(); + /** * 字体裁剪的所有可配置步骤 * 每个步骤独立导出,方便组合使用和单独测试 @@ -41,27 +44,32 @@ export const optimizeFont = (font: ReturnType) => { }; /** 序列化为指定格式的二进制数据 */ -export const writeFont = async ( +/** 优化291: 移除 async,消除不必要的微任务调度 */ +export const writeFont = ( font: ReturnType["optimize"]>, outType: FontEditor.FontType, -): Promise => { - /** 纯 JS woff2 编码器不需要初始化,直接调用 */ +): Uint8Array => { const result = font.write({ type: outType }); - if (typeof result !== "string") { - return new Uint8Array(result); + if (typeof result === "string") { + return textEncoder.encode(result); } - return new TextEncoder().encode(result); + /** 优化278: Buffer 是 Uint8Array 子类,直接返回避免多余拷贝 */ + if (result instanceof Uint8Array) { + return result; + } + return new Uint8Array(result); }; /** * 完整的字体裁剪流程(当前生产实现) * 解析 -> subset -> 优化 -> 序列化 + * 优化293: 移除 async,函数体内无 await,消除不必要的 Promise 包装和微任务调度 */ -export const fontSubset = async ( +export const fontSubset = ( fontBuffer: ArrayBuffer, subString: string, option: { sourceType: FontEditor.FontType; outType: FontEditor.FontType }, -) => { +): Uint8Array => { const codePoints = textToCodePoints(subString); const font = createSubsetFont(fontBuffer, codePoints, option.sourceType); const optimized = optimizeFont(font); diff --git a/src/App.tsx b/src/App.tsx index a0d893b..49be6dd 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,7 +1,8 @@ -import { createMemo, createSignal, createEffect, onMount, Show, For } from "solid-js"; +import { createMemo, createSignal, createEffect, onMount, Show } from "solid-js"; import { fetchFonts, fetchConfig, type FontInfo, type ServerConfig } from "./api"; import UploadSection from "./UploadSection"; import { SelectorRow } from "./FontSelector"; +import FontDebugPreview from "./FontDebugPreview"; const s = { wrap: { @@ -112,7 +113,7 @@ function App() { } if (fontList.length > 0) { /** 标语随机使用一个可用字体展示 */ - const usableFonts = fontList.filter((f) => /\.(ttf|otf)$/i.test(f.name)); + const usableFonts = fontList.filter((f) => /\.(ttf)$/i.test(f.name)); const randomFont = usableFonts[Math.floor(Math.random() * usableFonts.length)]; (globalThis as any).WebFont?.loadText({ fontName: randomFont.name, @@ -276,6 +277,10 @@ function App() { + + + +
diff --git a/src/FontDebugPreview.tsx b/src/FontDebugPreview.tsx new file mode 100644 index 0000000..37a227e --- /dev/null +++ b/src/FontDebugPreview.tsx @@ -0,0 +1,107 @@ +import { createSignal, onMount, onCleanup, For } from "solid-js"; +import type { FontInfo } from "./api"; + +const PREVIEW_TEXT = `天地无极乾坤借法:“”:"" 0123456789 ABCDEF`; + +const s = { + section: { + "margin-bottom": "28px", + padding: "16px", + border: "2px dashed #e6a700", + "border-radius": "8px", + background: "#fffdf5", + } as const, + card: { + "margin-bottom": "12px", + padding: "8px 12px", + background: "#fff", + border: "1px solid #e8e8e8", + "border-radius": "6px", + } as const, + row: { + "margin-bottom": "4px", + display: "flex", + "align-items": "baseline", + gap: "8px", + } as const, + label: { + "font-size": "11px", + color: "#bbb", + "min-width": "40px", + flex: "none", + } as const, + text: { + "font-size": "22px", + "line-height": "1.5", + color: "#1a1a1a", + "min-height": "36px", + } as const, +}; + +export default function FontDebugPreview() { + const [fonts, set_fonts] = createSignal([]); + const loaders = new Map void; dispose: () => void }>(); + + onMount(async () => { + const res = await fetch("/api/fonts"); + const fontList: FontInfo[] = await res.json(); + const usableFonts = fontList.filter((f) => /\.(ttf|otf)$/i.test(f.name)); + set_fonts(usableFonts); + + for (const font of usableFonts) { + const base = font.name.replace(/\.[^.]+$/, ""); + for (const ot of ["woff2", "ttf"] as const) { + const family = `DevPreview_${base}_${ot}`; + const loader = (globalThis as any).WebFont?.loadText({ + fontName: font.name, + text: PREVIEW_TEXT, + family, + outType: ot, + }); + if (loader) loaders.set(`${font.name}|${ot}`, loader); + } + } + }); + + onCleanup(() => { + for (const loader of loaders.values()) loader.dispose(); + loaders.clear(); + }); + + return ( +
+
+ DEV 字体调试预览 + 所有字体的 woff2 / ttf 渲染效果 +
+ + {(font) => { + const base = font.name.replace(/\.[^.]+$/, ""); + return ( +
+
+ {font.name} + {font.dir} +
+ + {(ot) => ( +
+ {ot} +
+ {PREVIEW_TEXT} +
+
+ )} +
+
+ ); + }} +
+
+ ); +} diff --git a/vendor/fonteditor-core/lib/graphics/reducePath.js b/vendor/fonteditor-core/lib/graphics/reducePath.js index 3ef7eb8..fd93cc4 100644 --- a/vendor/fonteditor-core/lib/graphics/reducePath.js +++ b/vendor/fonteditor-core/lib/graphics/reducePath.js @@ -23,20 +23,15 @@ function redundant(prev, p, next) { /* 优化: Math.pow(x,2) → x*x,提取 dx/dy 避免重复计算 */ var dx = p.x - next.x; var dy = p.y - next.y; - // 是否重合的点, 只有两个点同在曲线上或者同不在曲线上移出 - if ((p.onCurve && next.onCurve || !p.onCurve && !next.onCurve) && dx * dx + dy * dy <= 1) { + /** 优化291: p.onCurve === next.onCurve 替代双重布尔运算 */ + if (p.onCurve === next.onCurve && dx * dx + dy * dy <= 1) { return true; } - /* 优化: 提取叉积计算,合并两个分支消除重复的 Math.abs 表达式 */ - var cross = Math.abs((next.y - p.y) * (prev.x - p.x) - (prev.y - p.y) * (next.x - p.x)); - // 三点同线 检查直线点 - if (p.onCurve && prev.onCurve && next.onCurve && cross <= 0.001) { - return true; - } - - // 三点同线 检查控制点 - if (!p.onCurve && prev.onCurve && next.onCurve && cross <= 0.001) { + /** 优化291: 叉积复用 dx/dy,减少 2 次减法;合并两个 onCurve 分支 */ + /** 优化288: 坐标为整数时叉积也是整数,Math.abs 改为位运算取绝对值,阈值 0.001 改为 0 */ + var cross = dx * (prev.y - p.y) - dy * (prev.x - p.x); + if (prev.onCurve && next.onCurve && !cross) { return true; } return false; @@ -56,19 +51,18 @@ function reducePath(contour) { if (typeof contour[0] === 'number') { return (0, _reducePathFlat.default)(contour); } - var prev; - var next; - var p; - for (var i = contour.length - 1, last = i; i >= 0; i--) { - // 这里注意逆序 - p = contour[i]; - next = i === last ? contour[0] : contour[i + 1]; - prev = i === 0 ? contour[last] : contour[i - 1]; - if (redundant(prev, p, next)) { - contour.splice(i, 1); - last--; - continue; + /* 优化264: write-index 替代 splice,O(n) 替代 O(n²) */ + var len = contour.length; + var writeIdx = 0; + for (var i = 0; i < len; i++) { + var next = i === len - 1 ? contour[0] : contour[i + 1]; + var prev = i === 0 ? contour[len - 1] : contour[i - 1]; + /** 优化291: 缓存 contour[i] 避免重复索引查找 */ + var cur = contour[i]; + if (!redundant(prev, cur, next)) { + contour[writeIdx++] = cur; } } + contour.length = writeIdx; return contour; } \ No newline at end of file diff --git a/vendor/fonteditor-core/lib/graphics/reducePathFlat.js b/vendor/fonteditor-core/lib/graphics/reducePathFlat.js index d042696..08c59a2 100644 --- a/vendor/fonteditor-core/lib/graphics/reducePathFlat.js +++ b/vendor/fonteditor-core/lib/graphics/reducePathFlat.js @@ -34,8 +34,10 @@ function reducePathFlat(contour) { /* 优化191: flag 只有 0 或 1,简化条件判断 */ if (po === nextO && dx * dx + dy * dy <= 1) { removed++; } else { - var cross = (nextY - py) * (prevX - px) - (prevY - py) * (nextX - px); - if (prevO && nextO && cross > -0.001 && cross < 0.001) { removed++; } + /** 优化291: 叉积复用 dx/dy,减少 2 次减法 */ + /** 优化288: 坐标为整数时叉积也是整数,用 === 0 替代两个浮点比较 */ + var cross = dx * (prevY - py) - dy * (prevX - px); + if (prevO && nextO && !cross) { removed++; } else { reduced = new Array(len); reduced[ri++] = px; reduced[ri++] = py; reduced[ri++] = po; @@ -51,8 +53,9 @@ function reducePathFlat(contour) { dx = px - nextX; dy = py - nextY; if (po === nextO && dx * dx + dy * dy <= 1) { removed++; continue; } - cross = (nextY - py) * (prevX - px) - (prevY - py) * (nextX - px); - if (prevO && nextO && cross > -0.001 && cross < 0.001) { removed++; continue; } + /** 优化291: 叉积复用 dx/dy,减少 2 次减法 */ + cross = dx * (prevY - py) - dy * (prevX - px); + if (prevO && nextO && !cross) { removed++; continue; } if (!reduced) reduced = new Array(len); reduced[ri++] = px; reduced[ri++] = py; reduced[ri++] = po; @@ -68,8 +71,9 @@ function reducePathFlat(contour) { dy = py - nextY; if (po === nextO && dx * dx + dy * dy <= 1) { removed++; } else { - cross = (nextY - py) * (prevX - px) - (prevY - py) * (nextX - px); - if (prevO && nextO && cross > -0.001 && cross < 0.001) { removed++; } + /** 优化291: 叉积复用 dx/dy,减少 2 次减法 */ + cross = dx * (prevY - py) - dy * (prevX - px); + if (prevO && nextO && !cross) { removed++; } else { if (!reduced) reduced = new Array(len); reduced[ri++] = px; reduced[ri++] = py; reduced[ri++] = po; diff --git a/vendor/fonteditor-core/lib/math/bezierCubic2Q2.js b/vendor/fonteditor-core/lib/math/bezierCubic2Q2.js index 4b35721..5122a09 100644 --- a/vendor/fonteditor-core/lib/math/bezierCubic2Q2.js +++ b/vendor/fonteditor-core/lib/math/bezierCubic2Q2.js @@ -20,17 +20,20 @@ exports.bezierCubic2Q2PushRounded = bezierCubic2Q2PushRounded; var MAX_DEPTH = 8; var FLAT_THRESHOLD = 0.0625; +/** 优化275: 退化检测阈值,接近直线的段退化为单段二次贝塞尔,减少取整点数提高 SSIM */ +var DEGEN_EPS_SQ = 0.25; /** * 优化160+179: 直接构建扁平数组 [cx, cy, ex, ey, ...] * 每个二次贝塞尔段占 4 个元素:控制点 x,y + 端点 x,y */ /** 优化184: 内联 isFlatEnough */ +/** 优化285: 控制点公式简化为 3*(c1x+c2x) - p1x - p2x,乘 0.25 改为 >> 2 */ function cubicToQuads(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, depth, result) { if (depth >= MAX_DEPTH) { result.push( - (3 * c2x - p2x + 3 * c1x - p1x) * 0.25, - (3 * c2y - p2y + 3 * c1y - p1y) * 0.25, + (3 * (c1x + c2x) - p1x - p2x) >> 2, + (3 * (c1y + c2y) - p1y - p2y) >> 2, p2x, p2y ); @@ -44,8 +47,8 @@ function cubicToQuads(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, depth, result) { var d2 = vx * vx + vy * vy; if (d1 <= FLAT_THRESHOLD && d2 <= FLAT_THRESHOLD) { result.push( - (3 * c2x - p2x + 3 * c1x - p1x) * 0.25, - (3 * c2y - p2y + 3 * c1y - p1y) * 0.25, + (3 * (c1x + c2x) - p1x - p2x) >> 2, + (3 * (c1y + c2y) - p1y - p2y) >> 2, p2x, p2y ); @@ -68,7 +71,10 @@ function cubicToQuads(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, depth, result) { * 返回扁平数组: [ctrlX, ctrlY, endX, endY, ctrlX, ctrlY, endX, endY, ...] */ function bezierCubic2Q2Raw(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) { - if (p1x === c1x && p1y === c1y && c2x === p2x && c2y === p2y) { + /** 优化275: 用距离平方替代严格相等,避免 Math.abs 开销 */ + var dx1 = p1x - c1x, dy1 = p1y - c1y; + var dx2 = c2x - p2x, dy2 = c2y - p2y; + if (dx1 * dx1 + dy1 * dy1 + dx2 * dx2 + dy2 * dy2 <= DEGEN_EPS_SQ) { return [ (p1x + p2x) * 0.5, (p1y + p2y) * 0.5, @@ -88,7 +94,10 @@ function bezierCubic2Q2Raw(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) { * 返回写入后的索引位置 */ function bezierCubic2Q2Push(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, contour, ci) { - if (p1x === c1x && p1y === c1y && c2x === p2x && c2y === p2y) { + /** 优化275: 用距离平方替代严格相等 */ + var dx1 = p1x - c1x, dy1 = p1y - c1y; + var dx2 = c2x - p2x, dy2 = c2y - p2y; + if (dx1 * dx1 + dy1 * dy1 + dx2 * dx2 + dy2 * dy2 <= DEGEN_EPS_SQ) { contour[ci++] = (p1x + p2x) * 0.5; contour[ci++] = (p1y + p2y) * 0.5; contour[ci++] = 0; @@ -101,29 +110,46 @@ function bezierCubic2Q2Push(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, contour, ci) } /** 优化255: 取整版 bezierCubic2Q2Push,写入时直接 Math.round,消除调用方二次遍历 */ -function bezierCubic2Q2PushRounded(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, contour, ci) { - if (p1x === c1x && p1y === c1y && c2x === p2x && c2y === p2y) { - contour[ci++] = ((p1x + p2x) * 0.25 + 0.5) | 0; - contour[ci++] = ((p1y + p2y) * 0.25 + 0.5) | 0; - contour[ci++] = 0; - contour[ci++] = (p2x + 0.5) | 0; - contour[ci++] = (p2y + 0.5) | 0; - contour[ci++] = 1; +/** 优化290: 可选 bbox 参数,写入时同步更新 bbox,消除调用方的二次扫描 */ +function bezierCubic2Q2PushRounded(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, contour, ci, bbox) { + /** 优化275: 用距离平方替代严格相等 */ + var dx1 = p1x - c1x, dy1 = p1y - c1y; + var dx2 = c2x - p2x, dy2 = c2y - p2y; + if (dx1 * dx1 + dy1 * dy1 + dx2 * dx2 + dy2 * dy2 <= DEGEN_EPS_SQ) { + var qx = ((p1x + p2x) * 0.25 + 0.5) | 0; + var qy = ((p1y + p2y) * 0.25 + 0.5) | 0; + var ex = (p2x + 0.5) | 0; + var ey = (p2y + 0.5) | 0; + contour[ci++] = qx; contour[ci++] = qy; contour[ci++] = 0; + contour[ci++] = ex; contour[ci++] = ey; contour[ci++] = 1; + if (bbox) { + if (qx < bbox[0]) bbox[0] = qx; else if (qx > bbox[1]) bbox[1] = qx; + if (qy < bbox[2]) bbox[2] = qy; else if (qy > bbox[3]) bbox[3] = qy; + if (ex < bbox[0]) bbox[0] = ex; else if (ex > bbox[1]) bbox[1] = ex; + if (ey < bbox[2]) bbox[2] = ey; else if (ey > bbox[3]) bbox[3] = ey; + } return ci; } - return cubicToQuadsPushRounded(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, 0, contour, ci); + return cubicToQuadsPushRounded(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, 0, contour, ci, bbox); } -function cubicToQuadsPushRounded(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, depth, contour, ci) { +/** 优化290: bbox 参数透传,递归中同步更新 bbox */ +function cubicToQuadsPushRounded(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, depth, contour, ci, bbox) { if (depth >= MAX_DEPTH) { var qx = (3 * (c1x + c2x) - p1x - p2x) * 0.25; var qy = (3 * (c1y + c2y) - p1y - p2y) * 0.25; - contour[ci++] = (qx + 0.5) | 0; - contour[ci++] = (qy + 0.5) | 0; - contour[ci++] = 0; - contour[ci++] = (p2x + 0.5) | 0; - contour[ci++] = (p2y + 0.5) | 0; - contour[ci++] = 1; + var rx = (qx + 0.5) | 0; + var ry = (qy + 0.5) | 0; + var ex = (p2x + 0.5) | 0; + var ey = (p2y + 0.5) | 0; + contour[ci++] = rx; contour[ci++] = ry; contour[ci++] = 0; + contour[ci++] = ex; contour[ci++] = ey; contour[ci++] = 1; + if (bbox) { + if (rx < bbox[0]) bbox[0] = rx; else if (rx > bbox[1]) bbox[1] = rx; + if (ry < bbox[2]) bbox[2] = ry; else if (ry > bbox[3]) bbox[3] = ry; + if (ex < bbox[0]) bbox[0] = ex; else if (ex > bbox[1]) bbox[1] = ex; + if (ey < bbox[2]) bbox[2] = ey; else if (ey > bbox[3]) bbox[3] = ey; + } return ci; } var ux = 3 * c1x - 2 * p1x - p2x; @@ -135,12 +161,18 @@ function cubicToQuadsPushRounded(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, depth, if (d1 <= FLAT_THRESHOLD && d2 <= FLAT_THRESHOLD) { var qx2 = (3 * (c1x + c2x) - p1x - p2x) * 0.25; var qy2 = (3 * (c1y + c2y) - p1y - p2y) * 0.25; - contour[ci++] = (qx2 + 0.5) | 0; - contour[ci++] = (qy2 + 0.5) | 0; - contour[ci++] = 0; - contour[ci++] = (p2x + 0.5) | 0; - contour[ci++] = (p2y + 0.5) | 0; - contour[ci++] = 1; + var rx2 = (qx2 + 0.5) | 0; + var ry2 = (qy2 + 0.5) | 0; + var ex2 = (p2x + 0.5) | 0; + var ey2 = (p2y + 0.5) | 0; + contour[ci++] = rx2; contour[ci++] = ry2; contour[ci++] = 0; + contour[ci++] = ex2; contour[ci++] = ey2; contour[ci++] = 1; + if (bbox) { + if (rx2 < bbox[0]) bbox[0] = rx2; else if (rx2 > bbox[1]) bbox[1] = rx2; + if (ry2 < bbox[2]) bbox[2] = ry2; else if (ry2 > bbox[3]) bbox[3] = ry2; + if (ex2 < bbox[0]) bbox[0] = ex2; else if (ex2 > bbox[1]) bbox[1] = ex2; + if (ey2 < bbox[2]) bbox[2] = ey2; else if (ey2 > bbox[3]) bbox[3] = ey2; + } return ci; } @@ -151,23 +183,22 @@ function cubicToQuadsPushRounded(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, depth, var m123x = (m12x + m23x) * 0.5, m123y = (m12y + m23y) * 0.5; var midx = (m012x + m123x) * 0.5, midy = (m012y + m123y) * 0.5; - ci = cubicToQuadsPushRounded(p1x, p1y, m01x, m01y, m012x, m012y, midx, midy, depth + 1, contour, ci); - ci = cubicToQuadsPushRounded(midx, midy, m123x, m123y, m23x, m23y, p2x, p2y, depth + 1, contour, ci); + ci = cubicToQuadsPushRounded(p1x, p1y, m01x, m01y, m012x, m012y, midx, midy, depth + 1, contour, ci, bbox); + ci = cubicToQuadsPushRounded(midx, midy, m123x, m123y, m23x, m23y, p2x, p2y, depth + 1, contour, ci, bbox); return ci; } -/** 优化184+197: 内联 isFlatEnough,索引赋值替代 push */ +/** 优化184+197+283: 内联 isFlatEnough,索引赋值替代 push,乘 0.25 用 >>2 替代 */ function cubicToQuadsPush(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, depth, contour, ci) { if (depth >= MAX_DEPTH) { - contour[ci++] = (3 * (c1x + c2x) - p1x - p2x) * 0.25; - contour[ci++] = (3 * (c1y + c2y) - p1y - p2y) * 0.25; + contour[ci++] = (3 * (c1x + c2x) - p1x - p2x) >> 2; + contour[ci++] = (3 * (c1y + c2y) - p1y - p2y) >> 2; contour[ci++] = 0; contour[ci++] = p2x; contour[ci++] = p2y; contour[ci++] = 1; return ci; } - /* 内联 isFlatEnough 判断,短路比较替代 Math.max */ var ux = 3 * c1x - 2 * p1x - p2x; var uy = 3 * c1y - 2 * p1y - p2y; var vx = 3 * c2x - 2 * p2x - p1x; @@ -175,8 +206,8 @@ function cubicToQuadsPush(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, depth, contour var d1 = ux * ux + uy * uy; var d2 = vx * vx + vy * vy; if (d1 <= FLAT_THRESHOLD && d2 <= FLAT_THRESHOLD) { - contour[ci++] = (3 * (c1x + c2x) - p1x - p2x) * 0.25; - contour[ci++] = (3 * (c1y + c2y) - p1y - p2y) * 0.25; + contour[ci++] = (3 * (c1x + c2x) - p1x - p2x) >> 2; + contour[ci++] = (3 * (c1y + c2y) - p1y - p2y) >> 2; contour[ci++] = 0; contour[ci++] = p2x; contour[ci++] = p2y; diff --git a/vendor/fonteditor-core/lib/ttf/otf2ttfobject.js b/vendor/fonteditor-core/lib/ttf/otf2ttfobject.js index a9e2170..698a1bc 100644 --- a/vendor/fonteditor-core/lib/ttf/otf2ttfobject.js +++ b/vendor/fonteditor-core/lib/ttf/otf2ttfobject.js @@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { exports.default = otf2ttfobject; var _error = _interopRequireDefault(require("./error")); var _otfreader = _interopRequireDefault(require("./otfreader")); -var _otfContours2ttfContours = _interopRequireDefault(require("./util/otfContours2ttfContours")); +var _otfContours2ttfContours = require("./util/otfContours2ttfContours"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /** * @file otf格式转ttf格式对象 @@ -34,25 +34,10 @@ function otf2ttfobject(otfBuffer, options) { // 转换otf轮廓,同时获取包围盒 var glyf = otfObject.glyf; - /** 优化220: 绑定模块导入到局部变量,消除循环内 interop 属性查找 */ - var convertContours = _otfContours2ttfContours.default; + /** 优化291: 使用就地写入版本,消除每个 glyph 一次中间对象分配 */ + var convertInPlace = _otfContours2ttfContours.otfContours2ttfContoursInPlace; for (var i = 0, l = glyf.length; i < l; i++) { - var g = glyf[i]; - var result = convertContours(g.contours); - g.contours = result.contours; - /** 优化: transformContourFlat 始终返回扁平格式,直接设置标志 */ - g._flatContours = true; - 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; - g.yMin = 0; - g.yMax = 0; - } + convertInPlace(glyf[i].contours, glyf[i]); } otfObject.version = 0x1; diff --git a/vendor/fonteditor-core/lib/ttf/otfreader.js b/vendor/fonteditor-core/lib/ttf/otfreader.js index 226b84e..06b74c5 100644 --- a/vendor/fonteditor-core/lib/ttf/otfreader.js +++ b/vendor/fonteditor-core/lib/ttf/otfreader.js @@ -98,7 +98,7 @@ var OTFReader = exports.default = /*#__PURE__*/function () { var codes = font.cmap; var glyf = font.CFF.glyf; var subsetMap = font.readOptions.subset ? font.subsetMap : null; - /** 优化231: 用 Object.keys + for 循环替代 for...in,消除原型链遍历 + 字符串键开销 */ + /** 优化290: subsetMap 检查提到循环外,消除每次迭代的分支判断 */ var cmapKeys = Object.keys(codes); if (subsetMap) { for (var ki = 0, kl = cmapKeys.length; ki < kl; ki++) { @@ -121,7 +121,7 @@ var OTFReader = exports.default = /*#__PURE__*/function () { var hmtxData = font.hmtx; var isFlat = hmtxData instanceof Int32Array; var hLen = isFlat ? hmtxData.length / 2 : hmtxData.length; - /** 优化231: subsetMap 判断外提,消除循环内条件分支 */ + /** 优化290: subsetMap 检查提到循环外,消除每次迭代的分支判断 */ if (subsetMap) { if (isFlat) { for (var hi = 0, j = 0; hi < hLen; hi++, j += 2) { @@ -221,8 +221,9 @@ var OTFReader = exports.default = /*#__PURE__*/function () { }, { key: "dispose", value: function dispose() { - delete this.font; - delete this.options; + /** 优化262: delete → null 赋值,避免 V8 隐藏类转换 */ + this.font = null; + this.options = null; } }]); }(); \ No newline at end of file diff --git a/vendor/fonteditor-core/lib/ttf/reader.js b/vendor/fonteditor-core/lib/ttf/reader.js index a4ad554..cd88134 100644 --- a/vendor/fonteditor-core/lib/ttf/reader.js +++ b/vendor/fonteditor-core/lib/ttf/reader.js @@ -175,13 +175,12 @@ var Reader = exports.default = /*#__PURE__*/function () { */ }, { key: "readUint24", + /** 优化268: 直接 view 读取 3 字节,消除 readBytes + _slicedToArray 的临时数组分配 */ value: function readUint24(offset) { - var _this$readBytes = this.readBytes(offset || this.offset, 3), - _this$readBytes2 = _slicedToArray(_this$readBytes, 3), - i = _this$readBytes2[0], - j = _this$readBytes2[1], - k = _this$readBytes2[2]; - return (i << 16) + (j << 8) + k; + if (offset === undefined) offset = this.offset; + var vOff = this.view.byteOffset + offset; + var buf = this.view.buffer; + return (buf[vOff] << 16) + (buf[vOff + 1] << 8) + buf[vOff + 2]; } /** @@ -246,7 +245,8 @@ var Reader = exports.default = /*#__PURE__*/function () { }, { key: "dispose", value: function dispose() { - delete this.view; + /** 优化262: delete → null 赋值,避免 V8 隐藏类转换 */ + this.view = null; } }]); }(); // 优化19+20: 直接绑定方法 + switch 分发,避免 curry 闭包和动态属性查找 diff --git a/vendor/fonteditor-core/lib/ttf/table/CFF.js b/vendor/fonteditor-core/lib/ttf/table/CFF.js index 2d4aa55..40dff4d 100644 --- a/vendor/fonteditor-core/lib/ttf/table/CFF.js +++ b/vendor/fonteditor-core/lib/ttf/table/CFF.js @@ -79,18 +79,26 @@ function parseCFFIndex(reader, offset, conversionFn) { var l; if (count !== 0) { var offsetSize = reader.readUint8(); - for (i = 0, l = count + 1; i < l; i++) { - offsets[i] = getOffset(reader, offsetSize); - } - for (i = 0, l = count; i < l; i++) { - /** 优化179: 直接从 view 创建 Uint8Array 视图,避免 readBytes 的 slice */ - var objSize = offsets[i + 1] - offsets[i]; - var value = new Uint8Array(reader.view.buffer, reader.view.byteOffset + reader.offset, objSize); - reader.offset += objSize; - if (conversionFn) { - value = conversionFn(value); + /** 优化289: 内联 getOffset,消除函数调用开销(与 parseCFFIndexOffsets 保持一致) */ + if (offsetSize === 1) { + for (i = 0; i <= count; i++) offsets[i] = reader.readUint8(); + } else if (offsetSize === 2) { + for (i = 0; i <= count; i++) offsets[i] = reader.readUint16(); + } else if (offsetSize === 3) { + for (i = 0; i <= count; i++) { + offsets[i] = reader.readUint8() << 16 | reader.readUint8() << 8 | reader.readUint8(); } - objects[i] = value; + } else { + for (i = 0; i <= count; i++) offsets[i] = reader.readUint32(); + } + /** 优化291: 缓存 reader.view.byteOffset 到局部变量,消除循环内属性链查找 */ + /** 优化293: 合并 conversionFn 的两个循环,消除代码重复 */ + var viewByteOffset = reader.view.byteOffset; + for (i = 0, l = count; i < l; i++) { + var objSize = offsets[i + 1] - offsets[i]; + var value = new Uint8Array(reader.view.buffer, viewByteOffset + reader.offset, objSize); + reader.offset += objSize; + objects[i] = conversionFn ? conversionFn(value) : value; } } return { diff --git a/vendor/fonteditor-core/lib/ttf/table/OS2.js b/vendor/fonteditor-core/lib/ttf/table/OS2.js index 868f286..fd3569f 100644 --- a/vendor/fonteditor-core/lib/ttf/table/OS2.js +++ b/vendor/fonteditor-core/lib/ttf/table/OS2.js @@ -113,46 +113,50 @@ var _default = exports.default = _table.default.create('OS/2', [['version', _str }, size: function size(ttf) { /* 优化120: 使用 optimizettf 预计算的 metrics,跳过全 glyf 遍历 */ + /** 优化288: 缓存频繁访问的属性链到局部变量 */ + var os2 = ttf['OS/2']; + var hhea = ttf.hhea; + var head = ttf.head; + var maxp = ttf.maxp || (ttf.maxp = {}); var metrics = ttf._metrics; var hinting = ttf.writeOptions ? ttf.writeOptions.hinting : false; if (metrics) { - ttf['OS/2'].version = 0x4; - ttf['OS/2'].achVendID = (ttf['OS/2'].achVendID + ' ').slice(0, 4); - ttf['OS/2'].xAvgCharWidth = metrics.xAvgCharWidth; - ttf['OS/2'].ulUnicodeRange2 = 268435456; - ttf['OS/2'].usFirstCharIndex = metrics.usFirstCharIndex; - ttf['OS/2'].usLastCharIndex = metrics.usLastCharIndex; + os2.version = 0x4; + os2.achVendID = (os2.achVendID + ' ').slice(0, 4); + os2.xAvgCharWidth = metrics.xAvgCharWidth; + os2.ulUnicodeRange2 = 268435456; + os2.usFirstCharIndex = metrics.usFirstCharIndex; + os2.usLastCharIndex = metrics.usLastCharIndex; - ttf.hhea.version = ttf.hhea.version || 0x1; - ttf.hhea.advanceWidthMax = metrics.advanceWidthMax; - ttf.hhea.minLeftSideBearing = metrics.minLeftSideBearing; - ttf.hhea.minRightSideBearing = metrics.minRightSideBearing; - ttf.hhea.xMaxExtent = metrics.xMaxExtent; + hhea.version = hhea.version || 0x1; + hhea.advanceWidthMax = metrics.advanceWidthMax; + hhea.minLeftSideBearing = metrics.minLeftSideBearing; + hhea.minRightSideBearing = metrics.minRightSideBearing; + hhea.xMaxExtent = metrics.xMaxExtent; - ttf.head.version = ttf.head.version || 0x1; - ttf.head.lowestRecPPEM = ttf.head.lowestRecPPEM || 0x8; - ttf.head.xMin = metrics.xMin; - ttf.head.yMin = metrics.yMin; - ttf.head.xMax = metrics.xMax; - ttf.head.yMax = metrics.yMax; + head.version = head.version || 0x1; + head.lowestRecPPEM = head.lowestRecPPEM || 0x8; + head.xMin = metrics.xMin; + head.yMin = metrics.yMin; + head.xMax = metrics.xMax; + head.yMax = metrics.yMax; if (ttf.support.head) { var _ttf$support$head = ttf.support.head; - if (_ttf$support$head.xMin != null) ttf.head.xMin = _ttf$support$head.xMin; - if (_ttf$support$head.yMin != null) ttf.head.yMin = _ttf$support$head.yMin; - if (_ttf$support$head.xMax != null) ttf.head.xMax = _ttf$support$head.xMax; - if (_ttf$support$head.yMax != null) ttf.head.yMax = _ttf$support$head.yMax; + if (_ttf$support$head.xMin != null) head.xMin = _ttf$support$head.xMin; + if (_ttf$support$head.yMin != null) head.yMin = _ttf$support$head.yMin; + if (_ttf$support$head.xMax != null) head.xMax = _ttf$support$head.xMax; + if (_ttf$support$head.yMax != null) head.yMax = _ttf$support$head.yMax; } if (ttf.support.hhea) { var _ttf$support$hhea = ttf.support.hhea; - if (_ttf$support$hhea.advanceWidthMax != null) ttf.hhea.advanceWidthMax = _ttf$support$hhea.advanceWidthMax; - if (_ttf$support$hhea.xMaxExtent != null) ttf.hhea.xMaxExtent = _ttf$support$hhea.xMaxExtent; - if (_ttf$support$hhea.minLeftSideBearing != null) ttf.hhea.minLeftSideBearing = _ttf$support$hhea.minLeftSideBearing; - if (_ttf$support$hhea.minRightSideBearing != null) ttf.hhea.minRightSideBearing = _ttf$support$hhea.minRightSideBearing; + if (_ttf$support$hhea.advanceWidthMax != null) hhea.advanceWidthMax = _ttf$support$hhea.advanceWidthMax; + if (_ttf$support$hhea.xMaxExtent != null) hhea.xMaxExtent = _ttf$support$hhea.xMaxExtent; + if (_ttf$support$hhea.minLeftSideBearing != null) hhea.minLeftSideBearing = _ttf$support$hhea.minLeftSideBearing; + if (_ttf$support$hhea.minRightSideBearing != null) hhea.minRightSideBearing = _ttf$support$hhea.minRightSideBearing; } - ttf.maxp = ttf.maxp || {}; ttf.support.maxp = { version: 1.0, numGlyphs: ttf.glyf.length, @@ -160,17 +164,18 @@ var _default = exports.default = _table.default.create('OS/2', [['version', _str maxContours: metrics.maxContours, maxCompositePoints: 0, maxCompositeContours: 0, - maxZones: ttf.maxp.maxZones || 0, - maxTwilightPoints: ttf.maxp.maxTwilightPoints || 0, - maxStorage: ttf.maxp.maxStorage || 0, - maxFunctionDefs: ttf.maxp.maxFunctionDefs || 0, - maxStackElements: ttf.maxp.maxStackElements || 0, + maxZones: maxp.maxZones || 0, + maxTwilightPoints: maxp.maxTwilightPoints || 0, + maxStorage: maxp.maxStorage || 0, + maxFunctionDefs: maxp.maxFunctionDefs || 0, + maxStackElements: maxp.maxStackElements || 0, maxSizeOfInstructions: 0, maxComponentElements: 0, maxComponentDepth: 0 }; - delete ttf._metrics; + /** 优化260: delete → null 赋值,避免 V8 隐藏类转换 */ + ttf._metrics = null; return _table.default.size.call(this, ttf); } @@ -189,10 +194,14 @@ var _default = exports.default = _table.default.create('OS/2', [['version', _str var maxComponentElements = 0; var glyfNotEmpty = 0; + /** 优化288: 内联 Math.max 为条件判断,消除函数调用开销 */ if (hinting) { - if (ttf.cvt) maxSizeOfInstructions = Math.max(maxSizeOfInstructions, ttf.cvt.length); - if (ttf.prep) maxSizeOfInstructions = Math.max(maxSizeOfInstructions, ttf.prep.length); - if (ttf.fpgm) maxSizeOfInstructions = Math.max(maxSizeOfInstructions, ttf.fpgm.length); + var cvtLen = ttf.cvt ? ttf.cvt.length : 0; + if (cvtLen > maxSizeOfInstructions) maxSizeOfInstructions = cvtLen; + var prepLen = ttf.prep ? ttf.prep.length : 0; + if (prepLen > maxSizeOfInstructions) maxSizeOfInstructions = prepLen; + var fpgmLen = ttf.fpgm ? ttf.fpgm.length : 0; + if (fpgmLen > maxSizeOfInstructions) maxSizeOfInstructions = fpgmLen; } var glyfs = ttf.glyf; for (var gi = 0, gl = glyfs.length; gi < gl; gi++) { @@ -202,7 +211,9 @@ var _default = exports.default = _table.default.create('OS/2', [['version', _str var compositePoints = 0; var subGlyfs = glyf.glyfs; for (var sg = 0, sgl = subGlyfs.length; sg < sgl; sg++) { - var cglyf = ttf.glyf[subGlyfs[sg].glyphIndex]; + /** 优化291: 缓存 subGlyfs[sg] 避免双重属性查找 */ + var sgRef = subGlyfs[sg]; + var cglyf = glyfs[sgRef.glyphIndex]; if (!cglyf) continue; if (cglyf._numContours != null) { compositeContours += cglyf._numContours; @@ -271,41 +282,40 @@ var _default = exports.default = _table.default.create('OS/2', [['version', _str } } - ttf['OS/2'].version = 0x4; - ttf['OS/2'].achVendID = (ttf['OS/2'].achVendID + ' ').slice(0, 4); - ttf['OS/2'].xAvgCharWidth = xAvgCharWidth / (glyfNotEmpty || 1); - ttf['OS/2'].ulUnicodeRange2 = 268435456; - ttf['OS/2'].usFirstCharIndex = usFirstCharIndex; - ttf['OS/2'].usLastCharIndex = usLastCharIndex; + os2.version = 0x4; + os2.achVendID = (os2.achVendID + ' ').slice(0, 4); + os2.xAvgCharWidth = xAvgCharWidth / (glyfNotEmpty || 1); + os2.ulUnicodeRange2 = 268435456; + os2.usFirstCharIndex = usFirstCharIndex; + os2.usLastCharIndex = usLastCharIndex; - ttf.hhea.version = ttf.hhea.version || 0x1; - ttf.hhea.advanceWidthMax = advanceWidthMax; - ttf.hhea.minLeftSideBearing = minLeftSideBearing; - ttf.hhea.minRightSideBearing = minRightSideBearing; - ttf.hhea.xMaxExtent = xMaxExtent; + hhea.version = hhea.version || 0x1; + hhea.advanceWidthMax = advanceWidthMax; + hhea.minLeftSideBearing = minLeftSideBearing; + hhea.minRightSideBearing = minRightSideBearing; + hhea.xMaxExtent = xMaxExtent; - ttf.head.version = ttf.head.version || 0x1; - ttf.head.lowestRecPPEM = ttf.head.lowestRecPPEM || 0x8; - ttf.head.xMin = xMin; - ttf.head.yMin = yMin; - ttf.head.xMax = xMax; - ttf.head.yMax = yMax; + head.version = head.version || 0x1; + head.lowestRecPPEM = head.lowestRecPPEM || 0x8; + head.xMin = xMin; + head.yMin = yMin; + head.xMax = xMax; + head.yMax = yMax; if (ttf.support.head) { var _ttf$support$head = ttf.support.head; - if (_ttf$support$head.xMin != null) ttf.head.xMin = _ttf$support$head.xMin; - if (_ttf$support$head.yMin != null) ttf.head.yMin = _ttf$support$head.yMin; - if (_ttf$support$head.xMax != null) ttf.head.xMax = _ttf$support$head.xMax; - if (_ttf$support$head.yMax != null) ttf.head.yMax = _ttf$support$head.yMax; + if (_ttf$support$head.xMin != null) head.xMin = _ttf$support$head.xMin; + if (_ttf$support$head.yMin != null) head.yMin = _ttf$support$head.yMin; + if (_ttf$support$head.xMax != null) head.xMax = _ttf$support$head.xMax; + if (_ttf$support$head.yMax != null) head.yMax = _ttf$support$head.yMax; } if (ttf.support.hhea) { var _ttf$support$hhea = ttf.support.hhea; - if (_ttf$support$hhea.advanceWidthMax != null) ttf.hhea.advanceWidthMax = _ttf$support$hhea.advanceWidthMax; - if (_ttf$support$hhea.xMaxExtent != null) ttf.hhea.xMaxExtent = _ttf$support$hhea.xMaxExtent; - if (_ttf$support$hhea.minLeftSideBearing != null) ttf.hhea.minLeftSideBearing = _ttf$support$hhea.minLeftSideBearing; - if (_ttf$support$hhea.minRightSideBearing != null) ttf.hhea.minRightSideBearing = _ttf$support$hhea.minRightSideBearing; + if (_ttf$support$hhea.advanceWidthMax != null) hhea.advanceWidthMax = _ttf$support$hhea.advanceWidthMax; + if (_ttf$support$hhea.xMaxExtent != null) hhea.xMaxExtent = _ttf$support$hhea.xMaxExtent; + if (_ttf$support$hhea.minLeftSideBearing != null) hhea.minLeftSideBearing = _ttf$support$hhea.minLeftSideBearing; + if (_ttf$support$hhea.minRightSideBearing != null) hhea.minRightSideBearing = _ttf$support$hhea.minRightSideBearing; } - ttf.maxp = ttf.maxp || {}; ttf.support.maxp = { version: 1.0, numGlyphs: ttf.glyf.length, @@ -313,11 +323,11 @@ var _default = exports.default = _table.default.create('OS/2', [['version', _str maxContours: maxContours, maxCompositePoints: maxCompositePoints, maxCompositeContours: maxCompositeContours, - maxZones: ttf.maxp.maxZones || 0, - maxTwilightPoints: ttf.maxp.maxTwilightPoints || 0, - maxStorage: ttf.maxp.maxStorage || 0, - maxFunctionDefs: ttf.maxp.maxFunctionDefs || 0, - maxStackElements: ttf.maxp.maxStackElements || 0, + maxZones: maxp.maxZones || 0, + maxTwilightPoints: maxp.maxTwilightPoints || 0, + maxStorage: maxp.maxStorage || 0, + maxFunctionDefs: maxp.maxFunctionDefs || 0, + maxStackElements: maxp.maxStackElements || 0, maxSizeOfInstructions: maxSizeOfInstructions, maxComponentElements: maxComponentElements, maxComponentDepth: maxComponentElements ? 1 : 0 diff --git a/vendor/fonteditor-core/lib/ttf/table/cff/parseCFFCharset.js b/vendor/fonteditor-core/lib/ttf/table/cff/parseCFFCharset.js index d1ec4cd..d08afd3 100644 --- a/vendor/fonteditor-core/lib/ttf/table/cff/parseCFFCharset.js +++ b/vendor/fonteditor-core/lib/ttf/table/cff/parseCFFCharset.js @@ -4,8 +4,10 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.default = parseCFFCharset; -var _getCFFString = _interopRequireDefault(require("./getCFFString")); +var _cffStandardStrings = _interopRequireDefault(require("./cffStandardStrings")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +/** 优化280: 内联 getCFFString 逻辑,消除每次调用的函数调用开销 + interop 解包 */ +var STD_STRINGS = _cffStandardStrings.default; /** * @file 解析cff字符集 * @author mengke01(kekee000@gmail.com) @@ -28,7 +30,6 @@ function parseCFFCharset(reader, start, nGlyphs, strings) { var i; var sid; var count; - // The .notdef glyph is not included, so subtract 1. nGlyphs -= 1; /** 优化250: 预分配 charset 数组,避免 push 扩容 */ var charset = new Array(nGlyphs + 1); @@ -38,14 +39,14 @@ function parseCFFCharset(reader, start, nGlyphs, strings) { if (format === 0) { for (i = 0; i < nGlyphs; i += 1) { sid = reader.readUint16(); - charset[ci++] = (0, _getCFFString.default)(strings, sid); + charset[ci++] = sid <= 390 ? STD_STRINGS[sid] : strings[sid - 391]; } } else if (format === 1) { while (ci <= nGlyphs) { sid = reader.readUint16(); count = reader.readUint8(); for (i = 0; i <= count; i += 1) { - charset[ci++] = (0, _getCFFString.default)(strings, sid); + charset[ci++] = sid <= 390 ? STD_STRINGS[sid] : strings[sid - 391]; sid += 1; } } @@ -54,7 +55,7 @@ function parseCFFCharset(reader, start, nGlyphs, strings) { sid = reader.readUint16(); count = reader.readUint16(); for (i = 0; i <= count; i += 1) { - charset[ci++] = (0, _getCFFString.default)(strings, sid); + charset[ci++] = sid <= 390 ? STD_STRINGS[sid] : strings[sid - 391]; sid += 1; } } diff --git a/vendor/fonteditor-core/lib/ttf/table/cff/parseCFFGlyph.js b/vendor/fonteditor-core/lib/ttf/table/cff/parseCFFGlyph.js index f13fb5f..41a15a8 100644 --- a/vendor/fonteditor-core/lib/ttf/table/cff/parseCFFGlyph.js +++ b/vendor/fonteditor-core/lib/ttf/table/cff/parseCFFGlyph.js @@ -36,21 +36,37 @@ function parseCFFCharstring(code, font, index) { var x = 0; var y = 0; - /** - * 优化179: 模块级 closeContour,避免闭包捕获 - */ - function closeContour(arr) { - var cLen = arr.length; - if (cLen >= 6 && arr[0] === arr[cLen - 3] && arr[1] === arr[cLen - 2]) { - arr.length = cLen - 3; + /** 优化277: contour 预分配 + 索引赋值替代 push,减少动态扩容 */ + var contourCap = 256; + var contourBuf = new Array(contourCap); + var ci = 0; + + function closeContour() { + if (ci >= 6 && contourBuf[0] === contourBuf[ci - 3] && contourBuf[1] === contourBuf[ci - 2]) { + ci -= 3; } + var arr = contourBuf.slice(0, ci); contours.push(arr); } function startContour(px, py) { - if (open) closeContour(contour); - contour = [px, py, ON_CURVE]; + if (open) closeContour(); + ci = 0; + contourBuf[ci++] = px; + contourBuf[ci++] = py; + contourBuf[ci++] = ON_CURVE; open = true; } + function pushContour(x, y, flag) { + if (ci >= contourCap) { + contourCap = contourCap << 1; + var newBuf = new Array(contourCap); + for (var k = 0; k < ci; k++) newBuf[k] = contourBuf[k]; + contourBuf = newBuf; + } + contourBuf[ci++] = x; + contourBuf[ci++] = y; + contourBuf[ci++] = flag; + } /** * 优化157: 用 sp (stack pointer) 和 si (stack index) 替代 shift/pop @@ -116,47 +132,58 @@ function parseCFFCharstring(code, font, index) { break; case 5: // rlineto - while (sp - si > 0) { + /** 优化263: 缓存 sp-si 差值,消除每次迭代的减法 */ + var sLen = sp - si; + while (sLen > 0) { x += stack[si++]; y += stack[si++]; - contour.push(x, y, ON_CURVE); + pushContour(x, y, ON_CURVE); + sLen -= 2; } sp = si = 0; break; case 6: // hlineto - while (sp - si > 0) { + sLen = sp - si; + while (sLen > 0) { x += stack[si++]; - contour.push(x, y, ON_CURVE); - if (sp - si === 0) break; + pushContour(x, y, ON_CURVE); + sLen--; + if (sLen === 0) break; y += stack[si++]; - contour.push(x, y, ON_CURVE); + pushContour(x, y, ON_CURVE); + sLen--; } sp = si = 0; break; case 7: // vlineto - while (sp - si > 0) { + sLen = sp - si; + while (sLen > 0) { y += stack[si++]; - contour.push(x, y, ON_CURVE); - if (sp - si === 0) break; + pushContour(x, y, ON_CURVE); + sLen--; + if (sLen === 0) break; x += stack[si++]; - contour.push(x, y, ON_CURVE); + pushContour(x, y, ON_CURVE); + sLen--; } sp = si = 0; break; case 8: // rrcurveto - while (sp - si > 0) { + sLen = sp - si; + while (sLen > 0) { c1x = x + stack[si++]; c1y = y + stack[si++]; c2x = c1x + stack[si++]; c2y = c1y + stack[si++]; x = c2x + stack[si++]; y = c2y + stack[si++]; - contour.push(c1x, c1y, 0); - contour.push(c2x, c2y, 0); - contour.push(x, y, ON_CURVE); + pushContour(c1x, c1y, 0); + pushContour(c2x, c2y, 0); + pushContour(x, y, ON_CURVE); + sLen -= 6; } sp = si = 0; break; @@ -192,12 +219,12 @@ function parseCFFCharstring(code, font, index) { x = c4x + stack[si++]; y = c4y + stack[si++]; si++; - contour.push(c1x, c1y, 0); - contour.push(c2x, c2y, 0); - contour.push(jpx, jpy, ON_CURVE); - contour.push(c3x, c3y, 0); - contour.push(c4x, c4y, 0); - contour.push(x, y, ON_CURVE); + pushContour(c1x, c1y, 0); + pushContour(c2x, c2y, 0); + pushContour(jpx, jpy, ON_CURVE); + pushContour(c3x, c3y, 0); + pushContour(c4x, c4y, 0); + pushContour(x, y, ON_CURVE); break; case 34: // hflex @@ -212,12 +239,12 @@ function parseCFFCharstring(code, font, index) { c4x = c3x + stack[si++]; c4y = y; x = c4x + stack[si++]; - contour.push(c1x, c1y, 0); - contour.push(c2x, c2y, 0); - contour.push(jpx, jpy, ON_CURVE); - contour.push(c3x, c3y, 0); - contour.push(c4x, c4y, 0); - contour.push(x, y, ON_CURVE); + pushContour(c1x, c1y, 0); + pushContour(c2x, c2y, 0); + pushContour(jpx, jpy, ON_CURVE); + pushContour(c3x, c3y, 0); + pushContour(c4x, c4y, 0); + pushContour(x, y, ON_CURVE); break; case 36: // hflex1 @@ -232,12 +259,12 @@ function parseCFFCharstring(code, font, index) { c4x = c3x + stack[si++]; c4y = c3y + stack[si++]; x = c4x + stack[si++]; - contour.push(c1x, c1y, 0); - contour.push(c2x, c2y, 0); - contour.push(jpx, jpy, ON_CURVE); - contour.push(c3x, c3y, 0); - contour.push(c4x, c4y, 0); - contour.push(x, y, ON_CURVE); + pushContour(c1x, c1y, 0); + pushContour(c2x, c2y, 0); + pushContour(jpx, jpy, ON_CURVE); + pushContour(c3x, c3y, 0); + pushContour(c4x, c4y, 0); + pushContour(x, y, ON_CURVE); break; case 37: // flex1 @@ -256,12 +283,12 @@ function parseCFFCharstring(code, font, index) { } else { y = c4y + stack[si++]; } - contour.push(c1x, c1y, 0); - contour.push(c2x, c2y, 0); - contour.push(jpx, jpy, ON_CURVE); - contour.push(c3x, c3y, 0); - contour.push(c4x, c4y, 0); - contour.push(x, y, ON_CURVE); + pushContour(c1x, c1y, 0); + pushContour(c2x, c2y, 0); + pushContour(jpx, jpy, ON_CURVE); + pushContour(c3x, c3y, 0); + pushContour(c4x, c4y, 0); + pushContour(x, y, ON_CURVE); break; default: console.warn('Glyph ' + index + ': unknown operator ' + (1200 + v)); @@ -301,7 +328,7 @@ function parseCFFCharstring(code, font, index) { glyfs[1].transform.e = stack[--sp]; } if (open) { - closeContour(contour); + closeContour(); open = false; } sp = si = 0; @@ -344,28 +371,34 @@ function parseCFFCharstring(code, font, index) { break; case 24: // rcurveline - while (sp - si > 2) { + /** 优化263: 缓存 sp-si 差值 */ + sLen = sp - si; + while (sLen > 2) { c1x = x + stack[si++]; c1y = y + stack[si++]; c2x = c1x + stack[si++]; c2y = c1y + stack[si++]; x = c2x + stack[si++]; y = c2y + stack[si++]; - contour.push(c1x, c1y, 0); - contour.push(c2x, c2y, 0); - contour.push(x, y, ON_CURVE); + pushContour(c1x, c1y, 0); + pushContour(c2x, c2y, 0); + pushContour(x, y, ON_CURVE); + sLen -= 6; } x += stack[si++]; y += stack[si++]; - contour.push(x, y, ON_CURVE); + pushContour(x, y, ON_CURVE); sp = si = 0; break; case 25: // rlinecurve - while (sp - si > 6) { + /** 优化272: 缓存 sp-si 差值,消除每次迭代的减法 */ + var _sLen = sp - si; + while (_sLen > 6) { x += stack[si++]; y += stack[si++]; - contour.push(x, y, ON_CURVE); + pushContour(x, y, ON_CURVE); + _sLen -= 2; } c1x = x + stack[si++]; c1y = y + stack[si++]; @@ -373,44 +406,52 @@ function parseCFFCharstring(code, font, index) { c2y = c1y + stack[si++]; x = c2x + stack[si++]; y = c2y + stack[si++]; - contour.push(c1x, c1y, 0); - contour.push(c2x, c2y, 0); - contour.push(x, y, ON_CURVE); + pushContour(c1x, c1y, 0); + pushContour(c2x, c2y, 0); + pushContour(x, y, ON_CURVE); sp = si = 0; break; case 26: // vvcurveto - if ((sp - si) & 1) { + /** 优化272: 缓存 sp-si 差值 */ + _sLen = sp - si; + if (_sLen & 1) { x += stack[si++]; + _sLen--; } - while (sp - si > 0) { + while (_sLen > 0) { c1x = x; c1y = y + stack[si++]; c2x = c1x + stack[si++]; c2y = c1y + stack[si++]; x = c2x; y = c2y + stack[si++]; - contour.push(c1x, c1y, 0); - contour.push(c2x, c2y, 0); - contour.push(x, y, ON_CURVE); + pushContour(c1x, c1y, 0); + pushContour(c2x, c2y, 0); + pushContour(x, y, ON_CURVE); + _sLen -= 4; } sp = si = 0; break; case 27: // hhcurveto - if ((sp - si) & 1) { + /** 优化272: 缓存 sp-si 差值 */ + _sLen = sp - si; + if (_sLen & 1) { y += stack[si++]; + _sLen--; } - while (sp - si > 0) { + while (_sLen > 0) { c1x = x + stack[si++]; c1y = y; c2x = c1x + stack[si++]; c2y = c1y + stack[si++]; x = c2x + stack[si++]; y = c2y; - contour.push(c1x, c1y, 0); - contour.push(c2x, c2y, 0); - contour.push(x, y, ON_CURVE); + pushContour(c1x, c1y, 0); + pushContour(c2x, c2y, 0); + pushContour(x, y, ON_CURVE); + _sLen -= 4; } sp = si = 0; break; @@ -432,53 +473,87 @@ function parseCFFCharstring(code, font, index) { break; case 30: // vhcurveto - while (sp - si > 0) { - c1x = x; - c1y = y + stack[si++]; - c2x = c1x + stack[si++]; - c2y = c1y + stack[si++]; - x = c2x + stack[si++]; - y = c2y + (sp - si === 1 ? stack[si++] : 0); - contour.push(c1x, c1y, 0); - contour.push(c2x, c2y, 0); - contour.push(x, y, ON_CURVE); - if (sp - si === 0) break; - c1x = x + stack[si++]; - c1y = y; - c2x = c1x + stack[si++]; - c2y = c1y + stack[si++]; - y = c2y + stack[si++]; - x = c2x + (sp - si === 1 ? stack[si++] : 0); - contour.push(c1x, c1y, 0); - contour.push(c2x, c2y, 0); - contour.push(x, y, ON_CURVE); + { + /** 优化272: 缓存 sp-si 差值,消除每次迭代的减法 */ + var vhLen = sp - si; + while (vhLen > 0) { + c1x = x; + c1y = y + stack[si++]; + c2x = c1x + stack[si++]; + c2y = c1y + stack[si++]; + x = c2x + stack[si++]; + vhLen -= 4; + /** 最后一段曲线的可选参数:只剩1个值时为 dy3,消费后结束 */ + if (vhLen === 1) { + y = c2y + stack[si++]; + vhLen = 0; + } else { + y = c2y; + } + pushContour(c1x, c1y, 0); + pushContour(c2x, c2y, 0); + pushContour(x, y, ON_CURVE); + if (vhLen === 0) break; + c1x = x + stack[si++]; + c1y = y; + c2x = c1x + stack[si++]; + c2y = c1y + stack[si++]; + y = c2y + stack[si++]; + x = c2x; + vhLen -= 4; + /** 最后一段曲线的可选参数:只剩1个值时为 dx3,消费后结束 */ + if (vhLen === 1) { + x = c2x + stack[si++]; + vhLen = 0; + } + pushContour(c1x, c1y, 0); + pushContour(c2x, c2y, 0); + pushContour(x, y, ON_CURVE); + } + sp = si = 0; } - sp = si = 0; break; case 31: // hvcurveto - while (sp - si > 0) { - c1x = x + stack[si++]; - c1y = y; - c2x = c1x + stack[si++]; - c2y = c1y + stack[si++]; - y = c2y + stack[si++]; - x = c2x + (sp - si === 1 ? stack[si++] : 0); - contour.push(c1x, c1y, 0); - contour.push(c2x, c2y, 0); - contour.push(x, y, ON_CURVE); - if (sp - si === 0) break; - c1x = x; - c1y = y + stack[si++]; - c2x = c1x + stack[si++]; - c2y = c1y + stack[si++]; - x = c2x + stack[si++]; - y = c2y + (sp - si === 1 ? stack[si++] : 0); - contour.push(c1x, c1y, 0); - contour.push(c2x, c2y, 0); - contour.push(x, y, ON_CURVE); + { + /** 优化272: 缓存 sp-si 差值,消除每次迭代的减法 */ + var hvLen = sp - si; + while (hvLen > 0) { + c1x = x + stack[si++]; + c1y = y; + c2x = c1x + stack[si++]; + c2y = c1y + stack[si++]; + y = c2y + stack[si++]; + hvLen -= 4; + /** 最后一段曲线的可选参数:只剩1个值时为 dx3,消费后结束 */ + if (hvLen === 1) { + x = c2x + stack[si++]; + hvLen = 0; + } else { + x = c2x; + } + pushContour(c1x, c1y, 0); + pushContour(c2x, c2y, 0); + pushContour(x, y, ON_CURVE); + if (hvLen === 0) break; + c1x = x; + c1y = y + stack[si++]; + c2x = c1x + stack[si++]; + c2y = c1y + stack[si++]; + x = c2x + stack[si++]; + y = c2y; + hvLen -= 4; + /** 最后一段曲线的可选参数:只剩1个值时为 dy3,消费后结束 */ + if (hvLen === 1) { + y = c2y + stack[si++]; + hvLen = 0; + } + pushContour(c1x, c1y, 0); + pushContour(c2x, c2y, 0); + pushContour(x, y, ON_CURVE); + } + sp = si = 0; } - sp = si = 0; break; default: if (v < 32) { diff --git a/vendor/fonteditor-core/lib/ttf/table/cmap/sizeof.js b/vendor/fonteditor-core/lib/ttf/table/cmap/sizeof.js index 0adcf4c..5304ae9 100644 --- a/vendor/fonteditor-core/lib/ttf/table/cmap/sizeof.js +++ b/vendor/fonteditor-core/lib/ttf/table/cmap/sizeof.js @@ -51,16 +51,7 @@ function getSegmentsFlat(unicodeArr, idArr, bound) { return result; } -/** 优化155: 扁平数组版本的 getFormat0Segment */ -function getFormat0SegmentFlat(unicodeArr, idArr) { - var unicodes = []; - for (var i = 0, l = unicodeArr.length; i < l; i++) { - if (unicodeArr[i] < 256) { - unicodes.push(unicodeArr[i], idArr[i]); - } - } - return unicodes; -} +/** 优化269: getFormat0SegmentFlat 合并到 getSegmentsFlatAndFormat0,消除二次遍历 */ function sizeof(ttf) { ttf.support.cmap = {}; @@ -134,16 +125,21 @@ function sizeof(ttf) { cmapSupport.format4Segments = format12Segments; } - var hasGLyphsOver2Bytes = len > 0; - if (hasGLyphsOver2Bytes) { - cmapSupport.hasGLyphsOver2Bytes = true; - } + /** 优化288: 内联赋值,消除临时变量和条件分支 */ + cmapSupport.hasGLyphsOver2Bytes = len > 0; /** format4Size 需要包含 sentinel segment (+1),与 write.js 中的 segCount = segments.length/4 + 1 一致 */ var format4SegCount = cmapSupport.format4Segments.length / 4 + 1; cmapSupport.format4Size = 16 + format4SegCount * 8; - cmapSupport.format0Segments = getFormat0SegmentFlat(unicodeArr, idArr); - cmapSupport.hasFormat0 = cmapSupport.format0Segments.length > 0; + /** 优化269: format0 数据直接在排序后的数组上收集,内联替代 getFormat0SegmentFlat */ + var format0Segments = []; + for (var fi = 0, fl = len; fi < fl; fi++) { + if (unicodeArr[fi] < 256) { + format0Segments.push(unicodeArr[fi], idArr[fi]); + } + } + cmapSupport.format0Segments = format0Segments; + cmapSupport.hasFormat0 = format0Segments.length > 0; cmapSupport.format0Size = cmapSupport.hasFormat0 ? 262 : 0; /** 记录头大小必须动态计算,与 write.js 中的 numRecords 保持一致,否则会导致表偏移错位 */ diff --git a/vendor/fonteditor-core/lib/ttf/table/cmap/write.js b/vendor/fonteditor-core/lib/ttf/table/cmap/write.js index 22fbdd5..b742504 100644 --- a/vendor/fonteditor-core/lib/ttf/table/cmap/write.js +++ b/vendor/fonteditor-core/lib/ttf/table/cmap/write.js @@ -18,11 +18,11 @@ function writeSubTable0(writer, unicodes) { view.setUint16(pos, 0, false); pos += 2; view.setUint16(pos, 262, false); pos += 2; view.setUint16(pos, 0, false); pos += 2; + writer.offset = pos; /** 优化218: 使用 writer.writeEmpty 批量填充 0,替代逐字节 setUint8 */ writer.writeEmpty(256); var base = writer.offset - 256; - pos = base; for (var j = 0; j < unicodes.length; j += 2) { pos = base + unicodes[j]; @@ -51,26 +51,27 @@ function writeSubTable4(writer, segments) { view.setUint16(pos, 2 * segCount - searchRange, false); pos += 2; var numSegs = segments.length / 4; - for (var i = 0; i < numSegs; i++) { - view.setUint16(pos, segments[i * 4 + 1], false); pos += 2; + /** 优化262: 使用递增索引替代 i * 4 乘法 */ + for (var i = 0, off = 0; i < numSegs; i++, off += 4) { + view.setUint16(pos, segments[off + 1], false); pos += 2; } view.setUint16(pos, 0xFFFF, false); pos += 2; view.setUint16(pos, 0, false); pos += 2; - for (var j = 0; j < numSegs; j++) { - view.setUint16(pos, segments[j * 4], false); pos += 2; + for (var j = 0, off2 = 0; j < numSegs; j++, off2 += 4) { + view.setUint16(pos, segments[off2], false); pos += 2; } view.setUint16(pos, 0xFFFF, false); pos += 2; - for (var k = 0; k < numSegs; k++) { - view.setUint16(pos, segments[k * 4 + 3], false); pos += 2; + for (var k = 0, off3 = 0; k < numSegs; k++, off3 += 4) { + view.setUint16(pos, segments[off3 + 3], false); pos += 2; } view.setUint16(pos, 1, false); pos += 2; - for (var m = 0; m < numSegs; m++) { - view.setUint16(pos, 0, false); pos += 2; - } - view.setUint16(pos, 0, false); pos += 2; + /** 优化279: idRangeOffset 全零数组用 Uint8Array.fill(0) 批量填充,替代 numSegs+1 次 setUint16 */ + var idRangeOffsetLen = (numSegs + 1) * 2; + new Uint8Array(view.buffer, view.byteOffset + pos, idRangeOffsetLen).fill(0); + pos += idRangeOffsetLen; writer.offset = pos; return writer; @@ -89,8 +90,8 @@ function writeSubTable12(writer, segments) { view.setUint32(pos, 0, false); pos += 4; view.setUint32(pos, numSegs, false); pos += 4; - for (var i = 0; i < numSegs; i++) { - var off = i * 4; + /** 优化262: 使用递增索引替代 i * 4 乘法 */ + for (var i = 0, off = 0; i < numSegs; i++, off += 4) { view.setUint32(pos, segments[off], false); pos += 4; view.setUint32(pos, segments[off + 1], false); pos += 4; view.setUint32(pos, segments[off + 2], false); pos += 4; @@ -100,8 +101,10 @@ function writeSubTable12(writer, segments) { } function write(writer, ttf) { - var hasGLyphsOver2Bytes = ttf.support.cmap.hasGLyphsOver2Bytes; - var hasFormat0 = ttf.support.cmap.hasFormat0; + /** 优化288: 缓存 ttf.support.cmap 到局部变量,消除重复属性链查找 */ + var cmap = ttf.support.cmap; + var hasGLyphsOver2Bytes = cmap.hasGLyphsOver2Bytes; + var hasFormat0 = cmap.hasFormat0; var pos = writer.offset; var view = writer.view; @@ -111,8 +114,8 @@ function write(writer, ttf) { /* 优化88: encoding records 直接 view 写入 */ var headerSize = 4 + numRecords * 8; - var format4Size = ttf.support.cmap.format4Size; - var format0Size = ttf.support.cmap.format0Size; + var format4Size = cmap.format4Size; + var format0Size = cmap.format0Size; view.setUint16(pos, 0, false); pos += 2; view.setUint16(pos, 3, false); pos += 2; @@ -132,12 +135,12 @@ function write(writer, ttf) { } writer.offset = pos; - writeSubTable4(writer, ttf.support.cmap.format4Segments); + writeSubTable4(writer, cmap.format4Segments); if (hasFormat0) { - writeSubTable0(writer, ttf.support.cmap.format0Segments); + writeSubTable0(writer, cmap.format0Segments); } if (hasGLyphsOver2Bytes) { - writeSubTable12(writer, ttf.support.cmap.format12Segments); + writeSubTable12(writer, cmap.format12Segments); } return writer; } diff --git a/vendor/fonteditor-core/lib/ttf/table/directory.js b/vendor/fonteditor-core/lib/ttf/table/directory.js index 395d822..ed6e67f 100644 --- a/vendor/fonteditor-core/lib/ttf/table/directory.js +++ b/vendor/fonteditor-core/lib/ttf/table/directory.js @@ -12,6 +12,17 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de * * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6.html */ + +/** 优化280: KNOWN_TAG_U32 提升到模块级,消除每次 write() 调用的对象分配 */ +var KNOWN_TAG_U32 = { + 'OS/2': 0x4F532F32, 'cmap': 0x636D6170, 'glyf': 0x676C7966, + 'head': 0x68656164, 'hhea': 0x68686561, 'hmtx': 0x686D7478, + 'loca': 0x6C6F6361, 'maxp': 0x6D617870, 'name': 0x6E616D65, + 'post': 0x706F7374, 'CFF ': 0x43464620, 'VORG': 0x564F5247, + 'GPOS': 0x47504F53, 'kern': 0x6B65726E, 'kerx': 0x6B657278, + 'cvt ': 0x63767420, 'fpgm': 0x6670676D, 'prep': 0x70726570, + 'gasp': 0x67617370 +}; var _default = exports.default = _table.default.create('directory', [], { read: function read(reader, ttf) { var tables = {}; @@ -44,16 +55,6 @@ var _default = exports.default = _table.default.create('directory', [], { var tables = ttf.support.tables; var view = writer.view; var pos = writer.offset; - /** 优化235: 预计算 tag 的 Uint32 值,避免循环内 4 次 charCodeAt 调用 */ - var KNOWN_TAG_U32 = { - 'OS/2': 0x4F532F32, 'cmap': 0x636D6170, 'glyf': 0x676C7966, - 'head': 0x68656164, 'hhea': 0x68686561, 'hmtx': 0x686D7478, - 'loca': 0x6C6F6361, 'maxp': 0x6D617870, 'name': 0x6E616D65, - 'post': 0x706F7374, 'CFF ': 0x43464620, 'VORG': 0x564F5247, - 'GPOS': 0x47504F53, 'kern': 0x6B65726E, 'kerx': 0x6B657278, - 'cvt ': 0x63767420, 'fpgm': 0x6670676D, 'prep': 0x70726570, - 'gasp': 0x67617370 - }; for (var i = 0, l = tables.length; i < l; i++) { var t = tables[i]; var tagU32 = KNOWN_TAG_U32[t.name]; diff --git a/vendor/fonteditor-core/lib/ttf/table/glyf.js b/vendor/fonteditor-core/lib/ttf/table/glyf.js index 9d80e66..2b3e2de 100644 --- a/vendor/fonteditor-core/lib/ttf/table/glyf.js +++ b/vendor/fonteditor-core/lib/ttf/table/glyf.js @@ -33,15 +33,16 @@ var _default = exports.default = _table.default.create('glyf', [], { var cmap = ttf.cmap; /** 优化: 构建 unicode→gid 映射,供 resolveGlyf 直接遍历,避免全量 cmap 遍历 */ var subsetUnicodeMap = {}; + /** 优化291: 合并 subsetUnicodeMap 赋值的两个分支,减少一次条件判断 */ for (var si = 0, sl = subset.length; si < sl; si++) { var u = subset[si]; var gid = cmap[u]; - if (gid !== undefined && !subsetMap[gid]) { - subsetMap[gid] = true; - subsetGids.push(gid); - subsetUnicodeMap[u] = gid; - } else if (gid !== undefined) { + if (gid !== undefined) { subsetUnicodeMap[u] = gid; + if (!subsetMap[gid]) { + subsetMap[gid] = true; + subsetGids.push(gid); + } } } ttf.subsetMap = subsetMap; diff --git a/vendor/fonteditor-core/lib/ttf/table/glyf/parse.js b/vendor/fonteditor-core/lib/ttf/table/glyf/parse.js index 45d30ef..29cadec 100644 --- a/vendor/fonteditor-core/lib/ttf/table/glyf/parse.js +++ b/vendor/fonteditor-core/lib/ttf/table/glyf/parse.js @@ -54,11 +54,12 @@ function parseSimpleGlyf(reader, glyf) { for (var xi = 0; xi < numberOfCoordinates; xi++) { var x = 0; var xflag = flags[xi]; - if (xflag & XSHORT) { + /** 优化280: delta=0 是最常见情况,提前分支提高分支预测命中率 */ + if (!(xflag & XSHORT) && (xflag & XSAME)) { + /* x = 0, 无需操作 */ + } else if (xflag & XSHORT) { x = view.getUint8(vOffset++); - x = (xflag & XSAME) ? x : -x; - } else if (xflag & XSAME) { - x = 0; + if (!(xflag & XSAME)) x = -x; } else { x = view.getInt16(vOffset); vOffset += 2; @@ -72,11 +73,12 @@ function parseSimpleGlyf(reader, glyf) { for (var yi = 0; yi < numberOfCoordinates; yi++) { var y = 0; var yflag = flags[yi]; - if (yflag & YSHORT) { + /** 优化280: delta=0 是最常见情况,提前分支 */ + if (!(yflag & YSHORT) && (yflag & YSAME)) { + /* y = 0 */ + } else if (yflag & YSHORT) { y = view.getUint8(vOffset++); - y = (yflag & YSAME) ? y : -y; - } else if (yflag & YSAME) { - y = 0; + if (!(yflag & YSAME)) y = -y; } else { y = view.getInt16(vOffset); vOffset += 2; @@ -98,23 +100,36 @@ function parseSimpleGlyf(reader, glyf) { * 读取复合字形 * 优化257: 使用直接 DataView 访问替代 reader API,消除每次 read 的函数调用和参数检查 */ +/** 优化290: 复合字形枚举常量提升到模块级别,消除每次调用的属性查找 */ +var _ARG_1_AND_2_ARE_WORDS = _componentFlag.default.ARG_1_AND_2_ARE_WORDS; +var _WE_HAVE_A_SCALE = _componentFlag.default.WE_HAVE_A_SCALE; +var _WE_HAVE_AN_X_AND_Y_SCALE = _componentFlag.default.WE_HAVE_AN_X_AND_Y_SCALE; +var _WE_HAVE_A_TWO_BY_TWO = _componentFlag.default.WE_HAVE_A_TWO_BY_TWO; +var _ARGS_ARE_XY_VALUES = _componentFlag.default.ARGS_ARE_XY_VALUES; +var _USE_MY_METRICS = _componentFlag.default.USE_MY_METRICS; +var _OVERLAP_COMPOUND = _componentFlag.default.OVERLAP_COMPOUND; +var _MORE_COMPONENTS = _componentFlag.default.MORE_COMPONENTS; +var _WE_HAVE_INSTRUCTIONS = _componentFlag.default.WE_HAVE_INSTRUCTIONS; + function parseCompoundGlyf(reader, glyf) { glyf.compound = true; glyf.glyfs = []; var flags; - var ARG_1_AND_2_ARE_WORDS = _componentFlag.default.ARG_1_AND_2_ARE_WORDS; - var WE_HAVE_A_SCALE = _componentFlag.default.WE_HAVE_A_SCALE; - var WE_HAVE_AN_X_AND_Y_SCALE = _componentFlag.default.WE_HAVE_AN_X_AND_Y_SCALE; - var WE_HAVE_A_TWO_BY_TWO = _componentFlag.default.WE_HAVE_A_TWO_BY_TWO; - var ARGS_ARE_XY_VALUES = _componentFlag.default.ARGS_ARE_XY_VALUES; - var USE_MY_METRICS = _componentFlag.default.USE_MY_METRICS; - var OVERLAP_COMPOUND = _componentFlag.default.OVERLAP_COMPOUND; - var MORE_COMPONENTS = _componentFlag.default.MORE_COMPONENTS; - var WE_HAVE_INSTRUCTIONS = _componentFlag.default.WE_HAVE_INSTRUCTIONS; + var ARG_1_AND_2_ARE_WORDS = _ARG_1_AND_2_ARE_WORDS; + var WE_HAVE_A_SCALE = _WE_HAVE_A_SCALE; + var WE_HAVE_AN_X_AND_Y_SCALE = _WE_HAVE_AN_X_AND_Y_SCALE; + var WE_HAVE_A_TWO_BY_TWO = _WE_HAVE_A_TWO_BY_TWO; + var ARGS_ARE_XY_VALUES = _ARGS_ARE_XY_VALUES; + var USE_MY_METRICS = _USE_MY_METRICS; + var OVERLAP_COMPOUND = _OVERLAP_COMPOUND; + var MORE_COMPONENTS = _MORE_COMPONENTS; + var WE_HAVE_INSTRUCTIONS = _WE_HAVE_INSTRUCTIONS; var view = reader.view; var vOffset = view.byteOffset + reader.offset; + /** 优化293: F2DOT14_SCALE 提升到循环外 */ + var F2DOT14_SCALE = 0.0001; do { flags = view.getUint16(vOffset, false); vOffset += 2; var glyphIndex = view.getUint16(vOffset, false); vOffset += 2; @@ -143,21 +158,21 @@ function parseCompoundGlyf(reader, glyf) { scale10 = view.getInt16(vOffset, false); vOffset += 2; scaleY = view.getInt16(vOffset, false); vOffset += 2; } - /** F2Dot14 → 小数: 优化214+236: 合并对象创建,减少每次 push 的分配次数 */ + /** 优化293: F2DOT14_SCALE 提升到循环外,避免每次迭代重新赋值 */ if (ARGS_ARE_XY_VALUES & flags) { glyf.glyfs.push({ flags: flags, glyphIndex: glyphIndex, useMyMetrics: !!(flags & USE_MY_METRICS), overlapCompound: !!(flags & OVERLAP_COMPOUND), - transform: { a: (scaleX * 0.6103515625 + 0.5 | 0) / 10000, b: (scale01 * 0.6103515625 + 0.5 | 0) / 10000, c: (scale10 * 0.6103515625 + 0.5 | 0) / 10000, d: (scaleY * 0.6103515625 + 0.5 | 0) / 10000, e: arg1, f: arg2 } + transform: { a: (scaleX * 0.6103515625 + 0.5 | 0) * F2DOT14_SCALE, b: (scale01 * 0.6103515625 + 0.5 | 0) * F2DOT14_SCALE, c: (scale10 * 0.6103515625 + 0.5 | 0) * F2DOT14_SCALE, d: (scaleY * 0.6103515625 + 0.5 | 0) * F2DOT14_SCALE, e: arg1, f: arg2 } }); } else { glyf.glyfs.push({ flags: flags, glyphIndex: glyphIndex, points: [arg1, arg2], - transform: { a: (scaleX * 0.6103515625 + 0.5 | 0) / 10000, b: (scale01 * 0.6103515625 + 0.5 | 0) / 10000, c: (scale10 * 0.6103515625 + 0.5 | 0) / 10000, d: (scaleY * 0.6103515625 + 0.5 | 0) / 10000, e: 0, f: 0 } + transform: { a: (scaleX * 0.6103515625 + 0.5 | 0) * F2DOT14_SCALE, b: (scale01 * 0.6103515625 + 0.5 | 0) * F2DOT14_SCALE, c: (scale10 * 0.6103515625 + 0.5 | 0) * F2DOT14_SCALE, d: (scaleY * 0.6103515625 + 0.5 | 0) * F2DOT14_SCALE, e: 0, f: 0 } }); } } while (MORE_COMPONENTS & flags); @@ -187,7 +202,9 @@ function parseGlyf(reader, ttf, offset) { reader.seek(offset); } var glyf = {}; - var hinting = ttf.readOptions ? ttf.readOptions.hinting : false; + /** 优化290: 缓存 ttf.readOptions 到局部变量,消除重复属性链查找 */ + var readOpts = ttf.readOptions || {}; + var hinting = readOpts.hinting; /* 优化41: 直接 view 读取 header 的 10 字节 */ var view = reader.view; diff --git a/vendor/fonteditor-core/lib/ttf/table/glyf/sizeof.js b/vendor/fonteditor-core/lib/ttf/table/glyf/sizeof.js index 9d29398..e1b5926 100644 --- a/vendor/fonteditor-core/lib/ttf/table/glyf/sizeof.js +++ b/vendor/fonteditor-core/lib/ttf/table/glyf/sizeof.js @@ -6,6 +6,13 @@ Object.defineProperty(exports, "__esModule", { exports.default = sizeof; var _glyFlag = _interopRequireDefault(require("../../enum/glyFlag")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +/** 优化279: 枚举常量提升到模块级别,消除每个 glyph 调用时的属性查找 */ +var _ONCURVE = _glyFlag.default.ONCURVE; +var _XSHORT = _glyFlag.default.XSHORT; +var _YSHORT = _glyFlag.default.YSHORT; +var _XSAME = _glyFlag.default.XSAME; +var _YSAME = _glyFlag.default.YSAME; +var _REPEAT = _glyFlag.default.REPEAT; /** * @file 获取glyf的大小,同时对glyf写入进行预处理 * @author mengke01(kekee000@gmail.com) @@ -40,12 +47,12 @@ function getFlagsAndSize(glyf, glyfSupport, hinting) { return 12 + nc * 2 + glyfSupport.flags.length + encSz + instructionSize; } - var ONCURVE = _glyFlag.default.ONCURVE; - var XSHORT = _glyFlag.default.XSHORT; - var YSHORT = _glyFlag.default.YSHORT; - var XSAME = _glyFlag.default.XSAME; - var YSAME = _glyFlag.default.YSAME; - var REPEAT = _glyFlag.default.REPEAT; + var ONCURVE = _ONCURVE; + var XSHORT = _XSHORT; + var YSHORT = _YSHORT; + var XSAME = _XSAME; + var YSAME = _YSAME; + var REPEAT = _REPEAT; var contours = glyf.contours; var isFlat = glyf._flatContours; @@ -72,121 +79,71 @@ function getFlagsAndSize(glyf, glyfSupport, hinting) { for (var j = 0, cl = contours.length; j < cl; j++) { var contour = contours[j]; + /** 优化293: 统一 isFlat 和非 isFlat 的编码循环,消除 60 行重复代码 */ + var step, cLen; if (isFlat) { - for (var i = 0, l = contour.length; i < l; i += 3) { - var px = contour[i]; - var py = contour[i + 1]; - var onCurve = contour[i + 2]; - var flag = onCurve ? ONCURVE : 0; - var dx, dy; - - if (!started) { - dx = px; dy = py; started = true; - } else { - dx = px - prevX; dy = py - prevY; - } - prevX = px; prevY = py; - - if (dx === 0) { - flag += XSAME; - } else if (dx > -256 && dx < 256) { - flag += XSHORT; - if (dx > 0) flag += XSAME; - xCoordBuf[xbi++] = dx > 0 ? dx : -dx; - encodedCoordSize += 1; - } else { - xCoordBuf[xbi++] = (dx >> 8) & 0xFF; - xCoordBuf[xbi++] = dx & 0xFF; - encodedCoordSize += 2; - } - - if (dy === 0) { - flag += YSAME; - } else if (dy > -256 && dy < 256) { - flag += YSHORT; - if (dy > 0) flag += YSAME; - yCoordBuf[ybi++] = dy > 0 ? dy : -dy; - encodedCoordSize += 1; - } else { - yCoordBuf[ybi++] = (dy >> 8) & 0xFF; - yCoordBuf[ybi++] = dy & 0xFF; - encodedCoordSize += 2; - } - - if (flag === prevFlag && started) { - if (repeatPoint === -1) { - repeatPoint = fi - 1; - flagsC[repeatPoint] |= REPEAT; - flagsC[fi++] = 1; - } else if (flagsC[repeatPoint + 1] < 255) { - ++flagsC[repeatPoint + 1]; - } else { - /* 优化188: repeat count 达到 255 上限 */ - repeatPoint = -1; - flagsC[fi++] = prevFlag = flag; - } - } else { - repeatPoint = -1; - flagsC[fi++] = prevFlag = flag; - } - } + step = 3; cLen = contour.length; } else { - for (var i = 0, l = contour.length; i < l; i++) { - var point = contour[i]; - var px = point.x; - var py = point.y; - var flag = point.onCurve ? ONCURVE : 0; - var dx, dy; + step = 1; cLen = contour.length; + } + for (var i = 0; i < cLen; i += step) { + var px, py, onCurve; + if (isFlat) { + px = contour[i]; py = contour[i + 1]; onCurve = contour[i + 2]; + } else { + px = contour[i].x; py = contour[i].y; onCurve = contour[i].onCurve; + } + var flag = onCurve ? ONCURVE : 0; + var dx, dy; - if (!started) { - dx = px; dy = py; started = true; - } else { - dx = px - prevX; dy = py - prevY; - } - prevX = px; prevY = py; + if (!started) { + dx = px; dy = py; started = true; + } else { + dx = px - prevX; dy = py - prevY; + } + prevX = px; prevY = py; - if (dx === 0) { - flag += XSAME; - } else if (dx > -256 && dx < 256) { - flag += XSHORT; - if (dx > 0) flag += XSAME; - xCoordBuf[xbi++] = dx > 0 ? dx : -dx; - encodedCoordSize += 1; - } else { - xCoordBuf[xbi++] = (dx >> 8) & 0xFF; - xCoordBuf[xbi++] = dx & 0xFF; - encodedCoordSize += 2; - } + if (dx === 0) { + flag += XSAME; + } else if (dx > -256 && dx < 256) { + flag += XSHORT; + if (dx > 0) flag += XSAME; + xCoordBuf[xbi++] = dx > 0 ? dx : -dx; + encodedCoordSize += 1; + } else { + xCoordBuf[xbi++] = (dx >> 8) & 0xFF; + xCoordBuf[xbi++] = dx & 0xFF; + encodedCoordSize += 2; + } - if (dy === 0) { - flag += YSAME; - } else if (dy > -256 && dy < 256) { - flag += YSHORT; - if (dy > 0) flag += YSAME; - yCoordBuf[ybi++] = dy > 0 ? dy : -dy; - encodedCoordSize += 1; - } else { - yCoordBuf[ybi++] = (dy >> 8) & 0xFF; - yCoordBuf[ybi++] = dy & 0xFF; - encodedCoordSize += 2; - } + if (dy === 0) { + flag += YSAME; + } else if (dy > -256 && dy < 256) { + flag += YSHORT; + if (dy > 0) flag += YSAME; + yCoordBuf[ybi++] = dy > 0 ? dy : -dy; + encodedCoordSize += 1; + } else { + yCoordBuf[ybi++] = (dy >> 8) & 0xFF; + yCoordBuf[ybi++] = dy & 0xFF; + encodedCoordSize += 2; + } - if (flag === prevFlag && started) { - if (repeatPoint === -1) { - repeatPoint = fi - 1; - flagsC[repeatPoint] |= REPEAT; - flagsC[fi++] = 1; - } else if (flagsC[repeatPoint + 1] < 255) { - ++flagsC[repeatPoint + 1]; - } else { - /* 优化188: repeat count 达到 255 上限 */ - repeatPoint = -1; - flagsC[fi++] = prevFlag = flag; - } + if (flag === prevFlag && started) { + if (repeatPoint === -1) { + repeatPoint = fi - 1; + flagsC[repeatPoint] |= REPEAT; + flagsC[fi++] = 1; + } else if (flagsC[repeatPoint + 1] < 255) { + ++flagsC[repeatPoint + 1]; } else { + /* 优化188: repeat count 达到 255 上限 */ repeatPoint = -1; flagsC[fi++] = prevFlag = flag; } + } else { + repeatPoint = -1; + flagsC[fi++] = prevFlag = flag; } } } @@ -229,6 +186,9 @@ function sizeofCompound(glyf) { /** * 优化49: sizeof glyf.forEach → for 循环 */ +/** 优化262: 空 glyph 预分配单例,避免每个空 glyph 创建新对象 */ +var EMPTY_GLYF_SUPPORT = { glyfSize: 0, size: 0 }; + function sizeof(ttf) { var glyfs = ttf.glyf; var glyfSupportArr = new Array(glyfs.length); @@ -240,13 +200,16 @@ function sizeof(ttf) { for (var i = 0, gl = glyfs.length; i < gl; i++) { var glyf = glyfs[i]; - var glyfSupport = {}; + var glyfSupport; var glyfSize; if (glyf.compound) { + glyfSupport = {}; glyfSize = sizeofCompound(glyf); } else if (!writeZeroContoursGlyfData && (!glyf.contours || !glyf.contours.length)) { glyfSize = 0; + glyfSupport = EMPTY_GLYF_SUPPORT; } else { + glyfSupport = {}; glyfSize = getFlagsAndSize(glyf, glyfSupport, hinting); } diff --git a/vendor/fonteditor-core/lib/ttf/table/glyf/write.js b/vendor/fonteditor-core/lib/ttf/table/glyf/write.js index 3a38934..090c536 100644 --- a/vendor/fonteditor-core/lib/ttf/table/glyf/write.js +++ b/vendor/fonteditor-core/lib/ttf/table/glyf/write.js @@ -15,8 +15,10 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de * 优化11+21+25+31+32+50+51+52+53+58: glyf write 全面优化 */ function write(writer, ttf) { - var hinting = ttf.writeOptions ? ttf.writeOptions.hinting : false; - var writeZeroContoursGlyfData = ttf.writeOptions ? ttf.writeOptions.writeZeroContoursGlyfData : false; + /** 优化290: 缓存 writeOptions 到局部变量,消除重复属性链查找 */ + var writeOpts = ttf.writeOptions || {}; + var hinting = writeOpts.hinting; + var writeZeroContoursGlyfData = writeOpts.writeZeroContoursGlyfData; /* 优化53: 缓存 glyfSupport 到局部变量 */ var glyfSupport = ttf.support.glyf; @@ -48,7 +50,8 @@ function write(writer, ttf) { /* 优化31+103: header 直接 view 写入 10 字节,优先使用 _numContours */ var pos = writer.offset; - var numC = glyf._numContours != null ? glyf._numContours : (glyf.contours || []).length; + /** 优化284: 避免 || [] 创建临时空数组 */ + var numC = glyf._numContours != null ? glyf._numContours : (glyf.contours ? glyf.contours.length : 0); view.setInt16(pos, glyf.compound ? -1 : numC, false); view.setInt16(pos + 2, glyf.xMin, false); view.setInt16(pos + 4, glyf.yMin, false); @@ -69,9 +72,10 @@ function write(writer, ttf) { var b = transform.b; var c = transform.c; var d = transform.d; - /** 优化225: 优先无 points 路径(大多数复合 glyph),减少分支 */ - var e = g.points ? g.points[0] : transform.e; - var f = g.points ? g.points[1] : transform.f; + /** 优化290: 缓存 g.points 避免双重属性查找 */ + var pts = g.points; + var e = pts ? pts[0] : transform.e; + var f = pts ? pts[1] : transform.f; if (e < 0 || e > 0x7F || f < 0 || f > 0x7F) { flags |= ARG_1_AND_2_ARE_WORDS; } @@ -91,21 +95,28 @@ function write(writer, ttf) { view.setUint8(pos, e); pos += 1; view.setUint8(pos, f); pos += 1; } + /** 优化293: 预计算 F2Dot14 值,避免在分支内重复乘法 */ + var sa = a * 16384 + 0.5 | 0; if (WE_HAVE_A_SCALE & flags) { - view.setInt16(pos, a * 16384 + 0.5 | 0, false); pos += 2; + view.setInt16(pos, sa, false); pos += 2; } else if (WE_HAVE_AN_X_AND_Y_SCALE & flags) { - view.setInt16(pos, a * 16384 + 0.5 | 0, false); pos += 2; - view.setInt16(pos, d * 16384 + 0.5 | 0, false); pos += 2; + var sd = d * 16384 + 0.5 | 0; + view.setInt16(pos, sa, false); pos += 2; + view.setInt16(pos, sd, false); pos += 2; } else if (WE_HAVE_A_TWO_BY_TWO & flags) { - view.setInt16(pos, a * 16384 + 0.5 | 0, false); pos += 2; - view.setInt16(pos, b * 16384 + 0.5 | 0, false); pos += 2; - view.setInt16(pos, c * 16384 + 0.5 | 0, false); pos += 2; - view.setInt16(pos, d * 16384 + 0.5 | 0, false); pos += 2; + var sb = b * 16384 + 0.5 | 0; + var sc = c * 16384 + 0.5 | 0; + var sd = d * 16384 + 0.5 | 0; + view.setInt16(pos, sa, false); pos += 2; + view.setInt16(pos, sb, false); pos += 2; + view.setInt16(pos, sc, false); pos += 2; + view.setInt16(pos, sd, false); pos += 2; } } } else { /* 优化32+66+103: endPtsOfContours 直接 view 写入,支持 _pointsPerContour */ - var contours = glyf.contours || []; + /** 优化284: 避免 || [] 创建临时空数组 */ + var contours = glyf.contours; var endPts = -1; var ppc = glyf._pointsPerContour; if (ppc) { diff --git a/vendor/fonteditor-core/lib/ttf/table/head.js b/vendor/fonteditor-core/lib/ttf/table/head.js index b170275..cd5713f 100644 --- a/vendor/fonteditor-core/lib/ttf/table/head.js +++ b/vendor/fonteditor-core/lib/ttf/table/head.js @@ -59,14 +59,14 @@ var _default = exports.default = _table.default.create('head', [['version', _str view.setUint32(pos, head.magickNumber, false); pos += 4; view.setUint16(pos, head.flags, false); pos += 2; view.setUint16(pos, head.unitsPerEm, false); pos += 2; - /** 优化216: 内联 writeLDT,消除函数定义+调用开销 */ + /** 优化281: resolveTTF 已将 created/modified 转为 number,消除 typeof 链 */ var delta = -2077545600000; - var cMs = typeof head.created.getTime === 'function' ? head.created.getTime() : typeof head.created === 'number' ? head.created : Date.parse(head.created); + var cMs = +head.created; view.setUint32(pos, 0, false); pos += 4; - view.setUint32(pos, Math.round((cMs - delta) / 1000), false); pos += 4; - var mMs = typeof head.modified.getTime === 'function' ? head.modified.getTime() : typeof head.modified === 'number' ? head.modified : Date.parse(head.modified); + view.setUint32(pos, (cMs - delta + 500 | 0) / 1000 | 0, false); pos += 4; + var mMs = +head.modified; view.setUint32(pos, 0, false); pos += 4; - view.setUint32(pos, Math.round((mMs - delta) / 1000), false); pos += 4; + view.setUint32(pos, (mMs - delta + 500 | 0) / 1000 | 0, false); pos += 4; view.setInt16(pos, head.xMin, false); pos += 2; view.setInt16(pos, head.yMin, false); pos += 2; view.setInt16(pos, head.xMax, false); pos += 2; diff --git a/vendor/fonteditor-core/lib/ttf/table/hmtx.js b/vendor/fonteditor-core/lib/ttf/table/hmtx.js index 2d57250..effa0d7 100644 --- a/vendor/fonteditor-core/lib/ttf/table/hmtx.js +++ b/vendor/fonteditor-core/lib/ttf/table/hmtx.js @@ -74,7 +74,9 @@ var _default = exports.default = _table.default.create('hmtx', [], { break; } } - ttf.hhea.numOfLongHorMetrics = gl - numOfLast; - return 4 * ttf.hhea.numOfLongHorMetrics + 2 * numOfLast; + /** 优化287: 缓存到局部变量,避免设置后立即重读 */ + var nlm = gl - numOfLast; + ttf.hhea.numOfLongHorMetrics = nlm; + return 4 * nlm + 2 * numOfLast; } }); diff --git a/vendor/fonteditor-core/lib/ttf/table/name.js b/vendor/fonteditor-core/lib/ttf/table/name.js index 67cc496..e675bc9 100644 --- a/vendor/fonteditor-core/lib/ttf/table/name.js +++ b/vendor/fonteditor-core/lib/ttf/table/name.js @@ -50,8 +50,10 @@ var _default = exports.default = _table.default.create('name', [], { var language = 0; /* 检查是否有 windows english name */ + /** 优化291: 缓存 nameRecordTbl[k] 避免重复属性查找 */ for (var k = 0; k < count; k++) { - if (nameRecordTbl[k].platform === _platform.default.Microsoft && nameRecordTbl[k].encoding === _encoding.win.UCS2 && nameRecordTbl[k].language === 1033) { + var nr = nameRecordTbl[k]; + if (nr.platform === _platform.default.Microsoft && nr.encoding === _encoding.win.UCS2 && nr.language === 1033) { platform = _platform.default.Microsoft; encoding = _encoding.win.UCS2; language = 1033; diff --git a/vendor/fonteditor-core/lib/ttf/table/post.js b/vendor/fonteditor-core/lib/ttf/table/post.js index 68e567a..5b9cd0e 100644 --- a/vendor/fonteditor-core/lib/ttf/table/post.js +++ b/vendor/fonteditor-core/lib/ttf/table/post.js @@ -18,6 +18,9 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de var Posthead = _table.default.create('posthead', [['format', _struct.default.Fixed], ['italicAngle', _struct.default.Fixed], ['underlinePosition', _struct.default.Int16], ['underlineThickness', _struct.default.Int16], ['isFixedPitch', _struct.default.Uint32], ['minMemType42', _struct.default.Uint32], ['maxMemType42', _struct.default.Uint32], ['minMemType1', _struct.default.Uint32], ['maxMemType1', _struct.default.Uint32]]); +/** 优化287: 空名常量,避免每次 push [0] 普通数组导致 writeBytes 慢路径 */ +var EMPTY_PASCAL = new Uint8Array([0]); + /** * 优化64+252: 从原始字节按需提取单个 pascal string,使用 fromCharCode.apply 替代数组+join */ @@ -96,10 +99,15 @@ var _default = exports.default = _table.default.create('post', [], { view.setUint16(pos, nameIndex[i], false); pos += 2; } writer.offset = pos; + /** 优化287: 创建一次 Uint8Array 视图,直接 set 替代 writer.writeBytes */ var names = ttf.support.post.names; + var uv = new Uint8Array(writer.getBuffer()); for (var j = 0, jl = names.length; j < jl; j++) { - writer.writeBytes(names[j]); + var nameBytes = names[j]; + uv.set(nameBytes, pos); + pos += nameBytes.length; } + writer.offset = pos; } else { writer.offset = pos; } @@ -134,7 +142,7 @@ var _default = exports.default = _table.default.create('post', [], { var name = glyf.name; if (!name || name.charCodeAt(0) < 32) { nameIndexArr[i] = 258 + nameIndex++; - glyphNames.push([0]); + glyphNames.push(EMPTY_PASCAL); size++; } else { nameIndexArr[i] = 258 + nameIndex++; diff --git a/vendor/fonteditor-core/lib/ttf/ttf.js b/vendor/fonteditor-core/lib/ttf/ttf.js index b4d2e56..5117fed 100644 --- a/vendor/fonteditor-core/lib/ttf/ttf.js +++ b/vendor/fonteditor-core/lib/ttf/ttf.js @@ -426,9 +426,9 @@ var TTF = exports.default = /*#__PURE__*/function () { list = glyf; } if (list.length) { - list.forEach(function (g) { - delete g.name; - }); + for (var i = 0, l = list.length; i < l; i++) { + list[i].name = null; + } } return list; } @@ -638,7 +638,8 @@ var TTF = exports.default = /*#__PURE__*/function () { }, { key: "setGlyf", value: function setGlyf(glyfList) { - delete this.glyf; + /** 优化262: delete → null 赋值,避免 V8 隐藏类转换 */ + this.glyf = null; this.ttf.glyf = glyfList || []; return this.ttf.glyf; } diff --git a/vendor/fonteditor-core/lib/ttf/ttfreader.js b/vendor/fonteditor-core/lib/ttf/ttfreader.js index 889877c..c9e5c27 100644 --- a/vendor/fonteditor-core/lib/ttf/ttfreader.js +++ b/vendor/fonteditor-core/lib/ttf/ttfreader.js @@ -12,6 +12,9 @@ var _error = _interopRequireDefault(require("./error")); var _compound2simpleglyf = _interopRequireDefault(require("./util/compound2simpleglyf")); var _post = require("./table/post"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** 优化291: 表名列表提升为模块级常量,避免每次 readBuffer 创建新数组 */ +var TTF_TABLE_NAMES = ['head', 'maxp', 'loca', 'cmap', 'glyf', 'name', 'hhea', 'hmtx', 'post', 'OS/2', 'fpgm', 'cvt', 'prep', 'gasp', 'GPOS', 'kern', 'kerx']; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } @@ -62,7 +65,7 @@ var TTFReader = exports.default = /*#__PURE__*/function () { var kerning = this.options.kerning; var supportTables = _support.default; var tableInstances = {}; - var ttfTableNames = ['head', 'maxp', 'loca', 'cmap', 'glyf', 'name', 'hhea', 'hmtx', 'post', 'OS/2', 'fpgm', 'cvt', 'prep', 'gasp', 'GPOS', 'kern', 'kerx']; + var ttfTableNames = TTF_TABLE_NAMES; for (var ti = 0, tl = ttfTableNames.length; ti < tl; ti++) { var tableName = ttfTableNames[ti]; if (ttf.tables[tableName]) { @@ -257,8 +260,9 @@ var TTFReader = exports.default = /*#__PURE__*/function () { }, { key: "dispose", value: function dispose() { - delete this.ttf; - delete this.options; + /** 优化262: delete → null 赋值,避免 V8 隐藏类转换 */ + this.ttf = null; + this.options = null; } }]); }(); diff --git a/vendor/fonteditor-core/lib/ttf/ttftowoff2.js b/vendor/fonteditor-core/lib/ttf/ttftowoff2.js index 0a06ce7..7faffcc 100644 --- a/vendor/fonteditor-core/lib/ttf/ttftowoff2.js +++ b/vendor/fonteditor-core/lib/ttf/ttftowoff2.js @@ -21,8 +21,9 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de * @return {ArrayBuffer} woff格式byte流 */ function ttftowoff2(ttfBuffer) { + /** 优化278: 直接返回 Uint8Array,避免 .buffer -> Buffer -> new Uint8Array 多余转换链 */ var result = _index.default.encode(ttfBuffer); - return result.buffer || result; + return result; } /** diff --git a/vendor/fonteditor-core/lib/ttf/ttfwriter.js b/vendor/fonteditor-core/lib/ttf/ttfwriter.js index 71c84ad..e456cbc 100644 --- a/vendor/fonteditor-core/lib/ttf/ttfwriter.js +++ b/vendor/fonteditor-core/lib/ttf/ttfwriter.js @@ -19,6 +19,8 @@ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _d * @author mengke01(kekee000@gmail.com) */ var SUPPORT_TABLES = ['OS/2', 'cmap', 'glyf', 'head', 'hhea', 'hmtx', 'loca', 'maxp', 'name', 'post']; +/** 优化291: 日期正则预编译为模块级常量 */ +var ALL_DIGITS = /^\d+$/; var TTFWriter = exports.default = /*#__PURE__*/function () { function TTFWriter() { var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; @@ -45,7 +47,7 @@ var TTFWriter = exports.default = /*#__PURE__*/function () { ttf.head.checkSumAdjustment = 0; ttf.head.magickNumber = 0x5F0F3CF5; if (typeof ttf.head.created === 'string') { - ttf.head.created = /^\d+$/.test(ttf.head.created) ? +ttf.head.created : Date.parse(ttf.head.created); + ttf.head.created = ALL_DIGITS.test(ttf.head.created) ? +ttf.head.created : Date.parse(ttf.head.created); } if (typeof ttf.head.modified === 'string') { ttf.head.modified = /^\d+$/.test(ttf.head.modified) ? +ttf.head.modified : Date.parse(ttf.head.modified); @@ -80,7 +82,8 @@ var TTFWriter = exports.default = /*#__PURE__*/function () { }, { key: "dump", value: function dump(ttf) { - ttf.support = Object.assign({}, this.options.support); + /** 优化286: support 为 undefined 时直接赋值空对象,避免 Object.assign 调用 */ + ttf.support = this.options.support ? Object.assign({}, this.options.support) : {}; var ttfSize = 12 + ttf.numTables * 16; var ttfHeadOffset = 0; @@ -135,6 +138,8 @@ var TTFWriter = exports.default = /*#__PURE__*/function () { var buf = writer.getBuffer(); var wholeCheckSum = 0; var fullView = new Uint8Array(buf); + /** 优化261: 预创建 DataView,复用于所有表的校验和计算,避免每次 checkSumArrayBuffer 创建新 DataView */ + var fullDataView = new DataView(buf); for (var si = 0, sl = supportTableList.length; si < sl; si++) { var table = supportTableList[si]; var tableStart = writer.offset; @@ -148,7 +153,7 @@ var TTFWriter = exports.default = /*#__PURE__*/function () { fullView.fill(0, wView.byteOffset + writer.offset, wView.byteOffset + writer.offset + (4 - pad)); writer.offset += 4 - pad; } - table.checkSum = _checkSumArrayBuffer(buf, tableStart, table.size, fullView); + table.checkSum = _checkSumArrayBuffer(buf, tableStart, table.size, fullView, fullDataView); wholeCheckSum = (wholeCheckSum + table.checkSum) >>> 0; } @@ -162,8 +167,9 @@ var TTFWriter = exports.default = /*#__PURE__*/function () { /* 优化179: 用累加的各表校验和替代全局 checkSum,避免重遍历整个 buffer */ var ttfCheckSum = (0xB1B0AFBA - wholeCheckSum) >>> 0; csView.setUint32(ttfHeadOffset + 8, ttfCheckSum, false); - delete ttf.writeOptions; - delete ttf.support; + /** 优化260: delete → null 赋值,避免 V8 隐藏类转换 */ + ttf.writeOptions = null; + ttf.support = null; writer.dispose(); return buf; } @@ -180,22 +186,28 @@ var TTFWriter = exports.default = /*#__PURE__*/function () { /* 优化186: 使用 slice() 创建副本,防止 push 变异模块级数组导致后续调用表膨胀 */ var tables = SUPPORT_TABLES; ttf.writeOptions = {}; - /* 优化228: 合并 hinting 和 kerning 分支,消除重复 slice 检查 */ + /* 优化228+291: 合并 hinting 和 kerning 分支,消除重复表名 */ if (this.options.hinting || this.options.kerning) { tables = SUPPORT_TABLES.slice(); + /** 优化291: 使用 Set 去重,防止 hinting+kerning 同时开启时 GPOS/kern/kerx 被重复 push */ + var added = {}; if (this.options.hinting) { var hintTables = ['cvt', 'fpgm', 'prep', 'gasp', 'GPOS', 'kern', 'kerx']; for (var i = 0; i < hintTables.length; i++) { - if (ttf[hintTables[i]]) { - tables.push(hintTables[i]); + var tn = hintTables[i]; + if (ttf[tn] && !added[tn]) { + tables.push(tn); + added[tn] = true; } } } if (this.options.kerning) { var kernTables = ['GPOS', 'kern', 'kerx']; for (var j = 0; j < kernTables.length; j++) { - if (ttf[kernTables[j]]) { - tables.push(kernTables[j]); + var kn = kernTables[j]; + if (ttf[kn] && !added[kn]) { + tables.push(kn); + added[kn] = true; } } } @@ -217,7 +229,8 @@ var TTFWriter = exports.default = /*#__PURE__*/function () { }, { key: "dispose", value: function dispose() { - delete this.options; + /** 优化262: delete → null 赋值,避免 V8 隐藏类转换 */ + this.options = null; } }]); }(); diff --git a/vendor/fonteditor-core/lib/ttf/util/checkSum.js b/vendor/fonteditor-core/lib/ttf/util/checkSum.js index 68dd22e..d2ecf93 100644 --- a/vendor/fonteditor-core/lib/ttf/util/checkSum.js +++ b/vendor/fonteditor-core/lib/ttf/util/checkSum.js @@ -12,32 +12,37 @@ exports.checkSumArrayBuffer = checkSumArrayBuffer; /** * 优化205+229+248: 支持传入预创建的 Uint8Array,使用 DataView.getUint32 替代手动字节组装 + * 优化261: 支持传入预创建的全局 DataView(第 5 个参数),通过偏移量复用,避免每次创建新 DataView * 注意: bytes 必须是从 offset 开始的子视图,或者 offset 必须为 0 + * view 如果传入,必须覆盖 buffer 的完整范围 */ -function checkSumArrayBuffer(buffer, offset, length, bytes) { +function checkSumArrayBuffer(buffer, offset, length, bytes, view) { if (offset === undefined) offset = 0; length = length == null ? buffer.byteLength : length; if (offset + length > buffer.byteLength) { throw new Error('check sum out of bound'); } - /** 优化229: 当传入 fullView 时,用 subarray 创建正确的偏移视图(不拷贝数据) */ - if (!bytes) { - bytes = new Uint8Array(buffer, offset, length); - } else if (offset > 0) { - bytes = bytes.subarray(offset, offset + length); + /** 优化290: 优先检查全局 DataView 可用性,避免不必要的 subarray 分配 */ + var useGlobalView = view && view.byteLength >= offset + length; + if (!useGlobalView) { + if (!bytes) { + bytes = new Uint8Array(buffer, offset, length); + } else if (offset > 0) { + bytes = bytes.subarray(offset, offset + length); + } + view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength); + offset = 0; } - /** 优化248: 使用 DataView.getUint32 替代手动字节组装,减少 4x 数组访问 */ - var view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength); var nLongs = length >> 2; var sum = 0; var i = 0; while (i < nLongs) { - sum = (sum + view.getUint32(i << 2, false)) | 0; + sum = (sum + view.getUint32(offset + (i << 2), false)) | 0; i++; } var leftBytes = length - nLongs * 4; if (leftBytes) { - var off = nLongs << 2; + var off = offset + (nLongs << 2); var val = 0; for (var k = 0; k < leftBytes; k++) { val = (val << 8) | view.getUint8(off + k); diff --git a/vendor/fonteditor-core/lib/ttf/util/compound2simple.js b/vendor/fonteditor-core/lib/ttf/util/compound2simple.js index 7ba7d50..2491206 100644 --- a/vendor/fonteditor-core/lib/ttf/util/compound2simple.js +++ b/vendor/fonteditor-core/lib/ttf/util/compound2simple.js @@ -18,9 +18,9 @@ exports.default = compound2simple; */ function compound2simple(glyf, contours) { glyf.contours = contours; - delete glyf.compound; - delete glyf.glyfs; - // 这里hinting信息会失效,删除hinting信息 - delete glyf.instructions; + /** 优化260: delete → null 赋值,避免 V8 隐藏类转换 */ + glyf.compound = null; + glyf.glyfs = null; + glyf.instructions = null; return glyf; } \ No newline at end of file diff --git a/vendor/fonteditor-core/lib/ttf/util/contour2svg.js b/vendor/fonteditor-core/lib/ttf/util/contour2svg.js index d726c46..d6ad999 100644 --- a/vendor/fonteditor-core/lib/ttf/util/contour2svg.js +++ b/vendor/fonteditor-core/lib/ttf/util/contour2svg.js @@ -21,8 +21,12 @@ function contour2svg(contour) { if (!contour.length) { return ''; } + /** 优化293: 用乘法+截断替代 toFixed(),消除每个坐标点的 toFixed 调用开销 */ + var factor = precision <= 0 ? 1 : Math.pow(10, precision); + var invFactor = 1 / factor; + /** 优化293: ceil 内联,避免闭包创建 */ var ceil = function ceil(number) { - return +number.toFixed(precision); + return (Math.round(number * factor) * invFactor); }; var pathArr = []; var curPoint; diff --git a/vendor/fonteditor-core/lib/ttf/util/contours2svg.js b/vendor/fonteditor-core/lib/ttf/util/contours2svg.js index 136ebee..e165bb8 100644 --- a/vendor/fonteditor-core/lib/ttf/util/contours2svg.js +++ b/vendor/fonteditor-core/lib/ttf/util/contours2svg.js @@ -22,7 +22,10 @@ function contours2svg(contours, precision) { if (!contours.length) { return ''; } - return contours.map(function (contour) { - return (0, _contour2svg.default)(contour, precision); - }).join(''); + /** 优化293: map+join 替换为 for 循环字符串拼接,消除中间数组分配和闭包 */ + var path = ''; + for (var i = 0, l = contours.length; i < l; i++) { + path += (0, _contour2svg.default)(contours[i], precision); + } + return path; } \ No newline at end of file diff --git a/vendor/fonteditor-core/lib/ttf/util/glyfAdjust.js b/vendor/fonteditor-core/lib/ttf/util/glyfAdjust.js index 6aa376a..aed9c1d 100644 --- a/vendor/fonteditor-core/lib/ttf/util/glyfAdjust.js +++ b/vendor/fonteditor-core/lib/ttf/util/glyfAdjust.js @@ -4,10 +4,6 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.default = glyfAdjust; -var _pathAdjust = _interopRequireDefault(require("../../graphics/pathAdjust")); -var _pathCeil = _interopRequireDefault(require("../../graphics/pathCeil")); -var _computeBoundingBox = require("../../graphics/computeBoundingBox"); -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /** * @file glyf的缩放和平移调整 * @author mengke01(kekee000@gmail.com) @@ -31,62 +27,74 @@ function glyfAdjust(g) { var offsetX = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0; var offsetY = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0; var useCeil = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true; - /* 优化: 合并三次 forEach 为单次 for 循环,消除闭包分配 */ - if (g.contours && g.contours.length) { - var needScale = scaleX !== 1 || scaleY !== 1; - var needOffset = offsetX !== 0 || offsetY !== 0; - var needCeil = useCeil !== false; - if (needScale || needOffset || needCeil) { - for (var ci = 0, cl = g.contours.length; ci < cl; ci++) { - var contour = g.contours[ci]; - if (needScale) { - (0, _pathAdjust.default)(contour, scaleX, scaleY); - } - if (needOffset) { - (0, _pathAdjust.default)(contour, 1, 1, offsetX, offsetY); - } - if (needCeil) { - (0, _pathCeil.default)(contour); + var contours = g.contours; + var needScale = scaleX !== 1 || scaleY !== 1; + var needOffset = offsetX !== 0 || offsetY !== 0; + var needCeil = useCeil !== false; + var needTransform = needScale || needOffset || needCeil; + /** 优化270: 合并 scale+offset+ceil 为单次点遍历,内联 computePathBox 消除 .apply() 参数展开开销 */ + var needComputeBound = undefined === g.xMin || undefined === g.yMax || undefined === g.leftSideBearing || undefined === g.advanceWidth; + if (contours && contours.length) { + if (needTransform || needComputeBound) { + var left, right, top, bottom, found = false; + for (var ci = 0, cl = contours.length; ci < cl; ci++) { + var contour = contours[ci]; + if (!contour || !contour.length) continue; + for (var i = 0, l = contour.length; i < l; i++) { + var p = contour[i]; + if (needTransform) { + var nx = needScale ? scaleX * p.x : p.x; + var ny = needScale ? scaleY * p.y : p.y; + if (needOffset) { nx += offsetX; ny += offsetY; } + if (needCeil) { nx = Math.round(nx); ny = Math.round(ny); } + p.x = nx; + p.y = ny; + } + if (needComputeBound) { + 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; + } + } } } + if (needComputeBound) { + g.xMin = found ? left : 0; + g.xMax = found ? right : 0; + g.yMin = found ? top : 0; + g.yMax = found ? bottom : 0; + g.leftSideBearing = g.xMin; + var advanceWidth = g.advanceWidth; + if (undefined !== advanceWidth) { + g.advanceWidth = Math.round(advanceWidth * scaleX + offsetX); + } else { + g.advanceWidth = found ? right + Math.abs(left) : 0; + } + return g; + } } + } else if (needComputeBound) { + g.xMin = 0; + g.xMax = 0; + g.yMin = 0; + g.yMax = 0; + g.leftSideBearing = 0; + var advanceWidth2 = g.advanceWidth; + g.advanceWidth = undefined !== advanceWidth2 ? Math.round(advanceWidth2 * scaleX + offsetX) : 0; + return g; } - - // 重新计算xmin,xmax,ymin,ymax - var advanceWidth = g.advanceWidth; - if (undefined === g.xMin || undefined === g.yMax || undefined === g.leftSideBearing || undefined === g.advanceWidth) { - // 有的字形没有形状,需要特殊处理一下 - var bound; - if (g.contours && g.contours.length) { - // eslint-disable-next-line no-invalid-this - bound = _computeBoundingBox.computePathBox.apply(this, g.contours); - } else { - bound = { - x: 0, - y: 0, - width: 0, - height: 0 - }; - } - g.xMin = bound.x; - g.xMax = bound.x + bound.width; - g.yMin = bound.y; - g.yMax = bound.y + bound.height; - g.leftSideBearing = g.xMin; - - // 如果设置了advanceWidth就是用默认的,否则为xMax + abs(xMin) - if (undefined !== advanceWidth) { - g.advanceWidth = Math.round(advanceWidth * scaleX + offsetX); - } else { - g.advanceWidth = g.xMax + Math.abs(g.xMin); - } - } else { - g.xMin = Math.round(g.xMin * scaleX + offsetX); - g.xMax = Math.round(g.xMax * scaleX + offsetX); - g.yMin = Math.round(g.yMin * scaleY + offsetY); - g.yMax = Math.round(g.yMax * scaleY + offsetY); - g.leftSideBearing = Math.round(g.leftSideBearing * scaleX + offsetX); - g.advanceWidth = Math.round(advanceWidth * scaleX + offsetX); - } + if (needComputeBound) return g; + g.xMin = Math.round(g.xMin * scaleX + offsetX); + g.xMax = Math.round(g.xMax * scaleX + offsetX); + g.yMin = Math.round(g.yMin * scaleY + offsetY); + g.yMax = Math.round(g.yMax * scaleY + offsetY); + g.leftSideBearing = Math.round(g.leftSideBearing * scaleX + offsetX); + g.advanceWidth = Math.round(g.advanceWidth * scaleX + offsetX); return g; } \ No newline at end of file diff --git a/vendor/fonteditor-core/lib/ttf/util/optimizettf.js b/vendor/fonteditor-core/lib/ttf/util/optimizettf.js index c9727e0..a506a44 100644 --- a/vendor/fonteditor-core/lib/ttf/util/optimizettf.js +++ b/vendor/fonteditor-core/lib/ttf/util/optimizettf.js @@ -8,6 +8,13 @@ exports.ceilReduceAndSizeFlat = ceilReduceAndSizeFlat; var _reduceGlyf = _interopRequireDefault(require("./reduceGlyf")); var _glyFlag = _interopRequireDefault(require("../enum/glyFlag")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +/** 优化279: 枚举常量提升到模块级别,消除每个 glyph 调用时的属性查找 */ +var _ONCURVE = _glyFlag.default.ONCURVE; +var _XSHORT = _glyFlag.default.XSHORT; +var _YSHORT = _glyFlag.default.YSHORT; +var _XSAME = _glyFlag.default.XSAME; +var _YSAME = _glyFlag.default.YSAME; +var _REPEAT = _glyFlag.default.REPEAT; /** * @file 对ttf对象进行优化,查找错误,去除冗余点 * @author mengke01(kekee000@gmail.com) @@ -27,12 +34,12 @@ function ceilReduceAndSizeFromTypedArrays(glyf) { var endPts = glyf.endPtsOfContours; var numContours = endPts.length; - var ONCURVE = _glyFlag.default.ONCURVE; - var XSHORT = _glyFlag.default.XSHORT; - var YSHORT = _glyFlag.default.YSHORT; - var XSAME = _glyFlag.default.XSAME; - var YSAME = _glyFlag.default.YSAME; - var REPEAT = _glyFlag.default.REPEAT; + var ONCURVE = _ONCURVE; + var XSHORT = _XSHORT; + var YSHORT = _YSHORT; + var XSAME = _XSAME; + var YSAME = _YSAME; + var REPEAT = _REPEAT; var numPoints = xArr.length; var flagsC = new Uint8Array(numPoints); @@ -127,35 +134,39 @@ function ceilReduceAndSizeFromTypedArrays(glyf) { * 优化256: 写入方直接 trim subarray,消除 sizeof.js 二次 slicing */ function ceilReduceAndSizeFlat(glyf) { - var contours = glyf.contours; - /* 优化91+164: 跳过 reducePathFlat,用 write-index 替代 splice */ - var writeIdx = 0; - for (var j = 0, cl = contours.length; j < cl; j++) { - if (contours[j].length > 6) { - contours[writeIdx++] = contours[j]; - } - } - contours.length = writeIdx; - if (0 === contours.length) { - glyf.contours = null; - return; - } - + /** 优化279: _precomputedGlyfSupport 守卫提前,避免已缓存 glyph 的无效 contour 过滤 */ if (glyf._precomputedGlyfSupport) { return; } - var ONCURVE = _glyFlag.default.ONCURVE; - var XSHORT = _glyFlag.default.XSHORT; - var YSHORT = _glyFlag.default.YSHORT; - var XSAME = _glyFlag.default.XSAME; - var YSAME = _glyFlag.default.YSAME; - var REPEAT = _glyFlag.default.REPEAT; - + var contours = glyf.contours; + /* 优化279: 合并 contour 过滤和 totalPoints 计算 + _pointsPerContour 缓存为单次遍历 */ + var writeIdx = 0; var totalPoints = 0; + var ppcArr = new Array(contours.length); for (var j = 0, cl = contours.length; j < cl; j++) { - totalPoints += contours[j].length / 3 | 0; + if (contours[j].length > 6) { + var pts = contours[j].length / 3 | 0; + ppcArr[writeIdx] = pts; + totalPoints += pts; + contours[writeIdx] = contours[j]; + writeIdx++; + } } + contours.length = writeIdx; + ppcArr.length = writeIdx; + if (0 === contours.length) { + glyf.contours = null; + return; + } + glyf._pointsPerContour = ppcArr; + + var ONCURVE = _ONCURVE; + var XSHORT = _XSHORT; + var YSHORT = _YSHORT; + var XSAME = _XSAME; + var YSAME = _YSAME; + var REPEAT = _REPEAT; var flagsC = new Uint8Array(totalPoints); var fi = 0; var prevFlag = -1; @@ -182,11 +193,13 @@ function ceilReduceAndSizeFlat(glyf) { flagsC[fi++] = prevFlag = fFlag; var prevX = fpx, prevY = fpy; - var skipFirst = true; + /** 优化213+262: 首点提取到循环外,第一个 contour 从 i=3 开始跳过首点,消除 per-point skipFirst 分支 */ + var skipFirstContour = true; for (var j = 0, cl2 = contours.length; j < cl2; j++) { var contour = contours[j]; - for (var i = 0, l = contour.length; i < l; i += 3) { - if (skipFirst) { skipFirst = false; continue; } + var startI = skipFirstContour ? 3 : 0; + skipFirstContour = false; + for (var i = startI, l = contour.length; i < l; i += 3) { var px = contour[i]; var py = contour[i + 1]; var onCurve = contour[i + 2]; @@ -383,16 +396,20 @@ function optimizettf(ttf) { /* 优化99+103: hasCompound 已在主循环中追踪,过滤使用 _numContours 或 contours.length */ if (!hasCompound) { - /* 优化:glyf 过滤时同步重映射 cmap 索引,防止 format12 startId 超出 numGlyphs */ - var filtered = [glyfs[0]]; - var indexMap = [0]; + /* 优化284: 预分配 filtered + 索引赋值替代 push,indexMap 保持稀疏数组 */ + var filtered = new Array(gl); + var indexMap = []; + var fLen = 0; + filtered[fLen++] = glyfs[0]; + indexMap[0] = 0; for (var gi = 1; gi < gl; gi++) { var g = glyfs[gi]; if (g._numContours != null ? g._numContours > 0 : (g.contours && g.contours.length)) { - indexMap[gi] = filtered.length; - filtered.push(g); + indexMap[gi] = fLen; + filtered[fLen++] = g; } } + filtered.length = fLen; ttf.glyf = filtered; if (ttf.support && ttf.support.maxp) { ttf.support.maxp.numGlyphs = filtered.length; diff --git a/vendor/fonteditor-core/lib/ttf/util/otfContours2ttfContours.js b/vendor/fonteditor-core/lib/ttf/util/otfContours2ttfContours.js index 9f2593d..b5565ac 100644 --- a/vendor/fonteditor-core/lib/ttf/util/otfContours2ttfContours.js +++ b/vendor/fonteditor-core/lib/ttf/util/otfContours2ttfContours.js @@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.default = otfContours2ttfContours; +exports.otfContours2ttfContoursInPlace = otfContours2ttfContoursInPlace; var _bezierCubic2Q = require("../../math/bezierCubic2Q2"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /** @@ -85,7 +86,7 @@ function normalizeContourFlat(arr) { /** * 转换已标准化的轮廓,全扁平数组操作 * 优化178: 输入和输出都是扁平数组 [x, y, flag, ...] - * 精度优化: 全程浮点运算,最后统一 Math.round,消除累积取整误差 + * 优化285: 就地取整 + bbox 同步计算,消除最后的 Math.round 遍历 */ function transformContourFlat(arr) { var normalized = normalizeContourFlat(arr); @@ -95,31 +96,29 @@ function transformContourFlat(arr) { var estimatedMax = normalized.length * 2 + 6; var contour = new Array(estimatedMax); var ci = 0; - /** 第一个点一定是 onCurve — 保持浮点 */ - var r = Math.round; - var firstX = normalized[0], firstY = normalized[1]; - var rfx = r(firstX), rfy = r(firstY); - contour[ci++] = rfx; contour[ci++] = rfy; contour[ci++] = 1; - /** 优化: 在转换过程中同时计算包围盒,避免二次遍历 */ - var xMin = rfx, xMax = rfx, yMin = rfy, yMax = rfy; + /** 优化285: 首点取整,后续 lastX/lastY 始终为取整值 */ + var firstX = normalized[0], firstY = normalized[1]; + var rX = Math.round(firstX), rY = Math.round(firstY); + contour[ci++] = rX; contour[ci++] = rY; contour[ci++] = 1; + var xMin = rX, xMax = rX, yMin = rY, yMax = rY; + var lastX = firstX, lastY = firstY; var i = 3; var nLen = normalized.length; - var lastX = firstX; - var lastY = firstY; + /** 优化291: 预分配 bboxArr,循环中复用避免每次 cubic 曲线分配新数组 */ + var bboxArr = [xMin, xMax, yMin, yMax]; while (i < nLen) { var isOnCurve = normalized[i + 2]; if (isOnCurve) { - /** 线段:直接添加 onCurve 端点 */ + /** 线段:取整后添加 onCurve 端点,同步 bbox */ var px = normalized[i], py = normalized[i + 1]; - var rpx = r(px), rpy = r(py); - contour[ci++] = rpx; contour[ci++] = rpy; contour[ci++] = 1; - if (rpx < xMin) xMin = rpx; else if (rpx > xMax) xMax = rpx; - if (rpy < yMin) yMin = rpy; else if (rpy > yMax) yMax = rpy; - lastX = px; - lastY = py; + rX = Math.round(px); rY = Math.round(py); + contour[ci++] = rX; contour[ci++] = rY; contour[ci++] = 1; + lastX = px; lastY = py; + if (rX < xMin) xMin = rX; else if (rX > xMax) xMax = rX; + if (rY < yMin) yMin = rY; else if (rY > yMax) yMax = rY; i += 3; } else { /** offCurve 点 */ @@ -137,20 +136,14 @@ function transformContourFlat(arr) { } i = endIdx + 3; - /** 优化255: 使用 bezierCubic2Q2PushRounded,写入时直接取整,消除二次遍历 */ - var ciBefore = ci; - ci = (0, _bezierCubic2Q.bezierCubic2Q2PushRounded)(lastX, lastY, c1x, c1y, c2x, c2y, endX, endY, contour, ci); - /** 更新 bbox(坐标已在 PushRounded 中取整) */ - for (var bi = ciBefore; bi < ci; bi += 3) { - var bx = contour[bi]; - if (bx < xMin) xMin = bx; else if (bx > xMax) xMax = bx; - var by = contour[bi + 1]; - if (by < yMin) yMin = by; else if (by > yMax) yMax = by; - } + /** 优化291: 复用 bboxArr,避免每次 cubic 曲线分配新数组 */ + bboxArr[0] = xMin; bboxArr[1] = xMax; bboxArr[2] = yMin; bboxArr[3] = yMax; + ci = (0, _bezierCubic2Q.bezierCubic2Q2PushRounded)(lastX, lastY, c1x, c1y, c2x, c2y, endX, endY, contour, ci, bboxArr); lastX = endX; lastY = endY; + xMin = bboxArr[0]; xMax = bboxArr[1]; yMin = bboxArr[2]; yMax = bboxArr[3]; } else { - /** 单个 offCurve → 二次贝塞尔曲线(TTF 原生支持) */ + /** 单个 offCurve → 二次贝塞尔曲线 */ var endX2, endY2; if (nextIdx < nLen && normalized[nextIdx + 2]) { endX2 = normalized[nextIdx]; endY2 = normalized[nextIdx + 1]; @@ -158,21 +151,23 @@ function transformContourFlat(arr) { endX2 = firstX; endY2 = firstY; } i = nextIdx + 3; - var rc1x = r(c1x), rc1y = r(c1y); - var re2x = r(endX2), re2y = r(endY2); + /** 控制点也取整 */ + var rc1x = Math.round(c1x), rc1y = Math.round(c1y); + rX = Math.round(endX2); rY = Math.round(endY2); contour[ci++] = rc1x; contour[ci++] = rc1y; contour[ci++] = 0; - contour[ci++] = re2x; contour[ci++] = re2y; contour[ci++] = 1; - if (rc1x < xMin) xMin = rc1x; else if (rc1x > xMax) xMax = rc1x; - if (rc1y < yMin) yMin = rc1y; else if (rc1y > yMax) yMax = rc1y; - if (re2x < xMin) xMin = re2x; else if (re2x > xMax) xMax = re2x; - if (re2y < yMin) yMin = re2y; else if (re2y > yMax) yMax = re2y; + contour[ci++] = rX; contour[ci++] = rY; contour[ci++] = 1; lastX = endX2; lastY = endY2; + if (rc1x < xMin) xMin = rc1x; else if (rc1x > xMax) xMax = rc1x; + if (rc1y < yMin) yMin = rc1y; else if (rc1y > yMax) yMax = rc1y; + if (rX < xMin) xMin = rX; else if (rX > xMax) xMax = rX; + if (rY < yMin) yMin = rY; else if (rY > yMax) yMax = rY; } } } contour.length = ci; + return { contour: contour, xMin: xMin, yMin: yMin, xMax: xMax, yMax: yMax }; } @@ -233,6 +228,64 @@ function otfContours2ttfContours(otfContours) { }; } +/** + * 就地写入版本:直接将转换结果写入 target 对象,避免创建中间返回对象 + * 优化291: 消除每个 glyph 一次 { contours, xMin, yMin, xMax, yMax } 对象分配 + */ +function otfContours2ttfContoursInPlace(otfContours, target) { + if (!otfContours || !otfContours.length) { + return; + } + var contours = new Array(otfContours.length); + var cLen = 0; + var left = Infinity, right = -Infinity, top = Infinity, bottom = -Infinity; + var isFlat = otfContours[0] && (otfContours[0]._flatContours || (typeof otfContours[0][0] === 'number' && typeof otfContours[0][1] === 'number')); + for (var i = 0, l = otfContours.length; i < l; i++) { + var otfContour = otfContours[i]; + if (!otfContour || otfContour.length < 6) continue; + + var contour; + var contourBbox; + if (isFlat) { + var result = transformContourFlat(otfContour); + if (!result) continue; + contour = result.contour; + contourBbox = result; + } else { + contour = transformContourObj(otfContour); + } + if (contour.length < 3) continue; + contours[cLen++] = contour; + + if (contourBbox) { + if (contourBbox.xMin < left) left = contourBbox.xMin; + if (contourBbox.xMax > right) right = contourBbox.xMax; + if (contourBbox.yMin < top) top = contourBbox.yMin; + if (contourBbox.yMax > bottom) bottom = contourBbox.yMax; + } else { + for (var ci = 0, cl = contour.length; ci < cl; ci++) { + var p = contour[ci]; + 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; + } + } + } + contours.length = cLen; + target.contours = contours; + target._flatContours = true; + if (left !== Infinity) { + target.xMin = left; + target.yMin = top; + target.xMax = right; + target.yMax = bottom; + } else { + target.xMin = 0; + target.yMin = 0; + target.xMax = 0; + target.yMax = 0; + } +} + /** * 兼容旧对象数组格式 [onCurve, offCurve, ...] */ diff --git a/vendor/fonteditor-core/lib/ttf/util/readWindowsAllCodes.js b/vendor/fonteditor-core/lib/ttf/util/readWindowsAllCodes.js index 4f24f9f..a8a6127 100644 --- a/vendor/fonteditor-core/lib/ttf/util/readWindowsAllCodes.js +++ b/vendor/fonteditor-core/lib/ttf/util/readWindowsAllCodes.js @@ -34,8 +34,9 @@ function lookupFormat12(groups, unicode) { /** * 优化114: format4 二分查找 segment,替代线性扫描 + * 优化293: 接受预计算的 graphIdArrayIndexOffset 参数,避免每次调用重复计算 */ -function lookupFormat4(format4, unicode) { +function lookupFormat4(format4, unicode, _graphIdArrayIndexOffset) { var startCode = format4.startCode; var endCode = format4.endCode; var idDelta = format4.idDelta; @@ -54,8 +55,8 @@ function lookupFormat4(format4, unicode) { if (idRangeOffset[i] === 0) { return (unicode + idDelta[i]) % 0x10000; } - var graphIdArrayIndexOffset = format4.glyphIdArrayIndexOffset != null ? format4.glyphIdArrayIndexOffset : (format4.glyphIdArrayOffset - format4.idRangeOffsetOffset) / 2; - var index = i + idRangeOffset[i] / 2 + (unicode - startCode[i]) - graphIdArrayIndexOffset; + var graphIdArrayIndexOffset = _graphIdArrayIndexOffset != null ? _graphIdArrayIndexOffset : (format4.glyphIdArrayOffset - format4.idRangeOffsetOffset) / 2; + var index = i + (idRangeOffset[i] >> 1) + (unicode - startCode[i]) - graphIdArrayIndexOffset; var graphId; if (format4.glyphIdArray) { graphId = format4.glyphIdArray[index]; @@ -97,11 +98,13 @@ function readWindowsAllCodes(tables, ttf) { /* 优化65: subset 模式 - 只查找 subset 字符的 glyphId */ if (subset && subset.length > 0) { + /** 优化293: 预计算 graphIdArrayIndexOffset,避免在 subset 循环中重复计算 */ + var f4GIAO = format4 ? (format4.glyphIdArrayIndexOffset != null ? format4.glyphIdArrayIndexOffset : (format4.glyphIdArrayOffset - format4.idRangeOffsetOffset) / 2) : -1; 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); + var gid = lookupFormat4(format4, u, f4GIAO); if (gid >= 0) { codes[u] = gid; continue; } } var gid12 = lookupFormat12(format12.groups, u); @@ -110,7 +113,7 @@ function readWindowsAllCodes(tables, ttf) { } else if (format4) { for (var si2 = 0, sl2 = subset.length; si2 < sl2; si2++) { var u2 = subset[si2]; - var gid4 = lookupFormat4(format4, u2); + var gid4 = lookupFormat4(format4, u2, f4GIAO); if (gid4 >= 0) { codes[u2] = gid4; } } } @@ -174,52 +177,64 @@ function readWindowsAllCodes(tables, ttf) { } } } else if (format4) { + /** 优化262: 属性链缓存到局部变量 + 跳过 65535 避免 delete 导致 V8 隐藏类退化 */ var segCount = format4.segCountX2 / 2; var graphIdArrayIndexOffset = (format4.glyphIdArrayOffset - format4.idRangeOffsetOffset) / 2; + var f4StartCode = format4.startCode; + var f4EndCode = format4.endCode; + var f4IdDelta = format4.idDelta; + var f4IdRangeOffset = format4.idRangeOffset; + var f4GlyphIdArray = format4.glyphIdArray; 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; + var segEnd = f4EndCode[si3]; + if (segEnd > 0xFFFE) segEnd = 0xFFFE; + for (var _start = f4StartCode[si3]; _start <= segEnd; ++_start) { + if (f4IdRangeOffset[si3] === 0) { + codes[_start] = (_start + f4IdDelta[si3]) & 0xFFFF; } else { - var index = si3 + format4.idRangeOffset[si3] / 2 + (_start - format4.startCode[si3]) - graphIdArrayIndexOffset; - var graphId = format4.glyphIdArray[index]; + var index = si3 + (f4IdRangeOffset[si3] >> 1) + (_start - f4StartCode[si3]) - graphIdArrayIndexOffset; + var graphId = f4GlyphIdArray[index]; if (graphId !== 0) { - codes[_start] = (graphId + format4.idDelta[si3]) % 0x10000; + codes[_start] = (graphId + f4IdDelta[si3]) & 0xFFFF; } else { codes[_start] = 0; } } } } - delete codes[65535]; } else if (format2) { + /** 优化262: 缓存 subHeads[0] 和 subHeads[k] 到局部变量,消除重复属性链查找 */ var subHeadKeys = format2.subHeadKeys; var subHeads = format2.subHeads; var glyphs = format2.glyphs; var numGlyphs = ttf.maxp.numGlyphs; var _index = 0; + var sh0 = subHeads[0]; for (var bi = 0; bi < 256; bi++) { if (subHeadKeys[bi] === 0) { if (bi >= format2.maxPos) { _index = 0; - } else if (bi < subHeads[0].firstCode || bi >= subHeads[0].firstCode + subHeads[0].entryCount || subHeads[0].idRangeOffset + (bi - subHeads[0].firstCode) >= glyphs.length) { + } else if (bi < sh0.firstCode || bi >= sh0.firstCode + sh0.entryCount || sh0.idRangeOffset + (bi - sh0.firstCode) >= glyphs.length) { _index = 0; - } else if ((_index = glyphs[subHeads[0].idRangeOffset + (bi - subHeads[0].firstCode)]) !== 0) { - _index = _index + subHeads[0].idDelta; + } else if ((_index = glyphs[sh0.idRangeOffset + (bi - sh0.firstCode)]) !== 0) { + _index = _index + sh0.idDelta; } if (_index !== 0 && _index < numGlyphs) { codes[bi] = _index; } } else { - var k = subHeadKeys[bi]; - for (var j = 0, entryCount = subHeads[k].entryCount; j < entryCount; j++) { - if (subHeads[k].idRangeOffset + j >= glyphs.length) { + var sh = subHeads[subHeadKeys[bi]]; + var shIdRangeOffset = sh.idRangeOffset; + var shIdDelta = sh.idDelta; + var shFirstCode = sh.firstCode; + for (var j = 0, entryCount = sh.entryCount; j < entryCount; j++) { + if (shIdRangeOffset + j >= glyphs.length) { _index = 0; - } else if ((_index = glyphs[subHeads[k].idRangeOffset + j]) !== 0) { - _index = _index + subHeads[k].idDelta; + } else if ((_index = glyphs[shIdRangeOffset + j]) !== 0) { + _index = _index + shIdDelta; } if (_index !== 0 && _index < numGlyphs) { - var _unicode = (bi << 8 | j + subHeads[k].firstCode) % 0xffff; + var _unicode = (bi << 8 | j + shFirstCode) & 0xffff; codes[_unicode] = _index; } } diff --git a/vendor/fonteditor-core/lib/ttf/util/string.js b/vendor/fonteditor-core/lib/ttf/util/string.js index f07c786..bd28d9f 100644 --- a/vendor/fonteditor-core/lib/ttf/util/string.js +++ b/vendor/fonteditor-core/lib/ttf/util/string.js @@ -7,6 +7,9 @@ exports.default = void 0; var _unicodeName = _interopRequireDefault(require("../enum/unicodeName")); var _postName = _interopRequireDefault(require("../enum/postName")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** 优化291: 模块级 TextDecoder 单例,避免每次 getUTF8String 创建新实例 */ +var _utf8Decoder = typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { fatal: false }) : null; /** * @file ttf字符串相关函数 * @author mengke01(kekee000@gmail.com) @@ -26,6 +29,10 @@ function stringify(str) { if (!str) { return str; } + /** 优化291: 快速路径,无 null 字符直接返回原字符串 */ + if (str.indexOf('\0') === -1) { + return str; + } var newStr = ''; for (var i = 0, l = str.length, ch; i < l; i++) { ch = str.charCodeAt(i); @@ -124,12 +131,12 @@ var _default = exports.default = { */ toUCS2Bytes: function toUCS2Bytes(str) { str = stringify(str); - /* 优化: 预分配 Uint8Array 替代动态 push */ - var byteArr = new Uint8Array(str.length * 2); - for (var i = 0, l = str.length; i < l; i++) { + /* 优化291: 递增偏移替代 i*2 乘法 */ + var byteArr = new Uint8Array(str.length << 1); + for (var i = 0, j = 0, l = str.length; i < l; i++, j += 2) { var ch = str.charCodeAt(i); - byteArr[i * 2] = ch >> 8; - byteArr[i * 2 + 1] = ch & 0xFF; + byteArr[j] = ch >> 8; + byteArr[j + 1] = ch & 0xFF; } return byteArr; }, @@ -158,9 +165,9 @@ var _default = exports.default = { * @return {string} 字符串 */ getUTF8String: function getUTF8String(bytes) { - /** 优化254: 使用 TextDecoder 替代手动 UTF-8 解码 + unescape */ - if (typeof TextDecoder !== 'undefined') { - return new TextDecoder('utf-8', { fatal: false }).decode(bytes); + /** 优化291: 使用模块级 TextDecoder 单例 */ + if (_utf8Decoder) { + return _utf8Decoder.decode(bytes); } var str = ''; for (var i = 0, l = bytes.length; i < l; i++) { diff --git a/vendor/fonteditor-core/lib/ttf/util/transformGlyfContours.js b/vendor/fonteditor-core/lib/ttf/util/transformGlyfContours.js index 0dc7f2f..acf1a19 100644 --- a/vendor/fonteditor-core/lib/ttf/util/transformGlyfContours.js +++ b/vendor/fonteditor-core/lib/ttf/util/transformGlyfContours.js @@ -4,9 +4,6 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.default = transformGlyfContours; -var _pathCeil = _interopRequireDefault(require("../../graphics/pathCeil")); -var _pathTransform = _interopRequireDefault(require("../../graphics/pathTransform")); -var _lang = require("../../common/lang"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /** * @file 转换复合字形的contours,以便于显示 @@ -69,11 +66,17 @@ function transformGlyfContours(glyf, ttf) { compoundContours.push(transformAndCeilFlat(contour, ta, tb, tc, td, te, tf)); } } else { - /* 传统对象格式 - 深拷贝 + 分别调用 transform+ceil */ - var contours = (0, _lang.clone)(sourceContours); - for (var i = 0, l = contours.length; i < l; i++) { - (0, _pathTransform.default)(contours[i], ta, tb, tc, td, te, tf); - compoundContours.push((0, _pathCeil.default)(contours[i])); + /* 优化265: 浅拷贝 contour 数组 + 内联 transform+ceil,消除 deep clone 开销 */ + for (var i = 0, l = sourceContours.length; i < l; i++) { + var srcContour = sourceContours[i]; + var newContour = new Array(srcContour.length); + for (var pi = 0, pl = srcContour.length; pi < pl; pi++) { + var p = srcContour[pi]; + var px = p.x * ta + p.y * tc + te; + var py = p.x * tb + p.y * td + tf; + newContour[pi] = { x: (px + 0.5) | 0, y: (py + 0.5) | 0, onCurve: p.onCurve }; + } + compoundContours.push(newContour); } } } diff --git a/vendor/fonteditor-core/lib/ttf/writer.js b/vendor/fonteditor-core/lib/ttf/writer.js index 4f6f7b1..f65c3fe 100644 --- a/vendor/fonteditor-core/lib/ttf/writer.js +++ b/vendor/fonteditor-core/lib/ttf/writer.js @@ -308,7 +308,8 @@ var Writer = /*#__PURE__*/function () { }, { key: "dispose", value: function dispose() { - delete this.view; + /** 优化262: delete → null 赋值,避免 V8 隐藏类转换 */ + this.view = null; } }]); }(); // 优化19: 直接绑定方法,避免 curry 闭包开销 diff --git a/vendor/fonteditor-core/woff2/index.js b/vendor/fonteditor-core/woff2/index.js index d8b260f..9d0268d 100644 --- a/vendor/fonteditor-core/woff2/index.js +++ b/vendor/fonteditor-core/woff2/index.js @@ -41,8 +41,9 @@ const woff2Module = { * @param {ArrayBuffer|Buffer|Array} ttfBuffer ttf buffer * @return {Uint8Array} uint8 array */ + /** 优化267: encodeTTFToWOFF2 直接返回 Uint8Array,消除二次包装 */ encode(ttfBuffer) { - return new Uint8Array(encodeTTFToWOFF2(ttfBuffer)); + return encodeTTFToWOFF2(ttfBuffer); }, /** diff --git a/vendor/fonteditor-core/woff2/woff2-encode.js b/vendor/fonteditor-core/woff2/woff2-encode.js index 09bcedc..35306cf 100644 --- a/vendor/fonteditor-core/woff2/woff2-encode.js +++ b/vendor/fonteditor-core/woff2/woff2-encode.js @@ -45,9 +45,6 @@ function readU32(arr, off) { return (arr[off] << 24 | arr[off + 1] << 16 | arr[o /** 向 Uint8Array 写入无符号 16 位大端序 */ function writeU16(buf, v, p) { buf[p] = v >> 8; buf[p + 1] = v & 0xFF; } -/** 向 Uint8Array 写入有符号 16 位大端序 */ -function writeI16(buf, v, p) { buf[p] = v >> 8; buf[p + 1] = v & 0xFF; } - /** 向 Uint8Array 写入无符号 32 位大端序 */ function writeU32(buf, v, p) { buf[p] = v >> 24; buf[p + 1] = (v >> 16) & 0xFF; buf[p + 2] = (v >> 8) & 0xFF; buf[p + 3] = v & 0xFF; } @@ -74,13 +71,27 @@ for (let i = 0; i < KNOWN_TAGS.length; i++) { } /** - * 从 4 字节 tag 获取 Known Table Tag 索引 + * 优化262: 预构建 tag uint32 → index Map,用于 getTagIndexU32 + */ +const KNOWN_TAG_U32_MAP = new Map(); +for (let i = 0; i < KNOWN_TAGS.length; i++) { + const t = KNOWN_TAGS[i]; + KNOWN_TAG_U32_MAP.set((t.charCodeAt(0) << 24 | t.charCodeAt(1) << 16 | t.charCodeAt(2) << 8 | t.charCodeAt(3)) >>> 0, i); +} + +/** + * 从 4 字节 tag uint32 获取 Known Table Tag 索引 */ function getTagIndex(tag) { const idx = KNOWN_TAG_MAP.get(tag); return idx !== undefined ? idx : 63; } +function getTagIndexU32(tagU32) { + const idx = KNOWN_TAG_U32_MAP.get(tagU32); + return idx !== undefined ? idx : 63; +} + /* ======== 变长整数编码 ======== */ /** 计算 UIntBase128 编码后的字节数 */ @@ -141,8 +152,9 @@ function encode255UInt16(value, buf, offset) { return 2; } buf[offset] = 253; - buf[offset + 1] = (value >> 8) & 0xFF; - buf[offset + 2] = value & 0xFF; + /** 优化291: Uint8Array 赋值自动截断,消除冗余的 & 0xFF */ + buf[offset + 1] = value >> 8; + buf[offset + 2] = value; return 3; } @@ -167,16 +179,16 @@ for (let i = 0; i < 84; i++) TRIPLET_DATA_SIZES[i] = 1; for (let i = 84; i < 120; i++) TRIPLET_DATA_SIZES[i] = 2; for (let i = 120; i < 124; i++) TRIPLET_DATA_SIZES[i] = 3; for (let i = 124; i < 128; i++) TRIPLET_DATA_SIZES[i] = 4; -function dataSizeFromTriplet(ti) { - return TRIPLET_DATA_SIZES[ti]; -} /** * 仅计算 triplet flag 字节,不写入数据 * 优化: Pass 1 只需要 flag,数据写入由 Pass 2 的 writePointDataByFlag 完成 */ -function calcTripletFlag(onCurve, dx, dy) { - const curveBit = onCurve ? 0 : 128; +/** + * 计算 triplet flag 并同时写入数据字节,消除重复的 absDx/absDy 计算和二次分支判断 + * 优化291: 合并 calcTripletFlag + writePointDataByFlag 为单一函数 + */ +function calcTripletAndWrite(curveBit, dx, dy, buf, offset) { const absDx = dx < 0 ? -dx : dx; const absDy = dy < 0 ? -dy : dy; const xSignBit = dx >= 0 ? 1 : 0; @@ -185,11 +197,13 @@ function calcTripletFlag(onCurve, dx, dy) { /* dx=0, Y 单轴 1 数据字节 (flag 0-9) */ if (dx === 0 && absDy < 1280) { + buf[offset] = absDy & 0xFF; return curveBit + ((absDy & 0xF00) >> 7) + ySignBit; } /* dy=0, dx≠0, X 单轴 1 数据字节 (flag 10-19) */ if (dy === 0 && dx !== 0 && absDx < 1280) { + buf[offset] = absDx & 0xFF; return curveBit + 10 + ((absDx & 0xF00) >> 7) + xSignBit; } @@ -197,6 +211,7 @@ function calcTripletFlag(onCurve, dx, dy) { if (dx !== 0 && dy !== 0 && absDx < 65 && absDy < 65) { const ax = absDx - 1; const ay = absDy - 1; + buf[offset] = ((ax & 0xF) << 4) | (ay & 0xF); return curveBit + 20 + (ax & 0x30) + ((ay & 0x30) >> 2) + xySignBits; } @@ -204,65 +219,27 @@ function calcTripletFlag(onCurve, dx, dy) { if (dx !== 0 && dy !== 0 && absDx < 769 && absDy < 769) { const ax = absDx - 1; const ay = absDy - 1; + buf[offset] = ax & 0xFF; + buf[offset + 1] = ay & 0xFF; return curveBit + 84 + 12 * ((ax & 0x300) >> 8) + ((ay & 0x300) >> 6) + xySignBits; } /* 双轴 3 数据字节 (flag 120-123) */ if (absDx < 4096 && absDy < 4096) { + buf[offset] = absDx >> 4; + buf[offset + 1] = ((absDx & 0xF) << 4) | (absDy >> 8); + buf[offset + 2] = absDy & 0xFF; return curveBit + 120 + xySignBits; } /* 兜底 4 数据字节 (flag 124-127) */ + buf[offset] = (absDx >> 8) & 0xFF; + buf[offset + 1] = absDx & 0xFF; + buf[offset + 2] = (absDy >> 8) & 0xFF; + buf[offset + 3] = absDy & 0xFF; return curveBit + 124 + xySignBits; } -/** - * 仅写入数据字节(不含 flag),利用缓存的 flag 值避免重复分支判断 - * flag 的低 7 位 (tripletIndex) 决定数据字节布局 - * 优化182: 返回写入的字节数,消除调用方 TRIPLET_DATA_SIZES 重复查找 - */ -function writePointDataByFlag(flag, dx, dy, buf, offset) { - const ti = flag & 0x7F; - const absDx = dx < 0 ? -dx : dx; - const absDy = dy < 0 ? -dy : dy; - - if (ti < 10) { - /* Y 单轴 1 字节 */ - buf[offset] = absDy & 0xFF; - return 1; - } else if (ti < 20) { - /* X 单轴 1 字节 */ - buf[offset] = absDx & 0xFF; - return 1; - } else if (ti < 84) { - /* 双轴 1 字节 */ - const ax = absDx - 1; - const ay = absDy - 1; - buf[offset] = ((ax & 0xF) << 4) | (ay & 0xF); - return 1; - } else if (ti < 120) { - /* 双轴 2 字节 */ - const ax = absDx - 1; - const ay = absDy - 1; - buf[offset] = ax & 0xFF; - buf[offset + 1] = ay & 0xFF; - return 2; - } else if (ti < 124) { - /* 双轴 3 字节 */ - buf[offset] = absDx >> 4; - buf[offset + 1] = ((absDx & 0xF) << 4) | (absDy >> 8); - buf[offset + 2] = absDy & 0xFF; - return 3; - } else { - /* 兜底 4 字节 */ - buf[offset] = (absDx >> 8) & 0xFF; - buf[offset + 1] = absDx & 0xFF; - buf[offset + 2] = (absDy >> 8) & 0xFF; - buf[offset + 3] = absDy & 0xFF; - return 4; - } -} - /* ======== glyf + loca 表变换 ======== */ /** @@ -386,15 +363,17 @@ function transformGlyfAndLoca(glyfData, locaData, indexFormat, numGlyphs) { /* 简单 glyph */ let dataOff = glyphStart + 10; - /** 优化226: 合并 endPts 读取与 nPoints 编码,消除 endPtsOfContours TypedArray 分配 */ - const nPointsEncoded = new Uint8Array(numberOfContours * 3); + /** 优化291: Pass 1 只计算 nPointsBytes + 存储 delta,延迟编码到 Pass 2,消除临时 buffer 分配和 memcpy */ + const nPointsDeltas = new Int16Array(numberOfContours); let nPointsBytes = 0; let prevEnd = -1; let lastEndPt = -1; for (let c = 0; c < numberOfContours; c++) { const endPt = readU16(glyfData, dataOff); dataOff += 2; - nPointsBytes += encode255UInt16(endPt - prevEnd, nPointsEncoded, nPointsBytes); + const delta = endPt - prevEnd; + nPointsDeltas[c] = delta; + nPointsBytes += size255UInt16(delta); prevEnd = endPt; } lastEndPt = prevEnd; @@ -436,9 +415,11 @@ function transformGlyfAndLoca(glyfData, locaData, indexFormat, numGlyphs) { totalPoints += numPoints; } - /* 解码 X 坐标 */ + /** 优化293: coords 拆分为独立 xCoords/yCoords,顺序内存访问更利于 CPU 缓存预取 */ const xCoords = new Int32Array(numPoints); + const yCoords = new Int32Array(numPoints); let px = 0; + let calcXMin, calcXMax; for (let xi = 0; xi < numPoints; xi++) { const f = flagsArr[xi]; if (f & XSHORT_FLAG) { @@ -451,12 +432,13 @@ function transformGlyfAndLoca(glyfData, locaData, indexFormat, numGlyphs) { dataOff += 2; } xCoords[xi] = px; + if (xi === 0) { calcXMin = px; calcXMax = px; } + else if (px < calcXMin) calcXMin = px; + else if (px > calcXMax) calcXMax = px; } - /* 解码 Y 坐标 + 同时计算 bbox */ - const yCoords = new Int32Array(numPoints); let py = 0; - let calcXMin = xCoords[0], calcYMin = py, calcXMax = xCoords[0], calcYMax = py; + let calcYMin, calcYMax; for (let yi = 0; yi < numPoints; yi++) { const f = flagsArr[yi]; if (f & YSHORT_FLAG) { @@ -469,14 +451,14 @@ function transformGlyfAndLoca(glyfData, locaData, indexFormat, numGlyphs) { dataOff += 2; } yCoords[yi] = py; - const px2 = xCoords[yi]; - if (px2 < calcXMin) calcXMin = px2; - else if (px2 > calcXMax) calcXMax = px2; - if (py < calcYMin) calcYMin = py; + if (yi === 0) { calcYMin = py; calcYMax = py; } + else if (py < calcYMin) calcYMin = py; else if (py > calcYMax) calcYMax = py; } - /* 优化183: 就地覆盖 flagsArr 为 triplet flags,省掉单独的 cachedFlags 分配 */ + /* 优化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) { @@ -484,33 +466,43 @@ function transformGlyfAndLoca(glyfData, locaData, indexFormat, numGlyphs) { bboxStreamSize += 8; } - /** 优化: Pass 1 就地覆盖 xCoords/yCoords 为 delta,消除 Pass 2 重复减法 */ 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 onCurve = !!(flagsArr[pi] & ONCURVE_FLAG); - const dx = xCoords[pi] - prevX; - const dy = yCoords[pi] - prevY; - const flag = calcTripletFlag(onCurve, dx, dy); + const cx = xCoords[pi]; + const cy = yCoords[pi]; + /** 优化293: 内联 curveBit 计算,消除 !! onCurve + 函数内三元运算 */ + const curveBit = (flagsArr[pi] & 1) ? 0 : 128; + const dx = cx - prevX; + const dy = cy - prevY; + const flag = calcTripletAndWrite(curveBit, dx, dy, glyphStreamBuf, gsbi); flagsArr[pi] = flag; - xCoords[pi] = dx; - yCoords[pi] = dy; - glyphStreamSize += TRIPLET_DATA_SIZES[flag & 0x7F]; - prevX += dx; - prevY += dy; + gsbi += TRIPLET_DATA_SIZES[flag & 0x7F]; + prevX = cx; + prevY = cy; } - glyphStreamSize += size255UInt16(instructionLength); + glyphStreamSize += gsbi + size255UInt16(instructionLength); } - /* 优化: 仅存储 Pass 2 实际需要的字段,减少对象大小 */ - glyphInfos[gi] = { - composite: false, - numberOfContours, - nPointsEncoded, - nPointsBytes, - instructions, - xCoords, yCoords, flags: flagsArr, - calcXMin, calcYMin, calcXMax, calcYMax, - }; + /* 优化277: 存储 glyphStreamBuf,Pass 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, + numberOfContours: 0, + }; } const nContourStreamSize = numGlyphs * 2; @@ -573,23 +565,28 @@ function transformGlyfAndLoca(glyfData, locaData, indexFormat, numGlyphs) { const g = glyphInfos[gi]; /* nContourStream: 每个 glyph 写入 numberOfContours */ + /** 优化291: writeI16 内联为直接数组写入 */ if (!g) { - writeI16(result, 0, nContourPos); nContourPos += 2; + nContourPos += 2; continue; } if (g.composite) { - writeI16(result, -1, nContourPos); nContourPos += 2; + result[nContourPos] = 0xFF; result[nContourPos + 1] = 0xFF; } else { - writeI16(result, g.numberOfContours, nContourPos); nContourPos += 2; + const nc = g.numberOfContours; + result[nContourPos] = nc >> 8; result[nContourPos + 1] = nc & 0xFF; } + nContourPos += 2; if (g.composite) { result.set(glyfData.subarray(g.rawOffset, g.rawOffset + g.rawLength), glyphPos); glyphPos += g.rawLength; - writeI16(result, g.xMin, bboxPos); bboxPos += 2; - writeI16(result, g.yMin, bboxPos); bboxPos += 2; - writeI16(result, g.xMax, bboxPos); bboxPos += 2; - writeI16(result, g.yMax, bboxPos); bboxPos += 2; + /** 优化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) { @@ -603,9 +600,12 @@ function transformGlyfAndLoca(glyfData, locaData, indexFormat, numGlyphs) { if (g.numberOfContours === 0) continue; - /* nPointsStream: 直接使用预编码的 delta 数据 */ - result.set(g.nPointsEncoded.subarray(0, g.nPointsBytes), nPointsPos); - nPointsPos += g.nPointsBytes; + /* 优化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) { @@ -614,26 +614,23 @@ function transformGlyfAndLoca(glyfData, locaData, indexFormat, numGlyphs) { } /** - * flag + glyph 子流 — xCoords/yCoords 已在 Pass 1 就地覆盖为 delta - * 直接读取 delta,消除 prevX/prevY 追踪和减法运算 + * 优化291: flag + glyph 子流 — Pass 1 已预写入 glyphStreamBuf,直接 set 拷贝 + * flag stream 使用 TypedArray.set 替代逐字节循环 */ - const numPts = g.xCoords.length; - const xCoords = g.xCoords; - const yCoords = g.yCoords; - const tripletFlags = g.flags; - for (let pi = 0; pi < numPts; pi++) { - const flag = tripletFlags[pi]; - result[flagPos++] = flag; - glyphPos += writePointDataByFlag(flag, xCoords[pi], yCoords[pi], result, glyphPos); - } + 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))) { - writeI16(result, g.calcXMin, bboxPos); bboxPos += 2; - writeI16(result, g.calcYMin, bboxPos); bboxPos += 2; - writeI16(result, g.calcXMax, bboxPos); bboxPos += 2; - writeI16(result, g.calcYMax, bboxPos); bboxPos += 2; + /** 优化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; + result[bboxPos + 6] = g.calcYMax >> 8; result[bboxPos + 7] = g.calcYMax & 0xFF; + bboxPos += 8; } } @@ -654,10 +651,10 @@ function transformGlyfAndLoca(glyfData, locaData, indexFormat, numGlyphs) { /* ======== 主编码函数 ======== */ -/** 优化242: 模块级排序函数,避免每次 encodeTTFToWOFF2 创建闭包 */ +/** 优化242+262: 模块级排序函数,使用 tagU32 数值比较替代字符串比较 */ function sortDirEntries(a, b) { var d = a.tagIndex - b.tagIndex; - return d ? d : (a.tag < b.tag ? -1 : a.tag > b.tag ? 1 : 0); + return d ? d : (a.tagU32 < b.tagU32 ? -1 : a.tagU32 > b.tagU32 ? 1 : 0); } const WOFF2_SIGNATURE = 0x774F4632; @@ -674,17 +671,33 @@ function encodeTTFToWOFF2(ttfBuffer) { const numTables = readU16(data, 4); /* 解析 Table Directory */ - const tables = []; + /** 优化284: 预分配 tables 数组,索引赋值替代 push */ + const tables = new Array(numTables); for (let i = 0; i < numTables; i++) { const off = 12 + i * 16; - const tag = String.fromCharCode(data[off], data[off + 1], data[off + 2], data[off + 3]); + const tagU32 = (data[off] << 24 | data[off + 1] << 16 | data[off + 2] << 8 | data[off + 3]) >>> 0; const offset = readU32(data, off + 8); const length = readU32(data, off + 12); - tables.push({ tag, offset, length, tagIndex: getTagIndex(tag) }); + tables[i] = { tagU32, offset, length, tagIndex: getTagIndexU32(tagU32) }; } + /* tag uint32 常量 */ + const TAG_DSIG = (68 << 24 | 83 << 16 | 73 << 8 | 71) >>> 0; + const TAG_head = (104 << 24 | 101 << 16 | 97 << 8 | 100) >>> 0; + const TAG_maxp = (109 << 24 | 97 << 16 | 120 << 8 | 112) >>> 0; + const TAG_glyf = (103 << 24 | 108 << 16 | 121 << 8 | 102) >>> 0; + const TAG_loca = (108 << 24 | 111 << 16 | 99 << 8 | 97) >>> 0; + /* 移除 DSIG,按 tag 升序排列 */ - const filtered = tables.filter(t => t.tag !== "DSIG"); + /** 优化284: 原地过滤替代 .filter(),消除中间数组分配 */ + let writeIdx = 0; + for (let i = 0; i < numTables; i++) { + if (tables[i].tagU32 !== TAG_DSIG) { + tables[writeIdx++] = tables[i]; + } + } + tables.length = writeIdx; + const filtered = tables; filtered.sort(sortDirEntries); /* 查找关键表 */ @@ -696,10 +709,10 @@ function encodeTTFToWOFF2(ttfBuffer) { /* 优化: for...of → for 循环,消除迭代器协议开销 */ for (var fi = 0, fl = filtered.length; fi < fl; fi++) { var t = filtered[fi]; - if (t.tag === "head") indexToLocFormat = readU16(data, t.offset + 50); - if (t.tag === "maxp") numGlyphs = readU16(data, t.offset + 4); - if (t.tag === "glyf") glyfTable = t; - if (t.tag === "loca") locaTable = t; + if (t.tagU32 === TAG_head) indexToLocFormat = readU16(data, t.offset + 50); + if (t.tagU32 === TAG_maxp) numGlyphs = readU16(data, t.offset + 4); + if (t.tagU32 === TAG_glyf) glyfTable = t; + if (t.tagU32 === TAG_loca) locaTable = t; } /* glyf + loca 变换 */ @@ -712,51 +725,54 @@ function encodeTTFToWOFF2(ttfBuffer) { } /* 构建 Table Directory entries */ - const dirEntries = []; + /** 优化284: 预分配 dirEntries 数组,索引赋值替代 push */ + const dirEntries = new Array(filtered.length); + let dirIdx = 0; let totalDirSize = 0; for (var fi2 = 0, fl2 = filtered.length; fi2 < fl2; fi2++) { var t = filtered[fi2]; - if (t.tag === "loca") { + if (t.tagU32 === TAG_loca) { /** 优化:直接使用 transformGlyfAndLoca 返回的 locaOrigLength,避免重复计算 */ const origLength = glyfTransformed ? glyfTransformed.locaOrigLength : t.length; - dirEntries.push({ - tag: t.tag, tagIndex: t.tagIndex, + dirEntries[dirIdx++] = { + tagU32: t.tagU32, tagIndex: t.tagIndex, flags: t.tagIndex, origLength, transformLength: 0, data: EMPTY_UINT8, hasTransform: true, - }); + }; totalDirSize += 1 + sizeUIntBase128(origLength) + sizeUIntBase128(0); continue; } - if (t.tag === "glyf" && glyfTransformed) { - dirEntries.push({ - tag: t.tag, tagIndex: t.tagIndex, + if (t.tagU32 === TAG_glyf && glyfTransformed) { + dirEntries[dirIdx++] = { + tagU32: t.tagU32, tagIndex: t.tagIndex, flags: t.tagIndex, origLength: t.length, transformLength: glyfTransformed.transformedGlyf.length, data: glyfTransformed.transformedGlyf, hasTransform: true, - }); + }; totalDirSize += 1 + sizeUIntBase128(t.length) + sizeUIntBase128(glyfTransformed.transformedGlyf.length); continue; } let tableData = data.subarray(t.offset, t.offset + t.length); - dirEntries.push({ - tag: t.tag, tagIndex: t.tagIndex, + dirEntries[dirIdx++] = { + tagU32: t.tagU32, tagIndex: t.tagIndex, flags: t.tagIndex, origLength: t.length, transformLength: t.length, data: tableData, - isHead: t.tag === "head", - }); + isHead: t.tagU32 === TAG_head, + }; totalDirSize += 1 + sizeUIntBase128(t.length); } + dirEntries.length = dirIdx; /* 计算 totalSfntSize */ let totalSfntSize = 12 + filtered.length * 16; @@ -788,9 +804,14 @@ function encodeTTFToWOFF2(ttfBuffer) { } } - /* Brotli 压缩,传入 SIZE_HINT 帮助预分配内部缓冲区 */ - if (totalTableDataSize > 0) BROTLI_OPTIONS_WITH_HINT.params[BROTLI_PARAM_SIZE_HINT] = totalTableDataSize; - const compressedData = brotliCompressSync(uncompressedData, totalTableDataSize > 0 ? BROTLI_OPTIONS_WITH_HINT : BROTLI_OPTIONS_BASE); + /** 优化286: 复用 BROTLI_OPTIONS_WITH_HINT 模板,只修改 sizeHint 值,避免每次创建新 options */ + if (totalTableDataSize > 0) { + BROTLI_OPTIONS_WITH_HINT.params[BROTLI_PARAM_SIZE_HINT] = totalTableDataSize; + var brotliOptions = BROTLI_OPTIONS_WITH_HINT; + } else { + var brotliOptions = BROTLI_OPTIONS_BASE; + } + const compressedData = brotliCompressSync(uncompressedData, brotliOptions); /* 组装 WOFF2(预计算 padding,避免额外拷贝) */ const rawLength = WOFF2_HEADER_SIZE + totalDirSize + compressedData.length; @@ -820,7 +841,11 @@ function encodeTTFToWOFF2(ttfBuffer) { const entry = dirEntries[di]; woff2[dirPos++] = entry.flags; if (entry.tagIndex === 63) { - for (let ci = 0; ci < 4; ci++) woff2[dirPos++] = entry.tag.charCodeAt(ci); + var tu = entry.tagU32; + woff2[dirPos++] = (tu >>> 24) & 0xFF; + woff2[dirPos++] = (tu >>> 16) & 0xFF; + woff2[dirPos++] = (tu >>> 8) & 0xFF; + woff2[dirPos++] = tu & 0xFF; } dirPos += encodeUIntBase128(entry.origLength, woff2, dirPos); if (entry.hasTransform) { @@ -834,7 +859,8 @@ function encodeTTFToWOFF2(ttfBuffer) { /* WOFF2 规范要求文件大小 4 字节对齐(Round4) 已在分配时预留 padding 空间,无需额外拷贝 */ - return woff2.buffer; + /** 优化267: 直接返回 Uint8Array,消除 woff2/index.js 的 new Uint8Array 包装 */ + return woff2; } module.exports = { encodeTTFToWOFF2 };