优化性能

This commit is contained in:
崮生(子虚) 2026-04-11 17:53:57 +08:00
parent 6ba093ff70
commit bd3189b459
41 changed files with 1249 additions and 854 deletions

View File

@ -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<typeof Font.create>) => {
};
/** 序列化为指定格式的二进制数据 */
export const writeFont = async (
/** 优化291: 移除 async消除不必要的微任务调度 */
export const writeFont = (
font: ReturnType<ReturnType<typeof Font.create>["optimize"]>,
outType: FontEditor.FontType,
): Promise<Uint8Array> => {
/** 纯 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);

View File

@ -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() {
</section>
</Show>
<Show when={import.meta.env.DEV}>
<FontDebugPreview />
</Show>
<UploadSection config={serverConfig()} onUploaded={refreshFonts} />
<section style={{ ...s.section, "font-size": "12px", color: "#aaa", "line-height": "1.8" }}>

107
src/FontDebugPreview.tsx Normal file
View File

@ -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<FontInfo[]>([]);
const loaders = new Map<string, { update: (text: string) => 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 (
<section style={s.section}>
<div style={{ display: "flex", "align-items": "center", gap: "8px", "margin-bottom": "12px" }}>
<span style={{ "font-size": "13px", "font-weight": 600, color: "#e6a700" }}>DEV </span>
<span style={{ "font-size": "11px", color: "#aaa" }}> woff2 / ttf </span>
</div>
<For each={fonts()}>
{(font) => {
const base = font.name.replace(/\.[^.]+$/, "");
return (
<div style={s.card}>
<div style={{ "font-size": "11px", color: "#999", "margin-bottom": "6px", display: "flex", "justify-content": "space-between" }}>
<span style={{ "font-weight": 500, color: "#555" }}>{font.name}</span>
<span style={{ color: "#bbb" }}>{font.dir}</span>
</div>
<For each={["woff2", "ttf"] as const}>
{(ot) => (
<div style={s.row}>
<span style={s.label}>{ot}</span>
<div
style={{
...s.text,
"font-family": `"DevPreview_${base}_${ot}", "楷体", KaiTi, STKaiti, serif`,
}}
>
{PREVIEW_TEXT}
</div>
</div>
)}
</For>
</div>
);
}}
</For>
</section>
);
}

View File

@ -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 替代 spliceO(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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;
}
}]);
}();

View File

@ -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 闭包和动态属性查找

View File

@ -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 {

View File

@ -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

View File

@ -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;
}
}

View File

@ -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) {

View File

@ -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 保持一致,否则会导致表偏移错位 */

View File

@ -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;
}

View File

@ -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];

View File

@ -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;

View File

@ -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;

View File

@ -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);
}

View File

@ -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) {

View File

@ -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;

View File

@ -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;
}
});

View File

@ -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;

View File

@ -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++;

View File

@ -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;
}

View File

@ -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;
}
}]);
}();

View File

@ -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;
}
/**

View File

@ -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;
}
}]);
}();

View File

@ -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);

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}
// 重新计算xminxmaxyminymax
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;
}

View File

@ -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 + 索引赋值替代 pushindexMap 保持稀疏数组 */
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;

View File

@ -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, ...]
*/

View File

@ -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;
}
}

View File

@ -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++) {

View File

@ -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);
}
}
}

View File

@ -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 闭包开销

View File

@ -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);
},
/**

View File

@ -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: 存储 glyphStreamBufPass 2 直接 set 拷贝,不再需要 writePointDataByFlag */
glyphInfos[gi] = numberOfContours > 0
? {
composite: false,
numberOfContours,
/** 优化291: 存储 nPointsDeltas 替代 nPointsEncoded延迟编码到 Pass 2 */
nPointsDeltas,
nPointsBytes,
instructions,
flags: flagsArr,
calcXMin, calcYMin, calcXMax, calcYMax,
glyphStreamBuf, glyphStreamBytes: gsbi,
}
: {
composite: false,
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 };