mirror of
https://github.com/2234839/web-font.git
synced 2026-04-29 21:00:45 +08:00
修改为本地引用,清理冗余文件
This commit is contained in:
parent
a8fdb24de4
commit
765a301649
@ -1,4 +1,5 @@
|
||||
import { Font, type FontEditor } from "fonteditor-core";
|
||||
import { Font } from "../../vendor/fonteditor-core/lib/ttf/font.js";
|
||||
import type { FontEditor } from "../../vendor/fonteditor-core/lib/ttf/font.js";
|
||||
|
||||
/**
|
||||
* 字体裁剪的所有可配置步骤
|
||||
|
||||
48
debug_profiling.ts
Normal file
48
debug_profiling.ts
Normal file
@ -0,0 +1,48 @@
|
||||
/**
|
||||
* 精确分段计时:找出当前真正的瓶颈
|
||||
*/
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { Font } from "./vendor/fonteditor-core/lib/ttf/font.js";
|
||||
|
||||
const FONT_PATH = "font/令东齐伋复刻体.ttf";
|
||||
const raw = await readFile(FONT_PATH);
|
||||
const fontBuffer = new Uint8Array(raw).buffer;
|
||||
|
||||
const testCases = [
|
||||
{ label: "8个汉字", subset: [..."天地玄黄宇宙洪荒"].map(c => c.codePointAt(0)!) },
|
||||
{ label: "千字文前段", subset: [..."天地玄黄宇宙洪荒日月盈昃辰宿列张寒来暑往秋收冬藏闰余成岁律吕调阳云腾致雨露结为霜金生丽水玉出昆冈剑号巨阙珠称夜光果珍李柰菜重芥姜海咸河淡鳞潜羽翔"].map(c => c.codePointAt(0)!) },
|
||||
];
|
||||
|
||||
const ROUNDS = 30;
|
||||
|
||||
for (const { label, subset } of testCases) {
|
||||
let createSum = 0, optimizeSum = 0, sortSum = 0, writeSum = 0;
|
||||
|
||||
for (let i = 0; i < ROUNDS; i++) {
|
||||
let t0 = performance.now();
|
||||
const font = Font.create(fontBuffer, { type: "ttf", subset });
|
||||
let t1 = performance.now();
|
||||
createSum += t1 - t0;
|
||||
|
||||
const optimized = font.optimize();
|
||||
let t2 = performance.now();
|
||||
optimizeSum += t2 - t1;
|
||||
|
||||
const sorted = optimized.sort();
|
||||
let t3 = performance.now();
|
||||
sortSum += t3 - t2;
|
||||
|
||||
const result = sorted.write({ type: "ttf" });
|
||||
let t4 = performance.now();
|
||||
writeSum += t4 - t3;
|
||||
}
|
||||
|
||||
const total = createSum + optimizeSum + sortSum + writeSum;
|
||||
console.log(`${label} (${ROUNDS} rounds):`);
|
||||
console.log(` create: ${createSum.toFixed(1)}ms (${(createSum / total * 100).toFixed(1)}%)`);
|
||||
console.log(` optimize: ${optimizeSum.toFixed(1)}ms (${(optimizeSum / total * 100).toFixed(1)}%)`);
|
||||
console.log(` sort: ${sortSum.toFixed(1)}ms (${(sortSum / total * 100).toFixed(1)}%)`);
|
||||
console.log(` write: ${writeSum.toFixed(1)}ms (${(writeSum / total * 100).toFixed(1)}%)`);
|
||||
console.log(` total: ${total.toFixed(1)}ms`);
|
||||
console.log();
|
||||
}
|
||||
51
debug_table_timing3.ts
Normal file
51
debug_table_timing3.ts
Normal file
@ -0,0 +1,51 @@
|
||||
/**
|
||||
* 精确表读取计时
|
||||
*/
|
||||
import { readFile } from "node:fs/promises";
|
||||
|
||||
const FONT_PATH = "font/令东齐伋复刻体.ttf";
|
||||
const raw = await readFile(FONT_PATH);
|
||||
const fontBuffer = new Uint8Array(raw).buffer;
|
||||
|
||||
const ROUNDS = 20;
|
||||
const tableTimes: Record<string, number> = {};
|
||||
|
||||
for (let r = 0; r < ROUNDS; r++) {
|
||||
const ReaderModule = await import("./vendor/fonteditor-core/lib/ttf/reader.js");
|
||||
const Reader = (ReaderModule as any).default || ReaderModule;
|
||||
const DirectoryModule = await import("./vendor/fonteditor-core/lib/ttf/table/directory.js");
|
||||
const Directory = (DirectoryModule as any).default || DirectoryModule;
|
||||
const supportModule = await import("./vendor/fonteditor-core/lib/ttf/table/support.js");
|
||||
const support = (supportModule as any).default || supportModule;
|
||||
|
||||
const reader = new Reader(fontBuffer, 0, fontBuffer.byteLength, false);
|
||||
const ttf: any = {};
|
||||
|
||||
ttf.version = reader.readFixed(0);
|
||||
ttf.numTables = reader.readUint16();
|
||||
ttf.searchRange = reader.readUint16();
|
||||
ttf.entrySelector = reader.readUint16();
|
||||
ttf.rangeShift = reader.readUint16();
|
||||
ttf.tables = new Directory(reader.offset).read(reader, ttf);
|
||||
ttf.readOptions = { subset: [..."天地玄黄宇宙洪荒日月盈昃辰宿列张寒来暑往秋收冬藏闰余成岁律吕调阳云腾致雨露结为霜金生丽水玉出昆冈剑号巨阙珠称夜光果珍李柰菜重芥姜海咸河淡鳞潜羽翔"].map(c => c.codePointAt(0)) };
|
||||
|
||||
for (const tableName of Object.keys(support)) {
|
||||
if (ttf.tables[tableName]) {
|
||||
const offset = ttf.tables[tableName].offset;
|
||||
const t0 = performance.now();
|
||||
ttf[tableName] = new support[tableName](offset).read(reader, ttf);
|
||||
const t1 = performance.now();
|
||||
tableTimes[tableName] = (tableTimes[tableName] || 0) + (t1 - t0);
|
||||
}
|
||||
}
|
||||
reader.dispose();
|
||||
}
|
||||
|
||||
const total = Object.values(tableTimes).reduce((a: number, b: number) => a + b, 0);
|
||||
const sorted = Object.entries(tableTimes).sort((a, b) => b[1] - a[1]);
|
||||
|
||||
console.log(`表读取时间 (${ROUNDS} rounds):`);
|
||||
for (const [name, time] of sorted) {
|
||||
console.log(` ${name.padEnd(8)} ${time.toFixed(1).padStart(8)}ms ${(time / total * 100).toFixed(1).padStart(3)}%`);
|
||||
}
|
||||
console.log(` ${'total'.padEnd(8)} ${total.toFixed(1).padStart(8)}ms`);
|
||||
@ -15,18 +15,20 @@
|
||||
"release": "pnpm build && pnpm build_backend && pnpm docker_build && pnpm docker_push"
|
||||
},
|
||||
"dependencies": {
|
||||
"fonteditor-core": "file:./vendor/fonteditor-core",
|
||||
"solid-js": "^1.9.12",
|
||||
"web-streams-polyfill": "^4.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^25.5.2",
|
||||
"@xmldom/xmldom": "^0.9.9",
|
||||
"jsdom": "^29.0.2",
|
||||
"skia-canvas": "^3.0.8",
|
||||
"tsup": "^8.5.1",
|
||||
"typescript": "^6.0.2",
|
||||
"undici": "^8.0.2",
|
||||
"vite": "^8.0.7",
|
||||
"vite-plugin-pilot": "^1.0.19",
|
||||
"vite-plugin-solid": "^2.11.12"
|
||||
"vite-plugin-solid": "^2.11.12",
|
||||
"vitest": "^4.1.3"
|
||||
}
|
||||
}
|
||||
570
pnpm-lock.yaml
generated
570
pnpm-lock.yaml
generated
@ -8,9 +8,6 @@ importers:
|
||||
|
||||
.:
|
||||
dependencies:
|
||||
fonteditor-core:
|
||||
specifier: file:./vendor/fonteditor-core
|
||||
version: file:vendor/fonteditor-core
|
||||
solid-js:
|
||||
specifier: ^1.9.12
|
||||
version: 1.9.12
|
||||
@ -21,6 +18,12 @@ importers:
|
||||
'@types/node':
|
||||
specifier: ^25.5.2
|
||||
version: 25.5.2
|
||||
'@xmldom/xmldom':
|
||||
specifier: ^0.9.9
|
||||
version: 0.9.9
|
||||
jsdom:
|
||||
specifier: ^29.0.2
|
||||
version: 29.0.2
|
||||
skia-canvas:
|
||||
specifier: ^3.0.8
|
||||
version: 3.0.8
|
||||
@ -42,9 +45,23 @@ importers:
|
||||
vite-plugin-solid:
|
||||
specifier: ^2.11.12
|
||||
version: 2.11.12(solid-js@1.9.12)(vite@8.0.7(@types/node@25.5.2)(esbuild@0.27.7))
|
||||
vitest:
|
||||
specifier: ^4.1.3
|
||||
version: 4.1.3(@types/node@25.5.2)(jsdom@29.0.2)(vite@8.0.7(@types/node@25.5.2)(esbuild@0.27.7))
|
||||
|
||||
packages:
|
||||
|
||||
'@asamuzakjp/css-color@5.1.8':
|
||||
resolution: {integrity: sha512-OISPR9c2uPo23rUdvfEQiLPjoMLOpEeLNnP5iGkxr6tDDxJd3NjD+6fxY0mdaMbIPUjFGL4HFOJqLvow5q4aqQ==}
|
||||
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
|
||||
|
||||
'@asamuzakjp/dom-selector@7.0.8':
|
||||
resolution: {integrity: sha512-erMO6FgtM02dC24NGm0xufMzWz5OF0wXKR7BpvGD973bq/GbmR8/DbxNZbj0YevQ5hlToJaWSVK/G9/NDgGEVw==}
|
||||
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
|
||||
|
||||
'@asamuzakjp/nwsapi@2.3.9':
|
||||
resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==}
|
||||
|
||||
'@babel/code-frame@7.29.0':
|
||||
resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
@ -126,6 +143,46 @@ packages:
|
||||
resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@bramus/specificity@2.4.2':
|
||||
resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==}
|
||||
hasBin: true
|
||||
|
||||
'@csstools/color-helpers@6.0.2':
|
||||
resolution: {integrity: sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==}
|
||||
engines: {node: '>=20.19.0'}
|
||||
|
||||
'@csstools/css-calc@3.1.1':
|
||||
resolution: {integrity: sha512-HJ26Z/vmsZQqs/o3a6bgKslXGFAungXGbinULZO3eMsOyNJHeBBZfup5FiZInOghgoM4Hwnmw+OgbJCNg1wwUQ==}
|
||||
engines: {node: '>=20.19.0'}
|
||||
peerDependencies:
|
||||
'@csstools/css-parser-algorithms': ^4.0.0
|
||||
'@csstools/css-tokenizer': ^4.0.0
|
||||
|
||||
'@csstools/css-color-parser@4.0.2':
|
||||
resolution: {integrity: sha512-0GEfbBLmTFf0dJlpsNU7zwxRIH0/BGEMuXLTCvFYxuL1tNhqzTbtnFICyJLTNK4a+RechKP75e7w42ClXSnJQw==}
|
||||
engines: {node: '>=20.19.0'}
|
||||
peerDependencies:
|
||||
'@csstools/css-parser-algorithms': ^4.0.0
|
||||
'@csstools/css-tokenizer': ^4.0.0
|
||||
|
||||
'@csstools/css-parser-algorithms@4.0.0':
|
||||
resolution: {integrity: sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==}
|
||||
engines: {node: '>=20.19.0'}
|
||||
peerDependencies:
|
||||
'@csstools/css-tokenizer': ^4.0.0
|
||||
|
||||
'@csstools/css-syntax-patches-for-csstree@1.1.2':
|
||||
resolution: {integrity: sha512-5GkLzz4prTIpoyeUiIu3iV6CSG3Plo7xRVOFPKI7FVEJ3mZ0A8SwK0XU3Gl7xAkiQ+mDyam+NNp875/C5y+jSA==}
|
||||
peerDependencies:
|
||||
css-tree: ^3.2.1
|
||||
peerDependenciesMeta:
|
||||
css-tree:
|
||||
optional: true
|
||||
|
||||
'@csstools/css-tokenizer@4.0.0':
|
||||
resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==}
|
||||
engines: {node: '>=20.19.0'}
|
||||
|
||||
'@emnapi/core@1.9.1':
|
||||
resolution: {integrity: sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==}
|
||||
|
||||
@ -291,6 +348,15 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@exodus/bytes@1.15.0':
|
||||
resolution: {integrity: sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==}
|
||||
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
|
||||
peerDependencies:
|
||||
'@noble/hashes': ^1.8.0 || ^2.0.0
|
||||
peerDependenciesMeta:
|
||||
'@noble/hashes':
|
||||
optional: true
|
||||
|
||||
'@jridgewell/gen-mapping@0.3.13':
|
||||
resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
|
||||
|
||||
@ -552,6 +618,9 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@standard-schema/spec@1.1.0':
|
||||
resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==}
|
||||
|
||||
'@tybys/wasm-util@0.10.1':
|
||||
resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==}
|
||||
|
||||
@ -567,15 +636,50 @@ packages:
|
||||
'@types/babel__traverse@7.28.0':
|
||||
resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==}
|
||||
|
||||
'@types/chai@5.2.3':
|
||||
resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==}
|
||||
|
||||
'@types/deep-eql@4.0.2':
|
||||
resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==}
|
||||
|
||||
'@types/estree@1.0.8':
|
||||
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
|
||||
|
||||
'@types/node@25.5.2':
|
||||
resolution: {integrity: sha512-tO4ZIRKNC+MDWV4qKVZe3Ql/woTnmHDr5JD8UI5hn2pwBrHEwOEMZK7WlNb5RKB6EoJ02gwmQS9OrjuFnZYdpg==}
|
||||
|
||||
'@xmldom/xmldom@0.8.12':
|
||||
resolution: {integrity: sha512-9k/gHF6n/pAi/9tqr3m3aqkuiNosYTurLLUtc7xQ9sxB/wm7WPygCv8GYa6mS0fLJEHhqMC1ATYhz++U/lRHqg==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
'@vitest/expect@4.1.3':
|
||||
resolution: {integrity: sha512-CW8Q9KMtXDGHj0vCsqui0M5KqRsu0zm0GNDW7Gd3U7nZ2RFpPKSCpeCXoT+/+5zr1TNlsoQRDEz+LzZUyq6gnQ==}
|
||||
|
||||
'@vitest/mocker@4.1.3':
|
||||
resolution: {integrity: sha512-XN3TrycitDQSzGRnec/YWgoofkYRhouyVQj4YNsJ5r/STCUFqMrP4+oxEv3e7ZbLi4og5kIHrZwekDJgw6hcjw==}
|
||||
peerDependencies:
|
||||
msw: ^2.4.9
|
||||
vite: ^6.0.0 || ^7.0.0 || ^8.0.0
|
||||
peerDependenciesMeta:
|
||||
msw:
|
||||
optional: true
|
||||
vite:
|
||||
optional: true
|
||||
|
||||
'@vitest/pretty-format@4.1.3':
|
||||
resolution: {integrity: sha512-hYqqwuMbpkkBodpRh4k4cQSOELxXky1NfMmQvOfKvV8zQHz8x8Dla+2wzElkMkBvSAJX5TRGHJAQvK0TcOafwg==}
|
||||
|
||||
'@vitest/runner@4.1.3':
|
||||
resolution: {integrity: sha512-VwgOz5MmT0KhlUj40h02LWDpUBVpflZ/b7xZFA25F29AJzIrE+SMuwzFf0b7t4EXdwRNX61C3B6auIXQTR3ttA==}
|
||||
|
||||
'@vitest/snapshot@4.1.3':
|
||||
resolution: {integrity: sha512-9l+k/J9KG5wPJDX9BcFFzhhwNjwkRb8RsnYhaT1vPY7OufxmQFc9sZzScRCPTiETzl37mrIWVY9zxzmdVeJwDQ==}
|
||||
|
||||
'@vitest/spy@4.1.3':
|
||||
resolution: {integrity: sha512-ujj5Uwxagg4XUIfAUyRQxAg631BP6e9joRiN99mr48Bg9fRs+5mdUElhOoZ6rP5mBr8Bs3lmrREnkrQWkrsTCw==}
|
||||
|
||||
'@vitest/utils@4.1.3':
|
||||
resolution: {integrity: sha512-Pc/Oexse/khOWsGB+w3q4yzA4te7W4gpZZAvk+fr8qXfTURZUMj5i7kuxsNK5mP/dEB6ao3jfr0rs17fHhbHdw==}
|
||||
|
||||
'@xmldom/xmldom@0.9.9':
|
||||
resolution: {integrity: sha512-qycIHAucxy/LXAYIjmLmtQ8q9GPnMbnjG1KXhWm9o5sCr6pOYDATkMPiTNa6/v8eELyqOQ2FsEqeoFYmgv/gJg==}
|
||||
engines: {node: '>=14.6'}
|
||||
|
||||
acorn@8.16.0:
|
||||
resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==}
|
||||
@ -589,6 +693,10 @@ packages:
|
||||
any-promise@1.3.0:
|
||||
resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
|
||||
|
||||
assertion-error@2.0.1:
|
||||
resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
babel-plugin-jsx-dom-expressions@0.40.6:
|
||||
resolution: {integrity: sha512-v3P1MW46Lm7VMpAkq0QfyzLWWkC8fh+0aE5Km4msIgDx5kjenHU0pF2s+4/NH8CQn/kla6+Hvws+2AF7bfV5qQ==}
|
||||
peerDependencies:
|
||||
@ -608,6 +716,9 @@ packages:
|
||||
engines: {node: '>=6.0.0'}
|
||||
hasBin: true
|
||||
|
||||
bidi-js@1.0.3:
|
||||
resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==}
|
||||
|
||||
browserslist@4.28.2:
|
||||
resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==}
|
||||
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
|
||||
@ -626,6 +737,10 @@ packages:
|
||||
caniuse-lite@1.0.30001787:
|
||||
resolution: {integrity: sha512-mNcrMN9KeI68u7muanUpEejSLghOKlVhRqS/Za2IeyGllJ9I9otGpR9g3nsw7n4W378TE/LyIteA0+/FOZm4Kg==}
|
||||
|
||||
chai@6.2.2:
|
||||
resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
chokidar@4.0.3:
|
||||
resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
|
||||
engines: {node: '>= 14.16.0'}
|
||||
@ -644,9 +759,17 @@ packages:
|
||||
convert-source-map@2.0.0:
|
||||
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
|
||||
|
||||
css-tree@3.2.1:
|
||||
resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==}
|
||||
engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0}
|
||||
|
||||
csstype@3.2.3:
|
||||
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
|
||||
|
||||
data-urls@7.0.0:
|
||||
resolution: {integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==}
|
||||
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
|
||||
|
||||
debug@4.4.3:
|
||||
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
|
||||
engines: {node: '>=6.0'}
|
||||
@ -656,6 +779,9 @@ packages:
|
||||
supports-color:
|
||||
optional: true
|
||||
|
||||
decimal.js@10.6.0:
|
||||
resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==}
|
||||
|
||||
detect-libc@2.1.2:
|
||||
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
|
||||
engines: {node: '>=8'}
|
||||
@ -667,6 +793,9 @@ packages:
|
||||
resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==}
|
||||
engines: {node: '>=0.12'}
|
||||
|
||||
es-module-lexer@2.0.0:
|
||||
resolution: {integrity: sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==}
|
||||
|
||||
esbuild@0.27.7:
|
||||
resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==}
|
||||
engines: {node: '>=18'}
|
||||
@ -676,6 +805,13 @@ packages:
|
||||
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
estree-walker@3.0.3:
|
||||
resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
|
||||
|
||||
expect-type@1.3.0:
|
||||
resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
fdir@6.5.0:
|
||||
resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
@ -697,9 +833,6 @@ packages:
|
||||
debug:
|
||||
optional: true
|
||||
|
||||
fonteditor-core@file:vendor/fonteditor-core:
|
||||
resolution: {directory: vendor/fonteditor-core, type: directory}
|
||||
|
||||
fsevents@2.3.3:
|
||||
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||
@ -709,6 +842,10 @@ packages:
|
||||
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
html-encoding-sniffer@6.0.0:
|
||||
resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==}
|
||||
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
|
||||
|
||||
html-entities@2.3.3:
|
||||
resolution: {integrity: sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==}
|
||||
|
||||
@ -716,6 +853,9 @@ packages:
|
||||
resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
|
||||
engines: {node: '>= 14'}
|
||||
|
||||
is-potential-custom-element-name@1.0.1:
|
||||
resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
|
||||
|
||||
is-what@4.1.16:
|
||||
resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==}
|
||||
engines: {node: '>=12.13'}
|
||||
@ -727,6 +867,15 @@ packages:
|
||||
js-tokens@4.0.0:
|
||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||
|
||||
jsdom@29.0.2:
|
||||
resolution: {integrity: sha512-9VnGEBosc/ZpwyOsJBCQ/3I5p7Q5ngOY14a9bf5btenAORmZfDse1ZEheMiWcJ3h81+Fv7HmJFdS0szo/waF2w==}
|
||||
engines: {node: ^20.19.0 || ^22.13.0 || >=24.0.0}
|
||||
peerDependencies:
|
||||
canvas: ^3.0.0
|
||||
peerDependenciesMeta:
|
||||
canvas:
|
||||
optional: true
|
||||
|
||||
jsesc@3.1.0:
|
||||
resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
|
||||
engines: {node: '>=6'}
|
||||
@ -822,12 +971,19 @@ packages:
|
||||
resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
|
||||
lru-cache@11.3.3:
|
||||
resolution: {integrity: sha512-JvNw9Y81y33E+BEYPr0U7omo+U9AySnsMsEiXgwT6yqd31VQWTLNQqmT4ou5eqPFUrTfIDFta2wKhB1hyohtAQ==}
|
||||
engines: {node: 20 || >=22}
|
||||
|
||||
lru-cache@5.1.1:
|
||||
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
|
||||
|
||||
magic-string@0.30.21:
|
||||
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
|
||||
|
||||
mdn-data@2.27.1:
|
||||
resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==}
|
||||
|
||||
merge-anything@5.1.7:
|
||||
resolution: {integrity: sha512-eRtbOb1N5iyH0tkQDAoQ4Ipsp/5qSR79Dzrz8hEPxRX10RWWR/iQXdoKmBSRCThY1Fh5EhISDtpSc93fpxUniQ==}
|
||||
engines: {node: '>=12.13'}
|
||||
@ -853,12 +1009,18 @@ packages:
|
||||
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
obug@2.1.1:
|
||||
resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==}
|
||||
|
||||
parenthesis@3.1.8:
|
||||
resolution: {integrity: sha512-KF/U8tk54BgQewkJPvB4s/US3VQY68BRDpH638+7O/n58TpnwiwnOtGIOsT2/i+M78s61BBpeC83STB88d8sqw==}
|
||||
|
||||
parse5@7.3.0:
|
||||
resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==}
|
||||
|
||||
parse5@8.0.0:
|
||||
resolution: {integrity: sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==}
|
||||
|
||||
pathe@2.0.3:
|
||||
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
|
||||
|
||||
@ -898,10 +1060,18 @@ packages:
|
||||
resolution: {integrity: sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
|
||||
punycode@2.3.1:
|
||||
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
readdirp@4.1.2:
|
||||
resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
|
||||
engines: {node: '>= 14.18.0'}
|
||||
|
||||
require-from-string@2.0.2:
|
||||
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
resolve-from@5.0.0:
|
||||
resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
|
||||
engines: {node: '>=8'}
|
||||
@ -916,6 +1086,10 @@ packages:
|
||||
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
||||
hasBin: true
|
||||
|
||||
saxes@6.0.0:
|
||||
resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
|
||||
engines: {node: '>=v12.22.7'}
|
||||
|
||||
semver@6.3.1:
|
||||
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
|
||||
hasBin: true
|
||||
@ -930,6 +1104,9 @@ packages:
|
||||
resolution: {integrity: sha512-xcRN39BdsnO9Tf+VzsE7b3JyTJASItIV1FVFewJKCFcW4s4haIKS3e6vj8PGB9qBwC7tnuOywQMdv5N4qkzi7Q==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
siginfo@2.0.0:
|
||||
resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
|
||||
|
||||
skia-canvas@3.0.8:
|
||||
resolution: {integrity: sha512-FSYKxp8Ng2vOeeOBiyPhnn6ui6FirPJXMyjk4PKl8N/OWzVrkMawUgY9zubIWHMdYtyWFn0gfX3QlRwg6HBmdg==}
|
||||
|
||||
@ -949,6 +1126,12 @@ packages:
|
||||
resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==}
|
||||
engines: {node: '>= 12'}
|
||||
|
||||
stackback@0.0.2:
|
||||
resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
|
||||
|
||||
std-env@4.0.0:
|
||||
resolution: {integrity: sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==}
|
||||
|
||||
string-split-by@1.0.0:
|
||||
resolution: {integrity: sha512-KaJKY+hfpzNyet/emP81PJA9hTVSfxNLS9SFTWxdCnnW1/zOOwiV248+EfoX7IQFcBaOp4G5YE6xTJMF+pLg6A==}
|
||||
|
||||
@ -957,6 +1140,9 @@ packages:
|
||||
engines: {node: '>=16 || 14 >=14.17'}
|
||||
hasBin: true
|
||||
|
||||
symbol-tree@3.2.4:
|
||||
resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
|
||||
|
||||
thenify-all@1.6.0:
|
||||
resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}
|
||||
engines: {node: '>=0.8'}
|
||||
@ -964,13 +1150,39 @@ packages:
|
||||
thenify@3.3.1:
|
||||
resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
|
||||
|
||||
tinybench@2.9.0:
|
||||
resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
|
||||
|
||||
tinyexec@0.3.2:
|
||||
resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==}
|
||||
|
||||
tinyexec@1.1.1:
|
||||
resolution: {integrity: sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
tinyglobby@0.2.16:
|
||||
resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
tinyrainbow@3.1.0:
|
||||
resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
|
||||
tldts-core@7.0.28:
|
||||
resolution: {integrity: sha512-7W5Efjhsc3chVdFhqtaU0KtK32J37Zcr9RKtID54nG+tIpcY79CQK/veYPODxtD/LJ4Lue66jvrQzIX2Z2/pUQ==}
|
||||
|
||||
tldts@7.0.28:
|
||||
resolution: {integrity: sha512-+Zg3vWhRUv8B1maGSTFdev9mjoo8Etn2Ayfs4cnjlD3CsGkxXX4QyW3j2WJ0wdjYcYmy7Lx2RDsZMhgCWafKIw==}
|
||||
hasBin: true
|
||||
|
||||
tough-cookie@6.0.1:
|
||||
resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
tr46@6.0.0:
|
||||
resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==}
|
||||
engines: {node: '>=20'}
|
||||
|
||||
tree-kill@1.2.2:
|
||||
resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
|
||||
hasBin: true
|
||||
@ -1011,6 +1223,10 @@ packages:
|
||||
undici-types@7.18.2:
|
||||
resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==}
|
||||
|
||||
undici@7.24.7:
|
||||
resolution: {integrity: sha512-H/nlJ/h0ggGC+uRL3ovD+G0i4bqhvsDOpbDv7At5eFLlj2b41L8QliGbnl2H7SnDiYhENphh1tQFJZf+MyfLsQ==}
|
||||
engines: {node: '>=20.18.1'}
|
||||
|
||||
undici@8.0.2:
|
||||
resolution: {integrity: sha512-B9MeU5wuFhkFAuNeA19K2GDFcQXZxq33fL0nRy2Aq30wdufZbyyvxW3/ChaeipXVfy/wUweZyzovQGk39+9k2w==}
|
||||
engines: {node: '>=22.19.0'}
|
||||
@ -1096,15 +1312,100 @@ packages:
|
||||
vite:
|
||||
optional: true
|
||||
|
||||
vitest@4.1.3:
|
||||
resolution: {integrity: sha512-DBc4Tx0MPNsqb9isoyOq00lHftVx/KIU44QOm2q59npZyLUkENn8TMFsuzuO+4U2FUa9rgbbPt3udrP25GcjXw==}
|
||||
engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
'@edge-runtime/vm': '*'
|
||||
'@opentelemetry/api': ^1.9.0
|
||||
'@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0
|
||||
'@vitest/browser-playwright': 4.1.3
|
||||
'@vitest/browser-preview': 4.1.3
|
||||
'@vitest/browser-webdriverio': 4.1.3
|
||||
'@vitest/coverage-istanbul': 4.1.3
|
||||
'@vitest/coverage-v8': 4.1.3
|
||||
'@vitest/ui': 4.1.3
|
||||
happy-dom: '*'
|
||||
jsdom: '*'
|
||||
vite: ^6.0.0 || ^7.0.0 || ^8.0.0
|
||||
peerDependenciesMeta:
|
||||
'@edge-runtime/vm':
|
||||
optional: true
|
||||
'@opentelemetry/api':
|
||||
optional: true
|
||||
'@types/node':
|
||||
optional: true
|
||||
'@vitest/browser-playwright':
|
||||
optional: true
|
||||
'@vitest/browser-preview':
|
||||
optional: true
|
||||
'@vitest/browser-webdriverio':
|
||||
optional: true
|
||||
'@vitest/coverage-istanbul':
|
||||
optional: true
|
||||
'@vitest/coverage-v8':
|
||||
optional: true
|
||||
'@vitest/ui':
|
||||
optional: true
|
||||
happy-dom:
|
||||
optional: true
|
||||
jsdom:
|
||||
optional: true
|
||||
|
||||
w3c-xmlserializer@5.0.0:
|
||||
resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
web-streams-polyfill@4.2.0:
|
||||
resolution: {integrity: sha512-0rYDzGOh9EZpig92umN5g5D/9A1Kff7k0/mzPSSCY8jEQeYkgRMoY7LhbXtUCWzLCMX0TUE9aoHkjFNB7D9pfA==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
webidl-conversions@8.0.1:
|
||||
resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==}
|
||||
engines: {node: '>=20'}
|
||||
|
||||
whatwg-mimetype@5.0.0:
|
||||
resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==}
|
||||
engines: {node: '>=20'}
|
||||
|
||||
whatwg-url@16.0.1:
|
||||
resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==}
|
||||
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
|
||||
|
||||
why-is-node-running@2.3.0:
|
||||
resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==}
|
||||
engines: {node: '>=8'}
|
||||
hasBin: true
|
||||
|
||||
xml-name-validator@5.0.0:
|
||||
resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
xmlchars@2.2.0:
|
||||
resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
|
||||
|
||||
yallist@3.1.1:
|
||||
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
|
||||
|
||||
snapshots:
|
||||
|
||||
'@asamuzakjp/css-color@5.1.8':
|
||||
dependencies:
|
||||
'@csstools/css-calc': 3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)
|
||||
'@csstools/css-color-parser': 4.0.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)
|
||||
'@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0)
|
||||
'@csstools/css-tokenizer': 4.0.0
|
||||
|
||||
'@asamuzakjp/dom-selector@7.0.8':
|
||||
dependencies:
|
||||
'@asamuzakjp/nwsapi': 2.3.9
|
||||
bidi-js: 1.0.3
|
||||
css-tree: 3.2.1
|
||||
is-potential-custom-element-name: 1.0.1
|
||||
|
||||
'@asamuzakjp/nwsapi@2.3.9': {}
|
||||
|
||||
'@babel/code-frame@7.29.0':
|
||||
dependencies:
|
||||
'@babel/helper-validator-identifier': 7.28.5
|
||||
@ -1216,6 +1517,34 @@ snapshots:
|
||||
'@babel/helper-string-parser': 7.27.1
|
||||
'@babel/helper-validator-identifier': 7.28.5
|
||||
|
||||
'@bramus/specificity@2.4.2':
|
||||
dependencies:
|
||||
css-tree: 3.2.1
|
||||
|
||||
'@csstools/color-helpers@6.0.2': {}
|
||||
|
||||
'@csstools/css-calc@3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)':
|
||||
dependencies:
|
||||
'@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0)
|
||||
'@csstools/css-tokenizer': 4.0.0
|
||||
|
||||
'@csstools/css-color-parser@4.0.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)':
|
||||
dependencies:
|
||||
'@csstools/color-helpers': 6.0.2
|
||||
'@csstools/css-calc': 3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)
|
||||
'@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0)
|
||||
'@csstools/css-tokenizer': 4.0.0
|
||||
|
||||
'@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)':
|
||||
dependencies:
|
||||
'@csstools/css-tokenizer': 4.0.0
|
||||
|
||||
'@csstools/css-syntax-patches-for-csstree@1.1.2(css-tree@3.2.1)':
|
||||
optionalDependencies:
|
||||
css-tree: 3.2.1
|
||||
|
||||
'@csstools/css-tokenizer@4.0.0': {}
|
||||
|
||||
'@emnapi/core@1.9.1':
|
||||
dependencies:
|
||||
'@emnapi/wasi-threads': 1.2.0
|
||||
@ -1310,6 +1639,8 @@ snapshots:
|
||||
'@esbuild/win32-x64@0.27.7':
|
||||
optional: true
|
||||
|
||||
'@exodus/bytes@1.15.0': {}
|
||||
|
||||
'@jridgewell/gen-mapping@0.3.13':
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
@ -1464,6 +1795,8 @@ snapshots:
|
||||
'@rollup/rollup-win32-x64-msvc@4.60.1':
|
||||
optional: true
|
||||
|
||||
'@standard-schema/spec@1.1.0': {}
|
||||
|
||||
'@tybys/wasm-util@0.10.1':
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
@ -1490,13 +1823,61 @@ snapshots:
|
||||
dependencies:
|
||||
'@babel/types': 7.29.0
|
||||
|
||||
'@types/chai@5.2.3':
|
||||
dependencies:
|
||||
'@types/deep-eql': 4.0.2
|
||||
assertion-error: 2.0.1
|
||||
|
||||
'@types/deep-eql@4.0.2': {}
|
||||
|
||||
'@types/estree@1.0.8': {}
|
||||
|
||||
'@types/node@25.5.2':
|
||||
dependencies:
|
||||
undici-types: 7.18.2
|
||||
|
||||
'@xmldom/xmldom@0.8.12': {}
|
||||
'@vitest/expect@4.1.3':
|
||||
dependencies:
|
||||
'@standard-schema/spec': 1.1.0
|
||||
'@types/chai': 5.2.3
|
||||
'@vitest/spy': 4.1.3
|
||||
'@vitest/utils': 4.1.3
|
||||
chai: 6.2.2
|
||||
tinyrainbow: 3.1.0
|
||||
|
||||
'@vitest/mocker@4.1.3(vite@8.0.7(@types/node@25.5.2)(esbuild@0.27.7))':
|
||||
dependencies:
|
||||
'@vitest/spy': 4.1.3
|
||||
estree-walker: 3.0.3
|
||||
magic-string: 0.30.21
|
||||
optionalDependencies:
|
||||
vite: 8.0.7(@types/node@25.5.2)(esbuild@0.27.7)
|
||||
|
||||
'@vitest/pretty-format@4.1.3':
|
||||
dependencies:
|
||||
tinyrainbow: 3.1.0
|
||||
|
||||
'@vitest/runner@4.1.3':
|
||||
dependencies:
|
||||
'@vitest/utils': 4.1.3
|
||||
pathe: 2.0.3
|
||||
|
||||
'@vitest/snapshot@4.1.3':
|
||||
dependencies:
|
||||
'@vitest/pretty-format': 4.1.3
|
||||
'@vitest/utils': 4.1.3
|
||||
magic-string: 0.30.21
|
||||
pathe: 2.0.3
|
||||
|
||||
'@vitest/spy@4.1.3': {}
|
||||
|
||||
'@vitest/utils@4.1.3':
|
||||
dependencies:
|
||||
'@vitest/pretty-format': 4.1.3
|
||||
convert-source-map: 2.0.0
|
||||
tinyrainbow: 3.1.0
|
||||
|
||||
'@xmldom/xmldom@0.9.9': {}
|
||||
|
||||
acorn@8.16.0: {}
|
||||
|
||||
@ -1504,6 +1885,8 @@ snapshots:
|
||||
|
||||
any-promise@1.3.0: {}
|
||||
|
||||
assertion-error@2.0.1: {}
|
||||
|
||||
babel-plugin-jsx-dom-expressions@0.40.6(@babel/core@7.29.0):
|
||||
dependencies:
|
||||
'@babel/core': 7.29.0
|
||||
@ -1522,6 +1905,10 @@ snapshots:
|
||||
|
||||
baseline-browser-mapping@2.10.16: {}
|
||||
|
||||
bidi-js@1.0.3:
|
||||
dependencies:
|
||||
require-from-string: 2.0.2
|
||||
|
||||
browserslist@4.28.2:
|
||||
dependencies:
|
||||
baseline-browser-mapping: 2.10.16
|
||||
@ -1539,6 +1926,8 @@ snapshots:
|
||||
|
||||
caniuse-lite@1.0.30001787: {}
|
||||
|
||||
chai@6.2.2: {}
|
||||
|
||||
chokidar@4.0.3:
|
||||
dependencies:
|
||||
readdirp: 4.1.2
|
||||
@ -1551,18 +1940,34 @@ snapshots:
|
||||
|
||||
convert-source-map@2.0.0: {}
|
||||
|
||||
css-tree@3.2.1:
|
||||
dependencies:
|
||||
mdn-data: 2.27.1
|
||||
source-map-js: 1.2.1
|
||||
|
||||
csstype@3.2.3: {}
|
||||
|
||||
data-urls@7.0.0:
|
||||
dependencies:
|
||||
whatwg-mimetype: 5.0.0
|
||||
whatwg-url: 16.0.1
|
||||
transitivePeerDependencies:
|
||||
- '@noble/hashes'
|
||||
|
||||
debug@4.4.3:
|
||||
dependencies:
|
||||
ms: 2.1.3
|
||||
|
||||
decimal.js@10.6.0: {}
|
||||
|
||||
detect-libc@2.1.2: {}
|
||||
|
||||
electron-to-chromium@1.5.333: {}
|
||||
|
||||
entities@6.0.1: {}
|
||||
|
||||
es-module-lexer@2.0.0: {}
|
||||
|
||||
esbuild@0.27.7:
|
||||
optionalDependencies:
|
||||
'@esbuild/aix-ppc64': 0.27.7
|
||||
@ -1594,6 +1999,12 @@ snapshots:
|
||||
|
||||
escalade@3.2.0: {}
|
||||
|
||||
estree-walker@3.0.3:
|
||||
dependencies:
|
||||
'@types/estree': 1.0.8
|
||||
|
||||
expect-type@1.3.0: {}
|
||||
|
||||
fdir@6.5.0(picomatch@4.0.4):
|
||||
optionalDependencies:
|
||||
picomatch: 4.0.4
|
||||
@ -1606,15 +2017,17 @@ snapshots:
|
||||
|
||||
follow-redirects@1.15.11: {}
|
||||
|
||||
fonteditor-core@file:vendor/fonteditor-core:
|
||||
dependencies:
|
||||
'@xmldom/xmldom': 0.8.12
|
||||
|
||||
fsevents@2.3.3:
|
||||
optional: true
|
||||
|
||||
gensync@1.0.0-beta.2: {}
|
||||
|
||||
html-encoding-sniffer@6.0.0:
|
||||
dependencies:
|
||||
'@exodus/bytes': 1.15.0
|
||||
transitivePeerDependencies:
|
||||
- '@noble/hashes'
|
||||
|
||||
html-entities@2.3.3: {}
|
||||
|
||||
https-proxy-agent@7.0.6:
|
||||
@ -1624,12 +2037,40 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
is-potential-custom-element-name@1.0.1: {}
|
||||
|
||||
is-what@4.1.16: {}
|
||||
|
||||
joycon@3.1.1: {}
|
||||
|
||||
js-tokens@4.0.0: {}
|
||||
|
||||
jsdom@29.0.2:
|
||||
dependencies:
|
||||
'@asamuzakjp/css-color': 5.1.8
|
||||
'@asamuzakjp/dom-selector': 7.0.8
|
||||
'@bramus/specificity': 2.4.2
|
||||
'@csstools/css-syntax-patches-for-csstree': 1.1.2(css-tree@3.2.1)
|
||||
'@exodus/bytes': 1.15.0
|
||||
css-tree: 3.2.1
|
||||
data-urls: 7.0.0
|
||||
decimal.js: 10.6.0
|
||||
html-encoding-sniffer: 6.0.0
|
||||
is-potential-custom-element-name: 1.0.1
|
||||
lru-cache: 11.3.3
|
||||
parse5: 8.0.0
|
||||
saxes: 6.0.0
|
||||
symbol-tree: 3.2.4
|
||||
tough-cookie: 6.0.1
|
||||
undici: 7.24.7
|
||||
w3c-xmlserializer: 5.0.0
|
||||
webidl-conversions: 8.0.1
|
||||
whatwg-mimetype: 5.0.0
|
||||
whatwg-url: 16.0.1
|
||||
xml-name-validator: 5.0.0
|
||||
transitivePeerDependencies:
|
||||
- '@noble/hashes'
|
||||
|
||||
jsesc@3.1.0: {}
|
||||
|
||||
json5@2.2.3: {}
|
||||
@ -1689,6 +2130,8 @@ snapshots:
|
||||
|
||||
load-tsconfig@0.2.5: {}
|
||||
|
||||
lru-cache@11.3.3: {}
|
||||
|
||||
lru-cache@5.1.1:
|
||||
dependencies:
|
||||
yallist: 3.1.1
|
||||
@ -1697,6 +2140,8 @@ snapshots:
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
|
||||
mdn-data@2.27.1: {}
|
||||
|
||||
merge-anything@5.1.7:
|
||||
dependencies:
|
||||
is-what: 4.1.16
|
||||
@ -1722,12 +2167,18 @@ snapshots:
|
||||
|
||||
object-assign@4.1.1: {}
|
||||
|
||||
obug@2.1.1: {}
|
||||
|
||||
parenthesis@3.1.8: {}
|
||||
|
||||
parse5@7.3.0:
|
||||
dependencies:
|
||||
entities: 6.0.1
|
||||
|
||||
parse5@8.0.0:
|
||||
dependencies:
|
||||
entities: 6.0.1
|
||||
|
||||
pathe@2.0.3: {}
|
||||
|
||||
picocolors@1.1.1: {}
|
||||
@ -1754,8 +2205,12 @@ snapshots:
|
||||
picocolors: 1.1.1
|
||||
source-map-js: 1.2.1
|
||||
|
||||
punycode@2.3.1: {}
|
||||
|
||||
readdirp@4.1.2: {}
|
||||
|
||||
require-from-string@2.0.2: {}
|
||||
|
||||
resolve-from@5.0.0: {}
|
||||
|
||||
rolldown@1.0.0-rc.13:
|
||||
@ -1810,6 +2265,10 @@ snapshots:
|
||||
'@rollup/rollup-win32-x64-msvc': 4.60.1
|
||||
fsevents: 2.3.3
|
||||
|
||||
saxes@6.0.0:
|
||||
dependencies:
|
||||
xmlchars: 2.2.0
|
||||
|
||||
semver@6.3.1: {}
|
||||
|
||||
seroval-plugins@1.5.2(seroval@1.5.2):
|
||||
@ -1818,6 +2277,8 @@ snapshots:
|
||||
|
||||
seroval@1.5.2: {}
|
||||
|
||||
siginfo@2.0.0: {}
|
||||
|
||||
skia-canvas@3.0.8:
|
||||
dependencies:
|
||||
detect-libc: 2.1.2
|
||||
@ -1847,6 +2308,10 @@ snapshots:
|
||||
|
||||
source-map@0.7.6: {}
|
||||
|
||||
stackback@0.0.2: {}
|
||||
|
||||
std-env@4.0.0: {}
|
||||
|
||||
string-split-by@1.0.0:
|
||||
dependencies:
|
||||
parenthesis: 3.1.8
|
||||
@ -1861,6 +2326,8 @@ snapshots:
|
||||
tinyglobby: 0.2.16
|
||||
ts-interface-checker: 0.1.13
|
||||
|
||||
symbol-tree@3.2.4: {}
|
||||
|
||||
thenify-all@1.6.0:
|
||||
dependencies:
|
||||
thenify: 3.3.1
|
||||
@ -1869,13 +2336,33 @@ snapshots:
|
||||
dependencies:
|
||||
any-promise: 1.3.0
|
||||
|
||||
tinybench@2.9.0: {}
|
||||
|
||||
tinyexec@0.3.2: {}
|
||||
|
||||
tinyexec@1.1.1: {}
|
||||
|
||||
tinyglobby@0.2.16:
|
||||
dependencies:
|
||||
fdir: 6.5.0(picomatch@4.0.4)
|
||||
picomatch: 4.0.4
|
||||
|
||||
tinyrainbow@3.1.0: {}
|
||||
|
||||
tldts-core@7.0.28: {}
|
||||
|
||||
tldts@7.0.28:
|
||||
dependencies:
|
||||
tldts-core: 7.0.28
|
||||
|
||||
tough-cookie@6.0.1:
|
||||
dependencies:
|
||||
tldts: 7.0.28
|
||||
|
||||
tr46@6.0.0:
|
||||
dependencies:
|
||||
punycode: 2.3.1
|
||||
|
||||
tree-kill@1.2.2: {}
|
||||
|
||||
ts-interface-checker@0.1.13: {}
|
||||
@ -1917,6 +2404,8 @@ snapshots:
|
||||
|
||||
undici-types@7.18.2: {}
|
||||
|
||||
undici@7.24.7: {}
|
||||
|
||||
undici@8.0.2: {}
|
||||
|
||||
update-browserslist-db@1.2.3(browserslist@4.28.2):
|
||||
@ -1960,6 +2449,59 @@ snapshots:
|
||||
optionalDependencies:
|
||||
vite: 8.0.7(@types/node@25.5.2)(esbuild@0.27.7)
|
||||
|
||||
vitest@4.1.3(@types/node@25.5.2)(jsdom@29.0.2)(vite@8.0.7(@types/node@25.5.2)(esbuild@0.27.7)):
|
||||
dependencies:
|
||||
'@vitest/expect': 4.1.3
|
||||
'@vitest/mocker': 4.1.3(vite@8.0.7(@types/node@25.5.2)(esbuild@0.27.7))
|
||||
'@vitest/pretty-format': 4.1.3
|
||||
'@vitest/runner': 4.1.3
|
||||
'@vitest/snapshot': 4.1.3
|
||||
'@vitest/spy': 4.1.3
|
||||
'@vitest/utils': 4.1.3
|
||||
es-module-lexer: 2.0.0
|
||||
expect-type: 1.3.0
|
||||
magic-string: 0.30.21
|
||||
obug: 2.1.1
|
||||
pathe: 2.0.3
|
||||
picomatch: 4.0.4
|
||||
std-env: 4.0.0
|
||||
tinybench: 2.9.0
|
||||
tinyexec: 1.1.1
|
||||
tinyglobby: 0.2.16
|
||||
tinyrainbow: 3.1.0
|
||||
vite: 8.0.7(@types/node@25.5.2)(esbuild@0.27.7)
|
||||
why-is-node-running: 2.3.0
|
||||
optionalDependencies:
|
||||
'@types/node': 25.5.2
|
||||
jsdom: 29.0.2
|
||||
transitivePeerDependencies:
|
||||
- msw
|
||||
|
||||
w3c-xmlserializer@5.0.0:
|
||||
dependencies:
|
||||
xml-name-validator: 5.0.0
|
||||
|
||||
web-streams-polyfill@4.2.0: {}
|
||||
|
||||
webidl-conversions@8.0.1: {}
|
||||
|
||||
whatwg-mimetype@5.0.0: {}
|
||||
|
||||
whatwg-url@16.0.1:
|
||||
dependencies:
|
||||
'@exodus/bytes': 1.15.0
|
||||
tr46: 6.0.0
|
||||
webidl-conversions: 8.0.1
|
||||
transitivePeerDependencies:
|
||||
- '@noble/hashes'
|
||||
|
||||
why-is-node-running@2.3.0:
|
||||
dependencies:
|
||||
siginfo: 2.0.0
|
||||
stackback: 0.0.2
|
||||
|
||||
xml-name-validator@5.0.0: {}
|
||||
|
||||
xmlchars@2.2.0: {}
|
||||
|
||||
yallist@3.1.1: {}
|
||||
|
||||
12
task.md
12
task.md
@ -1,3 +1,11 @@
|
||||
/loop 持续优化字体子集化性能,可以大胆放开手脚的去做,但是优化完一定要通过基准测试。中途不要切换到其他模式,比如计划模式也不要询问我,你直接做就行了,请你持续的去优化,不要去询问我,不要去中断,好吧
|
||||
/loop 持续优化字体子集化性能,可以大胆放开手脚的去做,但是优化完一定要通过`pnpx tsx ./基准测试.test.ts`。中途不要切换到其他模式,比如计划模式也不要询问我,你直接做就行了,请你持续的去优化,不要去询问我,不要去中断,好吧
|
||||
|
||||
啊,你每次优化能不能把基准测试保存在本地目录下,这样我方便查看。你的文档中应该在每个重大节点更新基准测试结果,这样我能方便看到你使用了哪些优化方法,得到了什么样的优化效果。
|
||||
啊,你每次优化能不能把基准测试结果文档保存在本地目录下,这样我方便查看。你的文档中应该在每个重大节点更新基准测试结果,这样我能方便看到你使用了哪些优化方法,得到了什么样的优化效果。
|
||||
|
||||
|
||||
|
||||
=== 字体裁剪基准测试 ===
|
||||
|
||||
8个汉字: avg=23.6ms min=18.4ms max=37.2ms 输出=16,508 bytes ssim=1.0000
|
||||
拉丁+数字: avg=16.4ms min=13.7ms max=18.2ms 输出=1,272 bytes ssim=1.0000
|
||||
千字文前段: avg=59.4ms min=47.3ms max=76.5ms 输出=161,344 bytes ssim=1.0000
|
||||
|
||||
82
vendor/fonteditor-core/lib/graphics/reducePathFlat.js
vendored
Normal file
82
vendor/fonteditor-core/lib/graphics/reducePathFlat.js
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = reducePathFlat;
|
||||
/**
|
||||
* @file 缩减path大小(扁平格式专用),去除冗余节点
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 缩减glyf,去除冗余节点(扁平格式 [x, y, onCurve, ...])
|
||||
* 懒分配:首个点被移除前不分配 reduced 数组,大多数 contour 无冗余点可直接返回
|
||||
*/
|
||||
function reducePathFlat(contour) {
|
||||
if (!contour.length) {
|
||||
return contour;
|
||||
}
|
||||
var len = contour.length;
|
||||
var l = len / 3;
|
||||
/** 懒分配:仅在首个被保留的点出现时才创建 reduced 数组 */
|
||||
var reduced = null;
|
||||
var ri = 0;
|
||||
var removed = 0;
|
||||
// 段1: i=0(首点,prev 是尾点)
|
||||
var px = contour[0], py = contour[1], po = contour[2];
|
||||
var prevX = contour[(l - 1) * 3], prevY = contour[(l - 1) * 3 + 1], prevO = contour[(l - 1) * 3 + 2];
|
||||
var nextX = contour[3], nextY = contour[4], nextO = contour[5];
|
||||
|
||||
var dx = px - nextX;
|
||||
var dy = py - nextY;
|
||||
if ((po && nextO || !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++; }
|
||||
else {
|
||||
reduced = new Array(len);
|
||||
reduced[ri++] = px; reduced[ri++] = py; reduced[ri++] = po;
|
||||
}
|
||||
}
|
||||
// 段2: i=1..l-2(中间点,prev/next 简单偏移)
|
||||
for (var i = 1; i < l - 1; i++) {
|
||||
var pi = i * 3;
|
||||
px = contour[pi]; py = contour[pi + 1]; po = contour[pi + 2];
|
||||
prevX = contour[pi - 3]; prevY = contour[pi - 2]; prevO = contour[pi - 1];
|
||||
nextX = contour[pi + 3]; nextY = contour[pi + 4]; nextO = contour[pi + 5];
|
||||
|
||||
dx = px - nextX;
|
||||
dy = py - nextY;
|
||||
if ((po && nextO || !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; }
|
||||
|
||||
if (!reduced) reduced = new Array(len);
|
||||
reduced[ri++] = px; reduced[ri++] = py; reduced[ri++] = po;
|
||||
}
|
||||
// 段3: i=l-1(尾点,next 是首点)
|
||||
if (l > 1) {
|
||||
var pi = (l - 1) * 3;
|
||||
px = contour[pi]; py = contour[pi + 1]; po = contour[pi + 2];
|
||||
prevX = contour[pi - 3]; prevY = contour[pi - 2]; prevO = contour[pi - 1];
|
||||
nextX = contour[0]; nextY = contour[1]; nextO = contour[2];
|
||||
|
||||
dx = px - nextX;
|
||||
dy = py - nextY;
|
||||
if ((po && nextO || !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++; }
|
||||
else {
|
||||
if (!reduced) reduced = new Array(len);
|
||||
reduced[ri++] = px; reduced[ri++] = py; reduced[ri++] = po;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 没有任何缩减,直接返回原数组避免拷贝
|
||||
if (!reduced) return contour;
|
||||
// 截断到实际大小
|
||||
reduced.length = ri;
|
||||
return reduced;
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
/**
|
||||
* @file DOM解析器,兼容node端和浏览器端
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
/* eslint-disable no-undef */
|
||||
export default typeof window !== 'undefined' && window.DOMParser
|
||||
? window.DOMParser
|
||||
: require('@xmldom/xmldom').DOMParser;
|
||||
76
vendor/fonteditor-core/src/common/I18n.js
vendored
76
vendor/fonteditor-core/src/common/I18n.js
vendored
@ -1,76 +0,0 @@
|
||||
/**
|
||||
* @file 用于国际化的字符串管理类
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
function appendLanguage(store, languageList) {
|
||||
languageList.forEach(item => {
|
||||
const language = item[0];
|
||||
store[language] = Object.assign(store[language] || {}, item[1]);
|
||||
});
|
||||
return store;
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理国际化字符,根据lang切换语言版本
|
||||
*
|
||||
* @class I18n
|
||||
* @param {Array} languageList 当前支持的语言列表
|
||||
* @param {string=} defaultLanguage 默认语言
|
||||
* languageList = [
|
||||
* 'en-us', // 语言名称
|
||||
* langObject // 语言字符串列表
|
||||
* ]
|
||||
*/
|
||||
export default class I18n {
|
||||
constructor(languageList, defaultLanguage) {
|
||||
this.store = appendLanguage({}, languageList);
|
||||
this.setLanguage(
|
||||
defaultLanguage
|
||||
|| typeof navigator !== 'undefined' && navigator.language && navigator.language.toLowerCase()
|
||||
|| 'en-us'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置语言
|
||||
*
|
||||
* @param {string} language 语言
|
||||
* @return {this}
|
||||
*/
|
||||
setLanguage(language) {
|
||||
if (!this.store[language]) {
|
||||
language = 'en-us';
|
||||
}
|
||||
this.lang = this.store[this.language = language];
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一个语言字符串
|
||||
*
|
||||
* @param {string} language 语言
|
||||
* @param {Object} langObject 语言对象
|
||||
* @return {this}
|
||||
*/
|
||||
addLanguage(language, langObject) {
|
||||
appendLanguage(this.store, [[language, langObject]]);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前语言字符串
|
||||
*
|
||||
* @param {string} path 语言路径
|
||||
* @return {string} 语言字符串
|
||||
*/
|
||||
get(path) {
|
||||
const ref = path.split('.');
|
||||
let refObject = this.lang;
|
||||
let level;
|
||||
while (refObject != null && (level = ref.shift())) {
|
||||
refObject = refObject[level];
|
||||
}
|
||||
return refObject != null ? refObject : '';
|
||||
}
|
||||
}
|
||||
85
vendor/fonteditor-core/src/common/ajaxFile.js
vendored
85
vendor/fonteditor-core/src/common/ajaxFile.js
vendored
@ -1,85 +0,0 @@
|
||||
/**
|
||||
* @file ajax获取文本数据
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* ajax获取数据
|
||||
*
|
||||
* @param {Object} options 参数选项
|
||||
* @param {string=} options.type 类型
|
||||
* @param {string=} options.method method
|
||||
* @param {Function=} options.onSuccess 成功回调
|
||||
* @param {Function=} options.onError 失败回调
|
||||
* @param {Object=} options.params 参数集合
|
||||
*/
|
||||
export default function ajaxFile(options) {
|
||||
const xhr = new XMLHttpRequest();
|
||||
|
||||
xhr.onreadystatechange = function () {
|
||||
if (xhr.readyState === 4) {
|
||||
const status = xhr.status;
|
||||
if (status >= 200 && status < 300 || status === 304) {
|
||||
if (options.onSuccess) {
|
||||
if (options.type === 'binary') {
|
||||
const buffer = xhr.responseBlob || xhr.response;
|
||||
options.onSuccess(buffer);
|
||||
}
|
||||
else if (options.type === 'xml') {
|
||||
options.onSuccess(xhr.responseXML);
|
||||
}
|
||||
else if (options.type === 'json') {
|
||||
options.onSuccess(JSON.parse(xhr.responseText));
|
||||
}
|
||||
else {
|
||||
options.onSuccess(xhr.responseText);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else if (options.onError) {
|
||||
options.onError(xhr, xhr.status);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const method = (options.method || 'GET').toUpperCase();
|
||||
let params = null;
|
||||
if (options.params) {
|
||||
|
||||
let str = [];
|
||||
Object.keys(options.params).forEach(key => {
|
||||
str.push(key + '=' + encodeURIComponent(options.params[key]));
|
||||
});
|
||||
str = str.join('&');
|
||||
if (method === 'GET') {
|
||||
options.url += (options.url.indexOf('?') === -1 ? '?' : '&') + str;
|
||||
}
|
||||
else {
|
||||
params = str;
|
||||
}
|
||||
}
|
||||
|
||||
xhr.open(method, options.url, true);
|
||||
|
||||
if (options.type === 'binary') {
|
||||
xhr.responseType = 'arraybuffer';
|
||||
}
|
||||
xhr.send(params);
|
||||
}
|
||||
|
||||
export function loadFile(url, type = 'binary') {
|
||||
return new Promise((resolve, reject) => {
|
||||
ajaxFile({
|
||||
type,
|
||||
url,
|
||||
onSuccess(buffer) {
|
||||
resolve(buffer);
|
||||
},
|
||||
onError(e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
238
vendor/fonteditor-core/src/common/lang.js
vendored
238
vendor/fonteditor-core/src/common/lang.js
vendored
@ -1,238 +0,0 @@
|
||||
/**
|
||||
* @file 语言相关函数
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
|
||||
export function isArray(obj) {
|
||||
return obj != null && toString.call(obj).slice(8, -1) === 'Array';
|
||||
}
|
||||
|
||||
export function isObject(obj) {
|
||||
return obj != null && toString.call(obj).slice(8, -1) === 'Object';
|
||||
}
|
||||
|
||||
export function isString(obj) {
|
||||
return obj != null && toString.call(obj).slice(8, -1) === 'String';
|
||||
}
|
||||
|
||||
export function isFunction(obj) {
|
||||
return obj != null && toString.call(obj).slice(8, -1) === 'Function';
|
||||
}
|
||||
|
||||
export function isDate(obj) {
|
||||
return obj != null && toString.call(obj).slice(8, -1) === 'Date';
|
||||
}
|
||||
|
||||
export function isEmptyObject(object) {
|
||||
for (const name in object) {
|
||||
// eslint-disable-next-line no-prototype-builtins
|
||||
if (object.hasOwnProperty(name)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 为函数提前绑定前置参数(柯里化)
|
||||
*
|
||||
* @see http://en.wikipedia.org/wiki/Currying
|
||||
* @param {Function} fn 要绑定的函数
|
||||
* @param {...Array} cargs cargs
|
||||
* @return {Function}
|
||||
*/
|
||||
export function curry(fn, ...cargs) {
|
||||
return function (...rargs) {
|
||||
const args = cargs.concat(rargs);
|
||||
// eslint-disable-next-line no-invalid-this
|
||||
return fn.apply(this, args);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 方法静态化, 反绑定、延迟绑定
|
||||
*
|
||||
* @param {Function} method 待静态化的方法
|
||||
* @return {Function} 静态化包装后方法
|
||||
*/
|
||||
export function generic(method) {
|
||||
return function (...fargs) {
|
||||
return Function.call.apply(method, fargs);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置覆盖相关的属性值
|
||||
*
|
||||
* @param {Object} thisObj 覆盖对象
|
||||
* @param {Object} thatObj 值对象
|
||||
* @param {Array.<string>} fields 字段
|
||||
* @return {Object} thisObj
|
||||
*/
|
||||
export function overwrite(thisObj, thatObj, fields) {
|
||||
|
||||
if (!thatObj) {
|
||||
return thisObj;
|
||||
}
|
||||
|
||||
// 这里`fields`未指定则仅overwrite自身可枚举的字段,指定`fields`则不做限制
|
||||
fields = fields || Object.keys(thatObj);
|
||||
fields.forEach(field => {
|
||||
// 拷贝对象
|
||||
if (
|
||||
thisObj[field] && typeof thisObj[field] === 'object'
|
||||
&& thatObj[field] && typeof thatObj[field] === 'object'
|
||||
) {
|
||||
overwrite(thisObj[field], thatObj[field]);
|
||||
}
|
||||
else {
|
||||
thisObj[field] = thatObj[field];
|
||||
}
|
||||
});
|
||||
|
||||
return thisObj;
|
||||
}
|
||||
|
||||
/**
|
||||
* 深复制对象,仅复制数据
|
||||
*
|
||||
* @param {Object} source 源数据
|
||||
* @return {Object} 复制的数据
|
||||
*/
|
||||
export function clone(source) {
|
||||
if (!source || typeof source !== 'object') {
|
||||
return source;
|
||||
}
|
||||
|
||||
let cloned = source;
|
||||
|
||||
if (isArray(source)) {
|
||||
cloned = source.slice().map(clone);
|
||||
}
|
||||
else if (isObject(source) && 'isPrototypeOf' in source) {
|
||||
cloned = {};
|
||||
for (const key of Object.keys(source)) {
|
||||
cloned[key] = clone(source[key]);
|
||||
}
|
||||
}
|
||||
|
||||
return cloned;
|
||||
}
|
||||
|
||||
|
||||
// Returns a function, that, when invoked, will only be triggered at most once
|
||||
// during a given window of time.
|
||||
// @see underscore.js
|
||||
export function throttle(func, wait) {
|
||||
let context;
|
||||
let args;
|
||||
let timeout;
|
||||
let result;
|
||||
let previous = 0;
|
||||
const later = function () {
|
||||
previous = new Date();
|
||||
timeout = null;
|
||||
result = func.apply(context, args);
|
||||
};
|
||||
|
||||
return function (...args) {
|
||||
const now = new Date();
|
||||
const remaining = wait - (now - previous);
|
||||
// eslint-disable-next-line no-invalid-this
|
||||
context = this;
|
||||
if (remaining <= 0) {
|
||||
clearTimeout(timeout);
|
||||
timeout = null;
|
||||
previous = now;
|
||||
result = func.apply(context, args);
|
||||
}
|
||||
else if (!timeout) {
|
||||
timeout = setTimeout(later, remaining);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
// Returns a function, that, as long as it continues to be invoked, will not
|
||||
// be triggered. The function will be called after it stops being called for
|
||||
// N milliseconds. If `immediate` is passed, trigger the function on the
|
||||
// leading edge, instead of the trailing.
|
||||
// @see underscore.js
|
||||
export function debounce(func, wait, immediate) {
|
||||
let timeout;
|
||||
let result;
|
||||
|
||||
return function (...args) {
|
||||
// eslint-disable-next-line no-invalid-this
|
||||
const context = this;
|
||||
const later = function () {
|
||||
timeout = null;
|
||||
if (!immediate) {
|
||||
result = func.apply(context, args);
|
||||
}
|
||||
};
|
||||
|
||||
const callNow = immediate && !timeout;
|
||||
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
|
||||
if (callNow) {
|
||||
result = func.apply(context, args);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断两个对象的字段是否相等
|
||||
*
|
||||
* @param {Object} thisObj 要比较的对象
|
||||
* @param {Object} thatObj 参考对象
|
||||
* @param {Array} fields 指定字段
|
||||
* @return {boolean} 是否相等
|
||||
*/
|
||||
export function equals(thisObj, thatObj, fields) {
|
||||
|
||||
if (thisObj === thatObj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (thisObj == null && thatObj == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (thisObj == null && thatObj != null || thisObj != null && thatObj == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 这里`fields`未指定则仅overwrite自身可枚举的字段,指定`fields`则不做限制
|
||||
fields = fields || (typeof thisObj === 'object'
|
||||
? Object.keys(thisObj)
|
||||
: []);
|
||||
|
||||
if (!fields.length) {
|
||||
return thisObj === thatObj;
|
||||
}
|
||||
|
||||
let equal = true;
|
||||
for (let i = 0, l = fields.length, field; equal && i < l; i++) {
|
||||
field = fields[i];
|
||||
|
||||
if (
|
||||
thisObj[field] && typeof thisObj[field] === 'object'
|
||||
&& thatObj[field] && typeof thatObj[field] === 'object'
|
||||
) {
|
||||
equal = equal && equals(thisObj[field], thatObj[field]);
|
||||
}
|
||||
else {
|
||||
equal = equal && (thisObj[field] === thatObj[field]);
|
||||
}
|
||||
}
|
||||
|
||||
return equal;
|
||||
}
|
||||
106
vendor/fonteditor-core/src/common/string.js
vendored
106
vendor/fonteditor-core/src/common/string.js
vendored
@ -1,106 +0,0 @@
|
||||
/**
|
||||
* @file 字符串相关的函数
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
export default {
|
||||
|
||||
/**
|
||||
* HTML解码字符串
|
||||
*
|
||||
* @param {string} source 源字符串
|
||||
* @return {string}
|
||||
*/
|
||||
decodeHTML(source) {
|
||||
|
||||
const str = String(source)
|
||||
.replace(/"/g, '"')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/&/g, '&');
|
||||
|
||||
// 处理转义的中文和实体字符
|
||||
return str.replace(/&#([\d]+);/g, ($0, $1) => String.fromCodePoint(parseInt($1, 10)));
|
||||
},
|
||||
|
||||
/**
|
||||
* HTML编码字符串
|
||||
*
|
||||
* @param {string} source 源字符串
|
||||
* @return {string}
|
||||
*/
|
||||
encodeHTML(source) {
|
||||
return String(source)
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取string字节长度
|
||||
*
|
||||
* @param {string} source 源字符串
|
||||
* @return {number} 长度
|
||||
*/
|
||||
getLength(source) {
|
||||
// eslint-disable-next-line no-control-regex
|
||||
return String(source).replace(/[^\x00-\xff]/g, '11').length;
|
||||
},
|
||||
|
||||
/**
|
||||
* 字符串格式化,支持如 ${xxx.xxx} 的语法
|
||||
*
|
||||
* @param {string} source 模板字符串
|
||||
* @param {Object} data 数据
|
||||
* @return {string} 格式化后字符串
|
||||
*/
|
||||
format(source, data) {
|
||||
return source.replace(/\$\{([\w.]+)\}/g, ($0, $1) => {
|
||||
const ref = $1.split('.');
|
||||
let refObject = data;
|
||||
let level;
|
||||
|
||||
while (refObject != null && (level = ref.shift())) {
|
||||
refObject = refObject[level];
|
||||
}
|
||||
|
||||
return refObject != null ? refObject : '';
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 使用指定字符填充字符串,默认`0`
|
||||
*
|
||||
* @param {string} str 字符串
|
||||
* @param {number} size 填充到的大小
|
||||
* @param {string=} ch 填充字符
|
||||
* @return {string} 字符串
|
||||
*/
|
||||
pad(str, size, ch) {
|
||||
str = String(str);
|
||||
if (str.length > size) {
|
||||
return str.slice(str.length - size);
|
||||
}
|
||||
return new Array(size - str.length + 1).join(ch || '0') + str;
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取字符串哈希编码
|
||||
*
|
||||
* @param {string} str 字符串
|
||||
* @return {number} 哈希值
|
||||
*/
|
||||
hashcode(str) {
|
||||
if (!str) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let hash = 0;
|
||||
for (let i = 0, l = str.length; i < l; i++) {
|
||||
hash = 0x7FFFFFFFF & (hash * 31 + str.charCodeAt(i));
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
@ -1,190 +0,0 @@
|
||||
/**
|
||||
* @file 计算曲线包围盒
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*
|
||||
* modify from:
|
||||
* zrender
|
||||
* https://github.com/ecomfe/zrender/blob/master/src/tool/computeBoundingBox.js
|
||||
*/
|
||||
import pathIterator from './pathIterator';
|
||||
|
||||
/**
|
||||
* 计算包围盒
|
||||
*
|
||||
* @param {Array} points 点集
|
||||
* @return {Object} bounding box
|
||||
*/
|
||||
function computeBoundingBox(points) {
|
||||
|
||||
if (points.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let left = points[0].x;
|
||||
let right = points[0].x;
|
||||
let top = points[0].y;
|
||||
let bottom = points[0].y;
|
||||
|
||||
for (let i = 1; i < points.length; i++) {
|
||||
const p = points[i];
|
||||
|
||||
if (p.x < left) {
|
||||
left = p.x;
|
||||
}
|
||||
|
||||
if (p.x > right) {
|
||||
right = p.x;
|
||||
}
|
||||
|
||||
if (p.y < top) {
|
||||
top = p.y;
|
||||
}
|
||||
|
||||
if (p.y > bottom) {
|
||||
bottom = p.y;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
x: left,
|
||||
y: top,
|
||||
width: right - left,
|
||||
height: bottom - top
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算二阶贝塞尔曲线的包围盒
|
||||
* http://pissang.net/blog/?p=91
|
||||
*
|
||||
* @param {Object} p0 p0
|
||||
* @param {Object} p1 p1
|
||||
* @param {Object} p2 p2
|
||||
* @return {Object} bound对象
|
||||
*/
|
||||
function computeQuadraticBezierBoundingBox(p0, p1, p2) {
|
||||
// Find extremities, where derivative in x dim or y dim is zero
|
||||
let tmp = (p0.x + p2.x - 2 * p1.x);
|
||||
// p1 is center of p0 and p2 in x dim
|
||||
let t1;
|
||||
if (tmp === 0) {
|
||||
t1 = 0.5;
|
||||
}
|
||||
else {
|
||||
t1 = (p0.x - p1.x) / tmp;
|
||||
}
|
||||
|
||||
tmp = (p0.y + p2.y - 2 * p1.y);
|
||||
// p1 is center of p0 and p2 in y dim
|
||||
let t2;
|
||||
if (tmp === 0) {
|
||||
t2 = 0.5;
|
||||
}
|
||||
else {
|
||||
t2 = (p0.y - p1.y) / tmp;
|
||||
}
|
||||
|
||||
t1 = Math.max(Math.min(t1, 1), 0);
|
||||
t2 = Math.max(Math.min(t2, 1), 0);
|
||||
|
||||
const ct1 = 1 - t1;
|
||||
const ct2 = 1 - t2;
|
||||
|
||||
const x1 = ct1 * ct1 * p0.x + 2 * ct1 * t1 * p1.x + t1 * t1 * p2.x;
|
||||
const y1 = ct1 * ct1 * p0.y + 2 * ct1 * t1 * p1.y + t1 * t1 * p2.y;
|
||||
|
||||
const x2 = ct2 * ct2 * p0.x + 2 * ct2 * t2 * p1.x + t2 * t2 * p2.x;
|
||||
const y2 = ct2 * ct2 * p0.y + 2 * ct2 * t2 * p1.y + t2 * t2 * p2.y;
|
||||
|
||||
return computeBoundingBox(
|
||||
[
|
||||
p0,
|
||||
p2,
|
||||
{
|
||||
x: x1,
|
||||
y: y1
|
||||
},
|
||||
{
|
||||
x: x2,
|
||||
y: y2
|
||||
}
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算曲线包围盒
|
||||
*
|
||||
* @private
|
||||
* @param {...Array} args 坐标点集, 支持多个path
|
||||
* @return {Object} {x, y, width, height}
|
||||
*/
|
||||
function computePathBoundingBox(...args) {
|
||||
|
||||
const points = [];
|
||||
const iterator = function (c, p0, p1, p2) {
|
||||
if (c === 'L') {
|
||||
points.push(p0);
|
||||
points.push(p1);
|
||||
}
|
||||
else if (c === 'Q') {
|
||||
const bound = computeQuadraticBezierBoundingBox(p0, p1, p2);
|
||||
|
||||
points.push(bound);
|
||||
points.push({
|
||||
x: bound.x + bound.width,
|
||||
y: bound.y + bound.height
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if (args.length === 1) {
|
||||
pathIterator(args[0], (c, p0, p1, p2) => {
|
||||
if (c === 'L') {
|
||||
points.push(p0);
|
||||
points.push(p1);
|
||||
}
|
||||
else if (c === 'Q') {
|
||||
const bound = computeQuadraticBezierBoundingBox(p0, p1, p2);
|
||||
|
||||
points.push(bound);
|
||||
points.push({
|
||||
x: bound.x + bound.width,
|
||||
y: bound.y + bound.height
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
for (let i = 0, l = args.length; i < l; i++) {
|
||||
pathIterator(args[i], iterator);
|
||||
}
|
||||
}
|
||||
|
||||
return computeBoundingBox(points);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 计算曲线点边界
|
||||
*
|
||||
* @private
|
||||
* @param {...Array} args path对象, 支持多个path
|
||||
* @return {Object} {x, y, width, height}
|
||||
*/
|
||||
export function computePathBox(...args) {
|
||||
let points = [];
|
||||
if (args.length === 1) {
|
||||
points = args[0];
|
||||
}
|
||||
else {
|
||||
for (let i = 0, l = args.length; i < l; i++) {
|
||||
Array.prototype.splice.apply(points, [points.length, 0].concat(args[i]));
|
||||
}
|
||||
}
|
||||
return computeBoundingBox(points);
|
||||
}
|
||||
|
||||
export const computeBounding = computeBoundingBox;
|
||||
export const quadraticBezier = computeQuadraticBezierBoundingBox;
|
||||
export const computePath = computePathBoundingBox;
|
||||
247
vendor/fonteditor-core/src/graphics/getArc.js
vendored
247
vendor/fonteditor-core/src/graphics/getArc.js
vendored
@ -1,247 +0,0 @@
|
||||
/**
|
||||
* @file 使用插值法获取椭圆弧度,以支持svg arc命令
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*
|
||||
* modify from:
|
||||
* https://github.com/fontello/svgpath/blob/master/lib/a2c.js
|
||||
* references:
|
||||
* http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
|
||||
*/
|
||||
|
||||
import bezierCubic2Q2 from '../math/bezierCubic2Q2';
|
||||
|
||||
|
||||
const TAU = Math.PI * 2;
|
||||
|
||||
function vectorAngle(ux, uy, vx, vy) {
|
||||
// Calculate an angle between two vectors
|
||||
const sign = (ux * vy - uy * vx < 0) ? -1 : 1;
|
||||
const umag = Math.sqrt(ux * ux + uy * uy);
|
||||
const vmag = Math.sqrt(ux * ux + uy * uy);
|
||||
const dot = ux * vx + uy * vy;
|
||||
let div = dot / (umag * vmag);
|
||||
|
||||
if (div > 1 || div < -1) {
|
||||
// rounding errors, e.g. -1.0000000000000002 can screw up this
|
||||
div = Math.max(div, -1);
|
||||
div = Math.min(div, 1);
|
||||
}
|
||||
|
||||
return sign * Math.acos(div);
|
||||
}
|
||||
|
||||
function correctRadii(midx, midy, rx, ry) {
|
||||
// Correction of out-of-range radii
|
||||
rx = Math.abs(rx);
|
||||
ry = Math.abs(ry);
|
||||
|
||||
const Λ = (midx * midx) / (rx * rx) + (midy * midy) / (ry * ry);
|
||||
if (Λ > 1) {
|
||||
rx *= Math.sqrt(Λ);
|
||||
ry *= Math.sqrt(Λ);
|
||||
}
|
||||
|
||||
return [rx, ry];
|
||||
}
|
||||
|
||||
|
||||
function getArcCenter(x1, y1, x2, y2, fa, fs, rx, ry, sin_φ, cos_φ) {
|
||||
// Convert from endpoint to center parameterization,
|
||||
// see http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
|
||||
|
||||
// Step 1.
|
||||
//
|
||||
// Moving an ellipse so origin will be the middlepoint between our two
|
||||
// points. After that, rotate it to line up ellipse axes with coordinate
|
||||
// axes.
|
||||
//
|
||||
const x1p = cos_φ * (x1 - x2) / 2 + sin_φ * (y1 - y2) / 2;
|
||||
const y1p = -sin_φ * (x1 - x2) / 2 + cos_φ * (y1 - y2) / 2;
|
||||
|
||||
const rx_sq = rx * rx;
|
||||
const ry_sq = ry * ry;
|
||||
const x1p_sq = x1p * x1p;
|
||||
const y1p_sq = y1p * y1p;
|
||||
|
||||
// Step 2.
|
||||
//
|
||||
// Compute coordinates of the centre of this ellipse (cx', cy')
|
||||
// in the new coordinate system.
|
||||
//
|
||||
let radicant = (rx_sq * ry_sq) - (rx_sq * y1p_sq) - (ry_sq * x1p_sq);
|
||||
|
||||
if (radicant < 0) {
|
||||
// due to rounding errors it might be e.g. -1.3877787807814457e-17
|
||||
radicant = 0;
|
||||
}
|
||||
|
||||
radicant /= (rx_sq * y1p_sq) + (ry_sq * x1p_sq);
|
||||
radicant = Math.sqrt(radicant) * (fa === fs ? -1 : 1);
|
||||
|
||||
const cxp = radicant * rx / ry * y1p;
|
||||
const cyp = radicant * -ry / rx * x1p;
|
||||
|
||||
// Step 3.
|
||||
//
|
||||
// Transform back to get centre coordinates (cx, cy) in the original
|
||||
// coordinate system.
|
||||
//
|
||||
const cx = cos_φ * cxp - sin_φ * cyp + (x1 + x2) / 2;
|
||||
const cy = sin_φ * cxp + cos_φ * cyp + (y1 + y2) / 2;
|
||||
|
||||
// Step 4.
|
||||
//
|
||||
// Compute angles (θ1, Δθ).
|
||||
//
|
||||
const v1x = (x1p - cxp) / rx;
|
||||
const v1y = (y1p - cyp) / ry;
|
||||
const v2x = (-x1p - cxp) / rx;
|
||||
const v2y = (-y1p - cyp) / ry;
|
||||
|
||||
const θ1 = vectorAngle(1, 0, v1x, v1y);
|
||||
let Δθ = vectorAngle(v1x, v1y, v2x, v2y);
|
||||
|
||||
if (fs === 0 && Δθ > 0) {
|
||||
Δθ -= TAU;
|
||||
}
|
||||
if (fs === 1 && Δθ < 0) {
|
||||
Δθ += TAU;
|
||||
}
|
||||
|
||||
return [cx, cy, θ1, Δθ];
|
||||
}
|
||||
|
||||
function approximateUnitArc(θ1, Δθ) {
|
||||
// Approximate one unit arc segment with bézier curves,
|
||||
// see http://math.stackexchange.com/questions/873224/
|
||||
// calculate-control-points-of-cubic-bezier-curve-approximating-a-part-of-a-circle
|
||||
const α = 4 / 3 * Math.tan(Δθ / 4);
|
||||
|
||||
const x1 = Math.cos(θ1);
|
||||
const y1 = Math.sin(θ1);
|
||||
const x2 = Math.cos(θ1 + Δθ);
|
||||
const y2 = Math.sin(θ1 + Δθ);
|
||||
|
||||
return [x1, y1, x1 - y1 * α, y1 + x1 * α, x2 + y2 * α, y2 - x2 * α, x2, y2];
|
||||
}
|
||||
|
||||
|
||||
function a2c(x1, y1, x2, y2, fa, fs, rx, ry, φ) {
|
||||
const sin_φ = Math.sin(φ * TAU / 360);
|
||||
const cos_φ = Math.cos(φ * TAU / 360);
|
||||
|
||||
// Make sure radii are valid
|
||||
//
|
||||
const x1p = cos_φ * (x1 - x2) / 2 + sin_φ * (y1 - y2) / 2;
|
||||
const y1p = -sin_φ * (x1 - x2) / 2 + cos_φ * (y1 - y2) / 2;
|
||||
|
||||
if (x1p === 0 && y1p === 0) {
|
||||
// we're asked to draw line to itself
|
||||
return [];
|
||||
}
|
||||
|
||||
if (rx === 0 || ry === 0) {
|
||||
// one of the radii is zero
|
||||
return [];
|
||||
}
|
||||
|
||||
const radii = correctRadii(x1p, y1p, rx, ry);
|
||||
rx = radii[0];
|
||||
ry = radii[1];
|
||||
|
||||
// Get center parameters (cx, cy, θ1, Δθ)
|
||||
//
|
||||
const cc = getArcCenter(x1, y1, x2, y2, fa, fs, rx, ry, sin_φ, cos_φ);
|
||||
|
||||
const result = [];
|
||||
let θ1 = cc[2];
|
||||
let Δθ = cc[3];
|
||||
|
||||
// Split an arc to multiple segments, so each segment
|
||||
// will be less than τ/4 (= 90°)
|
||||
//
|
||||
const segments = Math.max(Math.ceil(Math.abs(Δθ) / (TAU / 4)), 1);
|
||||
Δθ /= segments;
|
||||
|
||||
for (let i = 0; i < segments; i++) {
|
||||
result.push(approximateUnitArc(θ1, Δθ));
|
||||
θ1 += Δθ;
|
||||
}
|
||||
|
||||
// We have a bezier approximation of a unit circle,
|
||||
// now need to transform back to the original ellipse
|
||||
//
|
||||
return result.map(curve => {
|
||||
for (let i = 0; i < curve.length; i += 2) {
|
||||
let x = curve[i + 0];
|
||||
let y = curve[i + 1];
|
||||
|
||||
// scale
|
||||
x *= rx;
|
||||
y *= ry;
|
||||
|
||||
// rotate
|
||||
const xp = cos_φ * x - sin_φ * y;
|
||||
const yp = sin_φ * x + cos_φ * y;
|
||||
|
||||
// translate
|
||||
curve[i + 0] = xp + cc[0];
|
||||
curve[i + 1] = yp + cc[1];
|
||||
}
|
||||
|
||||
return curve;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取椭圆弧度
|
||||
*
|
||||
* @param {number} rx 椭圆长半轴
|
||||
* @param {number} ry 椭圆短半轴
|
||||
* @param {number} angle 旋转角度
|
||||
* @param {number} largeArc 是否大圆弧
|
||||
* @param {number} sweep 是否延伸圆弧
|
||||
* @param {Object} p0 分割点1
|
||||
* @param {Object} p1 分割点2
|
||||
* @return {Array} 分割后的路径
|
||||
*/
|
||||
export default function getArc(rx, ry, angle, largeArc, sweep, p0, p1) {
|
||||
const result = a2c(p0.x, p0.y, p1.x, p1.y, largeArc, sweep, rx, ry, angle);
|
||||
const path = [];
|
||||
|
||||
if (result.length) {
|
||||
path.push({
|
||||
x: result[0][0],
|
||||
y: result[0][1],
|
||||
onCurve: true
|
||||
});
|
||||
|
||||
// 将三次曲线转换成二次曲线
|
||||
result.forEach(c => {
|
||||
const q2Array = bezierCubic2Q2({
|
||||
x: c[0],
|
||||
y: c[1]
|
||||
}, {
|
||||
x: c[2],
|
||||
y: c[3]
|
||||
}, {
|
||||
x: c[4],
|
||||
y: c[5]
|
||||
}, {
|
||||
x: c[6],
|
||||
y: c[7]
|
||||
});
|
||||
|
||||
q2Array[0][2].onCurve = true;
|
||||
path.push(q2Array[0][1]);
|
||||
path.push(q2Array[0][2]);
|
||||
if (q2Array[1]) {
|
||||
q2Array[1][2].onCurve = true;
|
||||
path.push(q2Array[1][1]);
|
||||
path.push(q2Array[1][2]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
49
vendor/fonteditor-core/src/graphics/matrix.js
vendored
49
vendor/fonteditor-core/src/graphics/matrix.js
vendored
@ -1,49 +0,0 @@
|
||||
/**
|
||||
* @file matrix变换操作
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 仿射矩阵相乘
|
||||
*
|
||||
* @param {Array=} matrix1 矩阵1
|
||||
* @param {Array=} matrix2 矩阵2
|
||||
* @return {Array} 新矩阵
|
||||
*/
|
||||
export function mul(matrix1 = [1, 0, 0, 1], matrix2 = [1, 0, 0, 1]) {
|
||||
// 旋转变换 4 个参数
|
||||
if (matrix1.length === 4) {
|
||||
return [
|
||||
matrix1[0] * matrix2[0] + matrix1[2] * matrix2[1],
|
||||
matrix1[1] * matrix2[0] + matrix1[3] * matrix2[1],
|
||||
matrix1[0] * matrix2[2] + matrix1[2] * matrix2[3],
|
||||
matrix1[1] * matrix2[2] + matrix1[3] * matrix2[3]
|
||||
];
|
||||
}
|
||||
// 旋转位移变换, 6 个参数
|
||||
|
||||
return [
|
||||
matrix1[0] * matrix2[0] + matrix1[2] * matrix2[1],
|
||||
matrix1[1] * matrix2[0] + matrix1[3] * matrix2[1],
|
||||
matrix1[0] * matrix2[2] + matrix1[2] * matrix2[3],
|
||||
matrix1[1] * matrix2[2] + matrix1[3] * matrix2[3],
|
||||
|
||||
matrix1[0] * matrix2[4] + matrix1[2] * matrix2[5] + matrix1[4],
|
||||
matrix1[1] * matrix2[4] + matrix1[3] * matrix2[5] + matrix1[5]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 多个仿射矩阵相乘
|
||||
*
|
||||
* @param {...Array} matrixs matrix array
|
||||
* @return {Array} 新矩阵
|
||||
*/
|
||||
export function multiply(...matrixs) {
|
||||
let result = matrixs[0];
|
||||
for (let i = 1, matrix; (matrix = matrixs[i]); i++) {
|
||||
result = mul(result, matrix);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -1,71 +0,0 @@
|
||||
/**
|
||||
* @file 圆路径集合,逆时针
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
export default [
|
||||
{
|
||||
x: 582,
|
||||
y: 0
|
||||
},
|
||||
{
|
||||
x: 758,
|
||||
y: 75
|
||||
},
|
||||
{
|
||||
x: 890,
|
||||
y: 208
|
||||
},
|
||||
{
|
||||
x: 965,
|
||||
y: 384
|
||||
},
|
||||
{
|
||||
x: 965,
|
||||
y: 583
|
||||
},
|
||||
{
|
||||
x: 890,
|
||||
y: 760
|
||||
},
|
||||
{
|
||||
x: 758,
|
||||
y: 891
|
||||
},
|
||||
{
|
||||
x: 582,
|
||||
y: 966
|
||||
},
|
||||
{
|
||||
x: 383,
|
||||
y: 966
|
||||
},
|
||||
{
|
||||
x: 207,
|
||||
y: 891
|
||||
},
|
||||
{
|
||||
x: 75,
|
||||
y: 760
|
||||
},
|
||||
{
|
||||
x: 0,
|
||||
y: 583
|
||||
},
|
||||
{
|
||||
x: 0,
|
||||
y: 384
|
||||
},
|
||||
{
|
||||
x: 75,
|
||||
y: 208
|
||||
},
|
||||
{
|
||||
x: 207,
|
||||
y: 75
|
||||
},
|
||||
{
|
||||
x: 383,
|
||||
y: 0
|
||||
}
|
||||
];
|
||||
@ -1,29 +0,0 @@
|
||||
/**
|
||||
* @file 调整路径缩放和平移
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 对path坐标进行调整
|
||||
*
|
||||
* @param {Object} contour 坐标点
|
||||
* @param {number} scaleX x缩放比例
|
||||
* @param {number} scaleY y缩放比例
|
||||
* @param {number} offsetX x偏移
|
||||
* @param {number} offsetY y偏移
|
||||
*
|
||||
* @return {Object} contour 坐标点
|
||||
*/
|
||||
export default function pathAdjust(contour, scaleX, scaleY, offsetX, offsetY) {
|
||||
scaleX = scaleX === undefined ? 1 : scaleX;
|
||||
scaleY = scaleY === undefined ? 1 : scaleY;
|
||||
const x = offsetX || 0;
|
||||
const y = offsetY || 0;
|
||||
let p;
|
||||
for (let i = 0, l = contour.length; i < l; i++) {
|
||||
p = contour[i];
|
||||
p.x = scaleX * (p.x + x);
|
||||
p.y = scaleY * (p.y + y);
|
||||
}
|
||||
return contour;
|
||||
}
|
||||
27
vendor/fonteditor-core/src/graphics/pathCeil.js
vendored
27
vendor/fonteditor-core/src/graphics/pathCeil.js
vendored
@ -1,27 +0,0 @@
|
||||
/**
|
||||
* @file 对路径进行四舍五入
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 对path坐标进行调整
|
||||
*
|
||||
* @param {Array} contour 轮廓点数组
|
||||
* @param {number} point 四舍五入的点数
|
||||
* @return {Object} contour 坐标点
|
||||
*/
|
||||
export default function pathCeil(contour, point) {
|
||||
let p;
|
||||
for (let i = 0, l = contour.length; i < l; i++) {
|
||||
p = contour[i];
|
||||
if (!point) {
|
||||
p.x = Math.round(p.x);
|
||||
p.y = Math.round(p.y);
|
||||
}
|
||||
else {
|
||||
p.x = Number(p.x.toFixed(point));
|
||||
p.y = Number(p.y.toFixed(point));
|
||||
}
|
||||
}
|
||||
return contour;
|
||||
}
|
||||
@ -1,71 +0,0 @@
|
||||
/**
|
||||
* @file 遍历路径的路径集合,包括segment和 bezier curve
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 遍历路径的路径集合
|
||||
*
|
||||
* @param {Array} contour 坐标点集
|
||||
* @param {Function} callBack 回调函数,参数集合:command, p0, p1, p2, i
|
||||
* p0, p1, p2 直线或者贝塞尔曲线参数
|
||||
* i 当前遍历的点
|
||||
* 其中command = L 或者 Q,表示直线或者贝塞尔曲线
|
||||
*/
|
||||
export default function pathIterator(contour, callBack) {
|
||||
|
||||
let curPoint;
|
||||
let prevPoint;
|
||||
let nextPoint;
|
||||
let cursorPoint; // cursorPoint 为当前单个绘制命令的起点
|
||||
|
||||
for (let i = 0, l = contour.length; i < l; i++) {
|
||||
curPoint = contour[i];
|
||||
prevPoint = i === 0 ? contour[l - 1] : contour[i - 1];
|
||||
nextPoint = i === l - 1 ? contour[0] : contour[i + 1];
|
||||
|
||||
// 起始坐标
|
||||
if (i === 0) {
|
||||
if (curPoint.onCurve) {
|
||||
cursorPoint = curPoint;
|
||||
}
|
||||
else if (prevPoint.onCurve) {
|
||||
cursorPoint = prevPoint;
|
||||
}
|
||||
else {
|
||||
cursorPoint = {
|
||||
x: (prevPoint.x + curPoint.x) / 2,
|
||||
y: (prevPoint.y + curPoint.y) / 2
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 直线
|
||||
if (curPoint.onCurve && nextPoint.onCurve) {
|
||||
if (false === callBack('L', curPoint, nextPoint, 0, i)) {
|
||||
break;
|
||||
}
|
||||
cursorPoint = nextPoint;
|
||||
}
|
||||
else if (!curPoint.onCurve) {
|
||||
|
||||
if (nextPoint.onCurve) {
|
||||
if (false === callBack('Q', cursorPoint, curPoint, nextPoint, i)) {
|
||||
break;
|
||||
}
|
||||
cursorPoint = nextPoint;
|
||||
}
|
||||
else {
|
||||
const last = {
|
||||
x: (curPoint.x + nextPoint.x) / 2,
|
||||
y: (curPoint.y + nextPoint.y) / 2
|
||||
};
|
||||
if (false === callBack('Q', cursorPoint, curPoint, last, i)) {
|
||||
break;
|
||||
}
|
||||
cursorPoint = last;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,37 +0,0 @@
|
||||
/**
|
||||
* @file 路径旋转
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 对path坐标进行调整
|
||||
*
|
||||
* @param {Object} contour 坐标点
|
||||
* @param {number} angle 角度
|
||||
* @param {number} centerX x偏移
|
||||
* @param {number} centerY y偏移
|
||||
*
|
||||
* @return {Object} contour 坐标点
|
||||
*/
|
||||
export default function pathRotate(contour, angle, centerX, centerY) {
|
||||
angle = angle === undefined ? 0 : angle;
|
||||
const x = centerX || 0;
|
||||
const y = centerY || 0;
|
||||
const cos = Math.cos(angle);
|
||||
const sin = Math.sin(angle);
|
||||
let px;
|
||||
let py;
|
||||
let p;
|
||||
|
||||
// x1=cos(angle)*x-sin(angle)*y;
|
||||
// y1=cos(angle)*y+sin(angle)*x;
|
||||
for (let i = 0, l = contour.length; i < l; i++) {
|
||||
p = contour[i];
|
||||
px = cos * (p.x - x) - sin * (p.y - y);
|
||||
py = cos * (p.y - y) + sin * (p.x - x);
|
||||
p.x = px + x;
|
||||
p.y = py + y;
|
||||
}
|
||||
|
||||
return contour;
|
||||
}
|
||||
40
vendor/fonteditor-core/src/graphics/pathSkew.js
vendored
40
vendor/fonteditor-core/src/graphics/pathSkew.js
vendored
@ -1,40 +0,0 @@
|
||||
/**
|
||||
* @file path倾斜变换
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
/**
|
||||
* path倾斜变换
|
||||
*
|
||||
* @param {Object} contour 坐标点
|
||||
* @param {number} angle 角度
|
||||
* @param {number} offsetX x偏移
|
||||
* @param {number} offsetY y偏移
|
||||
*
|
||||
* @return {Object} contour 坐标点
|
||||
*/
|
||||
export default function pathSkew(contour, angle, offsetX, offsetY) {
|
||||
angle = angle === undefined ? 0 : angle;
|
||||
const x = offsetX || 0;
|
||||
const tan = Math.tan(angle);
|
||||
let p;
|
||||
let i;
|
||||
let l;
|
||||
|
||||
// x 平移
|
||||
if (x === 0) {
|
||||
for (i = 0, l = contour.length; i < l; i++) {
|
||||
p = contour[i];
|
||||
p.x += tan * (p.y - offsetY);
|
||||
}
|
||||
}
|
||||
// y平移
|
||||
else {
|
||||
for (i = 0, l = contour.length; i < l; i++) {
|
||||
p = contour[i];
|
||||
p.y += tan * (p.x - offsetX);
|
||||
}
|
||||
}
|
||||
|
||||
return contour;
|
||||
}
|
||||
26
vendor/fonteditor-core/src/graphics/pathSkewX.js
vendored
26
vendor/fonteditor-core/src/graphics/pathSkewX.js
vendored
@ -1,26 +0,0 @@
|
||||
/**
|
||||
* @file 按X轴平移变换, 变换中心为图像中心点
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
import {computePath} from './computeBoundingBox';
|
||||
|
||||
/**
|
||||
* path倾斜变换
|
||||
*
|
||||
* @param {Object} contour 坐标点
|
||||
* @param {number} angle 角度
|
||||
*
|
||||
* @return {Object} contour 坐标点
|
||||
*/
|
||||
export default function pathSkewX(contour, angle) {
|
||||
angle = angle === undefined ? 0 : angle;
|
||||
const y = computePath(contour).y;
|
||||
const tan = Math.tan(angle);
|
||||
let p;
|
||||
// x 平移
|
||||
for (let i = 0, l = contour.length; i < l; i++) {
|
||||
p = contour[i];
|
||||
p.x += tan * (p.y - y);
|
||||
}
|
||||
return contour;
|
||||
}
|
||||
26
vendor/fonteditor-core/src/graphics/pathSkewY.js
vendored
26
vendor/fonteditor-core/src/graphics/pathSkewY.js
vendored
@ -1,26 +0,0 @@
|
||||
/**
|
||||
* @file 按Y轴平移变换, 变换中心为图像中心点
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
import {computePath} from './computeBoundingBox';
|
||||
|
||||
/**
|
||||
* path倾斜变换
|
||||
*
|
||||
* @param {Object} contour 坐标点
|
||||
* @param {number} angle 角度
|
||||
*
|
||||
* @return {Object} contour 坐标点
|
||||
*/
|
||||
export default function pathSkewY(contour, angle) {
|
||||
angle = angle === undefined ? 0 : angle;
|
||||
const x = computePath(contour).x;
|
||||
const tan = Math.tan(angle);
|
||||
let p;
|
||||
// y 平移
|
||||
for (let i = 0, l = contour.length; i < l; i++) {
|
||||
p = contour[i];
|
||||
p.y += tan * (p.x - x);
|
||||
}
|
||||
return contour;
|
||||
}
|
||||
@ -1,40 +0,0 @@
|
||||
/**
|
||||
* @file 对轮廓进行transform变换
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*
|
||||
* 参考资料:
|
||||
* http://blog.csdn.net/henren555/article/details/9699449
|
||||
*
|
||||
* |X| |a c e| |x|
|
||||
* |Y| = |b d f| * |y|
|
||||
* |1| |0 0 1| |1|
|
||||
*
|
||||
* X = x * a + y * c + e
|
||||
* Y = x * b + y * d + f
|
||||
*/
|
||||
|
||||
/**
|
||||
* 图形仿射矩阵变换
|
||||
*
|
||||
* @param {Array.<Object>} contour 轮廓点
|
||||
* @param {number} a m11
|
||||
* @param {number} b m12
|
||||
* @param {number} c m21
|
||||
* @param {number} d m22
|
||||
* @param {number} e dx
|
||||
* @param {number} f dy
|
||||
* @return {Array.<Object>} contour 轮廓点
|
||||
*/
|
||||
export default function transform(contour, a, b, c, d, e, f) {
|
||||
let x;
|
||||
let y;
|
||||
let p;
|
||||
for (let i = 0, l = contour.length; i < l; i++) {
|
||||
p = contour[i];
|
||||
x = p.x;
|
||||
y = p.y;
|
||||
p.x = x * a + y * c + e;
|
||||
p.y = x * b + y * d + f;
|
||||
}
|
||||
return contour;
|
||||
}
|
||||
185
vendor/fonteditor-core/src/graphics/pathUtil.js
vendored
185
vendor/fonteditor-core/src/graphics/pathUtil.js
vendored
@ -1,185 +0,0 @@
|
||||
/**
|
||||
* @file 路径相关的函数集合
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
import {getPointHash} from './util';
|
||||
|
||||
/**
|
||||
* 对路径进行插值,补全省略的点
|
||||
*
|
||||
* @param {Array} path 路径
|
||||
* @return {Array} 路径
|
||||
*/
|
||||
export function interpolate(path) {
|
||||
const newPath = [];
|
||||
for (let i = 0, l = path.length; i < l; i++) {
|
||||
const next = i === l - 1 ? 0 : i + 1;
|
||||
newPath.push(path[i]);
|
||||
// 插值
|
||||
if (!path[i].onCurve && !path[next].onCurve) {
|
||||
newPath.push({
|
||||
x: (path[i].x + path[next].x) / 2,
|
||||
y: (path[i].y + path[next].y) / 2,
|
||||
onCurve: true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return newPath;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 去除路径中的插值点
|
||||
*
|
||||
* @param {Array} path 路径
|
||||
* @return {Array} 路径
|
||||
*/
|
||||
export function deInterpolate(path) {
|
||||
const newPath = [];
|
||||
|
||||
for (let i = 0, l = path.length; i < l; i++) {
|
||||
const next = i === l - 1 ? 0 : i + 1;
|
||||
const prev = i === 0 ? l - 1 : i - 1;
|
||||
// 插值
|
||||
if (
|
||||
!path[prev].onCurve && path[i].onCurve && !path[next].onCurve
|
||||
&& Math.abs(2 * path[i].x - path[prev].x - path[next].x) < 0.001
|
||||
&& Math.abs(2 * path[i].y - path[prev].y - path[next].y) < 0.001
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
newPath.push(path[i]);
|
||||
}
|
||||
|
||||
return newPath;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 判断路径的方向是否顺时针
|
||||
*
|
||||
* see:
|
||||
* http://debian.fmi.uni-sofia.bg/~sergei/cgsr/docs/clockwise.htm
|
||||
*
|
||||
* @param {Array} path 路径
|
||||
* @return {number} 0 无方向 1 clockwise, -1 counter clockwise
|
||||
*/
|
||||
export function isClockWise(path) {
|
||||
|
||||
if (path.length < 3) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let zCount = 0;
|
||||
for (let i = 0, l = path.length; i < l; i++) {
|
||||
const cur = path[i];
|
||||
const prev = i === 0 ? path[l - 1] : path[i - 1];
|
||||
const next = i === l - 1 ? path[0] : path[i + 1];
|
||||
const z = (cur.x - prev.x) * (next.y - cur.y)
|
||||
- (cur.y - prev.y) * (next.x - cur.x);
|
||||
|
||||
if (z < 0) {
|
||||
zCount--;
|
||||
}
|
||||
else if (z > 0) {
|
||||
zCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return zCount === 0
|
||||
? 0
|
||||
: zCount < 0 ? 1 : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取路径哈希
|
||||
*
|
||||
* @param {Array} path 路径数组
|
||||
* @return {number} 哈希值
|
||||
*/
|
||||
export function getPathHash(path) {
|
||||
let hash = 0;
|
||||
const seed = 131;
|
||||
|
||||
path.forEach(p => {
|
||||
hash = 0x7FFFFFFF & (hash * seed + getPointHash(p) + (p.onCurve ? 1 : 0));
|
||||
});
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 移除重复点
|
||||
*
|
||||
* @param {Array} points 点集合
|
||||
* @return {Array} 移除后点集合
|
||||
*/
|
||||
export function removeOverlapPoints(points) {
|
||||
const hash = {};
|
||||
const ret = [];
|
||||
for (let i = 0, l = points.length; i < l; i++) {
|
||||
const hashcode = points[i].x * 31 + points[i].y;
|
||||
if (!hash[hashcode]) {
|
||||
ret.push(points[i]);
|
||||
hash[hashcode] = 1;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对path进行双向链表连接
|
||||
*
|
||||
* @param {Array} path 轮廓数组
|
||||
* @return {Array} path
|
||||
*/
|
||||
export function makeLink(path) {
|
||||
for (let i = 0, l = path.length; i < l; i++) {
|
||||
const cur = path[i];
|
||||
const prev = i === 0 ? path[l - 1] : path[i - 1];
|
||||
const next = i === l - 1 ? path[0] : path[i + 1];
|
||||
cur.index = i;
|
||||
cur.next = next;
|
||||
cur.prev = prev;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对path进行缩放
|
||||
*
|
||||
* @param {Array} path 轮廓数组
|
||||
* @param {number} ratio 缩放大小
|
||||
*
|
||||
* @return {Array} path
|
||||
*/
|
||||
export function scale(path, ratio) {
|
||||
for (let i = 0, l = path.length; i < l; i++) {
|
||||
const cur = path[i];
|
||||
cur.x *= ratio;
|
||||
cur.y *= ratio;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
export function clone(path) {
|
||||
return path ? path.map(p => {
|
||||
const newP = {
|
||||
x: p.x,
|
||||
y: p.y
|
||||
};
|
||||
|
||||
if (p.onCurve) {
|
||||
newP.onCurve = true;
|
||||
}
|
||||
|
||||
return newP;
|
||||
}) : path;
|
||||
}
|
||||
93
vendor/fonteditor-core/src/graphics/pathsUtil.js
vendored
93
vendor/fonteditor-core/src/graphics/pathsUtil.js
vendored
@ -1,93 +0,0 @@
|
||||
/**
|
||||
* @file 路径组变化函数
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
import {computePath} from './computeBoundingBox';
|
||||
import pathAdjust from './pathAdjust';
|
||||
import pathRotate from './pathRotate';
|
||||
|
||||
/**
|
||||
* 翻转路径
|
||||
*
|
||||
* @param {Array} paths 路径数组
|
||||
* @param {number} xScale x翻转
|
||||
* @param {number} yScale y翻转
|
||||
* @return {Array} 变换后的路径
|
||||
*/
|
||||
function mirrorPaths(paths, xScale, yScale) {
|
||||
const {x, y, width, height} = computePath(...paths);
|
||||
|
||||
if (xScale === -1) {
|
||||
paths.forEach(p => {
|
||||
pathAdjust(p, -1, 1, -x, 0);
|
||||
pathAdjust(p, 1, 1, x + width, 0);
|
||||
p.reverse();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
if (yScale === -1) {
|
||||
paths.forEach(p => {
|
||||
pathAdjust(p, 1, -1, 0, -y);
|
||||
pathAdjust(p, 1, 1, 0, y + height);
|
||||
p.reverse();
|
||||
});
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
|
||||
|
||||
export default {
|
||||
|
||||
/**
|
||||
* 旋转路径
|
||||
*
|
||||
* @param {Array} paths 路径数组
|
||||
* @param {number} angle 弧度
|
||||
* @return {Array} 变换后的路径
|
||||
*/
|
||||
rotate(paths, angle) {
|
||||
if (!angle) {
|
||||
return paths;
|
||||
}
|
||||
|
||||
const bound = computePath(...paths);
|
||||
|
||||
const cx = bound.x + (bound.width) / 2;
|
||||
const cy = bound.y + (bound.height) / 2;
|
||||
|
||||
paths.forEach(p => {
|
||||
pathRotate(p, angle, cx, cy);
|
||||
});
|
||||
|
||||
return paths;
|
||||
},
|
||||
|
||||
/**
|
||||
* 路径组变换
|
||||
*
|
||||
* @param {Array} paths 路径数组
|
||||
* @param {number} x x 方向缩放
|
||||
* @param {number} y y 方向缩放
|
||||
* @return {Array} 变换后的路径
|
||||
*/
|
||||
move(paths, x, y) {
|
||||
const bound = computePath(...paths);
|
||||
paths.forEach(path => {
|
||||
pathAdjust(path, 1, 1, x - bound.x, y - bound.y);
|
||||
});
|
||||
|
||||
return paths;
|
||||
},
|
||||
|
||||
mirror(paths) {
|
||||
return mirrorPaths(paths, -1, 1);
|
||||
},
|
||||
|
||||
flip(paths) {
|
||||
return mirrorPaths(paths, 1, -1);
|
||||
}
|
||||
};
|
||||
@ -1,73 +0,0 @@
|
||||
/**
|
||||
* @file 缩减path大小,去除冗余节点
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 判断点是否多余的点
|
||||
*
|
||||
* @param {Object} prev 上一个
|
||||
* @param {Object} p 当前
|
||||
* @param {Object} next 下一个
|
||||
* @return {boolean}
|
||||
*/
|
||||
function redundant(prev, p, next) {
|
||||
|
||||
// 是否重合的点, 只有两个点同在曲线上或者同不在曲线上移出
|
||||
if (
|
||||
(p.onCurve && next.onCurve || !p.onCurve && !next.onCurve)
|
||||
&& Math.pow(p.x - next.x, 2) + Math.pow(p.y - next.y, 2) <= 1
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 三点同线 检查直线点
|
||||
if (
|
||||
(p.onCurve && prev.onCurve && next.onCurve)
|
||||
&& Math.abs((next.y - p.y) * (prev.x - p.x) - (prev.y - p.y) * (next.x - p.x)) <= 0.001
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 三点同线 检查控制点
|
||||
if (
|
||||
(!p.onCurve && prev.onCurve && next.onCurve)
|
||||
&& Math.abs((next.y - p.y) * (prev.x - p.x) - (prev.y - p.y) * (next.x - p.x)) <= 0.001
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 缩减glyf,去除冗余节点
|
||||
*
|
||||
* @param {Array} contour 路径对象
|
||||
* @return {Array} 路径对象
|
||||
*/
|
||||
export default function reducePath(contour) {
|
||||
|
||||
if (!contour.length) {
|
||||
return contour;
|
||||
}
|
||||
|
||||
let prev;
|
||||
let next;
|
||||
let p;
|
||||
for (let 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;
|
||||
}
|
||||
}
|
||||
|
||||
return contour;
|
||||
}
|
||||
92
vendor/fonteditor-core/src/graphics/util.js
vendored
92
vendor/fonteditor-core/src/graphics/util.js
vendored
@ -1,92 +0,0 @@
|
||||
/**
|
||||
* @file grahpics点相关工具箱
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 将点进行误差舍入
|
||||
*
|
||||
* @param {Object} p 点对象
|
||||
* @return {Object} 点
|
||||
*/
|
||||
export function ceilPoint(p) {
|
||||
let t = p.x;
|
||||
|
||||
// 处理形如 4.99999 = 5, 5.00001 = 5的情况
|
||||
if (Math.abs(Math.round(t) - t) < 0.00002) {
|
||||
p.x = Math.round(t);
|
||||
}
|
||||
else {
|
||||
p.x = Math.round(p.x * 100000) / 100000;
|
||||
}
|
||||
|
||||
t = p.y;
|
||||
if (Math.abs(Math.round(t) - t) < 0.00005) {
|
||||
p.y = Math.round(t);
|
||||
}
|
||||
else {
|
||||
p.y = Math.round(p.y * 100000) / 100000;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将数值进行误差舍入
|
||||
*
|
||||
* @param {Object} x 数值
|
||||
* @return {number} 点
|
||||
*/
|
||||
export function ceil(x) {
|
||||
if (Math.abs(Math.round(x) - x) < 0.00002) {
|
||||
return Math.round(x);
|
||||
}
|
||||
|
||||
return Math.round(x * 100000) / 100000;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断点是否在bounding box内部
|
||||
*
|
||||
* @param {Object} bound bounding box对象
|
||||
* @param {Object} p 点对象
|
||||
* @param {boolean=} fixed 是否四舍五入
|
||||
* @return {boolean} 是否
|
||||
*/
|
||||
export function isPointInBound(bound, p, fixed) {
|
||||
|
||||
if (fixed) {
|
||||
return ceil(p.x) <= ceil(bound.x + bound.width)
|
||||
&& ceil(p.x) >= ceil(bound.x)
|
||||
&& ceil(p.y) <= ceil(bound.y + bound.height)
|
||||
&& ceil(p.y) >= ceil(bound.y);
|
||||
}
|
||||
|
||||
return p.x <= bound.x + bound.width
|
||||
&& p.x >= bound.x
|
||||
&& p.y <= bound.y + bound.height
|
||||
&& p.y >= bound.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断点是否重合
|
||||
*
|
||||
* @param {Object} p0 p0
|
||||
* @param {Object} p1 p1
|
||||
* @return {boolean} 是否
|
||||
*/
|
||||
export function isPointOverlap(p0, p1) {
|
||||
return ceil(p0.x) === ceil(p1.x) && ceil(p0.y) === ceil(p1.y);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取点的hash值
|
||||
*
|
||||
* @param {Object} p p
|
||||
* @param {Object} p1 p1
|
||||
* @return {number}
|
||||
*/
|
||||
export function getPointHash(p) {
|
||||
return Math.floor(7 * Math.floor(p.x * 10) + 131 * Math.floor(p.y * 100));
|
||||
}
|
||||
76
vendor/fonteditor-core/src/main.esm.js
vendored
76
vendor/fonteditor-core/src/main.esm.js
vendored
@ -1,76 +0,0 @@
|
||||
/**
|
||||
* @file 主函数
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
import {Font, createFont} from './ttf/font';
|
||||
import TTF from './ttf/ttf';
|
||||
import TTFReader from './ttf/ttfreader';
|
||||
import TTFWriter from './ttf/ttfwriter';
|
||||
import ttf2eot from './ttf/ttf2eot';
|
||||
import eot2ttf from './ttf/eot2ttf';
|
||||
import ttf2woff from './ttf/ttf2woff';
|
||||
import woff2ttf from './ttf/woff2ttf';
|
||||
import ttf2svg from './ttf/ttf2svg';
|
||||
import svg2ttfobject from './ttf/svg2ttfobject';
|
||||
import Reader from './ttf/reader';
|
||||
import Writer from './ttf/writer';
|
||||
import OTFReader from './ttf/otfreader';
|
||||
import otf2ttfobject from './ttf/otf2ttfobject';
|
||||
import ttf2base64 from './ttf/ttf2base64';
|
||||
import ttf2icon from './ttf/ttf2icon';
|
||||
import ttftowoff2 from './ttf/ttftowoff2';
|
||||
import woff2tottf from './ttf/woff2tottf';
|
||||
import woff2 from '../woff2/index';
|
||||
import bufferUtil from './nodejs/buffer';
|
||||
|
||||
export {
|
||||
createFont,
|
||||
Font,
|
||||
TTF,
|
||||
TTFReader,
|
||||
TTFWriter,
|
||||
ttf2eot,
|
||||
eot2ttf,
|
||||
ttf2woff,
|
||||
woff2ttf,
|
||||
ttf2svg,
|
||||
svg2ttfobject,
|
||||
Reader,
|
||||
Writer,
|
||||
OTFReader,
|
||||
otf2ttfobject,
|
||||
ttf2base64,
|
||||
ttf2icon,
|
||||
ttftowoff2,
|
||||
woff2tottf,
|
||||
woff2,
|
||||
};
|
||||
|
||||
export const toArrayBuffer = bufferUtil.toArrayBuffer;
|
||||
export const toBuffer = bufferUtil.toBuffer;
|
||||
|
||||
export default {
|
||||
createFont,
|
||||
Font,
|
||||
TTF,
|
||||
TTFReader,
|
||||
TTFWriter,
|
||||
ttf2eot,
|
||||
eot2ttf,
|
||||
ttf2woff,
|
||||
woff2ttf,
|
||||
ttf2svg,
|
||||
svg2ttfobject,
|
||||
Reader,
|
||||
Writer,
|
||||
OTFReader,
|
||||
otf2ttfobject,
|
||||
ttf2base64,
|
||||
ttf2icon,
|
||||
ttftowoff2,
|
||||
woff2tottf,
|
||||
woff2,
|
||||
toArrayBuffer: bufferUtil.toArrayBuffer,
|
||||
toBuffer: bufferUtil.toBuffer,
|
||||
};
|
||||
61
vendor/fonteditor-core/src/main.js
vendored
61
vendor/fonteditor-core/src/main.js
vendored
@ -1,61 +0,0 @@
|
||||
/**
|
||||
* @file 主函数
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
import {Font, createFont} from './ttf/font';
|
||||
import TTF from './ttf/ttf';
|
||||
import TTFReader from './ttf/ttfreader';
|
||||
import TTFWriter from './ttf/ttfwriter';
|
||||
import ttf2eot from './ttf/ttf2eot';
|
||||
import eot2ttf from './ttf/eot2ttf';
|
||||
import ttf2woff from './ttf/ttf2woff';
|
||||
import woff2ttf from './ttf/woff2ttf';
|
||||
import ttf2svg from './ttf/ttf2svg';
|
||||
import svg2ttfobject from './ttf/svg2ttfobject';
|
||||
import Reader from './ttf/reader';
|
||||
import Writer from './ttf/writer';
|
||||
import OTFReader from './ttf/otfreader';
|
||||
import otf2ttfobject from './ttf/otf2ttfobject';
|
||||
import ttf2base64 from './ttf/ttf2base64';
|
||||
import ttf2icon from './ttf/ttf2icon';
|
||||
import ttftowoff2 from './ttf/ttftowoff2';
|
||||
import woff2tottf from './ttf/woff2tottf';
|
||||
import woff2 from '../woff2/index';
|
||||
import bufferUtil from './nodejs/buffer';
|
||||
|
||||
const modules = {
|
||||
createFont,
|
||||
Font,
|
||||
TTF,
|
||||
TTFReader,
|
||||
TTFWriter,
|
||||
ttf2eot,
|
||||
eot2ttf,
|
||||
ttf2woff,
|
||||
woff2ttf,
|
||||
ttf2svg,
|
||||
svg2ttfobject,
|
||||
Reader,
|
||||
Writer,
|
||||
OTFReader,
|
||||
otf2ttfobject,
|
||||
ttf2base64,
|
||||
ttf2icon,
|
||||
ttftowoff2,
|
||||
woff2tottf,
|
||||
woff2,
|
||||
toArrayBuffer: bufferUtil.toArrayBuffer,
|
||||
toBuffer: bufferUtil.toBuffer,
|
||||
};
|
||||
|
||||
// Export named exports for ESM
|
||||
export { Font, woff2, createFont };
|
||||
|
||||
// Export default object
|
||||
export default modules;
|
||||
|
||||
if (typeof exports !== 'undefined') {
|
||||
// eslint-disable-next-line import/no-commonjs
|
||||
module.exports = modules;
|
||||
}
|
||||
@ -1,93 +0,0 @@
|
||||
/**
|
||||
* @file 三次贝塞尔转二次贝塞尔
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*
|
||||
* references:
|
||||
* https://github.com/search?utf8=%E2%9C%93&q=svg2ttf
|
||||
* http://www.caffeineowl.com/graphics/2d/vectorial/cubic2quad01.html
|
||||
*
|
||||
*/
|
||||
|
||||
function toQuad(p1, c1, c2, p2) {
|
||||
// Quad control point is (3*c2 - p2 + 3*c1 - p1)/4
|
||||
const x = (3 * c2.x - p2.x + 3 * c1.x - p1.x) / 4;
|
||||
const y = (3 * c2.y - p2.y + 3 * c1.y - p1.y) / 4;
|
||||
return [
|
||||
p1,
|
||||
{x, y},
|
||||
p2
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 三次贝塞尔转二次贝塞尔
|
||||
*
|
||||
* @param {Object} p1 开始点
|
||||
* @param {Object} c1 控制点1
|
||||
* @param {Object} c2 控制点2
|
||||
* @param {Object} p2 结束点
|
||||
* @return {Array} 二次贝塞尔控制点
|
||||
*/
|
||||
export default function bezierCubic2Q2(p1, c1, c2, p2) {
|
||||
|
||||
// 判断极端情况,控制点和起止点一样
|
||||
if (p1.x === c1.x && p1.y === c1.y && c2.x === p2.x && c2.y === p2.y) {
|
||||
return [
|
||||
[
|
||||
p1,
|
||||
{
|
||||
x: (p1.x + p2.x) / 2,
|
||||
y: (p1.y + p2.y) / 2
|
||||
},
|
||||
p2
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
const mx = p2.x - 3 * c2.x + 3 * c1.x - p1.x;
|
||||
const my = p2.y - 3 * c2.y + 3 * c1.y - p1.y;
|
||||
|
||||
// control points near
|
||||
if (mx * mx + my * my <= 4) {
|
||||
return [
|
||||
toQuad(p1, c1, c2, p2)
|
||||
];
|
||||
}
|
||||
|
||||
// Split to 2 qubic beziers by midpoints
|
||||
// (p2 + 3*c2 + 3*c1 + p1)/8
|
||||
const mp = {
|
||||
x: (p2.x + 3 * c2.x + 3 * c1.x + p1.x) / 8,
|
||||
y: (p2.y + 3 * c2.y + 3 * c1.y + p1.y) / 8
|
||||
};
|
||||
|
||||
return [
|
||||
toQuad(
|
||||
p1,
|
||||
{
|
||||
x: (p1.x + c1.x) / 2,
|
||||
y: (p1.y + c1.y) / 2
|
||||
|
||||
},
|
||||
{
|
||||
x: (p1.x + 2 * c1.x + c2.x) / 4,
|
||||
y: (p1.y + 2 * c1.y + c2.y) / 4
|
||||
},
|
||||
mp
|
||||
),
|
||||
toQuad(
|
||||
mp,
|
||||
{
|
||||
x: (p2.x + c1.x + 2 * c2.x) / 4,
|
||||
y: (p2.y + c1.y + 2 * c2.y) / 4
|
||||
|
||||
},
|
||||
{
|
||||
x: (p2.x + c2.x) / 2,
|
||||
y: (p2.y + c2.y) / 2
|
||||
},
|
||||
p2
|
||||
)
|
||||
];
|
||||
}
|
||||
43
vendor/fonteditor-core/src/nodejs/buffer.js
vendored
43
vendor/fonteditor-core/src/nodejs/buffer.js
vendored
@ -1,43 +0,0 @@
|
||||
/**
|
||||
* @file Buffer和ArrayBuffer转换
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
/* eslint-disable no-undef */
|
||||
export default {
|
||||
|
||||
/**
|
||||
* Buffer转换成ArrayBuffer
|
||||
*
|
||||
* @param {Buffer} buffer 缓冲数组
|
||||
* @return {ArrayBuffer}
|
||||
*/
|
||||
toArrayBuffer(buffer) {
|
||||
const length = buffer.length;
|
||||
const view = new DataView(new ArrayBuffer(length), 0, length);
|
||||
for (let i = 0, l = length; i < l; i++) {
|
||||
view.setUint8(i, buffer[i], false);
|
||||
}
|
||||
return view.buffer;
|
||||
},
|
||||
|
||||
/**
|
||||
* ArrayBuffer转换成Buffer
|
||||
*
|
||||
* @param {ArrayBuffer} arrayBuffer 缓冲数组
|
||||
* @return {Buffer}
|
||||
*/
|
||||
toBuffer(arrayBuffer) {
|
||||
if (Array.isArray(arrayBuffer)) {
|
||||
return Buffer.from(arrayBuffer);
|
||||
}
|
||||
|
||||
const length = arrayBuffer.byteLength;
|
||||
const view = new DataView(arrayBuffer, 0, length);
|
||||
const buffer = Buffer.alloc(length);
|
||||
for (let i = 0, l = length; i < l; i++) {
|
||||
buffer[i] = view.getUint8(i, false);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
};
|
||||
19
vendor/fonteditor-core/src/ttf/data/default.js
vendored
19
vendor/fonteditor-core/src/ttf/data/default.js
vendored
@ -1,19 +0,0 @@
|
||||
/**
|
||||
* @file 默认的ttf字体配置
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
export default {
|
||||
// 默认的字体编码
|
||||
fontId: 'fonteditor',
|
||||
|
||||
// 默认的名字集合
|
||||
name: {
|
||||
// 默认的字体家族
|
||||
fontFamily: 'fonteditor',
|
||||
fontSubFamily: 'Medium',
|
||||
uniqueSubFamily: 'FontEditor 1.0 : fonteditor',
|
||||
version: 'Version 1.0; FontEditor (v1.0)',
|
||||
postScriptName: 'fonteditor'
|
||||
}
|
||||
};
|
||||
181
vendor/fonteditor-core/src/ttf/data/empty.js
vendored
181
vendor/fonteditor-core/src/ttf/data/empty.js
vendored
@ -1,181 +0,0 @@
|
||||
/**
|
||||
* @file 空的ttf格式json对象
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
/* eslint-disable */
|
||||
export default {
|
||||
"version": 1,
|
||||
"numTables": 10,
|
||||
"searchRange": 128,
|
||||
"entrySelector": 3,
|
||||
"rangeShift": 64,
|
||||
"head": {
|
||||
"version": 1,
|
||||
"fontRevision": 1,
|
||||
"checkSumAdjustment": 0,
|
||||
"magickNumber": 1594834165,
|
||||
"flags": 11,
|
||||
"unitsPerEm": 1024,
|
||||
"created": 1428940800000,
|
||||
"modified": 1428940800000,
|
||||
"xMin": 34,
|
||||
"yMin": 0,
|
||||
"xMax": 306,
|
||||
"yMax": 682,
|
||||
"macStyle": 0,
|
||||
"lowestRecPPEM": 8,
|
||||
"fontDirectionHint": 2,
|
||||
"indexToLocFormat": 0,
|
||||
"glyphDataFormat": 0
|
||||
},
|
||||
"glyf": [{
|
||||
"contours": [
|
||||
[{
|
||||
"x": 34,
|
||||
"y": 0,
|
||||
"onCurve": true
|
||||
}, {
|
||||
"x": 34,
|
||||
"y": 682,
|
||||
"onCurve": true
|
||||
}, {
|
||||
"x": 306,
|
||||
"y": 682,
|
||||
"onCurve": true
|
||||
}, {
|
||||
"x": 306,
|
||||
"y": 0,
|
||||
"onCurve": true
|
||||
}],
|
||||
[{
|
||||
"x": 68,
|
||||
"y": 34,
|
||||
"onCurve": true
|
||||
}, {
|
||||
"x": 272,
|
||||
"y": 34,
|
||||
"onCurve": true
|
||||
}, {
|
||||
"x": 272,
|
||||
"y": 648,
|
||||
"onCurve": true
|
||||
}, {
|
||||
"x": 68,
|
||||
"y": 648,
|
||||
"onCurve": true
|
||||
}]
|
||||
],
|
||||
"xMin": 34,
|
||||
"yMin": 0,
|
||||
"xMax": 306,
|
||||
"yMax": 682,
|
||||
"advanceWidth": 374,
|
||||
"leftSideBearing": 34,
|
||||
"name": ".notdef"
|
||||
}],
|
||||
"cmap": {},
|
||||
"name": {
|
||||
"fontFamily": "fonteditor",
|
||||
"fontSubFamily": "Medium",
|
||||
"uniqueSubFamily": "FontEditor 1.0 : fonteditor",
|
||||
"version": "Version 1.0 ; FontEditor (v0.0.1)",
|
||||
"postScriptName": "fonteditor",
|
||||
"fullName": "fonteditor"
|
||||
},
|
||||
"hhea": {
|
||||
"version": 1,
|
||||
"ascent": 812,
|
||||
"descent": -212,
|
||||
"lineGap": 92,
|
||||
"advanceWidthMax": 374,
|
||||
"minLeftSideBearing": 34,
|
||||
"minRightSideBearing": 68,
|
||||
"xMaxExtent": 306,
|
||||
"caretSlopeRise": 1,
|
||||
"caretSlopeRun": 0,
|
||||
"caretOffset": 0,
|
||||
"reserved0": 0,
|
||||
"reserved1": 0,
|
||||
"reserved2": 0,
|
||||
"reserved3": 0,
|
||||
"metricDataFormat": 0,
|
||||
"numOfLongHorMetrics": 1
|
||||
},
|
||||
"post": {
|
||||
"italicAngle": 0,
|
||||
"postoints": 65411,
|
||||
"underlinePosition": 50,
|
||||
"underlineThickness": 0,
|
||||
"isFixedPitch": 0,
|
||||
"minMemType42": 0,
|
||||
"maxMemType42": 0,
|
||||
"minMemType1": 0,
|
||||
"maxMemType1": 1,
|
||||
"format": 2
|
||||
},
|
||||
"maxp": {
|
||||
"version": 1.0,
|
||||
"numGlyphs": 0,
|
||||
"maxPoints": 0,
|
||||
"maxContours": 0,
|
||||
"maxCompositePoints": 0,
|
||||
"maxCompositeContours": 0,
|
||||
"maxZones": 0,
|
||||
"maxTwilightPoints": 0,
|
||||
"maxStorage": 0,
|
||||
"maxFunctionDefs": 0,
|
||||
"maxStackElements": 0,
|
||||
"maxSizeOfInstructions": 0,
|
||||
"maxComponentElements": 0,
|
||||
"maxComponentDepth": 0
|
||||
},
|
||||
"OS/2": {
|
||||
"version": 4,
|
||||
"xAvgCharWidth": 1031,
|
||||
"usWeightClass": 400,
|
||||
"usWidthClass": 5,
|
||||
"fsType": 0,
|
||||
"ySubscriptXSize": 665,
|
||||
"ySubscriptYSize": 716,
|
||||
"ySubscriptXOffset": 0,
|
||||
"ySubscriptYOffset": 143,
|
||||
"ySuperscriptXSize": 665,
|
||||
"ySuperscriptYSize": 716,
|
||||
"ySuperscriptXOffset": 0,
|
||||
"ySuperscriptYOffset": 491,
|
||||
"yStrikeoutSize": 51,
|
||||
"yStrikeoutPosition": 265,
|
||||
"sFamilyClass": 0,
|
||||
"bFamilyType": 2,
|
||||
"bSerifStyle": 0,
|
||||
"bWeight": 6,
|
||||
"bProportion": 3,
|
||||
"bContrast": 0,
|
||||
"bStrokeVariation": 0,
|
||||
"bArmStyle": 0,
|
||||
"bLetterform": 0,
|
||||
"bMidline": 0,
|
||||
"bXHeight": 0,
|
||||
"ulUnicodeRange1": 1,
|
||||
"ulUnicodeRange2": 268435456,
|
||||
"ulUnicodeRange3": 0,
|
||||
"ulUnicodeRange4": 0,
|
||||
"achVendID": "PfEd",
|
||||
"fsSelection": 192,
|
||||
"usFirstCharIndex": 65535,
|
||||
"usLastCharIndex": -1,
|
||||
"sTypoAscender": 812,
|
||||
"sTypoDescender": -212,
|
||||
"sTypoLineGap": 92,
|
||||
"usWinAscent": 812,
|
||||
"usWinDescent": 212,
|
||||
"ulCodePageRange1": 1,
|
||||
"ulCodePageRange2": 0,
|
||||
"sxHeight": 792,
|
||||
"sCapHeight": 0,
|
||||
"usDefaultChar": 0,
|
||||
"usBreakChar": 32,
|
||||
"usMaxContext": 1
|
||||
}
|
||||
};
|
||||
@ -1,23 +0,0 @@
|
||||
/**
|
||||
* @file 复合图元标记位
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*
|
||||
* 复合图元标记位
|
||||
* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6glyf.html
|
||||
*/
|
||||
|
||||
export default {
|
||||
ARG_1_AND_2_ARE_WORDS: 0x01,
|
||||
ARGS_ARE_XY_VALUES: 0x02,
|
||||
ROUND_XY_TO_GRID: 0x04,
|
||||
WE_HAVE_A_SCALE: 0x08,
|
||||
RESERVED: 0x10,
|
||||
MORE_COMPONENTS: 0x20,
|
||||
WE_HAVE_AN_X_AND_Y_SCALE: 0x40,
|
||||
WE_HAVE_A_TWO_BY_TWO: 0x80,
|
||||
WE_HAVE_INSTRUCTIONS: 0x100,
|
||||
USE_MY_METRICS: 0x200,
|
||||
OVERLAP_COMPOUND: 0x400,
|
||||
SCALED_COMPONENT_OFFSET: 0x800,
|
||||
UNSCALED_COMPONENT_OFFSET: 0x1000
|
||||
};
|
||||
26
vendor/fonteditor-core/src/ttf/enum/encoding.js
vendored
26
vendor/fonteditor-core/src/ttf/enum/encoding.js
vendored
@ -1,26 +0,0 @@
|
||||
/**
|
||||
* @file Unicode Platform-specific Encoding Identifiers
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
// mac encoding id
|
||||
export const mac = {
|
||||
'Default': 0, // default use
|
||||
'Version1.1': 1,
|
||||
'ISO10646': 2,
|
||||
'UnicodeBMP': 3,
|
||||
'UnicodenonBMP': 4,
|
||||
'UnicodeVariationSequences': 5,
|
||||
'FullUnicodecoverage': 6
|
||||
};
|
||||
|
||||
// windows encoding id
|
||||
export const win = {
|
||||
Symbol: 0,
|
||||
UCS2: 1, // default use
|
||||
ShiftJIS: 2,
|
||||
PRC: 3,
|
||||
BigFive: 4,
|
||||
Johab: 5,
|
||||
UCS4: 6
|
||||
};
|
||||
|
||||
18
vendor/fonteditor-core/src/ttf/enum/glyFlag.js
vendored
18
vendor/fonteditor-core/src/ttf/enum/glyFlag.js
vendored
@ -1,18 +0,0 @@
|
||||
/**
|
||||
* @file 轮廓标记位
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*
|
||||
* see:
|
||||
* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6glyf.html
|
||||
*/
|
||||
|
||||
export default {
|
||||
ONCURVE: 0x01, // on curve ,off curve
|
||||
XSHORT: 0x02, // x-Short Vector
|
||||
YSHORT: 0x04, // y-Short Vector
|
||||
REPEAT: 0x08, // next byte is flag repeat count
|
||||
XSAME: 0x10, // This x is same (Positive x-Short vector)
|
||||
YSAME: 0x20, // This y is same (Positive y-Short vector)
|
||||
Reserved1: 0x40,
|
||||
Reserved2: 0x80
|
||||
};
|
||||
36
vendor/fonteditor-core/src/ttf/enum/nameId.js
vendored
36
vendor/fonteditor-core/src/ttf/enum/nameId.js
vendored
@ -1,36 +0,0 @@
|
||||
/**
|
||||
* @file ttf `name`编码表
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
const nameId = {
|
||||
0: 'copyright',
|
||||
1: 'fontFamily',
|
||||
2: 'fontSubFamily',
|
||||
3: 'uniqueSubFamily',
|
||||
4: 'fullName',
|
||||
5: 'version',
|
||||
6: 'postScriptName',
|
||||
7: 'tradeMark',
|
||||
8: 'manufacturer',
|
||||
9: 'designer',
|
||||
10: 'description',
|
||||
11: 'urlOfFontVendor',
|
||||
12: 'urlOfFontDesigner',
|
||||
13: 'licence',
|
||||
14: 'urlOfLicence',
|
||||
16: 'preferredFamily',
|
||||
17: 'preferredSubFamily',
|
||||
18: 'compatibleFull',
|
||||
19: 'sampleText'
|
||||
};
|
||||
|
||||
// 反转names
|
||||
const nameIdHash = {};
|
||||
Object.keys(nameId).forEach(id => {
|
||||
nameIdHash[nameId[id]] = +id;
|
||||
});
|
||||
|
||||
nameId.names = nameIdHash;
|
||||
|
||||
export default nameId;
|
||||
154
vendor/fonteditor-core/src/ttf/enum/panose.js
vendored
154
vendor/fonteditor-core/src/ttf/enum/panose.js
vendored
@ -1,154 +0,0 @@
|
||||
/**
|
||||
* @file 字体外观分类器
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*
|
||||
* @see:
|
||||
* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6OS2.html
|
||||
*/
|
||||
|
||||
export default {
|
||||
|
||||
bFamilyType: [
|
||||
'Any',
|
||||
'No Fit',
|
||||
'Text and Display',
|
||||
'Script',
|
||||
'Decorative',
|
||||
'Pictorial'
|
||||
],
|
||||
|
||||
bSerifStyle: [
|
||||
'Any',
|
||||
'No Fit',
|
||||
'Cove',
|
||||
'Obtuse Cove',
|
||||
'Square Cove',
|
||||
'Obtuse Square Cove',
|
||||
'Square',
|
||||
'Thin',
|
||||
'Bone',
|
||||
'Exaggerated',
|
||||
'Triangle',
|
||||
'Normal Sans',
|
||||
'Obtuse Sans',
|
||||
'Perp Sans',
|
||||
'Flared',
|
||||
'Rounded'
|
||||
],
|
||||
|
||||
bWeight: [
|
||||
'Any',
|
||||
'No Fit',
|
||||
'Very Light',
|
||||
'Light',
|
||||
'Thin',
|
||||
'Book',
|
||||
'Medium',
|
||||
'Demi',
|
||||
'Bold',
|
||||
'Heavy',
|
||||
'Black',
|
||||
'Nord'
|
||||
],
|
||||
|
||||
bProportion: [
|
||||
'Any',
|
||||
'No Fit',
|
||||
'Old Style',
|
||||
'Modern',
|
||||
'Even Width',
|
||||
'Expanded',
|
||||
'Condensed',
|
||||
'Very Expanded',
|
||||
'Very Condensed',
|
||||
'Monospaced'
|
||||
],
|
||||
|
||||
bContrast: [
|
||||
'Any',
|
||||
'No Fit',
|
||||
'None',
|
||||
'Very Low',
|
||||
'Low',
|
||||
'Medium Low',
|
||||
'Medium',
|
||||
'Medium High',
|
||||
'High',
|
||||
'Very High'
|
||||
],
|
||||
|
||||
bStrokeVariation: [
|
||||
'Any',
|
||||
'No Fit',
|
||||
'Gradual/Diagonal',
|
||||
'Gradual/Transitional',
|
||||
'Gradual/Vertical',
|
||||
'Gradual/Horizontal',
|
||||
'Rapid/Vertical',
|
||||
'Rapid/Horizontal',
|
||||
'Instant/Vertical'
|
||||
],
|
||||
|
||||
bArmStyle: [
|
||||
'Any',
|
||||
'No Fit',
|
||||
'Straight Arms/Horizontal',
|
||||
'Straight Arms/Wedge',
|
||||
'Straight Arms/Vertical',
|
||||
'Straight Arms/Single Serif',
|
||||
'Straight Arms/Double Serif',
|
||||
'Non-Straight Arms/Horizontal',
|
||||
'Non-Straight Arms/Wedge',
|
||||
'Non-Straight Arms/Vertical',
|
||||
'Non-Straight Arms/Single Serif',
|
||||
'Non-Straight Arms/Double Serif'
|
||||
],
|
||||
|
||||
|
||||
bLetterform: [
|
||||
'Any',
|
||||
'No Fit',
|
||||
'Normal/Contact',
|
||||
'Normal/Weighted',
|
||||
'Normal/Boxed',
|
||||
'Normal/Flattened',
|
||||
'Normal/Rounded',
|
||||
'Normal/Off Center',
|
||||
'Normal/Square',
|
||||
'Oblique/Contact',
|
||||
'Oblique/Weighted',
|
||||
'Oblique/Boxed',
|
||||
'Oblique/Flattened',
|
||||
'Oblique/Rounded',
|
||||
'Oblique/Off Center',
|
||||
'Oblique/Square'
|
||||
],
|
||||
|
||||
bMidline: [
|
||||
'Any',
|
||||
'No Fit',
|
||||
'Standard/Trimmed',
|
||||
'Standard/Pointed',
|
||||
'Standard/Serifed',
|
||||
'High/Trimmed',
|
||||
'High/Pointed',
|
||||
'High/Serifed',
|
||||
'Constant/Trimmed',
|
||||
'Constant/Pointed',
|
||||
'Constant/Serifed',
|
||||
'Low/Trimmed',
|
||||
'Low/Pointed',
|
||||
'Low/Serifed'
|
||||
],
|
||||
|
||||
bXHeight: [
|
||||
'Any',
|
||||
'No Fit',
|
||||
'Constant/Small',
|
||||
'Constant/Standard',
|
||||
'Constant/Large',
|
||||
'Ducking/Small',
|
||||
'Ducking/Standard',
|
||||
'Ducking/Large'
|
||||
]
|
||||
};
|
||||
11
vendor/fonteditor-core/src/ttf/enum/platform.js
vendored
11
vendor/fonteditor-core/src/ttf/enum/platform.js
vendored
@ -1,11 +0,0 @@
|
||||
/**
|
||||
* @file 字体所属平台
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
export default{
|
||||
Unicode: 0,
|
||||
Macintosh: 1, // mac
|
||||
reserved: 2,
|
||||
Microsoft: 3 // win
|
||||
};
|
||||
268
vendor/fonteditor-core/src/ttf/enum/postName.js
vendored
268
vendor/fonteditor-core/src/ttf/enum/postName.js
vendored
@ -1,268 +0,0 @@
|
||||
/**
|
||||
* @file Mac glyf命名表
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*
|
||||
* see:
|
||||
* http://www.microsoft.com/typography/otspec/WGL4.htm
|
||||
*/
|
||||
|
||||
export default {
|
||||
0: '.notdef',
|
||||
1: '.null',
|
||||
2: 'nonmarkingreturn',
|
||||
3: 'space',
|
||||
4: 'exclam',
|
||||
5: 'quotedbl',
|
||||
6: 'numbersign',
|
||||
7: 'dollar',
|
||||
8: 'percent',
|
||||
9: 'ampersand',
|
||||
10: 'quotesingle',
|
||||
11: 'parenleft',
|
||||
12: 'parenright',
|
||||
13: 'asterisk',
|
||||
14: 'plus',
|
||||
15: 'comma',
|
||||
16: 'hyphen',
|
||||
17: 'period',
|
||||
18: 'slash',
|
||||
19: 'zero',
|
||||
20: 'one',
|
||||
21: 'two',
|
||||
22: 'three',
|
||||
23: 'four',
|
||||
24: 'five',
|
||||
25: 'six',
|
||||
26: 'seven',
|
||||
27: 'eight',
|
||||
28: 'nine',
|
||||
29: 'colon',
|
||||
30: 'semicolon',
|
||||
31: 'less',
|
||||
32: 'equal',
|
||||
33: 'greater',
|
||||
34: 'question',
|
||||
35: 'at',
|
||||
36: 'A',
|
||||
37: 'B',
|
||||
38: 'C',
|
||||
39: 'D',
|
||||
40: 'E',
|
||||
41: 'F',
|
||||
42: 'G',
|
||||
43: 'H',
|
||||
44: 'I',
|
||||
45: 'J',
|
||||
46: 'K',
|
||||
47: 'L',
|
||||
48: 'M',
|
||||
49: 'N',
|
||||
50: 'O',
|
||||
51: 'P',
|
||||
52: 'Q',
|
||||
53: 'R',
|
||||
54: 'S',
|
||||
55: 'T',
|
||||
56: 'U',
|
||||
57: 'V',
|
||||
58: 'W',
|
||||
59: 'X',
|
||||
60: 'Y',
|
||||
61: 'Z',
|
||||
62: 'bracketleft',
|
||||
63: 'backslash',
|
||||
64: 'bracketright',
|
||||
65: 'asciicircum',
|
||||
66: 'underscore',
|
||||
67: 'grave',
|
||||
68: 'a',
|
||||
69: 'b',
|
||||
70: 'c',
|
||||
71: 'd',
|
||||
72: 'e',
|
||||
73: 'f',
|
||||
74: 'g',
|
||||
75: 'h',
|
||||
76: 'i',
|
||||
77: 'j',
|
||||
78: 'k',
|
||||
79: 'l',
|
||||
80: 'm',
|
||||
81: 'n',
|
||||
82: 'o',
|
||||
83: 'p',
|
||||
84: 'q',
|
||||
85: 'r',
|
||||
86: 's',
|
||||
87: 't',
|
||||
88: 'u',
|
||||
89: 'v',
|
||||
90: 'w',
|
||||
91: 'x',
|
||||
92: 'y',
|
||||
93: 'z',
|
||||
94: 'braceleft',
|
||||
95: 'bar',
|
||||
96: 'braceright',
|
||||
97: 'asciitilde',
|
||||
98: 'Adieresis',
|
||||
99: 'Aring',
|
||||
100: 'Ccedilla',
|
||||
101: 'Eacute',
|
||||
102: 'Ntilde',
|
||||
103: 'Odieresis',
|
||||
104: 'Udieresis',
|
||||
105: 'aacute',
|
||||
106: 'agrave',
|
||||
107: 'acircumflex',
|
||||
108: 'adieresis',
|
||||
109: 'atilde',
|
||||
110: 'aring',
|
||||
111: 'ccedilla',
|
||||
112: 'eacute',
|
||||
113: 'egrave',
|
||||
114: 'ecircumflex',
|
||||
115: 'edieresis',
|
||||
116: 'iacute',
|
||||
117: 'igrave',
|
||||
118: 'icircumflex',
|
||||
119: 'idieresis',
|
||||
120: 'ntilde',
|
||||
121: 'oacute',
|
||||
122: 'ograve',
|
||||
123: 'ocircumflex',
|
||||
124: 'odieresis',
|
||||
125: 'otilde',
|
||||
126: 'uacute',
|
||||
127: 'ugrave',
|
||||
128: 'ucircumflex',
|
||||
129: 'udieresis',
|
||||
130: 'dagger',
|
||||
131: 'degree',
|
||||
132: 'cent',
|
||||
133: 'sterling',
|
||||
134: 'section',
|
||||
135: 'bullet',
|
||||
136: 'paragraph',
|
||||
137: 'germandbls',
|
||||
138: 'registered',
|
||||
139: 'copyright',
|
||||
140: 'trademark',
|
||||
141: 'acute',
|
||||
142: 'dieresis',
|
||||
143: 'notequal',
|
||||
144: 'AE',
|
||||
145: 'Oslash',
|
||||
146: 'infinity',
|
||||
147: 'plusminus',
|
||||
148: 'lessequal',
|
||||
149: 'greaterequal',
|
||||
150: 'yen',
|
||||
151: 'mu',
|
||||
152: 'partialdiff',
|
||||
153: 'summation',
|
||||
154: 'product',
|
||||
155: 'pi',
|
||||
156: 'integral',
|
||||
157: 'ordfeminine',
|
||||
158: 'ordmasculine',
|
||||
159: 'Omega',
|
||||
160: 'ae',
|
||||
161: 'oslash',
|
||||
162: 'questiondown',
|
||||
163: 'exclamdown',
|
||||
164: 'logicalnot',
|
||||
165: 'radical',
|
||||
166: 'florin',
|
||||
167: 'approxequal',
|
||||
168: 'Delta',
|
||||
169: 'guillemotleft',
|
||||
170: 'guillemotright',
|
||||
171: 'ellipsis',
|
||||
172: 'nonbreakingspace',
|
||||
173: 'Agrave',
|
||||
174: 'Atilde',
|
||||
175: 'Otilde',
|
||||
176: 'OE',
|
||||
177: 'oe',
|
||||
178: 'endash',
|
||||
179: 'emdash',
|
||||
180: 'quotedblleft',
|
||||
181: 'quotedblright',
|
||||
182: 'quoteleft',
|
||||
183: 'quoteright',
|
||||
184: 'divide',
|
||||
185: 'lozenge',
|
||||
186: 'ydieresis',
|
||||
187: 'Ydieresis',
|
||||
188: 'fraction',
|
||||
189: 'currency',
|
||||
190: 'guilsinglleft',
|
||||
191: 'guilsinglright',
|
||||
192: 'fi',
|
||||
193: 'fl',
|
||||
194: 'daggerdbl',
|
||||
195: 'periodcentered',
|
||||
196: 'quotesinglbase',
|
||||
197: 'quotedblbase',
|
||||
198: 'perthousand',
|
||||
199: 'Acircumflex',
|
||||
200: 'Ecircumflex',
|
||||
201: 'Aacute',
|
||||
202: 'Edieresis',
|
||||
203: 'Egrave',
|
||||
204: 'Iacute',
|
||||
205: 'Icircumflex',
|
||||
206: 'Idieresis',
|
||||
207: 'Igrave',
|
||||
208: 'Oacute',
|
||||
209: 'Ocircumflex',
|
||||
210: 'apple',
|
||||
211: 'Ograve',
|
||||
212: 'Uacute',
|
||||
213: 'Ucircumflex',
|
||||
214: 'Ugrave',
|
||||
215: 'dotlessi',
|
||||
216: 'circumflex',
|
||||
217: 'tilde',
|
||||
218: 'macron',
|
||||
219: 'breve',
|
||||
220: 'dotaccent',
|
||||
221: 'ring',
|
||||
222: 'cedilla',
|
||||
223: 'hungarumlaut',
|
||||
224: 'ogonek',
|
||||
225: 'caron',
|
||||
226: 'Lslash',
|
||||
227: 'lslash',
|
||||
228: 'Scaron',
|
||||
229: 'scaron',
|
||||
230: 'Zcaron',
|
||||
231: 'zcaron',
|
||||
232: 'brokenbar',
|
||||
233: 'Eth',
|
||||
234: 'eth',
|
||||
235: 'Yacute',
|
||||
236: 'yacute',
|
||||
237: 'Thorn',
|
||||
238: 'thorn',
|
||||
239: 'minus',
|
||||
240: 'multiply',
|
||||
241: 'onesuperior',
|
||||
242: 'twosuperior',
|
||||
243: 'threesuperior',
|
||||
244: 'onehalf',
|
||||
245: 'onequarter',
|
||||
246: 'threequarters',
|
||||
247: 'franc',
|
||||
248: 'Gbreve',
|
||||
249: 'gbreve',
|
||||
250: 'Idotaccent',
|
||||
251: 'Scedilla',
|
||||
252: 'scedilla',
|
||||
253: 'Cacute',
|
||||
254: 'cacute',
|
||||
255: 'Ccaron',
|
||||
256: 'ccaron',
|
||||
257: 'dcroat'
|
||||
};
|
||||
298
vendor/fonteditor-core/src/ttf/enum/unicodeName.js
vendored
298
vendor/fonteditor-core/src/ttf/enum/unicodeName.js
vendored
@ -1,298 +0,0 @@
|
||||
/**
|
||||
* @file unicode 编码与postName对照表
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*
|
||||
* see:
|
||||
* http://www.microsoft.com/typography/otspec/WGL4.htm
|
||||
*/
|
||||
|
||||
export default {
|
||||
0: 1,
|
||||
1: 1,
|
||||
2: 1,
|
||||
3: 1,
|
||||
4: 1,
|
||||
5: 1,
|
||||
6: 1,
|
||||
7: 1,
|
||||
8: 1,
|
||||
9: 2,
|
||||
10: 1,
|
||||
11: 1,
|
||||
12: 1,
|
||||
13: 2,
|
||||
14: 1,
|
||||
15: 1,
|
||||
16: 1,
|
||||
17: 1,
|
||||
18: 1,
|
||||
19: 1,
|
||||
20: 1,
|
||||
21: 1,
|
||||
22: 1,
|
||||
23: 1,
|
||||
24: 1,
|
||||
25: 1,
|
||||
26: 1,
|
||||
27: 1,
|
||||
28: 1,
|
||||
29: 1,
|
||||
30: 1,
|
||||
31: 1,
|
||||
32: 3,
|
||||
33: 4,
|
||||
34: 5,
|
||||
35: 6,
|
||||
36: 7,
|
||||
37: 8,
|
||||
38: 9,
|
||||
39: 10,
|
||||
40: 11,
|
||||
41: 12,
|
||||
42: 13,
|
||||
43: 14,
|
||||
44: 15,
|
||||
45: 16,
|
||||
46: 17,
|
||||
47: 18,
|
||||
48: 19,
|
||||
49: 20,
|
||||
50: 21,
|
||||
51: 22,
|
||||
52: 23,
|
||||
53: 24,
|
||||
54: 25,
|
||||
55: 26,
|
||||
56: 27,
|
||||
57: 28,
|
||||
58: 29,
|
||||
59: 30,
|
||||
60: 31,
|
||||
61: 32,
|
||||
62: 33,
|
||||
63: 34,
|
||||
64: 35,
|
||||
65: 36,
|
||||
66: 37,
|
||||
67: 38,
|
||||
68: 39,
|
||||
69: 40,
|
||||
70: 41,
|
||||
71: 42,
|
||||
72: 43,
|
||||
73: 44,
|
||||
74: 45,
|
||||
75: 46,
|
||||
76: 47,
|
||||
77: 48,
|
||||
78: 49,
|
||||
79: 50,
|
||||
80: 51,
|
||||
81: 52,
|
||||
82: 53,
|
||||
83: 54,
|
||||
84: 55,
|
||||
85: 56,
|
||||
86: 57,
|
||||
87: 58,
|
||||
88: 59,
|
||||
89: 60,
|
||||
90: 61,
|
||||
91: 62,
|
||||
92: 63,
|
||||
93: 64,
|
||||
94: 65,
|
||||
95: 66,
|
||||
96: 67,
|
||||
97: 68,
|
||||
98: 69,
|
||||
99: 70,
|
||||
100: 71,
|
||||
101: 72,
|
||||
102: 73,
|
||||
103: 74,
|
||||
104: 75,
|
||||
105: 76,
|
||||
106: 77,
|
||||
107: 78,
|
||||
108: 79,
|
||||
109: 80,
|
||||
110: 81,
|
||||
111: 82,
|
||||
112: 83,
|
||||
113: 84,
|
||||
114: 85,
|
||||
115: 86,
|
||||
116: 87,
|
||||
117: 88,
|
||||
118: 89,
|
||||
119: 90,
|
||||
120: 91,
|
||||
121: 92,
|
||||
122: 93,
|
||||
123: 94,
|
||||
124: 95,
|
||||
125: 96,
|
||||
126: 97,
|
||||
160: 172,
|
||||
161: 163,
|
||||
162: 132,
|
||||
163: 133,
|
||||
164: 189,
|
||||
165: 150,
|
||||
166: 232,
|
||||
167: 134,
|
||||
168: 142,
|
||||
169: 139,
|
||||
170: 157,
|
||||
171: 169,
|
||||
172: 164,
|
||||
174: 138,
|
||||
175: 218,
|
||||
176: 131,
|
||||
177: 147,
|
||||
178: 242,
|
||||
179: 243,
|
||||
180: 141,
|
||||
181: 151,
|
||||
182: 136,
|
||||
184: 222,
|
||||
185: 241,
|
||||
186: 158,
|
||||
187: 170,
|
||||
188: 245,
|
||||
189: 244,
|
||||
190: 246,
|
||||
191: 162,
|
||||
192: 173,
|
||||
193: 201,
|
||||
194: 199,
|
||||
195: 174,
|
||||
196: 98,
|
||||
197: 99,
|
||||
198: 144,
|
||||
199: 100,
|
||||
200: 203,
|
||||
201: 101,
|
||||
202: 200,
|
||||
203: 202,
|
||||
204: 207,
|
||||
205: 204,
|
||||
206: 205,
|
||||
207: 206,
|
||||
208: 233,
|
||||
209: 102,
|
||||
210: 211,
|
||||
211: 208,
|
||||
212: 209,
|
||||
213: 175,
|
||||
214: 103,
|
||||
215: 240,
|
||||
216: 145,
|
||||
217: 214,
|
||||
218: 212,
|
||||
219: 213,
|
||||
220: 104,
|
||||
221: 235,
|
||||
222: 237,
|
||||
223: 137,
|
||||
224: 106,
|
||||
225: 105,
|
||||
226: 107,
|
||||
227: 109,
|
||||
228: 108,
|
||||
229: 110,
|
||||
230: 160,
|
||||
231: 111,
|
||||
232: 113,
|
||||
233: 112,
|
||||
234: 114,
|
||||
235: 115,
|
||||
236: 117,
|
||||
237: 116,
|
||||
238: 118,
|
||||
239: 119,
|
||||
240: 234,
|
||||
241: 120,
|
||||
242: 122,
|
||||
243: 121,
|
||||
244: 123,
|
||||
245: 125,
|
||||
246: 124,
|
||||
247: 184,
|
||||
248: 161,
|
||||
249: 127,
|
||||
250: 126,
|
||||
251: 128,
|
||||
252: 129,
|
||||
253: 236,
|
||||
254: 238,
|
||||
255: 186,
|
||||
262: 253,
|
||||
263: 254,
|
||||
268: 255,
|
||||
269: 256,
|
||||
273: 257,
|
||||
286: 248,
|
||||
287: 249,
|
||||
304: 250,
|
||||
305: 215,
|
||||
321: 226,
|
||||
322: 227,
|
||||
338: 176,
|
||||
339: 177,
|
||||
350: 251,
|
||||
351: 252,
|
||||
352: 228,
|
||||
353: 229,
|
||||
376: 187,
|
||||
381: 230,
|
||||
382: 231,
|
||||
402: 166,
|
||||
710: 216,
|
||||
711: 225,
|
||||
728: 219,
|
||||
729: 220,
|
||||
730: 221,
|
||||
731: 224,
|
||||
733: 223,
|
||||
960: 155,
|
||||
8211: 178,
|
||||
8212: 179,
|
||||
8216: 182,
|
||||
8217: 183,
|
||||
8218: 196,
|
||||
8220: 180,
|
||||
8221: 181,
|
||||
8222: 197,
|
||||
8224: 130,
|
||||
8225: 194,
|
||||
8226: 135,
|
||||
8230: 171,
|
||||
8240: 198,
|
||||
8249: 190,
|
||||
8250: 191,
|
||||
8355: 247,
|
||||
8482: 140,
|
||||
8486: 159,
|
||||
8706: 152,
|
||||
8710: 168,
|
||||
8719: 154,
|
||||
8721: 153,
|
||||
8722: 239,
|
||||
8725: 188,
|
||||
8729: 195,
|
||||
8730: 165,
|
||||
8734: 146,
|
||||
8747: 156,
|
||||
8776: 167,
|
||||
8800: 143,
|
||||
8804: 148,
|
||||
8805: 149,
|
||||
9674: 185,
|
||||
61441: 192,
|
||||
61442: 193,
|
||||
64257: 192,
|
||||
64258: 193,
|
||||
65535: 0 // 0xFFFF指向.notdef
|
||||
};
|
||||
@ -1,16 +0,0 @@
|
||||
/**
|
||||
* @file 字体粗细度量
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
export default {
|
||||
100: 'Ultra-light',
|
||||
200: 'Extra-light',
|
||||
300: 'Light',
|
||||
400: 'Semi-light',
|
||||
500: 'Medium (normal)',
|
||||
600: 'Semi-bold',
|
||||
700: 'Bold',
|
||||
800: 'Extra-Bold',
|
||||
900: 'Ultra-bold'
|
||||
};
|
||||
@ -1,16 +0,0 @@
|
||||
/**
|
||||
* @file 字体宽度度量
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
export default {
|
||||
1: 'Ultra-condensed',
|
||||
2: 'Extra-condensed',
|
||||
3: 'Condensed',
|
||||
4: 'Semi-condensed',
|
||||
5: 'Medium (normal)',
|
||||
6: 'Semi-expanded',
|
||||
7: 'Expanded',
|
||||
8: 'Extra-expanded',
|
||||
9: 'Ultra-expanded'
|
||||
};
|
||||
15
vendor/fonteditor-core/src/ttf/eot2base64.js
vendored
15
vendor/fonteditor-core/src/ttf/eot2base64.js
vendored
@ -1,15 +0,0 @@
|
||||
/**
|
||||
* @file eot数组转base64编码
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
import bytes2base64 from './util/bytes2base64';
|
||||
|
||||
/**
|
||||
* eot数组转base64编码
|
||||
*
|
||||
* @param {Array} arrayBuffer ArrayBuffer对象
|
||||
* @return {string} base64编码
|
||||
*/
|
||||
export default function eot2base64(arrayBuffer) {
|
||||
return 'data:font/eot;charset=utf-8;base64,' + bytes2base64(arrayBuffer);
|
||||
}
|
||||
79
vendor/fonteditor-core/src/ttf/eot2ttf.js
vendored
79
vendor/fonteditor-core/src/ttf/eot2ttf.js
vendored
@ -1,79 +0,0 @@
|
||||
/**
|
||||
* @file eot转ttf
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
import Reader from './reader';
|
||||
import Writer from './writer';
|
||||
import error from './error';
|
||||
|
||||
/**
|
||||
* eot格式转换成ttf字体格式
|
||||
*
|
||||
* @param {ArrayBuffer} eotBuffer eot缓冲数组
|
||||
* @param {Object} options 选项
|
||||
*
|
||||
* @return {ArrayBuffer} ttf格式byte流
|
||||
*/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
export default function eot2ttf(eotBuffer, options = {}) {
|
||||
// 这里用小尾方式读取
|
||||
const eotReader = new Reader(eotBuffer, 0, eotBuffer.byteLength, true);
|
||||
|
||||
// check magic number
|
||||
const magicNumber = eotReader.readUint16(34);
|
||||
if (magicNumber !== 0x504C) {
|
||||
error.raise(10110);
|
||||
}
|
||||
|
||||
// check version
|
||||
const version = eotReader.readUint32(8);
|
||||
if (version !== 0x20001 && version !== 0x10000 && version !== 0x20002) {
|
||||
error.raise(10110);
|
||||
}
|
||||
|
||||
const eotSize = eotBuffer.byteLength || eotBuffer.length;
|
||||
const fontSize = eotReader.readUint32(4);
|
||||
|
||||
let fontOffset = 82;
|
||||
const familyNameSize = eotReader.readUint16(fontOffset);
|
||||
fontOffset += 4 + familyNameSize;
|
||||
|
||||
const styleNameSize = eotReader.readUint16(fontOffset);
|
||||
fontOffset += 4 + styleNameSize;
|
||||
|
||||
const versionNameSize = eotReader.readUint16(fontOffset);
|
||||
fontOffset += 4 + versionNameSize;
|
||||
|
||||
const fullNameSize = eotReader.readUint16(fontOffset);
|
||||
fontOffset += 2 + fullNameSize;
|
||||
|
||||
// version 0x20001
|
||||
if (version === 0x20001 || version === 0x20002) {
|
||||
const rootStringSize = eotReader.readUint16(fontOffset + 2);
|
||||
fontOffset += 4 + rootStringSize;
|
||||
}
|
||||
|
||||
// version 0x20002
|
||||
if (version === 0x20002) {
|
||||
fontOffset += 10;
|
||||
const signatureSize = eotReader.readUint16(fontOffset);
|
||||
fontOffset += 2 + signatureSize;
|
||||
fontOffset += 4;
|
||||
const eudcFontSize = eotReader.readUint32(fontOffset);
|
||||
fontOffset += 4 + eudcFontSize;
|
||||
}
|
||||
|
||||
if (fontOffset + fontSize > eotSize) {
|
||||
error.raise(10001);
|
||||
}
|
||||
|
||||
// support slice
|
||||
if (eotBuffer.slice) {
|
||||
return eotBuffer.slice(fontOffset, fontOffset + fontSize);
|
||||
}
|
||||
|
||||
// not support ArrayBuffer.slice eg. IE10
|
||||
const bytes = eotReader.readBytes(fontOffset, fontSize);
|
||||
return new Writer(new ArrayBuffer(fontSize)).writeBytes(bytes).getBuffer();
|
||||
}
|
||||
53
vendor/fonteditor-core/src/ttf/error.js
vendored
53
vendor/fonteditor-core/src/ttf/error.js
vendored
@ -1,53 +0,0 @@
|
||||
/**
|
||||
* @file ttf 相关错误号定义
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
import string from '../common/string';
|
||||
import i18n from './i18n';
|
||||
|
||||
export default {
|
||||
|
||||
/**
|
||||
* 抛出一个异常
|
||||
*
|
||||
* @param {Object} e 异常号或者异常对象
|
||||
* @param {...Array} fargs args 参数
|
||||
*
|
||||
* 例如:
|
||||
* e = 1001
|
||||
* e = {
|
||||
* number: 1001,
|
||||
* data: 错误数据
|
||||
* }
|
||||
*/
|
||||
raise(e, ...fargs) {
|
||||
let number;
|
||||
let data;
|
||||
if (typeof e === 'object') {
|
||||
number = e.number || 0;
|
||||
data = e.data;
|
||||
}
|
||||
else {
|
||||
number = e;
|
||||
}
|
||||
|
||||
let message = i18n.lang[number];
|
||||
if (fargs.length > 0) {
|
||||
const args = typeof fargs[0] === 'object'
|
||||
? fargs[0]
|
||||
: fargs;
|
||||
message = string.format(message, args);
|
||||
}
|
||||
|
||||
const event = new Error(message);
|
||||
event.number = number;
|
||||
if (data) {
|
||||
event.data = data;
|
||||
}
|
||||
|
||||
throw event;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
361
vendor/fonteditor-core/src/ttf/font.js
vendored
361
vendor/fonteditor-core/src/ttf/font.js
vendored
@ -1,361 +0,0 @@
|
||||
/**
|
||||
* @file 字体管理对象,处理字体相关的读取、查询、转换
|
||||
*
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
import bufferTool from '../nodejs/buffer';
|
||||
|
||||
import getEmptyttfObject from './getEmptyttfObject';
|
||||
import TTF from './ttf';
|
||||
|
||||
import woff2ttf from './woff2ttf';
|
||||
import otf2ttfobject from './otf2ttfobject';
|
||||
import eot2ttf from './eot2ttf';
|
||||
import svg2ttfobject from './svg2ttfobject';
|
||||
import TTFReader from './ttfreader';
|
||||
|
||||
import TTFWriter from './ttfwriter';
|
||||
import ttf2eot from './ttf2eot';
|
||||
import ttf2woff from './ttf2woff';
|
||||
import ttf2svg from './ttf2svg';
|
||||
import ttf2symbol from './ttf2symbol';
|
||||
import ttftowoff2 from './ttftowoff2';
|
||||
import woff2tottf from './woff2tottf';
|
||||
|
||||
import ttf2base64 from './ttf2base64';
|
||||
import eot2base64 from './eot2base64';
|
||||
import woff2base64 from './woff2base64';
|
||||
import svg2base64 from './svg2base64';
|
||||
import bytes2base64 from './util/bytes2base64';
|
||||
import woff2tobase64 from './woff2tobase64';
|
||||
|
||||
import optimizettf from './util/optimizettf';
|
||||
|
||||
// 必须是nodejs环境下的Buffer对象才能触发buffer转换
|
||||
const SUPPORT_BUFFER =
|
||||
typeof process === 'object' &&
|
||||
typeof process.versions === 'object' &&
|
||||
typeof process.versions.node !== 'undefined' &&
|
||||
typeof Buffer === 'function';
|
||||
|
||||
class Font {
|
||||
/**
|
||||
* 字体对象构造函数
|
||||
*
|
||||
* @param {ArrayBuffer|Buffer|string|Document} buffer 字体数据
|
||||
* @param {Object} options 读取参数
|
||||
*/
|
||||
constructor(buffer, options = { type: 'ttf' }) {
|
||||
// 字形对象
|
||||
if (typeof buffer === 'object' && buffer.glyf) {
|
||||
this.set(buffer);
|
||||
}
|
||||
// buffer
|
||||
else if (buffer) {
|
||||
this.read(buffer, options);
|
||||
}
|
||||
// 空
|
||||
else {
|
||||
this.readEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Font instance
|
||||
*
|
||||
* @param {ArrayBuffer|Buffer|string|Document} buffer 字体数据
|
||||
* @param {Object} options 读取参数
|
||||
* @return {Font}
|
||||
*/
|
||||
static create(buffer, options) {
|
||||
return new Font(buffer, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置一个空的 ttfObject 对象
|
||||
*
|
||||
* @return {Font}
|
||||
*/
|
||||
readEmpty() {
|
||||
this.data = getEmptyttfObject();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取字体数据
|
||||
*
|
||||
* @param {ArrayBuffer|Buffer|string|Document} buffer 字体数据
|
||||
* @param {Object} options 读取参数
|
||||
* @param {string} options.type 字体类型
|
||||
*
|
||||
* ttf, woff , eot 读取配置
|
||||
* @param {boolean} options.hinting 是否保留 hinting 信息
|
||||
* @param {boolean} options.kerning 是否保留 kerning 信息
|
||||
* @param {boolean} options.compound2simple 复合字形转简单字形
|
||||
*
|
||||
* woff 读取配置
|
||||
* @param {Function} options.inflate 解压相关函数
|
||||
*
|
||||
* svg 读取配置
|
||||
* @param {boolean} options.combinePath 是否合并成单个字形,仅限于普通svg导入
|
||||
* @return {Font}
|
||||
*/
|
||||
read(buffer, options) {
|
||||
// nodejs buffer
|
||||
if (SUPPORT_BUFFER) {
|
||||
if (buffer instanceof Buffer) {
|
||||
buffer = bufferTool.toArrayBuffer(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
if (options.type === 'ttf') {
|
||||
this.data = new TTFReader(options).read(buffer);
|
||||
} else if (options.type === 'otf') {
|
||||
this.data = otf2ttfobject(buffer, options);
|
||||
} else if (options.type === 'eot') {
|
||||
buffer = eot2ttf(buffer, options);
|
||||
this.data = new TTFReader(options).read(buffer);
|
||||
} else if (options.type === 'woff') {
|
||||
buffer = woff2ttf(buffer, options);
|
||||
this.data = new TTFReader(options).read(buffer);
|
||||
} else if (options.type === 'woff2') {
|
||||
buffer = woff2tottf(buffer, options);
|
||||
this.data = new TTFReader(options).read(buffer);
|
||||
} else if (options.type === 'svg') {
|
||||
this.data = svg2ttfobject(buffer, options);
|
||||
} else {
|
||||
throw new Error('not support font type' + options.type);
|
||||
}
|
||||
|
||||
this.type = options.type;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入字体数据
|
||||
*
|
||||
* @param {Object} options 写入参数
|
||||
* @param {string} options.type 字体类型, 默认 ttf
|
||||
* @param {boolean} options.toBuffer nodejs 环境中返回 Buffer 对象, 默认 true
|
||||
*
|
||||
* ttf 字体参数
|
||||
* @param {boolean} options.hinting 是否保留 hinting 信息
|
||||
* @param {boolean} options.kerning 是否保留 kerning 信息
|
||||
* svg,woff 字体参数
|
||||
* @param {Object} options.metadata 字体相关的信息
|
||||
*
|
||||
* woff 字体参数
|
||||
* @param {Function} options.deflate 压缩相关函数
|
||||
* @return {Buffer|ArrayBuffer|string}
|
||||
*/
|
||||
write(options = {}) {
|
||||
if (!options.type) {
|
||||
options.type = this.type;
|
||||
}
|
||||
|
||||
let buffer = null;
|
||||
if (options.type === 'ttf') {
|
||||
buffer = new TTFWriter(options).write(this.data);
|
||||
} else if (options.type === 'eot') {
|
||||
buffer = new TTFWriter(options).write(this.data);
|
||||
buffer = ttf2eot(buffer, options);
|
||||
} else if (options.type === 'woff') {
|
||||
buffer = new TTFWriter(options).write(this.data);
|
||||
buffer = ttf2woff(buffer, options);
|
||||
} else if (options.type === 'woff2') {
|
||||
buffer = new TTFWriter(options).write(this.data);
|
||||
buffer = ttftowoff2(buffer, options);
|
||||
} else if (options.type === 'svg') {
|
||||
buffer = ttf2svg(this.data, options);
|
||||
} else if (options.type === 'symbol') {
|
||||
buffer = ttf2symbol(this.data, options);
|
||||
} else {
|
||||
throw new Error('not support font type' + options.type);
|
||||
}
|
||||
|
||||
if (SUPPORT_BUFFER) {
|
||||
if (false !== options.toBuffer && buffer instanceof ArrayBuffer) {
|
||||
buffer = bufferTool.toBuffer(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换成 base64编码
|
||||
*
|
||||
* @param {Object} options 写入参数
|
||||
* @param {string} options.type 字体类型, 默认 ttf
|
||||
* 其他 options参数, 参考 write
|
||||
* @see write
|
||||
*
|
||||
* @param {ArrayBuffer=} buffer 如果提供了buffer数据则使用 buffer数据, 否则转换现有的 font
|
||||
* @return {string}
|
||||
*/
|
||||
toBase64(options, buffer) {
|
||||
if (!options.type) {
|
||||
options.type = this.type;
|
||||
}
|
||||
|
||||
if (buffer) {
|
||||
if (SUPPORT_BUFFER) {
|
||||
if (buffer instanceof Buffer) {
|
||||
buffer = bufferTool.toArrayBuffer(buffer);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
options.toBuffer = false;
|
||||
buffer = this.write(options);
|
||||
}
|
||||
|
||||
let base64Str;
|
||||
if (options.type === 'ttf') {
|
||||
base64Str = ttf2base64(buffer);
|
||||
} else if (options.type === 'eot') {
|
||||
base64Str = eot2base64(buffer);
|
||||
} else if (options.type === 'woff') {
|
||||
base64Str = woff2base64(buffer);
|
||||
} else if (options.type === 'woff2') {
|
||||
base64Str = woff2tobase64(buffer);
|
||||
} else if (options.type === 'svg') {
|
||||
base64Str = svg2base64(buffer);
|
||||
} else if (options.type === 'symbol') {
|
||||
base64Str = svg2base64(buffer, 'image/svg+xml');
|
||||
} else {
|
||||
throw new Error('not support font type' + options.type);
|
||||
}
|
||||
|
||||
return base64Str;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 font 对象
|
||||
*
|
||||
* @param {Object} data font的ttfObject对象
|
||||
* @return {this}
|
||||
*/
|
||||
set(data) {
|
||||
this.data = data;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 font 数据
|
||||
*
|
||||
* @return {Object} ttfObject 对象
|
||||
*/
|
||||
get() {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对字形数据进行优化
|
||||
*
|
||||
* @param {Object} out 输出结果
|
||||
* @param {boolean|Object} out.result `true` 或者有问题的地方
|
||||
* @return {Font}
|
||||
*/
|
||||
optimize(out) {
|
||||
const result = optimizettf(this.data);
|
||||
if (out) {
|
||||
out.result = result;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字体中的复合字形转为简单字形
|
||||
*
|
||||
* @return {this}
|
||||
*/
|
||||
compound2simple() {
|
||||
const ttfHelper = this.getHelper();
|
||||
ttfHelper.compound2simple();
|
||||
this.data = ttfHelper.get();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对字形按照unicode编码排序
|
||||
*
|
||||
* @return {this}
|
||||
*/
|
||||
sort() {
|
||||
const ttfHelper = this.getHelper();
|
||||
ttfHelper.sortGlyf();
|
||||
this.data = ttfHelper.get();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找相关字形
|
||||
*
|
||||
* @param {Object} condition 查询条件
|
||||
* @param {Array|number} condition.unicode unicode编码列表或者单个unicode编码
|
||||
* @param {string} condition.name glyf名字,例如`uniE001`, `uniE`
|
||||
* @param {Function} condition.filter 自定义过滤器
|
||||
* @example
|
||||
* condition.filter(glyf) {
|
||||
* return glyf.name === 'logo';
|
||||
* }
|
||||
* @return {Array} glyf字形列表
|
||||
*/
|
||||
find(condition) {
|
||||
const ttfHelper = this.getHelper();
|
||||
const indexList = ttfHelper.findGlyf(condition);
|
||||
return indexList.length ? ttfHelper.getGlyf(indexList) : indexList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并 font 到当前的 font
|
||||
*
|
||||
* @param {Object} font Font 对象
|
||||
* @param {Object} options 参数选项
|
||||
* @param {boolean} options.scale 是否自动缩放
|
||||
* @param {boolean} options.adjustGlyf 是否调整字形以适应边界
|
||||
* (和 options.scale 参数互斥)
|
||||
*
|
||||
* @return {Font}
|
||||
*/
|
||||
merge(font, options) {
|
||||
const ttfHelper = this.getHelper();
|
||||
ttfHelper.mergeGlyf(font.get(), options);
|
||||
this.data = ttfHelper.get();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 TTF helper 实例
|
||||
*/
|
||||
getHelper() {
|
||||
return new TTF(this.data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* base64序列化buffer 数据
|
||||
*
|
||||
* @param {ArrayBuffer|Buffer|string} buffer 字体数据
|
||||
* @return {Font}
|
||||
*/
|
||||
Font.toBase64 = function (buffer) {
|
||||
if (typeof buffer === 'string') {
|
||||
// node 环境中没有 btoa 函数
|
||||
if (typeof btoa === 'undefined') {
|
||||
return Buffer.from(buffer, 'binary').toString('base64');
|
||||
}
|
||||
|
||||
return btoa(buffer);
|
||||
}
|
||||
return bytes2base64(buffer);
|
||||
};
|
||||
|
||||
function createFont(buffer, options) {
|
||||
return new Font(buffer, options);
|
||||
}
|
||||
|
||||
export {Font, createFont};
|
||||
|
||||
export default Font;
|
||||
@ -1,16 +0,0 @@
|
||||
/**
|
||||
* @file 获取空的ttf对象
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
import {clone} from '../common/lang';
|
||||
import emptyttf from './data/empty';
|
||||
import config from './data/default';
|
||||
|
||||
|
||||
export default function getEmpty() {
|
||||
const ttf = clone(emptyttf);
|
||||
Object.assign(ttf.name, config.name);
|
||||
ttf.head.created = ttf.head.modified = Date.now();
|
||||
return ttf;
|
||||
}
|
||||
82
vendor/fonteditor-core/src/ttf/i18n.js
vendored
82
vendor/fonteditor-core/src/ttf/i18n.js
vendored
@ -1,82 +0,0 @@
|
||||
/**
|
||||
* @file 语言字符串管理
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
import I18n from '../common/I18n';
|
||||
|
||||
const zh = {
|
||||
// error define
|
||||
10001: '超出读取范围:${0}, ${1}',
|
||||
10002: '超出写入范围:${0}, ${1}',
|
||||
10003: '未知数据类型:${0}, ${1}',
|
||||
10004: '不支持svg解析',
|
||||
|
||||
10101: '错误的ttf文件',
|
||||
10102: '错误的woff文件',
|
||||
10103: '错误的svg文件',
|
||||
10104: '读取ttf文件错误',
|
||||
10105: '读取woff文件错误',
|
||||
10106: '读取svg文件错误',
|
||||
10107: '写入ttf文件错误',
|
||||
10108: '写入woff文件错误',
|
||||
10109: '写入svg文件错误',
|
||||
10112: '写入svg symbol 错误',
|
||||
|
||||
10110: '读取eot文件错误',
|
||||
10111: '读取eot字体错误',
|
||||
|
||||
10200: '重复的unicode代码点,字形序号:${0}',
|
||||
10201: 'ttf字形轮廓数据为空',
|
||||
10202: '不支持标志位:ARGS_ARE_XY_VALUES',
|
||||
10203: '未找到表:${0}',
|
||||
10204: '读取ttf表错误',
|
||||
10205: '未找到解压函数',
|
||||
|
||||
10301: '错误的otf文件',
|
||||
10302: '读取otf表错误',
|
||||
10303: 'otf字形轮廓数据为空'
|
||||
};
|
||||
|
||||
|
||||
const en = {
|
||||
// error define
|
||||
10001: 'Reading index out of range: ${0}, ${1}',
|
||||
10002: 'Writing index out of range: ${0}, ${1}',
|
||||
10003: 'Unknown datatype: ${0}, ${1}',
|
||||
10004: 'No svg parser',
|
||||
|
||||
10101: 'ttf file damaged',
|
||||
10102: 'woff file damaged',
|
||||
10103: 'svg file damaged',
|
||||
10104: 'Read ttf error',
|
||||
10105: 'Read woff error',
|
||||
10106: 'Read svg error',
|
||||
10107: 'Write ttf error',
|
||||
10108: 'Write woff error',
|
||||
10109: 'Write svg error',
|
||||
10112: 'Write svg symbol error',
|
||||
|
||||
10110: 'Read eot error',
|
||||
10111: 'Write eot error',
|
||||
|
||||
10200: 'Repeat unicode, glyph index: ${0}',
|
||||
10201: 'ttf `glyph` data is empty',
|
||||
10202: 'Not support compound glyph flag: ARGS_ARE_XY_VALUES',
|
||||
10203: 'No ttf table: ${0}',
|
||||
10204: 'Read ttf table data error',
|
||||
10205: 'No zip deflate function',
|
||||
|
||||
10301: 'otf file damaged',
|
||||
10302: 'Read otf table error',
|
||||
10303: 'otf `glyph` data is empty'
|
||||
};
|
||||
|
||||
|
||||
export default new I18n(
|
||||
[
|
||||
['zh-cn', zh],
|
||||
['en-us', en]
|
||||
],
|
||||
typeof window !== 'undefined' ? window.language : 'en-us'
|
||||
);
|
||||
16
vendor/fonteditor-core/src/ttf/otf2base64.js
vendored
16
vendor/fonteditor-core/src/ttf/otf2base64.js
vendored
@ -1,16 +0,0 @@
|
||||
/**
|
||||
* @file otf转bse64字体
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
import bytes2base64 from './util/bytes2base64';
|
||||
|
||||
/**
|
||||
* ttf 二进制转base64编码
|
||||
*
|
||||
* @param {Array} arrayBuffer ArrayBuffer对象
|
||||
* @return {string} base64编码
|
||||
*/
|
||||
export default function ttf2base64(arrayBuffer) {
|
||||
return 'data:font/otf;charset=utf-8;base64,' + bytes2base64(arrayBuffer);
|
||||
}
|
||||
62
vendor/fonteditor-core/src/ttf/otf2ttfobject.js
vendored
62
vendor/fonteditor-core/src/ttf/otf2ttfobject.js
vendored
@ -1,62 +0,0 @@
|
||||
/**
|
||||
* @file otf格式转ttf格式对象
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
import error from './error';
|
||||
import OTFReader from './otfreader';
|
||||
import otfContours2ttfContours from './util/otfContours2ttfContours';
|
||||
import {computePathBox} from '../graphics/computeBoundingBox';
|
||||
|
||||
/**
|
||||
* otf格式转ttf格式对象
|
||||
*
|
||||
* @param {ArrayBuffer|otfObject} otfBuffer 原始数据或者解析后的otf数据
|
||||
* @param {Object} options 参数
|
||||
* @return {Object} ttfObject对象
|
||||
*/
|
||||
export default function otf2ttfobject(otfBuffer, options) {
|
||||
let otfObject;
|
||||
if (otfBuffer instanceof ArrayBuffer) {
|
||||
const otfReader = new OTFReader(options);
|
||||
otfObject = otfReader.read(otfBuffer);
|
||||
otfReader.dispose();
|
||||
}
|
||||
else if (otfBuffer.head && otfBuffer.glyf && otfBuffer.cmap) {
|
||||
otfObject = otfBuffer;
|
||||
}
|
||||
else {
|
||||
error.raise(10111);
|
||||
}
|
||||
|
||||
// 转换otf轮廓
|
||||
otfObject.glyf.forEach((g) => {
|
||||
g.contours = otfContours2ttfContours(g.contours);
|
||||
const box = computePathBox(...g.contours);
|
||||
if (box) {
|
||||
g.xMin = box.x;
|
||||
g.xMax = box.x + box.width;
|
||||
g.yMin = box.y;
|
||||
g.yMax = box.y + box.height;
|
||||
g.leftSideBearing = g.xMin;
|
||||
}
|
||||
else {
|
||||
g.xMin = 0;
|
||||
g.xMax = 0;
|
||||
g.yMin = 0;
|
||||
g.yMax = 0;
|
||||
g.leftSideBearing = 0;
|
||||
}
|
||||
});
|
||||
|
||||
otfObject.version = 0x1;
|
||||
|
||||
// 修改maxp相关配置
|
||||
otfObject.maxp.version = 1.0;
|
||||
otfObject.maxp.maxZones = otfObject.maxp.maxTwilightPoints ? 2 : 1;
|
||||
|
||||
delete otfObject.CFF;
|
||||
delete otfObject.VORG;
|
||||
|
||||
return otfObject;
|
||||
}
|
||||
171
vendor/fonteditor-core/src/ttf/otfreader.js
vendored
171
vendor/fonteditor-core/src/ttf/otfreader.js
vendored
@ -1,171 +0,0 @@
|
||||
/**
|
||||
* @file otf字体读取
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
import Directory from './table/directory';
|
||||
import supportTables from './table/support-otf';
|
||||
import Reader from './reader';
|
||||
import error from './error';
|
||||
|
||||
export default class OTFReader {
|
||||
|
||||
/**
|
||||
* OTF读取函数
|
||||
*
|
||||
* @param {Object} options 写入参数
|
||||
* @constructor
|
||||
*/
|
||||
constructor(options = {}) {
|
||||
options.subset = options.subset || [];
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*
|
||||
* @param {ArrayBuffer} buffer buffer对象
|
||||
* @return {Object} ttf对象
|
||||
*/
|
||||
readBuffer(buffer) {
|
||||
|
||||
const reader = new Reader(buffer, 0, buffer.byteLength, false);
|
||||
const font = {};
|
||||
|
||||
// version
|
||||
font.version = reader.readString(0, 4);
|
||||
|
||||
if (font.version !== 'OTTO') {
|
||||
error.raise(10301);
|
||||
}
|
||||
|
||||
// num tables
|
||||
font.numTables = reader.readUint16();
|
||||
|
||||
if (font.numTables <= 0 || font.numTables > 100) {
|
||||
error.raise(10302);
|
||||
}
|
||||
|
||||
// searchRange
|
||||
font.searchRange = reader.readUint16();
|
||||
|
||||
// entrySelector
|
||||
font.entrySelector = reader.readUint16();
|
||||
|
||||
// rangeShift
|
||||
font.rangeShift = reader.readUint16();
|
||||
|
||||
font.tables = new Directory(reader.offset).read(reader, font);
|
||||
|
||||
if (!font.tables.head || !font.tables.cmap || !font.tables.CFF) {
|
||||
error.raise(10302);
|
||||
}
|
||||
|
||||
font.readOptions = this.options;
|
||||
|
||||
// 读取支持的表数据
|
||||
Object.keys(supportTables).forEach((tableName) => {
|
||||
if (font.tables[tableName]) {
|
||||
const offset = font.tables[tableName].offset;
|
||||
font[tableName] = new supportTables[tableName](offset).read(reader, font);
|
||||
}
|
||||
});
|
||||
|
||||
if (!font.CFF.glyf) {
|
||||
error.raise(10303);
|
||||
}
|
||||
|
||||
reader.dispose();
|
||||
|
||||
return font;
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联glyf相关的信息
|
||||
*
|
||||
* @param {Object} font font对象
|
||||
*/
|
||||
resolveGlyf(font) {
|
||||
|
||||
const codes = font.cmap;
|
||||
let glyf = font.CFF.glyf;
|
||||
const subsetMap = font.readOptions.subset ? font.subsetMap : null; // 当前ttf的子集列表
|
||||
// unicode
|
||||
Object.keys(codes).forEach((c) => {
|
||||
const i = codes[c];
|
||||
if (subsetMap && !subsetMap[i]) {
|
||||
return;
|
||||
}
|
||||
if (!glyf[i].unicode) {
|
||||
glyf[i].unicode = [];
|
||||
}
|
||||
glyf[i].unicode.push(+c);
|
||||
});
|
||||
|
||||
// leftSideBearing
|
||||
font.hmtx.forEach((item, i) => {
|
||||
if (subsetMap && !subsetMap[i]) {
|
||||
return;
|
||||
}
|
||||
glyf[i].advanceWidth = glyf[i].advanceWidth || item.advanceWidth || 0;
|
||||
glyf[i].leftSideBearing = item.leftSideBearing;
|
||||
});
|
||||
|
||||
// 设置了subsetMap之后需要选取subset中的字形
|
||||
if (subsetMap) {
|
||||
const subGlyf = [];
|
||||
Object.keys(subsetMap).forEach((i) => {
|
||||
subGlyf.push(glyf[+i]);
|
||||
});
|
||||
glyf = subGlyf;
|
||||
}
|
||||
|
||||
font.glyf = glyf;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除非必须的表
|
||||
*
|
||||
* @param {Object} font font对象
|
||||
*/
|
||||
cleanTables(font) {
|
||||
delete font.readOptions;
|
||||
delete font.tables;
|
||||
delete font.hmtx;
|
||||
delete font.post.glyphNameIndex;
|
||||
delete font.post.names;
|
||||
delete font.subsetMap;
|
||||
|
||||
// 删除无用的表
|
||||
const cff = font.CFF;
|
||||
delete cff.glyf;
|
||||
delete cff.charset;
|
||||
delete cff.encoding;
|
||||
delete cff.gsubrs;
|
||||
delete cff.gsubrsBias;
|
||||
delete cff.subrs;
|
||||
delete cff.subrsBias;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取解析后的ttf文档
|
||||
*
|
||||
* @param {ArrayBuffer} buffer buffer对象
|
||||
*
|
||||
* @return {Object} ttf文档
|
||||
*/
|
||||
read(buffer) {
|
||||
this.font = this.readBuffer(buffer);
|
||||
this.resolveGlyf(this.font);
|
||||
this.cleanTables(this.font);
|
||||
return this.font;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注销
|
||||
*/
|
||||
dispose() {
|
||||
delete this.font;
|
||||
delete this.options;
|
||||
}
|
||||
}
|
||||
223
vendor/fonteditor-core/src/ttf/reader.js
vendored
223
vendor/fonteditor-core/src/ttf/reader.js
vendored
@ -1,223 +0,0 @@
|
||||
/**
|
||||
* @file 数据读取器
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*
|
||||
* thanks to:
|
||||
* ynakajima/ttf.js
|
||||
* https://github.com/ynakajima/ttf.js
|
||||
*/
|
||||
|
||||
import {curry} from '../common/lang';
|
||||
import error from './error';
|
||||
|
||||
// 检查数组支持情况
|
||||
if (typeof ArrayBuffer === 'undefined' || typeof DataView === 'undefined') {
|
||||
throw new Error('not support ArrayBuffer and DataView');
|
||||
}
|
||||
|
||||
// 数据类型
|
||||
const dataType = {
|
||||
Int8: 1,
|
||||
Int16: 2,
|
||||
Int32: 4,
|
||||
Uint8: 1,
|
||||
Uint16: 2,
|
||||
Uint32: 4,
|
||||
Float32: 4,
|
||||
Float64: 8
|
||||
};
|
||||
|
||||
export default class Reader {
|
||||
|
||||
/**
|
||||
* 读取器
|
||||
*
|
||||
* @constructor
|
||||
* @param {Array.<byte>} buffer 缓冲数组
|
||||
* @param {number} offset 起始偏移
|
||||
* @param {number} length 数组长度
|
||||
* @param {boolean} littleEndian 是否小尾
|
||||
*/
|
||||
constructor(buffer, offset, length, littleEndian) {
|
||||
|
||||
const bufferLength = buffer.byteLength || buffer.length;
|
||||
|
||||
this.offset = offset || 0;
|
||||
this.length = length || (bufferLength - this.offset);
|
||||
this.littleEndian = littleEndian || false;
|
||||
|
||||
this.view = new DataView(buffer, this.offset, this.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取指定的数据类型
|
||||
*
|
||||
* @param {string} type 数据类型
|
||||
* @param {number=} offset 位移
|
||||
* @param {boolean=} littleEndian 是否小尾
|
||||
* @return {number} 返回值
|
||||
*/
|
||||
read(type, offset, littleEndian) {
|
||||
|
||||
// 使用当前位移
|
||||
if (undefined === offset) {
|
||||
offset = this.offset;
|
||||
}
|
||||
|
||||
// 使用小尾
|
||||
if (undefined === littleEndian) {
|
||||
littleEndian = this.littleEndian;
|
||||
}
|
||||
|
||||
// 扩展方法
|
||||
if (undefined === dataType[type]) {
|
||||
return this['read' + type](offset, littleEndian);
|
||||
}
|
||||
|
||||
const size = dataType[type];
|
||||
this.offset = offset + size;
|
||||
return this.view['get' + type](offset, littleEndian);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定的字节数组
|
||||
*
|
||||
* @param {number} offset 偏移
|
||||
* @param {number} length 字节长度
|
||||
* @return {Array} 字节数组
|
||||
*/
|
||||
readBytes(offset, length = null) {
|
||||
|
||||
if (length == null) {
|
||||
length = offset;
|
||||
offset = this.offset;
|
||||
}
|
||||
|
||||
if (length < 0 || offset + length > this.length) {
|
||||
error.raise(10001, this.length, offset + length);
|
||||
}
|
||||
|
||||
const buffer = [];
|
||||
for (let i = 0; i < length; ++i) {
|
||||
buffer.push(this.view.getUint8(offset + i));
|
||||
}
|
||||
|
||||
this.offset = offset + length;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取一个string
|
||||
*
|
||||
* @param {number} offset 偏移
|
||||
* @param {number} length 长度
|
||||
* @return {string} 字符串
|
||||
*/
|
||||
readString(offset, length = null) {
|
||||
|
||||
if (length == null) {
|
||||
length = offset;
|
||||
offset = this.offset;
|
||||
}
|
||||
|
||||
if (length < 0 || offset + length > this.length) {
|
||||
error.raise(10001, this.length, offset + length);
|
||||
}
|
||||
|
||||
let value = '';
|
||||
for (let i = 0; i < length; ++i) {
|
||||
const c = this.readUint8(offset + i);
|
||||
value += String.fromCharCode(c);
|
||||
}
|
||||
|
||||
this.offset = offset + length;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取一个字符
|
||||
*
|
||||
* @param {number} offset 偏移
|
||||
* @return {string} 字符串
|
||||
*/
|
||||
readChar(offset) {
|
||||
return this.readString(offset, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取一个uint24整形
|
||||
*
|
||||
* @param {number} offset 偏移
|
||||
* @return {number}
|
||||
*/
|
||||
readUint24(offset) {
|
||||
const [i, j, k] = this.readBytes(offset || this.offset, 3);
|
||||
return (i << 16) + (j << 8) + k;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取fixed类型
|
||||
*
|
||||
* @param {number} offset 偏移
|
||||
* @return {number} float
|
||||
*/
|
||||
readFixed(offset) {
|
||||
if (undefined === offset) {
|
||||
offset = this.offset;
|
||||
}
|
||||
const val = this.readInt32(offset, false) / 65536.0;
|
||||
return Math.ceil(val * 100000) / 100000;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取长日期
|
||||
*
|
||||
* @param {number} offset 偏移
|
||||
* @return {Date} Date对象
|
||||
*/
|
||||
readLongDateTime(offset) {
|
||||
if (undefined === offset) {
|
||||
offset = this.offset;
|
||||
}
|
||||
|
||||
// new Date(1970, 1, 1).getTime() - new Date(1904, 1, 1).getTime();
|
||||
const delta = -2077545600000;
|
||||
const time = this.readUint32(offset + 4, false);
|
||||
const date = new Date();
|
||||
date.setTime(time * 1000 + delta);
|
||||
return date;
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转到指定偏移
|
||||
*
|
||||
* @param {number} offset 偏移
|
||||
* @return {Object} this
|
||||
*/
|
||||
seek(offset) {
|
||||
if (undefined === offset) {
|
||||
this.offset = 0;
|
||||
}
|
||||
|
||||
if (offset < 0 || offset > this.length) {
|
||||
error.raise(10001, this.length, offset);
|
||||
}
|
||||
|
||||
this.offset = offset;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注销
|
||||
*/
|
||||
dispose() {
|
||||
delete this.view;
|
||||
}
|
||||
}
|
||||
|
||||
// 直接支持的数据类型
|
||||
Object.keys(dataType).forEach((type) => {
|
||||
Reader.prototype['read' + type] = curry(Reader.prototype.read, type);
|
||||
});
|
||||
@ -1,74 +0,0 @@
|
||||
/**
|
||||
* @file 根据transform参数变换轮廓
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
import {mul, multiply} from '../../graphics/matrix';
|
||||
import pathTransform from '../../graphics/pathTransform';
|
||||
|
||||
/**
|
||||
* 根据transform参数变换轮廓
|
||||
*
|
||||
* @param {Array} contours 轮廓集合
|
||||
* @param {Array} transforms 变换指令集合
|
||||
* transforms = [{
|
||||
* name: 'scale'
|
||||
* params: [3,4]
|
||||
* }]
|
||||
*
|
||||
* @return {Array} 变换后的轮廓数组
|
||||
*/
|
||||
export default function contoursTransform(contours, transforms) {
|
||||
if (!contours || !contours.length || !transforms || !transforms.length) {
|
||||
return contours;
|
||||
}
|
||||
|
||||
let matrix = [1, 0, 0, 1, 0, 0];
|
||||
for (let i = 0, l = transforms.length; i < l; i++) {
|
||||
const transform = transforms[i];
|
||||
const params = transform.params;
|
||||
let radian = null;
|
||||
switch (transform.name) {
|
||||
case 'translate':
|
||||
matrix = mul(matrix, [1, 0, 0, 1, params[0], params[1]]);
|
||||
break;
|
||||
case 'scale':
|
||||
matrix = mul(matrix, [params[0], 0, 0, params[1], 0, 0]);
|
||||
break;
|
||||
case 'matrix':
|
||||
matrix = mul(matrix,
|
||||
[params[0], params[1], params[2], params[3], params[4], params[5]]);
|
||||
break;
|
||||
case 'rotate':
|
||||
radian = params[0] * Math.PI / 180;
|
||||
if (params.length > 1) {
|
||||
|
||||
matrix = multiply(
|
||||
matrix,
|
||||
[1, 0, 0, 1, -params[1], -params[2]],
|
||||
[Math.cos(radian), Math.sin(radian), -Math.sin(radian), Math.cos(radian), 0, 0],
|
||||
[1, 0, 0, 1, params[1], params[2]]
|
||||
);
|
||||
}
|
||||
else {
|
||||
matrix = mul(
|
||||
matrix, [Math.cos(radian), Math.sin(radian), -Math.sin(radian), Math.cos(radian), 0, 0]);
|
||||
}
|
||||
break;
|
||||
case 'skewX':
|
||||
matrix = mul(matrix,
|
||||
[1, 0, Math.tan(params[0] * Math.PI / 180), 1, 0, 0]);
|
||||
break;
|
||||
case 'skewY':
|
||||
matrix = mul(matrix,
|
||||
[1, Math.tan(params[0] * Math.PI / 180), 0, 1, 0, 0]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
contours.forEach(p => {
|
||||
pathTransform(p, matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]);
|
||||
});
|
||||
|
||||
return contours;
|
||||
}
|
||||
@ -1,36 +0,0 @@
|
||||
/**
|
||||
* @file 椭圆转换成轮廓
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
import {computePath} from '../../graphics/computeBoundingBox';
|
||||
import pathAdjust from '../../graphics/pathAdjust';
|
||||
import circlePath from '../../graphics/path/circle';
|
||||
import {clone} from '../../common/lang';
|
||||
|
||||
/**
|
||||
* 椭圆转换成轮廓
|
||||
*
|
||||
* @param {number} cx 椭圆中心点x
|
||||
* @param {number} cy 椭圆中心点y
|
||||
* @param {number} rx 椭圆x轴半径
|
||||
* @param {number} ry 椭圆y周半径
|
||||
* @return {Array} 轮廓数组
|
||||
*/
|
||||
export default function oval2contour(cx, cy, rx, ry) {
|
||||
|
||||
if (undefined === ry) {
|
||||
ry = rx;
|
||||
}
|
||||
|
||||
const bound = computePath(circlePath);
|
||||
const scaleX = (+rx) * 2 / bound.width;
|
||||
const scaleY = (+ry) * 2 / bound.height;
|
||||
const centerX = bound.width * scaleX / 2;
|
||||
const centerY = bound.height * scaleY / 2;
|
||||
const contour = clone(circlePath);
|
||||
pathAdjust(contour, scaleX, scaleY);
|
||||
pathAdjust(contour, 1, 1, +cx - centerX, +cy - centerY);
|
||||
|
||||
return contour;
|
||||
}
|
||||
@ -1,30 +0,0 @@
|
||||
/**
|
||||
* @file 解析参数数组
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
const SEGMENT_REGEX = /-?\d+(?:\.\d+)?(?:e[-+]?\d+)?\b/g;
|
||||
|
||||
/**
|
||||
* 获取参数值
|
||||
*
|
||||
* @param {string} d 参数
|
||||
* @return {number} 参数值
|
||||
*/
|
||||
function getSegment(d) {
|
||||
return +d.trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析参数数组
|
||||
*
|
||||
* @param {string} str 参数字符串
|
||||
* @return {Array} 参数数组
|
||||
*/
|
||||
export default function (str) {
|
||||
if (!str) {
|
||||
return [];
|
||||
}
|
||||
const matchs = str.match(SEGMENT_REGEX);
|
||||
return matchs ? matchs.map(getSegment) : [];
|
||||
}
|
||||
@ -1,39 +0,0 @@
|
||||
/**
|
||||
* @file 解析transform参数
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
import parseParams from './parseParams';
|
||||
const TRANSFORM_REGEX = /(\w+)\s*\(([\d-.,\s]*)\)/g;
|
||||
|
||||
/**
|
||||
* 解析transform参数
|
||||
*
|
||||
* @param {string} str 参数字符串
|
||||
* @return {Array} transform数组, 格式如下:
|
||||
* [
|
||||
* {
|
||||
* name: 'scale',
|
||||
* params: []
|
||||
* }
|
||||
* ]
|
||||
*/
|
||||
export default function parseTransform(str) {
|
||||
|
||||
if (!str) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TRANSFORM_REGEX.lastIndex = 0;
|
||||
const transforms = [];
|
||||
let match;
|
||||
|
||||
while ((match = TRANSFORM_REGEX.exec(str))) {
|
||||
transforms.push({
|
||||
name: match[1],
|
||||
params: parseParams(match[2])
|
||||
});
|
||||
}
|
||||
|
||||
return transforms;
|
||||
}
|
||||
529
vendor/fonteditor-core/src/ttf/svg/path2contours.js
vendored
529
vendor/fonteditor-core/src/ttf/svg/path2contours.js
vendored
@ -1,529 +0,0 @@
|
||||
/**
|
||||
* @file svg path转换为轮廓
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
import bezierCubic2Q2 from '../../math/bezierCubic2Q2';
|
||||
import getArc from '../../graphics/getArc';
|
||||
import parseParams from './parseParams';
|
||||
|
||||
/**
|
||||
* 三次贝塞尔曲线,转二次贝塞尔曲线
|
||||
*
|
||||
* @param {Array} cubicList 三次曲线数组
|
||||
* @param {Array} contour 当前解析后的轮廓数组
|
||||
* @return {Array} 当前解析后的轮廓数组
|
||||
*/
|
||||
function cubic2Points(cubicList, contour) {
|
||||
|
||||
let i;
|
||||
let l;
|
||||
const q2List = [];
|
||||
|
||||
cubicList.forEach(c => {
|
||||
const list = bezierCubic2Q2(c[0], c[1], c[2], c[3]);
|
||||
for (i = 0, l = list.length; i < l; i++) {
|
||||
q2List.push(list[i]);
|
||||
}
|
||||
});
|
||||
|
||||
let q2;
|
||||
let prevq2;
|
||||
for (i = 0, l = q2List.length; i < l; i++) {
|
||||
q2 = q2List[i];
|
||||
if (i === 0) {
|
||||
contour.push({
|
||||
x: q2[1].x,
|
||||
y: q2[1].y
|
||||
});
|
||||
contour.push({
|
||||
x: q2[2].x,
|
||||
y: q2[2].y,
|
||||
onCurve: true
|
||||
});
|
||||
}
|
||||
else {
|
||||
prevq2 = q2List[i - 1];
|
||||
// 检查是否存在切线点
|
||||
if (
|
||||
prevq2[1].x + q2[1].x === 2 * q2[0].x
|
||||
&& prevq2[1].y + q2[1].y === 2 * q2[0].y
|
||||
) {
|
||||
contour.pop();
|
||||
}
|
||||
contour.push({
|
||||
x: q2[1].x,
|
||||
y: q2[1].y
|
||||
});
|
||||
contour.push({
|
||||
x: q2[2].x,
|
||||
y: q2[2].y,
|
||||
onCurve: true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
contour.push({
|
||||
x: q2[2].x,
|
||||
y: q2[2].y,
|
||||
onCurve: true
|
||||
});
|
||||
|
||||
return contour;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* svg 命令数组转轮廓
|
||||
*
|
||||
* @param {Array} segments svg 命令数组
|
||||
* @return {Array} 轮廓数组
|
||||
*/
|
||||
function segments2Contours(segments) {
|
||||
|
||||
// 解析segments
|
||||
const contours = [];
|
||||
let contour = [];
|
||||
let prevX = 0;
|
||||
let prevY = 0;
|
||||
let segment;
|
||||
let args;
|
||||
let cmd;
|
||||
let relative;
|
||||
let q;
|
||||
let ql;
|
||||
let px;
|
||||
let py;
|
||||
let cubicList;
|
||||
let p1;
|
||||
let p2;
|
||||
let c1;
|
||||
let c2;
|
||||
let prevCubicC1; // 三次贝塞尔曲线前一个控制点,用于绘制`s`命令
|
||||
|
||||
for (let i = 0, l = segments.length; i < l; i++) {
|
||||
segment = segments[i];
|
||||
cmd = segment.cmd;
|
||||
relative = segment.relative;
|
||||
args = segment.args;
|
||||
|
||||
if (args && !args.length && cmd !== 'Z') {
|
||||
console.warn('`' + cmd + '` command args empty!');
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cmd === 'Z') {
|
||||
contours.push(contour);
|
||||
contour = [];
|
||||
}
|
||||
else if (cmd === 'M' || cmd === 'L') {
|
||||
if (args.length % 2) {
|
||||
throw new Error('`M` command error:' + args.join(','));
|
||||
}
|
||||
|
||||
// 这里可能会连续绘制,最后一个是终点
|
||||
if (relative) {
|
||||
px = prevX;
|
||||
py = prevY;
|
||||
}
|
||||
else {
|
||||
px = 0;
|
||||
py = 0;
|
||||
}
|
||||
|
||||
for (q = 0, ql = args.length; q < ql; q += 2) {
|
||||
|
||||
if (relative) {
|
||||
px += args[q];
|
||||
py += args[q + 1];
|
||||
}
|
||||
else {
|
||||
px = args[q];
|
||||
py = args[q + 1];
|
||||
}
|
||||
|
||||
contour.push({
|
||||
x: px,
|
||||
y: py,
|
||||
onCurve: true
|
||||
});
|
||||
}
|
||||
|
||||
prevX = px;
|
||||
prevY = py;
|
||||
}
|
||||
else if (cmd === 'H') {
|
||||
if (relative) {
|
||||
prevX += args[0];
|
||||
}
|
||||
else {
|
||||
prevX = args[0];
|
||||
}
|
||||
|
||||
contour.push({
|
||||
x: prevX,
|
||||
y: prevY,
|
||||
onCurve: true
|
||||
});
|
||||
}
|
||||
else if (cmd === 'V') {
|
||||
if (relative) {
|
||||
prevY += args[0];
|
||||
}
|
||||
else {
|
||||
prevY = args[0];
|
||||
}
|
||||
|
||||
contour.push({
|
||||
x: prevX,
|
||||
y: prevY,
|
||||
onCurve: true
|
||||
});
|
||||
}
|
||||
// 二次贝塞尔
|
||||
else if (cmd === 'Q') {
|
||||
// 这里可能会连续绘制,最后一个是终点
|
||||
if (relative) {
|
||||
px = prevX;
|
||||
py = prevY;
|
||||
}
|
||||
else {
|
||||
px = 0;
|
||||
py = 0;
|
||||
}
|
||||
|
||||
for (q = 0, ql = args.length; q < ql; q += 4) {
|
||||
|
||||
contour.push({
|
||||
x: px + args[q],
|
||||
y: py + args[q + 1]
|
||||
});
|
||||
contour.push({
|
||||
x: px + args[q + 2],
|
||||
y: py + args[q + 3],
|
||||
onCurve: true
|
||||
});
|
||||
|
||||
if (relative) {
|
||||
px += args[q + 2];
|
||||
py += args[q + 3];
|
||||
}
|
||||
else {
|
||||
px = 0;
|
||||
py = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (relative) {
|
||||
prevX = px;
|
||||
prevY = py;
|
||||
}
|
||||
else {
|
||||
prevX = args[ql - 2];
|
||||
prevY = args[ql - 1];
|
||||
}
|
||||
}
|
||||
// 二次贝塞尔平滑
|
||||
else if (cmd === 'T') {
|
||||
// 这里需要移除上一个曲线的终点
|
||||
let last = contour.pop();
|
||||
let pc = contour[contour.length - 1];
|
||||
if (!pc) {
|
||||
pc = last;
|
||||
}
|
||||
|
||||
contour.push(pc = {
|
||||
x: 2 * last.x - pc.x,
|
||||
y: 2 * last.y - pc.y
|
||||
});
|
||||
|
||||
px = prevX;
|
||||
py = prevY;
|
||||
|
||||
for (q = 0, ql = args.length - 2; q < ql; q += 2) {
|
||||
|
||||
if (relative) {
|
||||
px += args[q];
|
||||
py += args[q + 1];
|
||||
}
|
||||
else {
|
||||
px = args[q];
|
||||
py = args[q + 1];
|
||||
}
|
||||
|
||||
last = {
|
||||
x: px,
|
||||
y: py
|
||||
};
|
||||
|
||||
contour.push(pc = {
|
||||
x: 2 * last.x - pc.x,
|
||||
y: 2 * last.y - pc.y
|
||||
});
|
||||
}
|
||||
|
||||
if (relative) {
|
||||
prevX = px + args[ql];
|
||||
prevY = py + args[ql + 1];
|
||||
}
|
||||
else {
|
||||
prevX = args[ql];
|
||||
prevY = args[ql + 1];
|
||||
}
|
||||
|
||||
contour.push({
|
||||
x: prevX,
|
||||
y: prevY,
|
||||
onCurve: true
|
||||
});
|
||||
|
||||
}
|
||||
// 三次贝塞尔
|
||||
else if (cmd === 'C') {
|
||||
if (args.length % 6) {
|
||||
throw new Error('`C` command params error:' + args.join(','));
|
||||
}
|
||||
|
||||
// 这里可能会连续绘制,最后一个是终点
|
||||
cubicList = [];
|
||||
|
||||
if (relative) {
|
||||
px = prevX;
|
||||
py = prevY;
|
||||
}
|
||||
else {
|
||||
px = 0;
|
||||
py = 0;
|
||||
}
|
||||
|
||||
p1 = {
|
||||
x: prevX,
|
||||
y: prevY
|
||||
};
|
||||
|
||||
for (q = 0, ql = args.length; q < ql; q += 6) {
|
||||
|
||||
c1 = {
|
||||
x: px + args[q],
|
||||
y: py + args[q + 1]
|
||||
};
|
||||
|
||||
c2 = {
|
||||
x: px + args[q + 2],
|
||||
y: py + args[q + 3]
|
||||
};
|
||||
|
||||
p2 = {
|
||||
x: px + args[q + 4],
|
||||
y: py + args[q + 5]
|
||||
};
|
||||
|
||||
cubicList.push([p1, c1, c2, p2]);
|
||||
|
||||
p1 = p2;
|
||||
|
||||
if (relative) {
|
||||
px += args[q + 4];
|
||||
py += args[q + 5];
|
||||
}
|
||||
else {
|
||||
px = 0;
|
||||
py = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (relative) {
|
||||
prevX = px;
|
||||
prevY = py;
|
||||
}
|
||||
else {
|
||||
prevX = args[ql - 2];
|
||||
prevY = args[ql - 1];
|
||||
}
|
||||
|
||||
cubic2Points(cubicList, contour);
|
||||
prevCubicC1 = cubicList[cubicList.length - 1][2];
|
||||
}
|
||||
// 三次贝塞尔平滑
|
||||
else if (cmd === 'S') {
|
||||
if (args.length % 4) {
|
||||
throw new Error('`S` command params error:' + args.join(','));
|
||||
}
|
||||
|
||||
// 这里可能会连续绘制,最后一个是终点
|
||||
cubicList = [];
|
||||
|
||||
if (relative) {
|
||||
px = prevX;
|
||||
py = prevY;
|
||||
}
|
||||
else {
|
||||
px = 0;
|
||||
py = 0;
|
||||
}
|
||||
|
||||
// 这里需要移除上一个曲线的终点
|
||||
p1 = contour.pop();
|
||||
if (!prevCubicC1) {
|
||||
prevCubicC1 = p1;
|
||||
}
|
||||
|
||||
c1 = {
|
||||
x: 2 * p1.x - prevCubicC1.x,
|
||||
y: 2 * p1.y - prevCubicC1.y
|
||||
};
|
||||
|
||||
for (q = 0, ql = args.length; q < ql; q += 4) {
|
||||
|
||||
c2 = {
|
||||
x: px + args[q],
|
||||
y: py + args[q + 1]
|
||||
};
|
||||
|
||||
p2 = {
|
||||
x: px + args[q + 2],
|
||||
y: py + args[q + 3]
|
||||
};
|
||||
|
||||
cubicList.push([p1, c1, c2, p2]);
|
||||
|
||||
p1 = p2;
|
||||
|
||||
c1 = {
|
||||
x: 2 * p1.x - c2.x,
|
||||
y: 2 * p1.y - c2.y
|
||||
};
|
||||
|
||||
if (relative) {
|
||||
px += args[q + 2];
|
||||
py += args[q + 3];
|
||||
}
|
||||
else {
|
||||
px = 0;
|
||||
py = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (relative) {
|
||||
prevX = px;
|
||||
prevY = py;
|
||||
}
|
||||
else {
|
||||
prevX = args[ql - 2];
|
||||
prevY = args[ql - 1];
|
||||
}
|
||||
|
||||
cubic2Points(cubicList, contour);
|
||||
prevCubicC1 = cubicList[cubicList.length - 1][2];
|
||||
}
|
||||
// 求弧度, rx, ry, angle, largeArc, sweep, ex, ey
|
||||
else if (cmd === 'A') {
|
||||
if (args.length % 7) {
|
||||
throw new Error('arc command params error:' + args.join(','));
|
||||
}
|
||||
|
||||
for (q = 0, ql = args.length; q < ql; q += 7) {
|
||||
let ex = args[q + 5];
|
||||
let ey = args[q + 6];
|
||||
|
||||
if (relative) {
|
||||
ex = prevX + ex;
|
||||
ey = prevY + ey;
|
||||
}
|
||||
|
||||
const path = getArc(
|
||||
args[q], args[q + 1],
|
||||
args[q + 2], args[q + 3], args[q + 4],
|
||||
{x: prevX, y: prevY},
|
||||
{x: ex, y: ey}
|
||||
);
|
||||
|
||||
if (path && path.length > 1) {
|
||||
for (let r = 1, rl = path.length; r < rl; r++) {
|
||||
contour.push(path[r]);
|
||||
}
|
||||
}
|
||||
prevX = ex;
|
||||
prevY = ey;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return contours;
|
||||
}
|
||||
|
||||
/**
|
||||
* svg path转轮廓
|
||||
*
|
||||
* @param {string} path svg的path字符串
|
||||
* @return {Array} 转换后的轮廓
|
||||
*/
|
||||
export default function path2contours(path) {
|
||||
|
||||
if (!path || !path.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
path = path.trim();
|
||||
|
||||
// 修正头部不为`m`的情况
|
||||
if (path[0] !== 'M' && path[0] !== 'm') {
|
||||
path = 'M 0 0' + path;
|
||||
}
|
||||
|
||||
// 修复中间没有结束符`z`的情况
|
||||
path = path.replace(/(\d+)\s*(m|$)/gi, '$1z$2');
|
||||
|
||||
// 获取segments
|
||||
const segments = [];
|
||||
let cmd;
|
||||
let relative = false;
|
||||
let lastIndex;
|
||||
let args;
|
||||
|
||||
for (let i = 0, l = path.length; i < l; i++) {
|
||||
const c = path[i].toUpperCase();
|
||||
const r = c !== path[i];
|
||||
|
||||
switch (c) {
|
||||
case 'M':
|
||||
/* jshint -W086 */
|
||||
if (i === 0) {
|
||||
cmd = c;
|
||||
lastIndex = 1;
|
||||
break;
|
||||
}
|
||||
// eslint-disable-next-line no-fallthrough
|
||||
case 'Q':
|
||||
case 'T':
|
||||
case 'C':
|
||||
case 'S':
|
||||
case 'H':
|
||||
case 'V':
|
||||
case 'L':
|
||||
case 'A':
|
||||
case 'Z':
|
||||
if (cmd === 'Z') {
|
||||
segments.push({cmd: 'Z'});
|
||||
}
|
||||
else {
|
||||
args = path.slice(lastIndex, i);
|
||||
segments.push({
|
||||
cmd,
|
||||
relative,
|
||||
args: parseParams(args)
|
||||
});
|
||||
}
|
||||
|
||||
cmd = c;
|
||||
relative = r;
|
||||
lastIndex = i + 1;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
segments.push({cmd: 'Z'});
|
||||
|
||||
return segments2Contours(segments);
|
||||
}
|
||||
@ -1,31 +0,0 @@
|
||||
/**
|
||||
* @file 多边形转换成轮廓
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
import parseParams from './parseParams';
|
||||
|
||||
/**
|
||||
* 多边形转换成轮廓
|
||||
*
|
||||
* @param {Array} points 多边形点集合
|
||||
* @return {Array} contours
|
||||
*/
|
||||
export default function polygon2contour(points) {
|
||||
|
||||
if (!points || !points.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const contours = [];
|
||||
const segments = parseParams(points);
|
||||
for (let i = 0, l = segments.length; i < l; i += 2) {
|
||||
contours.push({
|
||||
x: segments[i],
|
||||
y: segments[i + 1],
|
||||
onCurve: true
|
||||
});
|
||||
}
|
||||
|
||||
return contours;
|
||||
}
|
||||
@ -1,43 +0,0 @@
|
||||
/**
|
||||
* @file 矩形转换成轮廓
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 矩形转换成轮廓
|
||||
*
|
||||
* @param {number} x 左上角x
|
||||
* @param {number} y 左上角y
|
||||
* @param {number} width 宽度
|
||||
* @param {number} height 高度
|
||||
* @return {Array} 轮廓数组
|
||||
*/
|
||||
export default function rect2contour(x, y, width, height) {
|
||||
x = +x;
|
||||
y = +y;
|
||||
width = +width;
|
||||
height = +height;
|
||||
|
||||
return [
|
||||
{
|
||||
x,
|
||||
y,
|
||||
onCurve: true
|
||||
},
|
||||
{
|
||||
x: x + width,
|
||||
y,
|
||||
onCurve: true
|
||||
},
|
||||
{
|
||||
x: x + width,
|
||||
y: y + height,
|
||||
onCurve: true
|
||||
},
|
||||
{
|
||||
x,
|
||||
y: y + height,
|
||||
onCurve: true
|
||||
}
|
||||
];
|
||||
}
|
||||
@ -1,124 +0,0 @@
|
||||
/**
|
||||
* @file svg节点转字形轮廓
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
import path2contours from './path2contours';
|
||||
import oval2contour from './oval2contour';
|
||||
import polygon2contour from './polygon2contour';
|
||||
import rect2contour from './rect2contour';
|
||||
import parseTransform from './parseTransform';
|
||||
import contoursTransform from './contoursTransform';
|
||||
|
||||
// 支持的解析器集合
|
||||
const support = {
|
||||
|
||||
path: {
|
||||
parse: path2contours, // 解析器
|
||||
params: ['d'], // 参数列表
|
||||
contours: true // 是否是多个轮廓
|
||||
},
|
||||
|
||||
circle: {
|
||||
parse: oval2contour,
|
||||
params: ['cx', 'cy', 'r']
|
||||
},
|
||||
|
||||
ellipse: {
|
||||
parse: oval2contour,
|
||||
params: ['cx', 'cy', 'rx', 'ry']
|
||||
},
|
||||
|
||||
rect: {
|
||||
parse: rect2contour,
|
||||
params: ['x', 'y', 'width', 'height']
|
||||
},
|
||||
|
||||
polygon: {
|
||||
parse: polygon2contour,
|
||||
params: ['points']
|
||||
},
|
||||
|
||||
polyline: {
|
||||
parse: polygon2contour,
|
||||
params: ['points']
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* svg节点转字形轮廓
|
||||
*
|
||||
* @param {Array} xmlNodes xml节点集合
|
||||
* @return {Array|false} 轮廓数组
|
||||
*/
|
||||
export default function svgnode2contours(xmlNodes) {
|
||||
let i;
|
||||
let length;
|
||||
let j;
|
||||
let jlength;
|
||||
let segment; // 当前指令
|
||||
const parsedSegments = []; // 解析后的指令
|
||||
|
||||
if (xmlNodes.length) {
|
||||
for (i = 0, length = xmlNodes.length; i < length; i++) {
|
||||
const node = xmlNodes[i];
|
||||
const name = node.tagName;
|
||||
if (support[name]) {
|
||||
const supportParams = support[name].params;
|
||||
const params = [];
|
||||
for (j = 0, jlength = supportParams.length; j < jlength; j++) {
|
||||
params.push(node.getAttribute(supportParams[j]));
|
||||
}
|
||||
|
||||
segment = {
|
||||
name,
|
||||
params,
|
||||
transform: parseTransform(node.getAttribute('transform'))
|
||||
};
|
||||
|
||||
if (node.parentNode) {
|
||||
let curNode = node.parentNode;
|
||||
const transforms = segment.transform || [];
|
||||
let transAttr;
|
||||
const iterator = function (t) {
|
||||
transforms.unshift(t);
|
||||
};
|
||||
while (curNode !== null && curNode.tagName !== 'svg') {
|
||||
transAttr = curNode.getAttribute('transform');
|
||||
if (transAttr) {
|
||||
parseTransform(transAttr).reverse().forEach(iterator);
|
||||
}
|
||||
curNode = curNode.parentNode;
|
||||
}
|
||||
|
||||
segment.transform = transforms.length ? transforms : null;
|
||||
}
|
||||
parsedSegments.push(segment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (parsedSegments.length) {
|
||||
const result = [];
|
||||
for (i = 0, length = parsedSegments.length; i < length; i++) {
|
||||
segment = parsedSegments[i];
|
||||
const parser = support[segment.name];
|
||||
const contour = parser.parse.apply(null, segment.params);
|
||||
if (contour && contour.length) {
|
||||
let contours = parser.contours ? contour : [contour];
|
||||
|
||||
// 如果有变换则应用变换规则
|
||||
if (segment.transform) {
|
||||
contours = contoursTransform(contours, segment.transform);
|
||||
}
|
||||
|
||||
for (j = 0, jlength = contours.length; j < jlength; j++) {
|
||||
result.push(contours[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
19
vendor/fonteditor-core/src/ttf/svg2base64.js
vendored
19
vendor/fonteditor-core/src/ttf/svg2base64.js
vendored
@ -1,19 +0,0 @@
|
||||
/**
|
||||
* @file svg字符串转base64编码
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
/**
|
||||
* svg字符串转base64编码
|
||||
*
|
||||
* @param {string} svg svg对象
|
||||
* @param {string} scheme 头部
|
||||
* @return {string} base64编码
|
||||
*/
|
||||
export default function svg2base64(svg, scheme = 'font/svg') {
|
||||
if (typeof btoa === 'undefined') {
|
||||
return 'data:' + scheme + ';charset=utf-8;base64,'
|
||||
+ Buffer.from(svg, 'binary').toString('base64');
|
||||
}
|
||||
return 'data:' + scheme + ';charset=utf-8;base64,' + btoa(svg);
|
||||
}
|
||||
431
vendor/fonteditor-core/src/ttf/svg2ttfobject.js
vendored
431
vendor/fonteditor-core/src/ttf/svg2ttfobject.js
vendored
@ -1,431 +0,0 @@
|
||||
/**
|
||||
* @file svg格式转ttfObject格式
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
import string from '../common/string';
|
||||
import DOMParser from '../common/DOMParser';
|
||||
import path2contours from './svg/path2contours';
|
||||
import svgnode2contours from './svg/svgnode2contours';
|
||||
import {computePathBox} from '../graphics/computeBoundingBox';
|
||||
import pathsUtil from '../graphics/pathsUtil';
|
||||
import glyfAdjust from './util/glyfAdjust';
|
||||
import error from './error';
|
||||
import getEmptyttfObject from './getEmptyttfObject';
|
||||
import reduceGlyf from './util/reduceGlyf';
|
||||
|
||||
/**
|
||||
* 加载xml字符串
|
||||
*
|
||||
* @param {string} xml xml字符串
|
||||
* @return {Document}
|
||||
*/
|
||||
function loadXML(xml) {
|
||||
if (DOMParser) {
|
||||
try {
|
||||
const domParser = new DOMParser();
|
||||
const xmlDoc = domParser.parseFromString(xml, 'text/xml');
|
||||
return xmlDoc;
|
||||
}
|
||||
catch (exp) {
|
||||
error.raise(10103);
|
||||
}
|
||||
}
|
||||
error.raise(10004);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对xml文本进行处理
|
||||
*
|
||||
* @param {string} svg svg文本
|
||||
* @return {string} 处理后文本
|
||||
*/
|
||||
function resolveSVG(svg) {
|
||||
// 去除xmlns,防止xmlns导致svg解析错误
|
||||
svg = svg.replace(/\s+xmlns(?::[\w-]+)?=("|')[^"']*\1/g, ' ')
|
||||
.replace(/<defs[>\s][\s\S]+?\/defs>/g, (text) => {
|
||||
if (text.indexOf('</font>') >= 0) {
|
||||
return text;
|
||||
}
|
||||
return '';
|
||||
})
|
||||
.replace(/<use[>\s][\s\S]+?\/use>/g, '');
|
||||
return svg;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取空的ttf格式对象
|
||||
*
|
||||
* @return {Object} ttfObject对象
|
||||
*/
|
||||
function getEmptyTTF() {
|
||||
const ttf = getEmptyttfObject();
|
||||
ttf.head.unitsPerEm = 0; // 去除unitsPerEm以便于重新计算
|
||||
ttf.from = 'svgfont';
|
||||
return ttf;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取空的对象,用来作为ttf的容器
|
||||
*
|
||||
* @return {Object} ttfObject对象
|
||||
*/
|
||||
function getEmptyObject() {
|
||||
return {
|
||||
'from': 'svg',
|
||||
'OS/2': {},
|
||||
'name': {},
|
||||
'hhea': {},
|
||||
'head': {},
|
||||
'post': {},
|
||||
'glyf': []
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据边界获取unitsPerEm
|
||||
*
|
||||
* @param {number} xMin x最小值
|
||||
* @param {number} xMax x最大值
|
||||
* @param {number} yMin y最小值
|
||||
* @param {number} yMax y最大值
|
||||
* @return {number}
|
||||
*/
|
||||
function getUnitsPerEm(xMin, xMax, yMin, yMax) {
|
||||
const seed = Math.ceil(Math.min(yMax - yMin, xMax - xMin));
|
||||
|
||||
if (!seed) {
|
||||
return 1024;
|
||||
}
|
||||
|
||||
if (seed <= 128) {
|
||||
return seed;
|
||||
}
|
||||
|
||||
// 获取合适的unitsPerEm
|
||||
let unitsPerEm = 128;
|
||||
while (unitsPerEm < 16384) {
|
||||
|
||||
if (seed <= 1.2 * unitsPerEm) {
|
||||
return unitsPerEm;
|
||||
}
|
||||
|
||||
unitsPerEm <<= 1;
|
||||
}
|
||||
|
||||
return 1024;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对ttfObject进行处理,去除小数
|
||||
*
|
||||
* @param {Object} ttf ttfObject
|
||||
* @return {Object} ttfObject
|
||||
*/
|
||||
function resolve(ttf) {
|
||||
|
||||
|
||||
// 如果是svg格式字体,则去小数
|
||||
// 由于svg格式导入时候会出现字形重复问题,这里进行优化
|
||||
if (ttf.from === 'svgfont' && ttf.head.unitsPerEm > 128) {
|
||||
ttf.glyf.forEach((g) => {
|
||||
if (g.contours) {
|
||||
glyfAdjust(g);
|
||||
reduceGlyf(g);
|
||||
}
|
||||
});
|
||||
}
|
||||
// 否则重新计算字形大小,缩放到1024的em
|
||||
else {
|
||||
let xMin = 16384;
|
||||
let xMax = -16384;
|
||||
let yMin = 16384;
|
||||
let yMax = -16384;
|
||||
|
||||
ttf.glyf.forEach((g) => {
|
||||
if (g.contours) {
|
||||
const bound = computePathBox(...g.contours);
|
||||
if (bound) {
|
||||
xMin = Math.min(xMin, bound.x);
|
||||
xMax = Math.max(xMax, bound.x + bound.width);
|
||||
yMin = Math.min(yMin, bound.y);
|
||||
yMax = Math.max(yMax, bound.y + bound.height);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const unitsPerEm = getUnitsPerEm(xMin, xMax, yMin, yMax);
|
||||
const scale = 1024 / unitsPerEm;
|
||||
|
||||
ttf.glyf.forEach((g) => {
|
||||
glyfAdjust(g, scale, scale);
|
||||
reduceGlyf(g);
|
||||
});
|
||||
ttf.head.unitsPerEm = 1024;
|
||||
}
|
||||
|
||||
return ttf;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析字体信息相关节点
|
||||
*
|
||||
* @param {Document} xmlDoc XML文档对象
|
||||
* @param {Object} ttf ttf对象
|
||||
* @return {Object} ttf对象
|
||||
*/
|
||||
function parseFont(xmlDoc, ttf) {
|
||||
|
||||
const metaNode = xmlDoc.getElementsByTagName('metadata')[0];
|
||||
const fontNode = xmlDoc.getElementsByTagName('font')[0];
|
||||
const fontFaceNode = xmlDoc.getElementsByTagName('font-face')[0];
|
||||
|
||||
if (metaNode && metaNode.textContent) {
|
||||
ttf.metadata = string.decodeHTML(metaNode.textContent.trim());
|
||||
}
|
||||
|
||||
// 解析font,如果有font节点说明是svg格式字体文件
|
||||
if (fontNode) {
|
||||
ttf.id = fontNode.getAttribute('id') || '';
|
||||
ttf.hhea.advanceWidthMax = +(fontNode.getAttribute('horiz-adv-x') || 0);
|
||||
ttf.from = 'svgfont';
|
||||
}
|
||||
|
||||
if (fontFaceNode) {
|
||||
const OS2 = ttf['OS/2'];
|
||||
ttf.name.fontFamily = fontFaceNode.getAttribute('font-family') || '';
|
||||
OS2.usWeightClass = +(fontFaceNode.getAttribute('font-weight') || 0);
|
||||
ttf.head.unitsPerEm = +(fontFaceNode.getAttribute('units-per-em') || 0);
|
||||
|
||||
// 解析panose, eg: 2 0 6 3 0 0 0 0 0 0
|
||||
const panose = (fontFaceNode.getAttribute('panose-1') || '').split(' ');
|
||||
[
|
||||
'bFamilyType', 'bSerifStyle', 'bWeight', 'bProportion', 'bContrast',
|
||||
'bStrokeVariation', 'bArmStyle', 'bLetterform', 'bMidline', 'bXHeight'
|
||||
].forEach((name, i) => {
|
||||
OS2[name] = +(panose[i] || 0);
|
||||
});
|
||||
|
||||
ttf.hhea.ascent = +(fontFaceNode.getAttribute('ascent') || 0);
|
||||
ttf.hhea.descent = +(fontFaceNode.getAttribute('descent') || 0);
|
||||
OS2.bXHeight = +(fontFaceNode.getAttribute('x-height') || 0);
|
||||
|
||||
// 解析bounding
|
||||
const box = (fontFaceNode.getAttribute('bbox') || '').split(' ');
|
||||
['xMin', 'yMin', 'xMax', 'yMax'].forEach((name, i) => {
|
||||
ttf.head[name] = +(box[i] || '');
|
||||
});
|
||||
|
||||
ttf.post.underlineThickness = +(fontFaceNode.getAttribute('underline-thickness') || 0);
|
||||
ttf.post.underlinePosition = +(fontFaceNode.getAttribute('underline-position') || 0);
|
||||
|
||||
// unicode range
|
||||
const unicodeRange = fontFaceNode.getAttribute('unicode-range');
|
||||
if (unicodeRange) {
|
||||
unicodeRange.replace(/u\+([0-9A-Z]+)(-[0-9A-Z]+)?/i, ($0, a, b) => {
|
||||
OS2.usFirstCharIndex = Number('0x' + a);
|
||||
OS2.usLastCharIndex = b ? Number('0x' + b.slice(1)) : 0xFFFFFFFF;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return ttf;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析字体信息相关节点
|
||||
*
|
||||
* @param {Document} xmlDoc XML文档对象
|
||||
* @param {Object} ttf ttf对象
|
||||
* @return {Object} ttf对象
|
||||
*/
|
||||
function parseGlyf(xmlDoc, ttf) {
|
||||
|
||||
const missingNode = xmlDoc.getElementsByTagName('missing-glyph')[0];
|
||||
|
||||
// 解析glyf
|
||||
let d;
|
||||
let unicode;
|
||||
if (missingNode) {
|
||||
|
||||
const missing = {
|
||||
name: '.notdef'
|
||||
};
|
||||
|
||||
if (missingNode.getAttribute('horiz-adv-x')) {
|
||||
missing.advanceWidth = +missingNode.getAttribute('horiz-adv-x');
|
||||
}
|
||||
|
||||
if ((d = missingNode.getAttribute('d'))) {
|
||||
missing.contours = path2contours(d);
|
||||
}
|
||||
|
||||
// 去除默认的空字形
|
||||
if (ttf.glyf[0] && ttf.glyf[0].name === '.notdef') {
|
||||
ttf.glyf.splice(0, 1);
|
||||
}
|
||||
|
||||
ttf.glyf.unshift(missing);
|
||||
}
|
||||
|
||||
const glyfNodes = xmlDoc.getElementsByTagName('glyph');
|
||||
|
||||
if (glyfNodes.length) {
|
||||
|
||||
|
||||
for (let i = 0, l = glyfNodes.length; i < l; i++) {
|
||||
|
||||
const node = glyfNodes[i];
|
||||
const glyf = {
|
||||
name: node.getAttribute('glyph-name') || node.getAttribute('name') || ''
|
||||
};
|
||||
|
||||
if (node.getAttribute('horiz-adv-x')) {
|
||||
glyf.advanceWidth = +node.getAttribute('horiz-adv-x');
|
||||
}
|
||||
|
||||
if ((unicode = node.getAttribute('unicode'))) {
|
||||
const nextUnicode = [];
|
||||
let totalCodePoints = 0;
|
||||
for (let ui = 0; ui < unicode.length; ui++) {
|
||||
const ucp = unicode.codePointAt(ui);
|
||||
nextUnicode.push(ucp);
|
||||
ui = ucp > 0xffff ? ui + 1 : ui;
|
||||
totalCodePoints += 1;
|
||||
}
|
||||
if (totalCodePoints === 1) {
|
||||
// TTF can't handle ligatures
|
||||
glyf.unicode = nextUnicode;
|
||||
|
||||
if ((d = node.getAttribute('d'))) {
|
||||
glyf.contours = path2contours(d);
|
||||
}
|
||||
ttf.glyf.push(glyf);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return ttf;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 解析字体信息相关节点
|
||||
*
|
||||
* @param {Document} xmlDoc XML文档对象
|
||||
* @param {Object} ttf ttf对象
|
||||
*/
|
||||
function parsePath(xmlDoc, ttf) {
|
||||
|
||||
// 单个path组成一个glfy字形
|
||||
let contours;
|
||||
let glyf;
|
||||
let node;
|
||||
const pathNodes = xmlDoc.getElementsByTagName('path');
|
||||
|
||||
if (pathNodes.length) {
|
||||
for (let i = 0, l = pathNodes.length; i < l; i++) {
|
||||
node = pathNodes[i];
|
||||
glyf = {
|
||||
name: node.getAttribute('name') || ''
|
||||
};
|
||||
contours = svgnode2contours([node]);
|
||||
glyf.contours = contours;
|
||||
ttf.glyf.push(glyf);
|
||||
}
|
||||
}
|
||||
|
||||
// 其他svg指令组成一个glyf字形
|
||||
contours = svgnode2contours(
|
||||
Array.prototype.slice.call(xmlDoc.getElementsByTagName('*')).filter((node) => node.tagName !== 'path')
|
||||
);
|
||||
if (contours) {
|
||||
glyf = {
|
||||
name: ''
|
||||
};
|
||||
|
||||
glyf.contours = contours;
|
||||
ttf.glyf.push(glyf);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析xml文档
|
||||
*
|
||||
* @param {Document} xmlDoc XML文档对象
|
||||
* @param {Object} options 导入选项
|
||||
*
|
||||
* @return {Object} 解析后对象
|
||||
*/
|
||||
function parseXML(xmlDoc, options) {
|
||||
|
||||
if (!xmlDoc.getElementsByTagName('svg').length) {
|
||||
error.raise(10106);
|
||||
}
|
||||
|
||||
let ttf;
|
||||
|
||||
// 如果是svg字体格式,则解析glyf,否则解析path
|
||||
if (xmlDoc.getElementsByTagName('font')[0]) {
|
||||
ttf = getEmptyTTF();
|
||||
parseFont(xmlDoc, ttf);
|
||||
parseGlyf(xmlDoc, ttf);
|
||||
}
|
||||
else {
|
||||
ttf = getEmptyObject();
|
||||
parsePath(xmlDoc, ttf);
|
||||
}
|
||||
|
||||
if (!ttf.glyf.length) {
|
||||
error.raise(10201);
|
||||
}
|
||||
|
||||
if (ttf.from === 'svg') {
|
||||
const glyf = ttf.glyf;
|
||||
let i;
|
||||
let l;
|
||||
// 合并导入的字形为单个字形
|
||||
if (options.combinePath) {
|
||||
const combined = [];
|
||||
for (i = 0, l = glyf.length; i < l; i++) {
|
||||
const contours = glyf[i].contours;
|
||||
for (let index = 0, length = contours.length; index < length; index++) {
|
||||
combined.push(contours[index]);
|
||||
}
|
||||
}
|
||||
|
||||
glyf[0].contours = combined;
|
||||
glyf.splice(1);
|
||||
}
|
||||
|
||||
// 对字形进行反转
|
||||
for (i = 0, l = glyf.length; i < l; i++) {
|
||||
// 这里为了使ai等工具里面的字形方便导入,对svg做了反向处理
|
||||
glyf[i].contours = pathsUtil.flip(glyf[i].contours);
|
||||
}
|
||||
}
|
||||
|
||||
return ttf;
|
||||
}
|
||||
|
||||
/**
|
||||
* svg格式转ttfObject格式
|
||||
*
|
||||
* @param {string|Document} svg svg格式
|
||||
* @param {Object=} options 导入选项
|
||||
* @param {boolean} options.combinePath 是否合并成单个字形,仅限于普通svg导入
|
||||
* @return {Object} ttfObject
|
||||
*/
|
||||
export default function svg2ttfObject(svg, options = {combinePath: false}) {
|
||||
let xmlDoc = svg;
|
||||
if (typeof svg === 'string') {
|
||||
svg = resolveSVG(svg);
|
||||
xmlDoc = loadXML(svg);
|
||||
}
|
||||
|
||||
const ttf = parseXML(xmlDoc, options);
|
||||
return resolve(ttf);
|
||||
}
|
||||
259
vendor/fonteditor-core/src/ttf/table/CFF.js
vendored
259
vendor/fonteditor-core/src/ttf/table/CFF.js
vendored
@ -1,259 +0,0 @@
|
||||
/**
|
||||
* @file cff表
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*
|
||||
* reference:
|
||||
* http://wwwimages.adobe.com/content/dam/Adobe/en/devnet/font/pdfs/5176.CFF.pdf
|
||||
*
|
||||
* modify from:
|
||||
* https://github.com/nodebox/opentype.js/blob/master/src/tables/cff.js
|
||||
*/
|
||||
|
||||
import table from './table';
|
||||
import string from '../util/string';
|
||||
import encoding from './cff/encoding';
|
||||
import cffStandardStrings from './cff/cffStandardStrings';
|
||||
import parseCFFDict from './cff/parseCFFDict';
|
||||
import parseCFFGlyph from './cff/parseCFFGlyph';
|
||||
import parseCFFCharset from './cff/parseCFFCharset';
|
||||
import parseCFFEncoding from './cff/parseCFFEncoding';
|
||||
import Reader from '../reader';
|
||||
|
||||
/**
|
||||
* 获取cff偏移
|
||||
*
|
||||
* @param {Reader} reader 读取器
|
||||
* @param {number} offSize 偏移大小
|
||||
* @param {number} offset 起始偏移
|
||||
* @return {number} 偏移
|
||||
*/
|
||||
function getOffset(reader, offSize) {
|
||||
let v = 0;
|
||||
for (let i = 0; i < offSize; i++) {
|
||||
v <<= 8;
|
||||
v += reader.readUint8();
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析cff表头部
|
||||
*
|
||||
* @param {Reader} reader 读取器
|
||||
* @return {Object} 头部字段
|
||||
*/
|
||||
function parseCFFHead(reader) {
|
||||
const head = {};
|
||||
head.startOffset = reader.offset;
|
||||
head.endOffset = head.startOffset + 4;
|
||||
head.formatMajor = reader.readUint8();
|
||||
head.formatMinor = reader.readUint8();
|
||||
head.size = reader.readUint8();
|
||||
head.offsetSize = reader.readUint8();
|
||||
return head;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析`CFF`表索引
|
||||
*
|
||||
* @param {Reader} reader 读取器
|
||||
* @param {number} offset 偏移
|
||||
* @param {Funciton} conversionFn 转换函数
|
||||
* @return {Object} 表对象
|
||||
*/
|
||||
function parseCFFIndex(reader, offset, conversionFn) {
|
||||
if (offset) {
|
||||
reader.seek(offset);
|
||||
}
|
||||
const start = reader.offset;
|
||||
const offsets = [];
|
||||
const objects = [];
|
||||
const count = reader.readUint16();
|
||||
let i;
|
||||
let l;
|
||||
if (count !== 0) {
|
||||
const offsetSize = reader.readUint8();
|
||||
for (i = 0, l = count + 1; i < l; i++) {
|
||||
offsets.push(getOffset(reader, offsetSize));
|
||||
}
|
||||
|
||||
for (i = 0, l = count; i < l; i++) {
|
||||
let value = reader.readBytes(offsets[i + 1] - offsets[i]);
|
||||
if (conversionFn) {
|
||||
value = conversionFn(value);
|
||||
}
|
||||
objects.push(value);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
objects,
|
||||
startOffset: start,
|
||||
endOffset: reader.offset
|
||||
};
|
||||
}
|
||||
|
||||
// Subroutines are encoded using the negative half of the number space.
|
||||
// See type 2 chapter 4.7 "Subroutine operators".
|
||||
function calcCFFSubroutineBias(subrs) {
|
||||
let bias;
|
||||
if (subrs.length < 1240) {
|
||||
bias = 107;
|
||||
}
|
||||
else if (subrs.length < 33900) {
|
||||
bias = 1131;
|
||||
}
|
||||
else {
|
||||
bias = 32768;
|
||||
}
|
||||
|
||||
return bias;
|
||||
}
|
||||
|
||||
|
||||
export default table.create(
|
||||
'cff',
|
||||
[],
|
||||
{
|
||||
read(reader, font) {
|
||||
|
||||
const offset = this.offset;
|
||||
reader.seek(offset);
|
||||
|
||||
const head = parseCFFHead(reader);
|
||||
const nameIndex = parseCFFIndex(reader, head.endOffset, string.getString);
|
||||
const topDictIndex = parseCFFIndex(reader, nameIndex.endOffset);
|
||||
const stringIndex = parseCFFIndex(reader, topDictIndex.endOffset, string.getString);
|
||||
const globalSubrIndex = parseCFFIndex(reader, stringIndex.endOffset);
|
||||
|
||||
const cff = {
|
||||
head
|
||||
};
|
||||
|
||||
// 全局子glyf数据
|
||||
cff.gsubrs = globalSubrIndex.objects;
|
||||
cff.gsubrsBias = calcCFFSubroutineBias(globalSubrIndex.objects);
|
||||
|
||||
// 顶级字典数据
|
||||
const dictReader = new Reader(new Uint8Array(topDictIndex.objects[0]).buffer);
|
||||
const topDict = parseCFFDict.parseTopDict(
|
||||
dictReader,
|
||||
0,
|
||||
dictReader.length,
|
||||
stringIndex.objects
|
||||
);
|
||||
cff.topDict = topDict;
|
||||
|
||||
// 私有字典数据
|
||||
const privateDictLength = topDict.private[0];
|
||||
let privateDict = {};
|
||||
let privateDictOffset;
|
||||
if (privateDictLength) {
|
||||
privateDictOffset = offset + topDict.private[1];
|
||||
privateDict = parseCFFDict.parsePrivateDict(
|
||||
reader,
|
||||
privateDictOffset,
|
||||
privateDictLength,
|
||||
stringIndex.objects
|
||||
);
|
||||
cff.defaultWidthX = privateDict.defaultWidthX;
|
||||
cff.nominalWidthX = privateDict.nominalWidthX;
|
||||
}
|
||||
else {
|
||||
cff.defaultWidthX = 0;
|
||||
cff.nominalWidthX = 0;
|
||||
}
|
||||
|
||||
// 私有子glyf数据
|
||||
if (privateDict.subrs) {
|
||||
const subrOffset = privateDictOffset + privateDict.subrs;
|
||||
const subrIndex = parseCFFIndex(reader, subrOffset);
|
||||
cff.subrs = subrIndex.objects;
|
||||
cff.subrsBias = calcCFFSubroutineBias(cff.subrs);
|
||||
}
|
||||
else {
|
||||
cff.subrs = [];
|
||||
cff.subrsBias = 0;
|
||||
}
|
||||
cff.privateDict = privateDict;
|
||||
|
||||
// 解析glyf数据和名字
|
||||
const charStringsIndex = parseCFFIndex(reader, offset + topDict.charStrings);
|
||||
const nGlyphs = charStringsIndex.objects.length;
|
||||
|
||||
if (topDict.charset < 3) {
|
||||
// @author: fr33z00
|
||||
// See end of chapter 13 (p22) of #5176.CFF.pdf :
|
||||
// Still more optimization is possible by
|
||||
// observing that many fonts adopt one of 3 common charsets. In
|
||||
// these cases the operand to the charset operator in the Top DICT
|
||||
// specifies a predefined charset id, in place of an offset, as shown in table 22
|
||||
cff.charset = cffStandardStrings;
|
||||
}
|
||||
else {
|
||||
cff.charset = parseCFFCharset(reader, offset + topDict.charset, nGlyphs, stringIndex.objects);
|
||||
}
|
||||
|
||||
// Standard encoding
|
||||
if (topDict.encoding === 0) {
|
||||
cff.encoding = encoding.standardEncoding;
|
||||
}
|
||||
// Expert encoding
|
||||
else if (topDict.encoding === 1) {
|
||||
cff.encoding = encoding.expertEncoding;
|
||||
}
|
||||
else {
|
||||
cff.encoding = parseCFFEncoding(reader, offset + topDict.encoding);
|
||||
}
|
||||
|
||||
cff.glyf = [];
|
||||
|
||||
// only parse subset glyphs
|
||||
const subset = font.readOptions.subset;
|
||||
if (subset && subset.length > 0) {
|
||||
|
||||
// subset map
|
||||
const subsetMap = {
|
||||
0: true // 设置.notdef
|
||||
};
|
||||
const codes = font.cmap;
|
||||
|
||||
// unicode to index
|
||||
Object.keys(codes).forEach((c) => {
|
||||
if (subset.indexOf(+c) > -1) {
|
||||
const i = codes[c];
|
||||
subsetMap[i] = true;
|
||||
}
|
||||
});
|
||||
font.subsetMap = subsetMap;
|
||||
|
||||
Object.keys(subsetMap).forEach((i) => {
|
||||
i = +i;
|
||||
const glyf = parseCFFGlyph(charStringsIndex.objects[i], cff, i);
|
||||
glyf.name = cff.charset[i];
|
||||
cff.glyf[i] = glyf;
|
||||
});
|
||||
}
|
||||
// parse all
|
||||
else {
|
||||
for (let i = 0, l = nGlyphs; i < l; i++) {
|
||||
const glyf = parseCFFGlyph(charStringsIndex.objects[i], cff, i);
|
||||
glyf.name = cff.charset[i];
|
||||
cff.glyf.push(glyf);
|
||||
}
|
||||
}
|
||||
|
||||
return cff;
|
||||
},
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
write(writer, font) {
|
||||
throw new Error('not support write cff table');
|
||||
},
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
size(font) {
|
||||
throw new Error('not support get cff table size');
|
||||
}
|
||||
}
|
||||
);
|
||||
30
vendor/fonteditor-core/src/ttf/table/GPOS.js
vendored
30
vendor/fonteditor-core/src/ttf/table/GPOS.js
vendored
@ -1,30 +0,0 @@
|
||||
/**
|
||||
* @file GPOS
|
||||
* @author fr33z00(https://github.com/fr33z00)
|
||||
*
|
||||
* @reference: https://learn.microsoft.com/en-us/typography/opentype/spec/gpos
|
||||
*/
|
||||
|
||||
import table from './table';
|
||||
|
||||
export default table.create(
|
||||
'GPOS',
|
||||
[],
|
||||
{
|
||||
|
||||
read(reader, ttf) {
|
||||
const length = ttf.tables.GPOS.length;
|
||||
return reader.readBytes(this.offset, length);
|
||||
},
|
||||
|
||||
write(writer, ttf) {
|
||||
if (ttf.GPOS) {
|
||||
writer.writeBytes(ttf.GPOS, ttf.GPOS.length);
|
||||
}
|
||||
},
|
||||
|
||||
size(ttf) {
|
||||
return ttf.GPOS ? ttf.GPOS.length : 0;
|
||||
}
|
||||
}
|
||||
);
|
||||
321
vendor/fonteditor-core/src/ttf/table/OS2.js
vendored
321
vendor/fonteditor-core/src/ttf/table/OS2.js
vendored
@ -1,321 +0,0 @@
|
||||
/**
|
||||
* @file OS/2表
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*
|
||||
* http://www.microsoft.com/typography/otspec/os2.htm
|
||||
*/
|
||||
|
||||
import table from './table';
|
||||
import struct from './struct';
|
||||
|
||||
export default table.create(
|
||||
'OS/2',
|
||||
[
|
||||
['version', struct.Uint16],
|
||||
|
||||
['xAvgCharWidth', struct.Int16],
|
||||
['usWeightClass', struct.Uint16],
|
||||
['usWidthClass', struct.Uint16],
|
||||
|
||||
['fsType', struct.Uint16],
|
||||
|
||||
['ySubscriptXSize', struct.Uint16],
|
||||
['ySubscriptYSize', struct.Uint16],
|
||||
['ySubscriptXOffset', struct.Uint16],
|
||||
['ySubscriptYOffset', struct.Uint16],
|
||||
|
||||
['ySuperscriptXSize', struct.Uint16],
|
||||
['ySuperscriptYSize', struct.Uint16],
|
||||
['ySuperscriptXOffset', struct.Uint16],
|
||||
['ySuperscriptYOffset', struct.Uint16],
|
||||
|
||||
['yStrikeoutSize', struct.Uint16],
|
||||
['yStrikeoutPosition', struct.Uint16],
|
||||
|
||||
['sFamilyClass', struct.Uint16],
|
||||
|
||||
// Panose
|
||||
['bFamilyType', struct.Uint8],
|
||||
['bSerifStyle', struct.Uint8],
|
||||
['bWeight', struct.Uint8],
|
||||
['bProportion', struct.Uint8],
|
||||
['bContrast', struct.Uint8],
|
||||
['bStrokeVariation', struct.Uint8],
|
||||
['bArmStyle', struct.Uint8],
|
||||
['bLetterform', struct.Uint8],
|
||||
['bMidline', struct.Uint8],
|
||||
['bXHeight', struct.Uint8],
|
||||
|
||||
// unicode range
|
||||
['ulUnicodeRange1', struct.Uint32],
|
||||
['ulUnicodeRange2', struct.Uint32],
|
||||
['ulUnicodeRange3', struct.Uint32],
|
||||
['ulUnicodeRange4', struct.Uint32],
|
||||
|
||||
// char 4
|
||||
['achVendID', struct.String, 4],
|
||||
|
||||
['fsSelection', struct.Uint16],
|
||||
['usFirstCharIndex', struct.Uint16],
|
||||
['usLastCharIndex', struct.Uint16],
|
||||
|
||||
['sTypoAscender', struct.Int16],
|
||||
['sTypoDescender', struct.Int16],
|
||||
['sTypoLineGap', struct.Int16],
|
||||
|
||||
['usWinAscent', struct.Uint16],
|
||||
['usWinDescent', struct.Uint16],
|
||||
// version 0 above 39
|
||||
|
||||
['ulCodePageRange1', struct.Uint32],
|
||||
['ulCodePageRange2', struct.Uint32],
|
||||
// version 1 above 41
|
||||
|
||||
['sxHeight', struct.Int16],
|
||||
['sCapHeight', struct.Int16],
|
||||
|
||||
['usDefaultChar', struct.Uint16],
|
||||
['usBreakChar', struct.Uint16],
|
||||
['usMaxContext', struct.Uint16]
|
||||
// version 2,3,4 above 46
|
||||
],
|
||||
{
|
||||
|
||||
read(reader, ttf) {
|
||||
const format = reader.readUint16(this.offset);
|
||||
let struct = this.struct;
|
||||
|
||||
// format2
|
||||
if (format === 0) {
|
||||
struct = struct.slice(0, 39);
|
||||
}
|
||||
else if (format === 1) {
|
||||
struct = struct.slice(0, 41);
|
||||
}
|
||||
|
||||
const OS2Head = table.create('os2head', struct);
|
||||
const tbl = new OS2Head(this.offset).read(reader, ttf);
|
||||
|
||||
// 补齐其他version的字段
|
||||
const os2Fields = {
|
||||
ulCodePageRange1: 1,
|
||||
ulCodePageRange2: 0,
|
||||
sxHeight: 0,
|
||||
sCapHeight: 0,
|
||||
usDefaultChar: 0,
|
||||
usBreakChar: 32,
|
||||
usMaxContext: 0
|
||||
};
|
||||
|
||||
return Object.assign(os2Fields, tbl);
|
||||
},
|
||||
|
||||
size(ttf) {
|
||||
|
||||
// 更新其他表的统计信息
|
||||
// header
|
||||
let xMin = 16384;
|
||||
let yMin = 16384;
|
||||
let xMax = -16384;
|
||||
let yMax = -16384;
|
||||
|
||||
// hhea
|
||||
let advanceWidthMax = -1;
|
||||
let minLeftSideBearing = 16384;
|
||||
let minRightSideBearing = 16384;
|
||||
let xMaxExtent = -16384;
|
||||
|
||||
// os2 count
|
||||
let xAvgCharWidth = 0;
|
||||
let usFirstCharIndex = 0x10FFFF;
|
||||
let usLastCharIndex = -1;
|
||||
|
||||
// maxp
|
||||
let maxPoints = 0;
|
||||
let maxContours = 0;
|
||||
let maxCompositePoints = 0;
|
||||
let maxCompositeContours = 0;
|
||||
let maxSizeOfInstructions = 0;
|
||||
let maxComponentElements = 0;
|
||||
|
||||
let glyfNotEmpty = 0; // 非空glyf
|
||||
const hinting = ttf.writeOptions ? ttf.writeOptions.hinting : false;
|
||||
|
||||
// 计算instructions和functiondefs
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
ttf.glyf.forEach((glyf) => {
|
||||
|
||||
// 统计control point信息
|
||||
if (glyf.compound) {
|
||||
let compositeContours = 0;
|
||||
let compositePoints = 0;
|
||||
glyf.glyfs.forEach((g) => {
|
||||
const cglyf = ttf.glyf[g.glyphIndex];
|
||||
if (!cglyf) {
|
||||
return;
|
||||
}
|
||||
compositeContours += cglyf.contours ? cglyf.contours.length : 0;
|
||||
if (cglyf.contours && cglyf.contours.length) {
|
||||
cglyf.contours.forEach((contour) => {
|
||||
compositePoints += contour.length;
|
||||
});
|
||||
}
|
||||
});
|
||||
maxComponentElements = Math.max(maxComponentElements, glyf.glyfs.length);
|
||||
maxCompositePoints = Math.max(maxCompositePoints, compositePoints);
|
||||
maxCompositeContours = Math.max(maxCompositeContours, compositeContours);
|
||||
}
|
||||
// 简单图元
|
||||
else if (glyf.contours && glyf.contours.length) {
|
||||
maxContours = Math.max(maxContours, glyf.contours.length);
|
||||
|
||||
let points = 0;
|
||||
glyf.contours.forEach((contour) => {
|
||||
points += contour.length;
|
||||
});
|
||||
maxPoints = Math.max(maxPoints, points);
|
||||
}
|
||||
|
||||
if (hinting && glyf.instructions) {
|
||||
maxSizeOfInstructions = Math.max(maxSizeOfInstructions, glyf.instructions.length);
|
||||
}
|
||||
|
||||
// 统计边界信息
|
||||
if (null != glyf.xMin && glyf.xMin < xMin) {
|
||||
xMin = glyf.xMin;
|
||||
}
|
||||
|
||||
if (null != glyf.yMin && glyf.yMin < yMin) {
|
||||
yMin = glyf.yMin;
|
||||
}
|
||||
|
||||
if (null != glyf.xMax && glyf.xMax > xMax) {
|
||||
xMax = glyf.xMax;
|
||||
}
|
||||
|
||||
if (null != glyf.yMax && glyf.yMax > yMax) {
|
||||
yMax = glyf.yMax;
|
||||
}
|
||||
|
||||
advanceWidthMax = Math.max(advanceWidthMax, glyf.advanceWidth);
|
||||
minLeftSideBearing = Math.min(minLeftSideBearing, glyf.leftSideBearing);
|
||||
if (null != glyf.xMax) {
|
||||
minRightSideBearing = Math.min(minRightSideBearing, glyf.advanceWidth - glyf.xMax);
|
||||
xMaxExtent = Math.max(xMaxExtent, glyf.xMax);
|
||||
}
|
||||
if (null != glyf.advanceWidth) {
|
||||
xAvgCharWidth += glyf.advanceWidth;
|
||||
glyfNotEmpty++;
|
||||
}
|
||||
|
||||
let unicodes = glyf.unicode;
|
||||
|
||||
if (typeof glyf.unicode === 'number') {
|
||||
unicodes = [glyf.unicode];
|
||||
}
|
||||
|
||||
if (Array.isArray(unicodes)) {
|
||||
unicodes.forEach((unicode) => {
|
||||
if (unicode !== 0xFFFF) {
|
||||
usFirstCharIndex = Math.min(usFirstCharIndex, unicode);
|
||||
usLastCharIndex = Math.max(usLastCharIndex, unicode);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 重新设置version 4
|
||||
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;
|
||||
|
||||
// rewrite hhea
|
||||
ttf.hhea.version = ttf.hhea.version || 0x1;
|
||||
ttf.hhea.advanceWidthMax = advanceWidthMax;
|
||||
ttf.hhea.minLeftSideBearing = minLeftSideBearing;
|
||||
ttf.hhea.minRightSideBearing = minRightSideBearing;
|
||||
ttf.hhea.xMaxExtent = xMaxExtent;
|
||||
|
||||
// rewrite head
|
||||
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 rewrite
|
||||
if (ttf.support.head) {
|
||||
const {xMin, yMin, xMax, yMax} = ttf.support.head;
|
||||
if (xMin != null) {
|
||||
ttf.head.xMin = xMin;
|
||||
}
|
||||
if (yMin != null) {
|
||||
ttf.head.yMin = yMin;
|
||||
}
|
||||
if (xMax != null) {
|
||||
ttf.head.xMax = xMax;
|
||||
}
|
||||
if (yMax != null) {
|
||||
ttf.head.yMax = yMax;
|
||||
}
|
||||
|
||||
}
|
||||
// hhea rewrite
|
||||
if (ttf.support.hhea) {
|
||||
const {advanceWidthMax, xMaxExtent, minLeftSideBearing, minRightSideBearing} = ttf.support.hhea;
|
||||
if (advanceWidthMax != null) {
|
||||
ttf.hhea.advanceWidthMax = advanceWidthMax;
|
||||
}
|
||||
if (xMaxExtent != null) {
|
||||
ttf.hhea.xMaxExtent = xMaxExtent;
|
||||
}
|
||||
if (minLeftSideBearing != null) {
|
||||
ttf.hhea.minLeftSideBearing = minLeftSideBearing;
|
||||
}
|
||||
if (minRightSideBearing != null) {
|
||||
ttf.hhea.minRightSideBearing = minRightSideBearing;
|
||||
}
|
||||
}
|
||||
// 这里根据存储的maxp来设置新的maxp,避免重复计算maxp
|
||||
ttf.maxp = ttf.maxp || {};
|
||||
ttf.support.maxp = {
|
||||
version: 1.0,
|
||||
numGlyphs: ttf.glyf.length,
|
||||
maxPoints,
|
||||
maxContours,
|
||||
maxCompositePoints,
|
||||
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,
|
||||
maxSizeOfInstructions,
|
||||
maxComponentElements,
|
||||
maxComponentDepth: maxComponentElements ? 1 : 0
|
||||
};
|
||||
|
||||
return table.size.call(this, ttf);
|
||||
}
|
||||
}
|
||||
);
|
||||
@ -1,67 +0,0 @@
|
||||
/**
|
||||
* @file cffStandardStrings.js
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
const cffStandardStrings = [
|
||||
'.notdef', 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent', 'ampersand', 'quoteright',
|
||||
'parenleft', 'parenright', 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one', 'two',
|
||||
'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', 'less', 'equal', 'greater',
|
||||
'question', 'at', 'A', 'B', 'C', 'D', 'E',
|
||||
'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
|
||||
'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash', 'bracketright', 'asciicircum', 'underscore',
|
||||
'quoteleft', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
|
||||
'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
|
||||
'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright',
|
||||
'asciitilde', 'exclamdown', 'cent', 'sterling',
|
||||
'fraction', 'yen', 'florin', 'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft',
|
||||
'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'endash', 'dagger',
|
||||
'daggerdbl', 'periodcentered', 'paragraph',
|
||||
'bullet', 'quotesinglbase', 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand',
|
||||
'questiondown', 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve', 'dotaccent', 'dieresis', 'ring',
|
||||
'cedilla', 'hungarumlaut', 'ogonek', 'caron', 'emdash', 'AE', 'ordfeminine', 'Lslash', 'Oslash', 'OE',
|
||||
'ordmasculine', 'ae', 'dotlessi', 'lslash', 'oslash', 'oe', 'germandbls', 'onesuperior', 'logicalnot', 'mu',
|
||||
'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn', 'onequarter', 'divide', 'brokenbar', 'degree', 'thorn',
|
||||
'threequarters', 'twosuperior', 'registered', 'minus', 'eth', 'multiply', 'threesuperior', 'copyright',
|
||||
'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring', 'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex',
|
||||
'Edieresis', 'Egrave', 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute', 'Ocircumflex',
|
||||
'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute', 'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute',
|
||||
'Ydieresis', 'Zcaron', 'aacute', 'acircumflex', 'adieresis',
|
||||
'agrave', 'aring', 'atilde', 'ccedilla', 'eacute',
|
||||
'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex', 'idieresis', 'igrave', 'ntilde', 'oacute',
|
||||
'ocircumflex', 'odieresis', 'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis', 'ugrave',
|
||||
'yacute', 'ydieresis', 'zcaron', 'exclamsmall', 'Hungarumlautsmall', 'dollaroldstyle', 'dollarsuperior',
|
||||
'ampersandsmall', 'Acutesmall', 'parenleftsuperior', 'parenrightsuperior', '266 ff', 'onedotenleader',
|
||||
'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle',
|
||||
'fouroldstyle', 'fiveoldstyle', 'sixoldstyle',
|
||||
'sevenoldstyle', 'eightoldstyle', 'nineoldstyle', 'commasuperior', 'threequartersemdash', 'periodsuperior',
|
||||
'questionsmall', 'asuperior', 'bsuperior', 'centsuperior',
|
||||
'dsuperior', 'esuperior', 'isuperior', 'lsuperior',
|
||||
'msuperior', 'nsuperior', 'osuperior', 'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'ffi', 'ffl',
|
||||
'parenleftinferior', 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall', 'Asmall',
|
||||
'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall',
|
||||
'Hsmall', 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall',
|
||||
'Msmall', 'Nsmall', 'Osmall', 'Psmall', 'Qsmall', 'Rsmall',
|
||||
'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall',
|
||||
'Xsmall', 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall', 'exclamdownsmall',
|
||||
'centoldstyle', 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall', 'Dieresissmall', 'Brevesmall', 'Caronsmall',
|
||||
'Dotaccentsmall', 'Macronsmall', 'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall',
|
||||
'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths', 'onethird', 'twothirds',
|
||||
'zerosuperior', 'foursuperior', 'fivesuperior', 'sixsuperior',
|
||||
'sevensuperior', 'eightsuperior', 'ninesuperior',
|
||||
'zeroinferior', 'oneinferior', 'twoinferior', 'threeinferior',
|
||||
'fourinferior', 'fiveinferior', 'sixinferior',
|
||||
'seveninferior', 'eightinferior', 'nineinferior', 'centinferior', 'dollarinferior', 'periodinferior',
|
||||
'commainferior', 'Agravesmall', 'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall',
|
||||
'Aringsmall', 'AEsmall', 'Ccedillasmall', 'Egravesmall',
|
||||
'Eacutesmall', 'Ecircumflexsmall', 'Edieresissmall',
|
||||
'Igravesmall', 'Iacutesmall', 'Icircumflexsmall',
|
||||
'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall',
|
||||
'Oacutesmall', 'Ocircumflexsmall', 'Otildesmall',
|
||||
'Odieresissmall', 'OEsmall', 'Oslashsmall', 'Ugravesmall',
|
||||
'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall',
|
||||
'Yacutesmall', 'Thornsmall', 'Ydieresissmall', '001.000',
|
||||
'001.001', '001.002', '001.003', 'Black', 'Bold',
|
||||
'Book', 'Light', 'Medium', 'Regular', 'Roman', 'Semibold'
|
||||
];
|
||||
|
||||
export default cffStandardStrings;
|
||||
@ -1,81 +0,0 @@
|
||||
/**
|
||||
* @file cff名字设置
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
|
||||
const cffStandardEncoding = [
|
||||
'', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
|
||||
'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
|
||||
'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent', 'ampersand', 'quoteright',
|
||||
'parenleft', 'parenright', 'asterisk', 'plus', 'comma', 'hyphen',
|
||||
'period', 'slash', 'zero', 'one', 'two',
|
||||
'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', 'less', 'equal', 'greater',
|
||||
'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F',
|
||||
'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
|
||||
'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft',
|
||||
'backslash', 'bracketright', 'asciicircum', 'underscore',
|
||||
'quoteleft', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
|
||||
'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
|
||||
'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar',
|
||||
'braceright', 'asciitilde', '', '', '', '', '', '', '', '',
|
||||
'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
|
||||
'exclamdown', 'cent', 'sterling', 'fraction', 'yen', 'florin', 'section', 'currency', 'quotesingle',
|
||||
'quotedblleft', 'guillemotleft', 'guilsinglleft', 'guilsinglright', 'fi', 'fl', '', 'endash', 'dagger',
|
||||
'daggerdbl', 'periodcentered', '', 'paragraph', 'bullet', 'quotesinglbase', 'quotedblbase', 'quotedblright',
|
||||
'guillemotright', 'ellipsis', 'perthousand', '',
|
||||
'questiondown', '', 'grave', 'acute', 'circumflex', 'tilde',
|
||||
'macron', 'breve', 'dotaccent', 'dieresis', '',
|
||||
'ring', 'cedilla', '', 'hungarumlaut', 'ogonek', 'caron',
|
||||
'emdash', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
|
||||
'AE', '', 'ordfeminine', '', '', '',
|
||||
'', 'Lslash', 'Oslash', 'OE', 'ordmasculine', '', '', '', '', '', 'ae', '', '', '', 'dotlessi', '', '',
|
||||
'lslash', 'oslash', 'oe', 'germandbls'
|
||||
];
|
||||
|
||||
const cffExpertEncoding = [
|
||||
'', '', '', '', '', '', '', '', '', '', '', '', '', '',
|
||||
'', '', '', '', '', '', '', '', '', '', '', '', '', '',
|
||||
'', '', '', '', 'space', 'exclamsmall', 'Hungarumlautsmall', '', 'dollaroldstyle', 'dollarsuperior',
|
||||
'ampersandsmall', 'Acutesmall', 'parenleftsuperior',
|
||||
'parenrightsuperior', 'twodotenleader', 'onedotenleader',
|
||||
'comma', 'hyphen', 'period', 'fraction', 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle',
|
||||
'fouroldstyle', 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle', 'nineoldstyle', 'colon',
|
||||
'semicolon', 'commasuperior', 'threequartersemdash', 'periodsuperior', 'questionsmall', '', 'asuperior',
|
||||
'bsuperior', 'centsuperior', 'dsuperior', 'esuperior',
|
||||
'', '', 'isuperior', '', '', 'lsuperior', 'msuperior',
|
||||
'nsuperior', 'osuperior', '', '', 'rsuperior', 'ssuperior', 'tsuperior', '', 'ff', 'fi', 'fl', 'ffi', 'ffl',
|
||||
'parenleftinferior', '', 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall', 'Asmall',
|
||||
'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall',
|
||||
'Hsmall', 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall',
|
||||
'Msmall', 'Nsmall', 'Osmall', 'Psmall', 'Qsmall', 'Rsmall',
|
||||
'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall',
|
||||
'Xsmall', 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall',
|
||||
'', '', '', '', '', '', '',
|
||||
'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
|
||||
'exclamdownsmall', 'centoldstyle', 'Lslashsmall', '', '', 'Scaronsmall', 'Zcaronsmall', 'Dieresissmall',
|
||||
'Brevesmall', 'Caronsmall', '', 'Dotaccentsmall', '', '',
|
||||
'Macronsmall', '', '', 'figuredash', 'hypheninferior',
|
||||
'', '', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall', '', '', '', 'onequarter', 'onehalf', 'threequarters',
|
||||
'questiondownsmall', 'oneeighth', 'threeeighths',
|
||||
'fiveeighths', 'seveneighths', 'onethird', 'twothirds', '',
|
||||
'', 'zerosuperior', 'onesuperior', 'twosuperior', 'threesuperior', 'foursuperior', 'fivesuperior',
|
||||
'sixsuperior', 'sevensuperior', 'eightsuperior',
|
||||
'ninesuperior', 'zeroinferior', 'oneinferior', 'twoinferior',
|
||||
'threeinferior', 'fourinferior', 'fiveinferior', 'sixinferior', 'seveninferior', 'eightinferior',
|
||||
'nineinferior', 'centinferior', 'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall',
|
||||
'Aacutesmall', 'Acircumflexsmall', 'Atildesmall',
|
||||
'Adieresissmall', 'Aringsmall', 'AEsmall', 'Ccedillasmall',
|
||||
'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall', 'Edieresissmall', 'Igravesmall', 'Iacutesmall',
|
||||
'Icircumflexsmall', 'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall',
|
||||
'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall',
|
||||
'OEsmall', 'Oslashsmall', 'Ugravesmall', 'Uacutesmall',
|
||||
'Ucircumflexsmall', 'Udieresissmall', 'Yacutesmall', 'Thornsmall', 'Ydieresissmall'
|
||||
];
|
||||
|
||||
|
||||
|
||||
export default {
|
||||
standardEncoding: cffStandardEncoding,
|
||||
expertEncoding: cffExpertEncoding
|
||||
};
|
||||
@ -1,25 +0,0 @@
|
||||
/**
|
||||
* @file 获取cff字符串
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
import cffStandardStrings from './cffStandardStrings';
|
||||
|
||||
/**
|
||||
* 根据索引获取cff字符串
|
||||
*
|
||||
* @param {Object} strings 标准cff字符串索引
|
||||
* @param {number} index 索引号
|
||||
* @return {number} 字符串索引
|
||||
*/
|
||||
export default function getCFFString(strings, index) {
|
||||
if (index <= 390) {
|
||||
index = cffStandardStrings[index];
|
||||
}
|
||||
// Strings below index 392 are standard CFF strings and are not encoded in the font.
|
||||
else {
|
||||
index = strings[index - 391];
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
@ -1,62 +0,0 @@
|
||||
/**
|
||||
* @file 解析cff字符集
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
import getCFFString from './getCFFString';
|
||||
|
||||
/**
|
||||
* 解析cff字形名称
|
||||
* See Adobe TN #5176 chapter 13, "Charsets".
|
||||
*
|
||||
* @param {Reader} reader 读取器
|
||||
* @param {number} start 起始偏移
|
||||
* @param {number} nGlyphs 字形个数
|
||||
* @param {Object} strings cff字符串字典
|
||||
* @return {Array} 字符集
|
||||
*/
|
||||
export default function parseCFFCharset(reader, start, nGlyphs, strings) {
|
||||
if (start) {
|
||||
reader.seek(start);
|
||||
}
|
||||
|
||||
let i;
|
||||
let sid;
|
||||
let count;
|
||||
// The .notdef glyph is not included, so subtract 1.
|
||||
nGlyphs -= 1;
|
||||
const charset = ['.notdef'];
|
||||
|
||||
const format = reader.readUint8();
|
||||
if (format === 0) {
|
||||
for (i = 0; i < nGlyphs; i += 1) {
|
||||
sid = reader.readUint16();
|
||||
charset.push(getCFFString(strings, sid));
|
||||
}
|
||||
}
|
||||
else if (format === 1) {
|
||||
while (charset.length <= nGlyphs) {
|
||||
sid = reader.readUint16();
|
||||
count = reader.readUint8();
|
||||
for (i = 0; i <= count; i += 1) {
|
||||
charset.push(getCFFString(strings, sid));
|
||||
sid += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (format === 2) {
|
||||
while (charset.length <= nGlyphs) {
|
||||
sid = reader.readUint16();
|
||||
count = reader.readUint16();
|
||||
for (i = 0; i <= count; i += 1) {
|
||||
charset.push(getCFFString(strings, sid));
|
||||
sid += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new Error('Unknown charset format ' + format);
|
||||
}
|
||||
|
||||
return charset;
|
||||
}
|
||||
@ -1,350 +0,0 @@
|
||||
/**
|
||||
* @file 解析cffdict数据
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
import getCFFString from './getCFFString';
|
||||
|
||||
const TOP_DICT_META = [
|
||||
{
|
||||
name: 'version',
|
||||
op: 0,
|
||||
type: 'SID'
|
||||
},
|
||||
{
|
||||
name: 'notice',
|
||||
op: 1,
|
||||
type: 'SID'
|
||||
},
|
||||
{
|
||||
name: 'copyright',
|
||||
op: 1200,
|
||||
type: 'SID'
|
||||
},
|
||||
{
|
||||
name: 'fullName',
|
||||
op: 2,
|
||||
type: 'SID'
|
||||
},
|
||||
{
|
||||
name: 'familyName',
|
||||
op: 3,
|
||||
type: 'SID'
|
||||
},
|
||||
{
|
||||
name: 'weight',
|
||||
op: 4,
|
||||
type: 'SID'
|
||||
},
|
||||
{
|
||||
name: 'isFixedPitch',
|
||||
op: 1201,
|
||||
type: 'number',
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
name: 'italicAngle',
|
||||
op: 1202,
|
||||
type: 'number',
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
name: 'underlinePosition',
|
||||
op: 1203,
|
||||
type: 'number',
|
||||
value: -100
|
||||
},
|
||||
{
|
||||
name: 'underlineThickness',
|
||||
op: 1204,
|
||||
type: 'number',
|
||||
value: 50
|
||||
},
|
||||
{
|
||||
name: 'paintType',
|
||||
op: 1205,
|
||||
type: 'number',
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
name: 'charstringType',
|
||||
op: 1206,
|
||||
type: 'number',
|
||||
value: 2
|
||||
},
|
||||
{
|
||||
name: 'fontMatrix',
|
||||
op: 1207,
|
||||
type: ['real', 'real', 'real', 'real', 'real', 'real'],
|
||||
value: [0.001, 0, 0, 0.001, 0, 0]
|
||||
},
|
||||
{
|
||||
name: 'uniqueId',
|
||||
op: 13,
|
||||
type: 'number'
|
||||
},
|
||||
{
|
||||
name: 'fontBBox',
|
||||
op: 5,
|
||||
type: ['number', 'number', 'number', 'number'],
|
||||
value: [0, 0, 0, 0]
|
||||
},
|
||||
{
|
||||
name: 'strokeWidth',
|
||||
op: 1208,
|
||||
type: 'number',
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
name: 'xuid',
|
||||
op: 14,
|
||||
type: [],
|
||||
value: null
|
||||
},
|
||||
{
|
||||
name: 'charset',
|
||||
op: 15,
|
||||
type: 'offset',
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
name: 'encoding',
|
||||
op: 16,
|
||||
type: 'offset',
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
name: 'charStrings',
|
||||
op: 17,
|
||||
type: 'offset',
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
name: 'private',
|
||||
op: 18,
|
||||
type: ['number', 'offset'],
|
||||
value: [0, 0]
|
||||
}
|
||||
];
|
||||
|
||||
const PRIVATE_DICT_META = [
|
||||
{
|
||||
name: 'subrs',
|
||||
op: 19,
|
||||
type: 'offset',
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
name: 'defaultWidthX',
|
||||
op: 20,
|
||||
type: 'number',
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
name: 'nominalWidthX',
|
||||
op: 21,
|
||||
type: 'number',
|
||||
value: 0
|
||||
}
|
||||
];
|
||||
|
||||
function entriesToObject(entries) {
|
||||
const hash = {};
|
||||
|
||||
for (let i = 0, l = entries.length; i < l; i++) {
|
||||
const key = entries[i][0];
|
||||
if (undefined !== hash[key]) {
|
||||
console.warn('dict already has key:' + key);
|
||||
continue;
|
||||
}
|
||||
|
||||
const values = entries[i][1];
|
||||
hash[key] = values.length === 1 ? values[0] : values;
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
|
||||
/* eslint-disable no-constant-condition */
|
||||
function parseFloatOperand(reader) {
|
||||
let s = '';
|
||||
const eof = 15;
|
||||
const lookup = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', 'E', 'E-', null, '-'];
|
||||
|
||||
while (true) {
|
||||
const b = reader.readUint8();
|
||||
const n1 = b >> 4;
|
||||
const n2 = b & 15;
|
||||
|
||||
if (n1 === eof) {
|
||||
break;
|
||||
}
|
||||
|
||||
s += lookup[n1];
|
||||
|
||||
if (n2 === eof) {
|
||||
break;
|
||||
}
|
||||
|
||||
s += lookup[n2];
|
||||
}
|
||||
|
||||
return parseFloat(s);
|
||||
}
|
||||
/* eslint-enable no-constant-condition */
|
||||
|
||||
/**
|
||||
* 解析cff字典数据
|
||||
*
|
||||
* @param {Reader} reader 读取器
|
||||
* @param {number} b0 操作码
|
||||
* @return {number} 数据
|
||||
*/
|
||||
function parseOperand(reader, b0) {
|
||||
let b1;
|
||||
let b2;
|
||||
let b3;
|
||||
let b4;
|
||||
if (b0 === 28) {
|
||||
b1 = reader.readUint8();
|
||||
b2 = reader.readUint8();
|
||||
return b1 << 8 | b2;
|
||||
}
|
||||
|
||||
if (b0 === 29) {
|
||||
b1 = reader.readUint8();
|
||||
b2 = reader.readUint8();
|
||||
b3 = reader.readUint8();
|
||||
b4 = reader.readUint8();
|
||||
return b1 << 24 | b2 << 16 | b3 << 8 | b4;
|
||||
}
|
||||
|
||||
if (b0 === 30) {
|
||||
return parseFloatOperand(reader);
|
||||
}
|
||||
|
||||
if (b0 >= 32 && b0 <= 246) {
|
||||
return b0 - 139;
|
||||
}
|
||||
|
||||
if (b0 >= 247 && b0 <= 250) {
|
||||
b1 = reader.readUint8();
|
||||
return (b0 - 247) * 256 + b1 + 108;
|
||||
}
|
||||
|
||||
if (b0 >= 251 && b0 <= 254) {
|
||||
b1 = reader.readUint8();
|
||||
return -(b0 - 251) * 256 - b1 - 108;
|
||||
}
|
||||
|
||||
throw new Error('invalid b0 ' + b0 + ',at:' + reader.offset);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 解析字典值
|
||||
*
|
||||
* @param {Object} dict 字典数据
|
||||
* @param {Array} meta 元数据
|
||||
* @param {Object} strings cff字符串字典
|
||||
* @return {Object} 解析后数据
|
||||
*/
|
||||
function interpretDict(dict, meta, strings) {
|
||||
const newDict = {};
|
||||
|
||||
// Because we also want to include missing values, we start out from the meta list
|
||||
// and lookup values in the dict.
|
||||
for (let i = 0, l = meta.length; i < l; i++) {
|
||||
const m = meta[i];
|
||||
let value = dict[m.op];
|
||||
if (value === undefined) {
|
||||
value = m.value !== undefined ? m.value : null;
|
||||
}
|
||||
|
||||
if (m.type === 'SID') {
|
||||
value = getCFFString(strings, value);
|
||||
}
|
||||
|
||||
newDict[m.name] = value;
|
||||
}
|
||||
|
||||
return newDict;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 解析cff dict字典
|
||||
*
|
||||
* @param {Reader} reader 读取器
|
||||
* @param {number} offset 起始偏移
|
||||
* @param {number} length 大小
|
||||
* @return {Object} 配置
|
||||
*/
|
||||
function parseCFFDict(reader, offset, length) {
|
||||
if (null != offset) {
|
||||
reader.seek(offset);
|
||||
}
|
||||
|
||||
const entries = [];
|
||||
let operands = [];
|
||||
const lastOffset = reader.offset + (null != length ? length : reader.length);
|
||||
|
||||
while (reader.offset < lastOffset) {
|
||||
let op = reader.readUint8();
|
||||
|
||||
// The first byte for each dict item distinguishes between operator (key) and operand (value).
|
||||
// Values <= 21 are operators.
|
||||
if (op <= 21) {
|
||||
// Two-byte operators have an initial escape byte of 12.
|
||||
if (op === 12) {
|
||||
op = 1200 + reader.readUint8();
|
||||
}
|
||||
|
||||
entries.push([op, operands]);
|
||||
operands = [];
|
||||
}
|
||||
else {
|
||||
// Since the operands (values) come before the operators (keys), we store all operands in a list
|
||||
// until we encounter an operator.
|
||||
operands.push(parseOperand(reader, op));
|
||||
}
|
||||
}
|
||||
|
||||
return entriesToObject(entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析cff top字典
|
||||
*
|
||||
* @param {Reader} reader 读取器
|
||||
* @param {number} start 开始offset
|
||||
* @param {number} length 大小
|
||||
* @param {Object} strings 字符串集合
|
||||
* @return {Object} 字典数据
|
||||
*/
|
||||
function parseTopDict(reader, start, length, strings) {
|
||||
const dict = parseCFFDict(reader, start || 0, length || reader.length);
|
||||
return interpretDict(dict, TOP_DICT_META, strings);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析cff私有字典
|
||||
*
|
||||
* @param {Reader} reader 读取器
|
||||
* @param {number} start 开始offset
|
||||
* @param {number} length 大小
|
||||
* @param {Object} strings 字符串集合
|
||||
* @return {Object} 字典数据
|
||||
*/
|
||||
function parsePrivateDict(reader, start, length, strings) {
|
||||
const dict = parseCFFDict(reader, start || 0, length || reader.length);
|
||||
return interpretDict(dict, PRIVATE_DICT_META, strings);
|
||||
}
|
||||
|
||||
|
||||
export default {
|
||||
parseTopDict,
|
||||
parsePrivateDict
|
||||
};
|
||||
@ -1,48 +0,0 @@
|
||||
/**
|
||||
* @file 解析cff编码
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 解析cff encoding数据
|
||||
* See Adobe TN #5176 chapter 12, "Encodings".
|
||||
*
|
||||
* @param {Reader} reader 读取器
|
||||
* @param {number=} start 偏移
|
||||
* @return {Object} 编码表
|
||||
*/
|
||||
export default function parseCFFEncoding(reader, start) {
|
||||
if (null != start) {
|
||||
reader.seek(start);
|
||||
}
|
||||
|
||||
let i;
|
||||
let code;
|
||||
const encoding = {};
|
||||
const format = reader.readUint8();
|
||||
|
||||
if (format === 0) {
|
||||
const nCodes = reader.readUint8();
|
||||
for (i = 0; i < nCodes; i += 1) {
|
||||
code = reader.readUint8();
|
||||
encoding[code] = i;
|
||||
}
|
||||
}
|
||||
else if (format === 1) {
|
||||
const nRanges = reader.readUint8();
|
||||
code = 1;
|
||||
for (i = 0; i < nRanges; i += 1) {
|
||||
const first = reader.readUint8();
|
||||
const nLeft = reader.readUint8();
|
||||
for (let j = first; j <= first + nLeft; j += 1) {
|
||||
encoding[j] = code;
|
||||
code += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
console.warn('unknown encoding format:' + format);
|
||||
}
|
||||
|
||||
return encoding;
|
||||
}
|
||||
@ -1,489 +0,0 @@
|
||||
/**
|
||||
* @file 解析cff字形
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 解析cff字形,返回直线和三次bezier曲线点数组
|
||||
*
|
||||
* @param {Array} code 操作码
|
||||
* @param {Object} font 相关联的font对象
|
||||
* @param {number} index glyf索引
|
||||
* @return {Object} glyf对象
|
||||
*/
|
||||
export default function parseCFFCharstring(code, font, index) {
|
||||
let c1x;
|
||||
let c1y;
|
||||
let c2x;
|
||||
let c2y;
|
||||
const contours = [];
|
||||
let contour = [];
|
||||
const stack = [];
|
||||
const glyfs = [];
|
||||
let nStems = 0;
|
||||
let haveWidth = false;
|
||||
let width = font.defaultWidthX;
|
||||
let open = false;
|
||||
let x = 0;
|
||||
let y = 0;
|
||||
|
||||
function lineTo(x, y) {
|
||||
contour.push({
|
||||
onCurve: true,
|
||||
x,
|
||||
y
|
||||
});
|
||||
}
|
||||
|
||||
function curveTo(c1x, c1y, c2x, c2y, x, y) {
|
||||
contour.push({
|
||||
x: c1x,
|
||||
y: c1y
|
||||
});
|
||||
contour.push({
|
||||
x: c2x,
|
||||
y: c2y
|
||||
});
|
||||
contour.push({
|
||||
onCurve: true,
|
||||
x,
|
||||
y
|
||||
});
|
||||
}
|
||||
|
||||
function newContour(x, y) {
|
||||
if (open) {
|
||||
contours.push(contour);
|
||||
}
|
||||
|
||||
contour = [];
|
||||
lineTo(x, y);
|
||||
open = true;
|
||||
}
|
||||
|
||||
function parseStems() {
|
||||
// The number of stem operators on the stack is always even.
|
||||
// If the value is uneven, that means a width is specified.
|
||||
const hasWidthArg = stack.length % 2 !== 0;
|
||||
if (hasWidthArg && !haveWidth) {
|
||||
width = stack.shift() + font.nominalWidthX;
|
||||
}
|
||||
|
||||
nStems += stack.length >> 1;
|
||||
stack.length = 0;
|
||||
haveWidth = true;
|
||||
}
|
||||
|
||||
function parse(code) {
|
||||
let b1;
|
||||
let b2;
|
||||
let b3;
|
||||
let b4;
|
||||
let codeIndex;
|
||||
let subrCode;
|
||||
let jpx;
|
||||
let jpy;
|
||||
let c3x;
|
||||
let c3y;
|
||||
let c4x;
|
||||
let c4y;
|
||||
|
||||
let i = 0;
|
||||
while (i < code.length) {
|
||||
let v = code[i];
|
||||
i += 1;
|
||||
switch (v) {
|
||||
case 1: // hstem
|
||||
parseStems();
|
||||
break;
|
||||
case 3: // vstem
|
||||
parseStems();
|
||||
break;
|
||||
case 4: // vmoveto
|
||||
if (stack.length > 1 && !haveWidth) {
|
||||
width = stack.shift() + font.nominalWidthX;
|
||||
haveWidth = true;
|
||||
}
|
||||
|
||||
y += stack.pop();
|
||||
newContour(x, y);
|
||||
break;
|
||||
case 5: // rlineto
|
||||
while (stack.length > 0) {
|
||||
x += stack.shift();
|
||||
y += stack.shift();
|
||||
lineTo(x, y);
|
||||
}
|
||||
|
||||
break;
|
||||
case 6: // hlineto
|
||||
while (stack.length > 0) {
|
||||
x += stack.shift();
|
||||
lineTo(x, y);
|
||||
if (stack.length === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
y += stack.shift();
|
||||
lineTo(x, y);
|
||||
}
|
||||
|
||||
break;
|
||||
case 7: // vlineto
|
||||
while (stack.length > 0) {
|
||||
y += stack.shift();
|
||||
lineTo(x, y);
|
||||
if (stack.length === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
x += stack.shift();
|
||||
lineTo(x, y);
|
||||
}
|
||||
|
||||
break;
|
||||
case 8: // rrcurveto
|
||||
while (stack.length > 0) {
|
||||
c1x = x + stack.shift();
|
||||
c1y = y + stack.shift();
|
||||
c2x = c1x + stack.shift();
|
||||
c2y = c1y + stack.shift();
|
||||
x = c2x + stack.shift();
|
||||
y = c2y + stack.shift();
|
||||
curveTo(c1x, c1y, c2x, c2y, x, y);
|
||||
}
|
||||
|
||||
break;
|
||||
case 10: // callsubr
|
||||
codeIndex = stack.pop() + font.subrsBias;
|
||||
subrCode = font.subrs[codeIndex];
|
||||
if (subrCode) {
|
||||
parse(subrCode);
|
||||
}
|
||||
|
||||
break;
|
||||
case 11: // return
|
||||
return;
|
||||
case 12: // flex operators
|
||||
v = code[i];
|
||||
i += 1;
|
||||
switch (v) {
|
||||
case 35: // flex
|
||||
// |- dx1 dy1 dx2 dy2 dx3 dy3 dx4 dy4 dx5 dy5 dx6 dy6 fd flex (12 35) |-
|
||||
c1x = x + stack.shift(); // dx1
|
||||
c1y = y + stack.shift(); // dy1
|
||||
c2x = c1x + stack.shift(); // dx2
|
||||
c2y = c1y + stack.shift(); // dy2
|
||||
jpx = c2x + stack.shift(); // dx3
|
||||
jpy = c2y + stack.shift(); // dy3
|
||||
c3x = jpx + stack.shift(); // dx4
|
||||
c3y = jpy + stack.shift(); // dy4
|
||||
c4x = c3x + stack.shift(); // dx5
|
||||
c4y = c3y + stack.shift(); // dy5
|
||||
x = c4x + stack.shift(); // dx6
|
||||
y = c4y + stack.shift(); // dy6
|
||||
stack.shift(); // flex depth
|
||||
curveTo(c1x, c1y, c2x, c2y, jpx, jpy);
|
||||
curveTo(c3x, c3y, c4x, c4y, x, y);
|
||||
break;
|
||||
case 34: // hflex
|
||||
// |- dx1 dx2 dy2 dx3 dx4 dx5 dx6 hflex (12 34) |-
|
||||
c1x = x + stack.shift(); // dx1
|
||||
c1y = y; // dy1
|
||||
c2x = c1x + stack.shift(); // dx2
|
||||
c2y = c1y + stack.shift(); // dy2
|
||||
jpx = c2x + stack.shift(); // dx3
|
||||
jpy = c2y; // dy3
|
||||
c3x = jpx + stack.shift(); // dx4
|
||||
c3y = c2y; // dy4
|
||||
c4x = c3x + stack.shift(); // dx5
|
||||
c4y = y; // dy5
|
||||
x = c4x + stack.shift(); // dx6
|
||||
curveTo(c1x, c1y, c2x, c2y, jpx, jpy);
|
||||
curveTo(c3x, c3y, c4x, c4y, x, y);
|
||||
break;
|
||||
case 36: // hflex1
|
||||
// |- dx1 dy1 dx2 dy2 dx3 dx4 dx5 dy5 dx6 hflex1 (12 36) |-
|
||||
c1x = x + stack.shift(); // dx1
|
||||
c1y = y + stack.shift(); // dy1
|
||||
c2x = c1x + stack.shift(); // dx2
|
||||
c2y = c1y + stack.shift(); // dy2
|
||||
jpx = c2x + stack.shift(); // dx3
|
||||
jpy = c2y; // dy3
|
||||
c3x = jpx + stack.shift(); // dx4
|
||||
c3y = c2y; // dy4
|
||||
c4x = c3x + stack.shift(); // dx5
|
||||
c4y = c3y + stack.shift(); // dy5
|
||||
x = c4x + stack.shift(); // dx6
|
||||
curveTo(c1x, c1y, c2x, c2y, jpx, jpy);
|
||||
curveTo(c3x, c3y, c4x, c4y, x, y);
|
||||
break;
|
||||
case 37: // flex1
|
||||
// |- dx1 dy1 dx2 dy2 dx3 dy3 dx4 dy4 dx5 dy5 d6 flex1 (12 37) |-
|
||||
c1x = x + stack.shift(); // dx1
|
||||
c1y = y + stack.shift(); // dy1
|
||||
c2x = c1x + stack.shift(); // dx2
|
||||
c2y = c1y + stack.shift(); // dy2
|
||||
jpx = c2x + stack.shift(); // dx3
|
||||
jpy = c2y + stack.shift(); // dy3
|
||||
c3x = jpx + stack.shift(); // dx4
|
||||
c3y = jpy + stack.shift(); // dy4
|
||||
c4x = c3x + stack.shift(); // dx5
|
||||
c4y = c3y + stack.shift(); // dy5
|
||||
if (Math.abs(c4x - x) > Math.abs(c4y - y)) {
|
||||
x = c4x + stack.shift();
|
||||
}
|
||||
else {
|
||||
y = c4y + stack.shift();
|
||||
}
|
||||
|
||||
curveTo(c1x, c1y, c2x, c2y, jpx, jpy);
|
||||
curveTo(c3x, c3y, c4x, c4y, x, y);
|
||||
break;
|
||||
default:
|
||||
console.warn('Glyph ' + index + ': unknown operator ' + (1200 + v));
|
||||
stack.length = 0;
|
||||
}
|
||||
break;
|
||||
case 14: // endchar
|
||||
if (stack.length === 1 && !haveWidth) {
|
||||
width = stack.shift() + font.nominalWidthX;
|
||||
haveWidth = true;
|
||||
}
|
||||
else if (stack.length === 4) {
|
||||
glyfs[1] = {
|
||||
glyphIndex: font.charset.indexOf(font.encoding[stack.pop()]),
|
||||
transform: {a: 1, b: 0, c: 0, d: 1, e: 0, f: 0}
|
||||
};
|
||||
glyfs[0] = {
|
||||
glyphIndex: font.charset.indexOf(font.encoding[stack.pop()]),
|
||||
transform: {a: 1, b: 0, c: 0, d: 1, e: 0, f: 0}
|
||||
};
|
||||
glyfs[1].transform.f = stack.pop();
|
||||
glyfs[1].transform.e = stack.pop();
|
||||
}
|
||||
else if (stack.length === 5) {
|
||||
if (!haveWidth) {
|
||||
width = stack.shift() + font.nominalWidthX;
|
||||
}
|
||||
haveWidth = true;
|
||||
glyfs[1] = {
|
||||
glyphIndex: font.charset.indexOf(font.encoding[stack.pop()]),
|
||||
transform: {a: 1, b: 0, c: 0, d: 1, e: 0, f: 0}
|
||||
};
|
||||
glyfs[0] = {
|
||||
glyphIndex: font.charset.indexOf(font.encoding[stack.pop()]),
|
||||
transform: {a: 1, b: 0, c: 0, d: 1, e: 0, f: 0}
|
||||
};
|
||||
glyfs[1].transform.f = stack.pop();
|
||||
glyfs[1].transform.e = stack.pop();
|
||||
}
|
||||
|
||||
if (open) {
|
||||
contours.push(contour);
|
||||
open = false;
|
||||
}
|
||||
|
||||
break;
|
||||
case 18: // hstemhm
|
||||
parseStems();
|
||||
break;
|
||||
case 19: // hintmask
|
||||
case 20: // cntrmask
|
||||
parseStems();
|
||||
i += (nStems + 7) >> 3;
|
||||
break;
|
||||
case 21: // rmoveto
|
||||
if (stack.length > 2 && !haveWidth) {
|
||||
width = stack.shift() + font.nominalWidthX;
|
||||
haveWidth = true;
|
||||
}
|
||||
|
||||
y += stack.pop();
|
||||
x += stack.pop();
|
||||
newContour(x, y);
|
||||
break;
|
||||
case 22: // hmoveto
|
||||
if (stack.length > 1 && !haveWidth) {
|
||||
width = stack.shift() + font.nominalWidthX;
|
||||
haveWidth = true;
|
||||
}
|
||||
|
||||
x += stack.pop();
|
||||
newContour(x, y);
|
||||
break;
|
||||
case 23: // vstemhm
|
||||
parseStems();
|
||||
break;
|
||||
case 24: // rcurveline
|
||||
while (stack.length > 2) {
|
||||
c1x = x + stack.shift();
|
||||
c1y = y + stack.shift();
|
||||
c2x = c1x + stack.shift();
|
||||
c2y = c1y + stack.shift();
|
||||
x = c2x + stack.shift();
|
||||
y = c2y + stack.shift();
|
||||
curveTo(c1x, c1y, c2x, c2y, x, y);
|
||||
}
|
||||
|
||||
x += stack.shift();
|
||||
y += stack.shift();
|
||||
lineTo(x, y);
|
||||
break;
|
||||
case 25: // rlinecurve
|
||||
while (stack.length > 6) {
|
||||
x += stack.shift();
|
||||
y += stack.shift();
|
||||
lineTo(x, y);
|
||||
}
|
||||
|
||||
c1x = x + stack.shift();
|
||||
c1y = y + stack.shift();
|
||||
c2x = c1x + stack.shift();
|
||||
c2y = c1y + stack.shift();
|
||||
x = c2x + stack.shift();
|
||||
y = c2y + stack.shift();
|
||||
curveTo(c1x, c1y, c2x, c2y, x, y);
|
||||
break;
|
||||
case 26: // vvcurveto
|
||||
if (stack.length % 2) {
|
||||
x += stack.shift();
|
||||
}
|
||||
|
||||
while (stack.length > 0) {
|
||||
c1x = x;
|
||||
c1y = y + stack.shift();
|
||||
c2x = c1x + stack.shift();
|
||||
c2y = c1y + stack.shift();
|
||||
x = c2x;
|
||||
y = c2y + stack.shift();
|
||||
curveTo(c1x, c1y, c2x, c2y, x, y);
|
||||
}
|
||||
|
||||
break;
|
||||
case 27: // hhcurveto
|
||||
if (stack.length % 2) {
|
||||
y += stack.shift();
|
||||
}
|
||||
|
||||
while (stack.length > 0) {
|
||||
c1x = x + stack.shift();
|
||||
c1y = y;
|
||||
c2x = c1x + stack.shift();
|
||||
c2y = c1y + stack.shift();
|
||||
x = c2x + stack.shift();
|
||||
y = c2y;
|
||||
curveTo(c1x, c1y, c2x, c2y, x, y);
|
||||
}
|
||||
|
||||
break;
|
||||
case 28: // shortint
|
||||
b1 = code[i];
|
||||
b2 = code[i + 1];
|
||||
stack.push(((b1 << 24) | (b2 << 16)) >> 16);
|
||||
i += 2;
|
||||
break;
|
||||
case 29: // callgsubr
|
||||
codeIndex = stack.pop() + font.gsubrsBias;
|
||||
subrCode = font.gsubrs[codeIndex];
|
||||
if (subrCode) {
|
||||
parse(subrCode);
|
||||
}
|
||||
|
||||
break;
|
||||
case 30: // vhcurveto
|
||||
while (stack.length > 0) {
|
||||
c1x = x;
|
||||
c1y = y + stack.shift();
|
||||
c2x = c1x + stack.shift();
|
||||
c2y = c1y + stack.shift();
|
||||
x = c2x + stack.shift();
|
||||
y = c2y + (stack.length === 1 ? stack.shift() : 0);
|
||||
curveTo(c1x, c1y, c2x, c2y, x, y);
|
||||
if (stack.length === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
c1x = x + stack.shift();
|
||||
c1y = y;
|
||||
c2x = c1x + stack.shift();
|
||||
c2y = c1y + stack.shift();
|
||||
y = c2y + stack.shift();
|
||||
x = c2x + (stack.length === 1 ? stack.shift() : 0);
|
||||
curveTo(c1x, c1y, c2x, c2y, x, y);
|
||||
}
|
||||
|
||||
break;
|
||||
case 31: // hvcurveto
|
||||
while (stack.length > 0) {
|
||||
c1x = x + stack.shift();
|
||||
c1y = y;
|
||||
c2x = c1x + stack.shift();
|
||||
c2y = c1y + stack.shift();
|
||||
y = c2y + stack.shift();
|
||||
x = c2x + (stack.length === 1 ? stack.shift() : 0);
|
||||
curveTo(c1x, c1y, c2x, c2y, x, y);
|
||||
if (stack.length === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
c1x = x;
|
||||
c1y = y + stack.shift();
|
||||
c2x = c1x + stack.shift();
|
||||
c2y = c1y + stack.shift();
|
||||
x = c2x + stack.shift();
|
||||
y = c2y + (stack.length === 1 ? stack.shift() : 0);
|
||||
curveTo(c1x, c1y, c2x, c2y, x, y);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
if (v < 32) {
|
||||
console.warn('Glyph ' + index + ': unknown operator ' + v);
|
||||
}
|
||||
else if (v < 247) {
|
||||
stack.push(v - 139);
|
||||
}
|
||||
else if (v < 251) {
|
||||
b1 = code[i];
|
||||
i += 1;
|
||||
stack.push((v - 247) * 256 + b1 + 108);
|
||||
}
|
||||
else if (v < 255) {
|
||||
b1 = code[i];
|
||||
i += 1;
|
||||
stack.push(-(v - 251) * 256 - b1 - 108);
|
||||
}
|
||||
else {
|
||||
b1 = code[i];
|
||||
b2 = code[i + 1];
|
||||
b3 = code[i + 2];
|
||||
b4 = code[i + 3];
|
||||
i += 4;
|
||||
stack.push(((b1 << 24) | (b2 << 16) | (b3 << 8) | b4) / 65536);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parse(code);
|
||||
|
||||
const glyf = {
|
||||
|
||||
// 移除重复的起点和终点
|
||||
contours: contours.map(contour => {
|
||||
const last = contour.length - 1;
|
||||
if (contour[0].x === contour[last].x && contour[0].y === contour[last].y) {
|
||||
contour.splice(last, 1);
|
||||
}
|
||||
return contour;
|
||||
}),
|
||||
|
||||
advanceWidth: width
|
||||
};
|
||||
if (glyfs.length) {
|
||||
glyf.compound = true;
|
||||
glyf.glyfs = glyfs;
|
||||
}
|
||||
return glyf;
|
||||
}
|
||||
23
vendor/fonteditor-core/src/ttf/table/cmap.js
vendored
23
vendor/fonteditor-core/src/ttf/table/cmap.js
vendored
@ -1,23 +0,0 @@
|
||||
/**
|
||||
* @file cmap 表
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*
|
||||
* @see
|
||||
* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6cmap.html
|
||||
*/
|
||||
|
||||
import table from './table';
|
||||
import parse from './cmap/parse';
|
||||
import write from './cmap/write';
|
||||
import sizeof from './cmap/sizeof';
|
||||
|
||||
export default table.create(
|
||||
'cmap',
|
||||
[],
|
||||
{
|
||||
write,
|
||||
read: parse,
|
||||
size: sizeof
|
||||
}
|
||||
);
|
||||
|
||||
253
vendor/fonteditor-core/src/ttf/table/cmap/parse.js
vendored
253
vendor/fonteditor-core/src/ttf/table/cmap/parse.js
vendored
@ -1,253 +0,0 @@
|
||||
/**
|
||||
* @file 解析cmap表
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
import readWindowsAllCodes from '../../util/readWindowsAllCodes';
|
||||
|
||||
/**
|
||||
* 读取cmap子表
|
||||
*
|
||||
* @param {Reader} reader Reader对象
|
||||
* @param {Object} ttf ttf对象
|
||||
* @param {Object} subTable 子表对象
|
||||
* @param {number} cmapOffset 子表的偏移
|
||||
*/
|
||||
function readSubTable(reader, ttf, subTable, cmapOffset) {
|
||||
let i;
|
||||
let l;
|
||||
let glyphIdArray;
|
||||
const startOffset = cmapOffset + subTable.offset;
|
||||
let glyphCount;
|
||||
subTable.format = reader.readUint16(startOffset);
|
||||
|
||||
// 0~256 紧凑排列
|
||||
if (subTable.format === 0) {
|
||||
const format0 = subTable;
|
||||
// 跳过format字段
|
||||
format0.length = reader.readUint16();
|
||||
format0.language = reader.readUint16();
|
||||
glyphIdArray = [];
|
||||
for (i = 0, l = format0.length - 6; i < l; i++) {
|
||||
glyphIdArray.push(reader.readUint8());
|
||||
}
|
||||
format0.glyphIdArray = glyphIdArray;
|
||||
}
|
||||
else if (subTable.format === 2) {
|
||||
const format2 = subTable;
|
||||
// 跳过format字段
|
||||
format2.length = reader.readUint16();
|
||||
format2.language = reader.readUint16();
|
||||
|
||||
const subHeadKeys = [];
|
||||
let maxSubHeadKey = 0;// 最大索引
|
||||
let maxPos = -1; // 最大位置
|
||||
for (let i = 0, l = 256; i < l; i++) {
|
||||
subHeadKeys[i] = reader.readUint16() / 8;
|
||||
if (subHeadKeys[i] > maxSubHeadKey) {
|
||||
maxSubHeadKey = subHeadKeys[i];
|
||||
maxPos = i;
|
||||
}
|
||||
}
|
||||
|
||||
const subHeads = [];
|
||||
for (i = 0; i <= maxSubHeadKey; i++) {
|
||||
subHeads[i] = {
|
||||
firstCode: reader.readUint16(),
|
||||
entryCount: reader.readUint16(),
|
||||
idDelta: reader.readUint16(),
|
||||
idRangeOffset: (reader.readUint16() - (maxSubHeadKey - i) * 8 - 2) / 2
|
||||
};
|
||||
}
|
||||
|
||||
glyphCount = (startOffset + format2.length - reader.offset) / 2;
|
||||
const glyphs = [];
|
||||
for (i = 0; i < glyphCount; i++) {
|
||||
glyphs[i] = reader.readUint16();
|
||||
}
|
||||
|
||||
format2.subHeadKeys = subHeadKeys;
|
||||
format2.maxPos = maxPos;
|
||||
format2.subHeads = subHeads;
|
||||
format2.glyphs = glyphs;
|
||||
|
||||
}
|
||||
// 双字节编码,非紧凑排列
|
||||
else if (subTable.format === 4) {
|
||||
const format4 = subTable;
|
||||
// 跳过format字段
|
||||
format4.length = reader.readUint16();
|
||||
format4.language = reader.readUint16();
|
||||
format4.segCountX2 = reader.readUint16();
|
||||
format4.searchRange = reader.readUint16();
|
||||
format4.entrySelector = reader.readUint16();
|
||||
format4.rangeShift = reader.readUint16();
|
||||
|
||||
const segCount = format4.segCountX2 / 2;
|
||||
|
||||
// end code
|
||||
const endCode = [];
|
||||
for (i = 0; i < segCount; ++i) {
|
||||
endCode.push(reader.readUint16());
|
||||
}
|
||||
format4.endCode = endCode;
|
||||
|
||||
format4.reservedPad = reader.readUint16();
|
||||
|
||||
// start code
|
||||
const startCode = [];
|
||||
for (i = 0; i < segCount; ++i) {
|
||||
startCode.push(reader.readUint16());
|
||||
}
|
||||
format4.startCode = startCode;
|
||||
|
||||
// idDelta
|
||||
const idDelta = [];
|
||||
for (i = 0; i < segCount; ++i) {
|
||||
idDelta.push(reader.readUint16());
|
||||
}
|
||||
format4.idDelta = idDelta;
|
||||
|
||||
|
||||
format4.idRangeOffsetOffset = reader.offset;
|
||||
|
||||
// idRangeOffset
|
||||
const idRangeOffset = [];
|
||||
for (i = 0; i < segCount; ++i) {
|
||||
idRangeOffset.push(reader.readUint16());
|
||||
}
|
||||
format4.idRangeOffset = idRangeOffset;
|
||||
|
||||
// 总长度 - glyphIdArray起始偏移/2
|
||||
glyphCount = (format4.length - (reader.offset - startOffset)) / 2;
|
||||
|
||||
// 记录array offset
|
||||
format4.glyphIdArrayOffset = reader.offset;
|
||||
|
||||
// glyphIdArray
|
||||
glyphIdArray = [];
|
||||
for (i = 0; i < glyphCount; ++i) {
|
||||
glyphIdArray.push(reader.readUint16());
|
||||
}
|
||||
|
||||
format4.glyphIdArray = glyphIdArray;
|
||||
}
|
||||
|
||||
else if (subTable.format === 6) {
|
||||
const format6 = subTable;
|
||||
|
||||
format6.length = reader.readUint16();
|
||||
format6.language = reader.readUint16();
|
||||
format6.firstCode = reader.readUint16();
|
||||
format6.entryCount = reader.readUint16();
|
||||
|
||||
// 记录array offset
|
||||
format6.glyphIdArrayOffset = reader.offset;
|
||||
|
||||
const glyphIndexArray = [];
|
||||
const entryCount = format6.entryCount;
|
||||
// 读取字符分组
|
||||
for (i = 0; i < entryCount; ++i) {
|
||||
glyphIndexArray.push(reader.readUint16());
|
||||
}
|
||||
format6.glyphIdArray = glyphIndexArray;
|
||||
|
||||
}
|
||||
// defines segments for sparse representation in 4-byte character space
|
||||
else if (subTable.format === 12) {
|
||||
const format12 = subTable;
|
||||
|
||||
format12.reserved = reader.readUint16();
|
||||
format12.length = reader.readUint32();
|
||||
format12.language = reader.readUint32();
|
||||
format12.nGroups = reader.readUint32();
|
||||
|
||||
const groups = [];
|
||||
const nGroups = format12.nGroups;
|
||||
// 读取字符分组
|
||||
for (i = 0; i < nGroups; ++i) {
|
||||
const group = {};
|
||||
group.start = reader.readUint32();
|
||||
group.end = reader.readUint32();
|
||||
group.startId = reader.readUint32();
|
||||
groups.push(group);
|
||||
}
|
||||
format12.groups = groups;
|
||||
}
|
||||
// format 14
|
||||
else if (subTable.format === 14) {
|
||||
const format14 = subTable;
|
||||
format14.length = reader.readUint32();
|
||||
const numVarSelectorRecords = reader.readUint32();
|
||||
const groups = [];
|
||||
let offset = reader.offset;
|
||||
for (let i = 0; i < numVarSelectorRecords; i++) {
|
||||
const varSelector = reader.readUint24(offset);
|
||||
const defaultUVSOffset = reader.readUint32(offset + 3);
|
||||
const nonDefaultUVSOffset = reader.readUint32(offset + 7);
|
||||
offset += 11;
|
||||
|
||||
if (defaultUVSOffset) {
|
||||
const numUnicodeValueRanges = reader.readUint32(startOffset + defaultUVSOffset);
|
||||
for (let j = 0; j < numUnicodeValueRanges; j++) {
|
||||
const startUnicode = reader.readUint24();
|
||||
const additionalCount = reader.readUint8();
|
||||
groups.push({
|
||||
start: startUnicode,
|
||||
end: startUnicode + additionalCount,
|
||||
varSelector
|
||||
});
|
||||
}
|
||||
}
|
||||
if (nonDefaultUVSOffset) {
|
||||
const numUVSMappings = reader.readUint32(startOffset + nonDefaultUVSOffset);
|
||||
for (let j = 0; j < numUVSMappings; j++) {
|
||||
const unicode = reader.readUint24();
|
||||
const glyphId = reader.readUint16();
|
||||
groups.push({
|
||||
unicode,
|
||||
glyphId,
|
||||
varSelector
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
format14.groups = groups;
|
||||
}
|
||||
else {
|
||||
console.warn('not support cmap format:' + subTable.format);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default function parse(reader, ttf) {
|
||||
const tcmap = {};
|
||||
// eslint-disable-next-line no-invalid-this
|
||||
const cmapOffset = this.offset;
|
||||
|
||||
reader.seek(cmapOffset);
|
||||
|
||||
tcmap.version = reader.readUint16(); // 编码方式
|
||||
const numberSubtables = tcmap.numberSubtables = reader.readUint16(); // 表个数
|
||||
|
||||
|
||||
const subTables = tcmap.tables = []; // 名字表
|
||||
let offset = reader.offset;
|
||||
|
||||
// 使用offset读取,以便于查找
|
||||
for (let i = 0, l = numberSubtables; i < l; i++) {
|
||||
const subTable = {};
|
||||
subTable.platformID = reader.readUint16(offset);
|
||||
subTable.encodingID = reader.readUint16(offset + 2);
|
||||
subTable.offset = reader.readUint32(offset + 4);
|
||||
|
||||
readSubTable(reader, ttf, subTable, cmapOffset);
|
||||
subTables.push(subTable);
|
||||
|
||||
offset += 8;
|
||||
}
|
||||
|
||||
const cmap = readWindowsAllCodes(subTables, ttf);
|
||||
|
||||
return cmap;
|
||||
}
|
||||
153
vendor/fonteditor-core/src/ttf/table/cmap/sizeof.js
vendored
153
vendor/fonteditor-core/src/ttf/table/cmap/sizeof.js
vendored
@ -1,153 +0,0 @@
|
||||
/**
|
||||
* @file 获取cmap表的大小
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 获取format4 delta值
|
||||
* Delta is saved in signed int in cmap format 4 subtable,
|
||||
* but can be in -0xFFFF..0 interval.
|
||||
* -0x10000..-0x7FFF values are stored with offset.
|
||||
*
|
||||
* @param {number} delta delta值
|
||||
* @return {number} delta值
|
||||
*/
|
||||
function encodeDelta(delta) {
|
||||
return delta > 0x7FFF
|
||||
? delta - 0x10000
|
||||
: (delta < -0x7FFF ? delta + 0x10000 : delta);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据bound获取glyf segment
|
||||
*
|
||||
* @param {Array} glyfUnicodes glyf编码集合
|
||||
* @param {number} bound 编码范围
|
||||
* @return {Array} 码表
|
||||
*/
|
||||
function getSegments(glyfUnicodes, bound) {
|
||||
|
||||
let prevGlyph = null;
|
||||
const result = [];
|
||||
let segment = {};
|
||||
|
||||
glyfUnicodes.forEach((glyph) => {
|
||||
|
||||
if (bound === undefined || glyph.unicode <= bound) {
|
||||
// 初始化编码头部,这里unicode和graph id 都必须连续
|
||||
if (prevGlyph === null
|
||||
|| glyph.unicode !== prevGlyph.unicode + 1
|
||||
|| glyph.id !== prevGlyph.id + 1
|
||||
) {
|
||||
if (prevGlyph !== null) {
|
||||
segment.end = prevGlyph.unicode;
|
||||
result.push(segment);
|
||||
segment = {
|
||||
start: glyph.unicode,
|
||||
startId: glyph.id,
|
||||
delta: encodeDelta(glyph.id - glyph.unicode)
|
||||
};
|
||||
}
|
||||
else {
|
||||
segment.start = glyph.unicode;
|
||||
segment.startId = glyph.id;
|
||||
segment.delta = encodeDelta(glyph.id - glyph.unicode);
|
||||
}
|
||||
}
|
||||
|
||||
prevGlyph = glyph;
|
||||
}
|
||||
});
|
||||
|
||||
// need to finish the last segment
|
||||
if (prevGlyph !== null) {
|
||||
segment.end = prevGlyph.unicode;
|
||||
result.push(segment);
|
||||
}
|
||||
|
||||
// 返回编码范围
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取format0编码集合
|
||||
*
|
||||
* @param {Array} glyfUnicodes glyf编码集合
|
||||
* @return {Array} 码表
|
||||
*/
|
||||
function getFormat0Segment(glyfUnicodes) {
|
||||
const unicodes = [];
|
||||
glyfUnicodes.forEach((u) => {
|
||||
if (u.unicode !== undefined && u.unicode < 256) {
|
||||
unicodes.push([u.unicode, u.id]);
|
||||
}
|
||||
});
|
||||
|
||||
// 按编码排序
|
||||
unicodes.sort((a, b) => a[0] - b[0]);
|
||||
|
||||
return unicodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对cmap数据进行预处理,获取大小
|
||||
*
|
||||
* @param {Object} ttf ttf对象
|
||||
* @return {number} 大小
|
||||
*/
|
||||
export default function sizeof(ttf) {
|
||||
ttf.support.cmap = {};
|
||||
let glyfUnicodes = [];
|
||||
ttf.glyf.forEach((glyph, index) => {
|
||||
|
||||
let unicodes = glyph.unicode;
|
||||
|
||||
if (typeof glyph.unicode === 'number') {
|
||||
unicodes = [glyph.unicode];
|
||||
}
|
||||
|
||||
if (unicodes && unicodes.length) {
|
||||
unicodes.forEach((unicode) => {
|
||||
glyfUnicodes.push({
|
||||
unicode,
|
||||
id: unicode !== 0xFFFF ? index : 0
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
glyfUnicodes = glyfUnicodes.sort((a, b) => a.unicode - b.unicode);
|
||||
|
||||
ttf.support.cmap.unicodes = glyfUnicodes;
|
||||
|
||||
const unicodes2Bytes = glyfUnicodes;
|
||||
|
||||
ttf.support.cmap.format4Segments = getSegments(unicodes2Bytes, 0xFFFF);
|
||||
ttf.support.cmap.format4Size = 24
|
||||
+ ttf.support.cmap.format4Segments.length * 8;
|
||||
|
||||
ttf.support.cmap.format0Segments = getFormat0Segment(glyfUnicodes);
|
||||
ttf.support.cmap.format0Size = 262;
|
||||
|
||||
// we need subtable 12 only if found unicodes with > 2 bytes.
|
||||
const hasGLyphsOver2Bytes = unicodes2Bytes.some((glyph) => glyph.unicode > 0xFFFF);
|
||||
|
||||
if (hasGLyphsOver2Bytes) {
|
||||
ttf.support.cmap.hasGLyphsOver2Bytes = hasGLyphsOver2Bytes;
|
||||
|
||||
const unicodes4Bytes = glyfUnicodes;
|
||||
|
||||
ttf.support.cmap.format12Segments = getSegments(unicodes4Bytes);
|
||||
ttf.support.cmap.format12Size = 16
|
||||
+ ttf.support.cmap.format12Segments.length * 12;
|
||||
}
|
||||
|
||||
const size = 4 + (hasGLyphsOver2Bytes ? 32 : 24) // cmap header
|
||||
+ ttf.support.cmap.format0Size // format 0
|
||||
+ ttf.support.cmap.format4Size // format 4
|
||||
+ (hasGLyphsOver2Bytes ? ttf.support.cmap.format12Size : 0); // format 12
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
172
vendor/fonteditor-core/src/ttf/table/cmap/write.js
vendored
172
vendor/fonteditor-core/src/ttf/table/cmap/write.js
vendored
@ -1,172 +0,0 @@
|
||||
/**
|
||||
* @file 写cmap表
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 创建`子表0`
|
||||
*
|
||||
* @param {Writer} writer 写对象
|
||||
* @param {Array} unicodes unicodes列表
|
||||
* @return {Writer}
|
||||
*/
|
||||
function writeSubTable0(writer, unicodes) {
|
||||
|
||||
writer.writeUint16(0); // format
|
||||
writer.writeUint16(262); // length
|
||||
writer.writeUint16(0); // language
|
||||
|
||||
// Array of unicodes 0..255
|
||||
let i = -1;
|
||||
let unicode;
|
||||
while ((unicode = unicodes.shift())) {
|
||||
while (++i < unicode[0]) {
|
||||
writer.writeUint8(0);
|
||||
}
|
||||
|
||||
writer.writeUint8(unicode[1]);
|
||||
i = unicode[0];
|
||||
}
|
||||
|
||||
while (++i < 256) {
|
||||
writer.writeUint8(0);
|
||||
}
|
||||
|
||||
return writer;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 创建`子表4`
|
||||
*
|
||||
* @param {Writer} writer 写对象
|
||||
* @param {Array} segments 分块编码列表
|
||||
* @return {Writer}
|
||||
*/
|
||||
function writeSubTable4(writer, segments) {
|
||||
|
||||
writer.writeUint16(4); // format
|
||||
writer.writeUint16(24 + segments.length * 8); // length
|
||||
writer.writeUint16(0); // language
|
||||
|
||||
const segCount = segments.length + 1;
|
||||
const maxExponent = Math.floor(Math.log(segCount) / Math.LN2);
|
||||
const searchRange = 2 * Math.pow(2, maxExponent);
|
||||
|
||||
writer.writeUint16(segCount * 2); // segCountX2
|
||||
writer.writeUint16(searchRange); // searchRange
|
||||
writer.writeUint16(maxExponent); // entrySelector
|
||||
writer.writeUint16(2 * segCount - searchRange); // rangeShift
|
||||
|
||||
// end list
|
||||
segments.forEach((segment) => {
|
||||
writer.writeUint16(segment.end);
|
||||
});
|
||||
writer.writeUint16(0xFFFF); // end code
|
||||
writer.writeUint16(0); // reservedPad
|
||||
|
||||
|
||||
// start list
|
||||
segments.forEach((segment) => {
|
||||
writer.writeUint16(segment.start);
|
||||
});
|
||||
writer.writeUint16(0xFFFF); // start code
|
||||
|
||||
// id delta
|
||||
segments.forEach((segment) => {
|
||||
writer.writeUint16(segment.delta);
|
||||
});
|
||||
writer.writeUint16(1);
|
||||
|
||||
// Array of range offsets, it doesn't matter when deltas present
|
||||
for (let i = 0, l = segments.length; i < l; i++) {
|
||||
writer.writeUint16(0);
|
||||
}
|
||||
writer.writeUint16(0); // rangeOffsetArray should be finished with 0
|
||||
|
||||
return writer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建`子表12`
|
||||
*
|
||||
* @param {Writer} writer 写对象
|
||||
* @param {Array} segments 分块编码列表
|
||||
* @return {Writer}
|
||||
*/
|
||||
function writeSubTable12(writer, segments) {
|
||||
|
||||
writer.writeUint16(12); // format
|
||||
writer.writeUint16(0); // reserved
|
||||
writer.writeUint32(16 + segments.length * 12); // length
|
||||
writer.writeUint32(0); // language
|
||||
writer.writeUint32(segments.length); // nGroups
|
||||
|
||||
segments.forEach((segment) => {
|
||||
writer.writeUint32(segment.start);
|
||||
writer.writeUint32(segment.end);
|
||||
writer.writeUint32(segment.startId);
|
||||
});
|
||||
|
||||
return writer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写subtableheader
|
||||
*
|
||||
* @param {Writer} writer Writer对象
|
||||
* @param {number} platform 平台
|
||||
* @param {number} encoding 编码
|
||||
* @param {number} offset 偏移
|
||||
* @return {Writer}
|
||||
*/
|
||||
function writeSubTableHeader(writer, platform, encoding, offset) {
|
||||
writer.writeUint16(platform); // platform
|
||||
writer.writeUint16(encoding); // encoding
|
||||
writer.writeUint32(offset); // offset
|
||||
return writer;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 写cmap表数据
|
||||
*
|
||||
* @param {Object} writer 写入器
|
||||
* @param {Object} ttf ttf对象
|
||||
* @return {Object} 写入器
|
||||
*/
|
||||
export default function write(writer, ttf) {
|
||||
const hasGLyphsOver2Bytes = ttf.support.cmap.hasGLyphsOver2Bytes;
|
||||
|
||||
// write table header.
|
||||
writer.writeUint16(0); // version
|
||||
writer.writeUint16(hasGLyphsOver2Bytes ? 4 : 3); // count
|
||||
|
||||
// header size
|
||||
const subTableOffset = 4 + (hasGLyphsOver2Bytes ? 32 : 24);
|
||||
const format4Size = ttf.support.cmap.format4Size;
|
||||
const format0Size = ttf.support.cmap.format0Size;
|
||||
|
||||
// subtable 4, unicode
|
||||
writeSubTableHeader(writer, 0, 3, subTableOffset);
|
||||
|
||||
// subtable 0, mac standard
|
||||
writeSubTableHeader(writer, 1, 0, subTableOffset + format4Size);
|
||||
|
||||
// subtable 4, windows standard
|
||||
writeSubTableHeader(writer, 3, 1, subTableOffset);
|
||||
|
||||
if (hasGLyphsOver2Bytes) {
|
||||
writeSubTableHeader(writer, 3, 10, subTableOffset + format4Size + format0Size);
|
||||
}
|
||||
|
||||
// write tables, order of table seem to be magic, it is taken from TTX tool
|
||||
writeSubTable4(writer, ttf.support.cmap.format4Segments);
|
||||
writeSubTable0(writer, ttf.support.cmap.format0Segments);
|
||||
|
||||
if (hasGLyphsOver2Bytes) {
|
||||
writeSubTable12(writer, ttf.support.cmap.format12Segments);
|
||||
}
|
||||
|
||||
return writer;
|
||||
}
|
||||
30
vendor/fonteditor-core/src/ttf/table/cvt.js
vendored
30
vendor/fonteditor-core/src/ttf/table/cvt.js
vendored
@ -1,30 +0,0 @@
|
||||
/**
|
||||
* @file cvt表
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*
|
||||
* @reference: https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6cvt.html
|
||||
*/
|
||||
|
||||
import table from './table';
|
||||
|
||||
export default table.create(
|
||||
'cvt',
|
||||
[],
|
||||
{
|
||||
|
||||
read(reader, ttf) {
|
||||
const length = ttf.tables.cvt.length;
|
||||
return reader.readBytes(this.offset, length);
|
||||
},
|
||||
|
||||
write(writer, ttf) {
|
||||
if (ttf.cvt) {
|
||||
writer.writeBytes(ttf.cvt, ttf.cvt.length);
|
||||
}
|
||||
},
|
||||
|
||||
size(ttf) {
|
||||
return ttf.cvt ? ttf.cvt.length : 0;
|
||||
}
|
||||
}
|
||||
);
|
||||
@ -1,50 +0,0 @@
|
||||
/**
|
||||
* @file directory 表, 读取和写入ttf表索引
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*
|
||||
* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6.html
|
||||
*/
|
||||
|
||||
import table from './table';
|
||||
|
||||
export default table.create(
|
||||
'directory',
|
||||
[],
|
||||
{
|
||||
read(reader, ttf) {
|
||||
const tables = {};
|
||||
const numTables = ttf.numTables;
|
||||
const offset = this.offset;
|
||||
|
||||
for (let i = offset, l = numTables * 16; i < l; i += 16) {
|
||||
const name = reader.readString(i, 4).trim();
|
||||
|
||||
tables[name] = {
|
||||
name,
|
||||
checkSum: reader.readUint32(i + 4),
|
||||
offset: reader.readUint32(i + 8),
|
||||
length: reader.readUint32(i + 12)
|
||||
};
|
||||
}
|
||||
|
||||
return tables;
|
||||
},
|
||||
|
||||
write(writer, ttf) {
|
||||
|
||||
const tables = ttf.support.tables;
|
||||
for (let i = 0, l = tables.length; i < l; i++) {
|
||||
writer.writeString((tables[i].name + ' ').slice(0, 4));
|
||||
writer.writeUint32(tables[i].checkSum);
|
||||
writer.writeUint32(tables[i].offset);
|
||||
writer.writeUint32(tables[i].length);
|
||||
}
|
||||
|
||||
return writer;
|
||||
},
|
||||
|
||||
size(ttf) {
|
||||
return ttf.numTables * 16;
|
||||
}
|
||||
}
|
||||
);
|
||||
31
vendor/fonteditor-core/src/ttf/table/fpgm.js
vendored
31
vendor/fonteditor-core/src/ttf/table/fpgm.js
vendored
@ -1,31 +0,0 @@
|
||||
/**
|
||||
* @file fpgm 表
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*
|
||||
* reference: https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6fpgm.html
|
||||
*/
|
||||
|
||||
import table from './table';
|
||||
|
||||
export default table.create(
|
||||
'fpgm',
|
||||
[],
|
||||
{
|
||||
|
||||
read(reader, ttf) {
|
||||
const length = ttf.tables.fpgm.length;
|
||||
return reader.readBytes(this.offset, length);
|
||||
},
|
||||
|
||||
write(writer, ttf) {
|
||||
if (ttf.fpgm) {
|
||||
writer.writeBytes(ttf.fpgm, ttf.fpgm.length);
|
||||
}
|
||||
},
|
||||
|
||||
size(ttf) {
|
||||
return ttf.fpgm ? ttf.fpgm.length : 0;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
31
vendor/fonteditor-core/src/ttf/table/gasp.js
vendored
31
vendor/fonteditor-core/src/ttf/table/gasp.js
vendored
@ -1,31 +0,0 @@
|
||||
/**
|
||||
* @file gasp 表
|
||||
* 对于需要hinting的字号需要这个表,否则会导致错误
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
* reference: https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6gasp.html
|
||||
*/
|
||||
|
||||
import table from './table';
|
||||
|
||||
export default table.create(
|
||||
'gasp',
|
||||
[],
|
||||
{
|
||||
|
||||
read(reader, ttf) {
|
||||
const length = ttf.tables.gasp.length;
|
||||
return reader.readBytes(this.offset, length);
|
||||
},
|
||||
|
||||
write(writer, ttf) {
|
||||
if (ttf.gasp) {
|
||||
writer.writeBytes(ttf.gasp, ttf.gasp.length);
|
||||
}
|
||||
},
|
||||
|
||||
size(ttf) {
|
||||
return ttf.gasp ? ttf.gasp.length : 0;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
112
vendor/fonteditor-core/src/ttf/table/glyf.js
vendored
112
vendor/fonteditor-core/src/ttf/table/glyf.js
vendored
@ -1,112 +0,0 @@
|
||||
/**
|
||||
* @file glyf表
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*
|
||||
* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6glyf.html
|
||||
*/
|
||||
|
||||
import table from './table';
|
||||
import parse from './glyf/parse';
|
||||
import write from './glyf/write';
|
||||
import sizeof from './glyf/sizeof';
|
||||
import {isEmptyObject} from '../../common/lang';
|
||||
|
||||
export default table.create(
|
||||
'glyf',
|
||||
[],
|
||||
{
|
||||
|
||||
read(reader, ttf) {
|
||||
const startOffset = this.offset;
|
||||
const loca = ttf.loca;
|
||||
const numGlyphs = ttf.maxp.numGlyphs;
|
||||
const glyphs = [];
|
||||
|
||||
reader.seek(startOffset);
|
||||
|
||||
// subset
|
||||
const subset = ttf.readOptions.subset;
|
||||
|
||||
if (subset && subset.length > 0) {
|
||||
const subsetMap = {
|
||||
0: true // 设置.notdef
|
||||
};
|
||||
subsetMap[0] = true;
|
||||
// subset map
|
||||
const cmap = ttf.cmap;
|
||||
|
||||
// unicode to index
|
||||
Object.keys(cmap).forEach((c) => {
|
||||
if (subset.indexOf(+c) > -1) {
|
||||
const i = cmap[c];
|
||||
subsetMap[i] = true;
|
||||
}
|
||||
});
|
||||
ttf.subsetMap = subsetMap;
|
||||
const parsedGlyfMap = {};
|
||||
// 循环解析subset相关的glyf,包括复合字形相关的字形
|
||||
const travelsParse = function travels(subsetMap) {
|
||||
const newSubsetMap = {};
|
||||
Object.keys(subsetMap).forEach((i) => {
|
||||
const index = +i;
|
||||
parsedGlyfMap[index] = true;
|
||||
// 当前的和下一个一样,或者最后一个无轮廓
|
||||
if (loca[index] === loca[index + 1]) {
|
||||
glyphs[index] = {
|
||||
contours: []
|
||||
};
|
||||
}
|
||||
else {
|
||||
glyphs[index] = parse(reader, ttf, startOffset + loca[index]);
|
||||
}
|
||||
|
||||
if (glyphs[index].compound) {
|
||||
glyphs[index].glyfs.forEach((g) => {
|
||||
if (!parsedGlyfMap[g.glyphIndex]) {
|
||||
newSubsetMap[g.glyphIndex] = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (!isEmptyObject(newSubsetMap)) {
|
||||
travels(newSubsetMap);
|
||||
}
|
||||
};
|
||||
|
||||
travelsParse(subsetMap);
|
||||
return glyphs;
|
||||
}
|
||||
|
||||
// 解析字体轮廓, 前n-1个
|
||||
let i;
|
||||
let l;
|
||||
for (i = 0, l = numGlyphs - 1; i < l; i++) {
|
||||
// 当前的和下一个一样,或者最后一个无轮廓
|
||||
if (loca[i] === loca[i + 1]) {
|
||||
glyphs[i] = {
|
||||
contours: []
|
||||
};
|
||||
}
|
||||
else {
|
||||
glyphs[i] = parse(reader, ttf, startOffset + loca[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// 最后一个轮廓
|
||||
if ((ttf.tables.glyf.length - loca[i]) < 5) {
|
||||
glyphs[i] = {
|
||||
contours: []
|
||||
};
|
||||
}
|
||||
else {
|
||||
glyphs[i] = parse(reader, ttf, startOffset + loca[i]);
|
||||
}
|
||||
|
||||
return glyphs;
|
||||
},
|
||||
|
||||
write,
|
||||
size: sizeof
|
||||
}
|
||||
);
|
||||
308
vendor/fonteditor-core/src/ttf/table/glyf/parse.js
vendored
308
vendor/fonteditor-core/src/ttf/table/glyf/parse.js
vendored
@ -1,308 +0,0 @@
|
||||
/**
|
||||
* @file 解析glyf轮廓
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
import glyFlag from '../../enum/glyFlag';
|
||||
import componentFlag from '../../enum/componentFlag';
|
||||
|
||||
const MAX_INSTRUCTION_LENGTH = 5000; // 设置instructions阈值防止读取错误
|
||||
const MAX_NUMBER_OF_COORDINATES = 20000; // 设置坐标最大个数阈值,防止glyf读取错误
|
||||
|
||||
/**
|
||||
* 读取简单字形
|
||||
*
|
||||
* @param {Reader} reader Reader对象
|
||||
* @param {Object} glyf 空glyf
|
||||
* @return {Object} 解析后的glyf
|
||||
*/
|
||||
function parseSimpleGlyf(reader, glyf) {
|
||||
const offset = reader.offset;
|
||||
|
||||
// 轮廓点个数
|
||||
const numberOfCoordinates = glyf.endPtsOfContours[
|
||||
glyf.endPtsOfContours.length - 1
|
||||
] + 1;
|
||||
|
||||
// 判断坐标是否超过最大个数
|
||||
if (numberOfCoordinates > MAX_NUMBER_OF_COORDINATES) {
|
||||
console.warn('error read glyf coordinates:' + offset);
|
||||
return glyf;
|
||||
}
|
||||
|
||||
// 获取flag标志
|
||||
let i;
|
||||
let length;
|
||||
const flags = [];
|
||||
let flag;
|
||||
|
||||
i = 0;
|
||||
while (i < numberOfCoordinates) {
|
||||
flag = reader.readUint8();
|
||||
flags.push(flag);
|
||||
i++;
|
||||
|
||||
// 标志位3重复flag
|
||||
if ((flag & glyFlag.REPEAT) && i < numberOfCoordinates) {
|
||||
// 重复个数
|
||||
const repeat = reader.readUint8();
|
||||
for (let j = 0; j < repeat; j++) {
|
||||
flags.push(flag);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 坐标集合
|
||||
const coordinates = [];
|
||||
const xCoordinates = [];
|
||||
let prevX = 0;
|
||||
let x;
|
||||
|
||||
for (i = 0, length = flags.length; i < length; ++i) {
|
||||
x = 0;
|
||||
flag = flags[i];
|
||||
|
||||
// 标志位1
|
||||
// If set, the corresponding y-coordinate is 1 byte long, not 2
|
||||
if (flag & glyFlag.XSHORT) {
|
||||
x = reader.readUint8();
|
||||
|
||||
// 标志位5
|
||||
x = (flag & glyFlag.XSAME) ? x : -1 * x;
|
||||
}
|
||||
// 与上一值一致
|
||||
else if (flag & glyFlag.XSAME) {
|
||||
x = 0;
|
||||
}
|
||||
// 新值
|
||||
else {
|
||||
x = reader.readInt16();
|
||||
}
|
||||
|
||||
prevX += x;
|
||||
xCoordinates[i] = prevX;
|
||||
coordinates[i] = {
|
||||
x: prevX,
|
||||
y: 0
|
||||
};
|
||||
if (flag & glyFlag.ONCURVE) {
|
||||
coordinates[i].onCurve = true;
|
||||
}
|
||||
}
|
||||
|
||||
const yCoordinates = [];
|
||||
let prevY = 0;
|
||||
let y;
|
||||
|
||||
for (i = 0, length = flags.length; i < length; i++) {
|
||||
y = 0;
|
||||
flag = flags[i];
|
||||
|
||||
if (flag & glyFlag.YSHORT) {
|
||||
y = reader.readUint8();
|
||||
y = (flag & glyFlag.YSAME) ? y : -1 * y;
|
||||
}
|
||||
else if (flag & glyFlag.YSAME) {
|
||||
y = 0;
|
||||
}
|
||||
else {
|
||||
y = reader.readInt16();
|
||||
}
|
||||
|
||||
prevY += y;
|
||||
yCoordinates[i] = prevY;
|
||||
if (coordinates[i]) {
|
||||
coordinates[i].y = prevY;
|
||||
}
|
||||
}
|
||||
|
||||
// 计算轮廓集合
|
||||
if (coordinates.length) {
|
||||
const endPtsOfContours = glyf.endPtsOfContours;
|
||||
const contours = [];
|
||||
contours.push(coordinates.slice(0, endPtsOfContours[0] + 1));
|
||||
|
||||
for (i = 1, length = endPtsOfContours.length; i < length; i++) {
|
||||
contours.push(coordinates.slice(endPtsOfContours[i - 1] + 1, endPtsOfContours[i] + 1));
|
||||
}
|
||||
|
||||
glyf.contours = contours;
|
||||
}
|
||||
|
||||
return glyf;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取复合字形
|
||||
*
|
||||
* @param {Reader} reader Reader对象
|
||||
* @param {Object} glyf glyf对象
|
||||
* @return {Object} glyf对象
|
||||
*/
|
||||
function parseCompoundGlyf(reader, glyf) {
|
||||
glyf.compound = true;
|
||||
glyf.glyfs = [];
|
||||
|
||||
let flags;
|
||||
let g;
|
||||
|
||||
// 读取复杂字形
|
||||
do {
|
||||
flags = reader.readUint16();
|
||||
g = {};
|
||||
g.flags = flags;
|
||||
g.glyphIndex = reader.readUint16();
|
||||
|
||||
let arg1 = 0;
|
||||
let arg2 = 0;
|
||||
let scaleX = 16384;
|
||||
let scaleY = 16384;
|
||||
let scale01 = 0;
|
||||
let scale10 = 0;
|
||||
|
||||
if (componentFlag.ARG_1_AND_2_ARE_WORDS & flags) {
|
||||
arg1 = reader.readInt16();
|
||||
arg2 = reader.readInt16();
|
||||
|
||||
}
|
||||
else {
|
||||
arg1 = reader.readInt8();
|
||||
arg2 = reader.readInt8();
|
||||
}
|
||||
|
||||
if (componentFlag.ROUND_XY_TO_GRID & flags) {
|
||||
arg1 = Math.round(arg1);
|
||||
arg2 = Math.round(arg2);
|
||||
}
|
||||
|
||||
if (componentFlag.WE_HAVE_A_SCALE & flags) {
|
||||
scaleX = reader.readInt16();
|
||||
scaleY = scaleX;
|
||||
}
|
||||
else if (componentFlag.WE_HAVE_AN_X_AND_Y_SCALE & flags) {
|
||||
scaleX = reader.readInt16();
|
||||
scaleY = reader.readInt16();
|
||||
}
|
||||
else if (componentFlag.WE_HAVE_A_TWO_BY_TWO & flags) {
|
||||
scaleX = reader.readInt16();
|
||||
scale01 = reader.readInt16();
|
||||
scale10 = reader.readInt16();
|
||||
scaleY = reader.readInt16();
|
||||
}
|
||||
|
||||
if (componentFlag.ARGS_ARE_XY_VALUES & flags) {
|
||||
g.useMyMetrics = !!flags & componentFlag.USE_MY_METRICS;
|
||||
g.overlapCompound = !!flags & componentFlag.OVERLAP_COMPOUND;
|
||||
|
||||
g.transform = {
|
||||
a: Math.round(10000 * scaleX / 16384) / 10000,
|
||||
b: Math.round(10000 * scale01 / 16384) / 10000,
|
||||
c: Math.round(10000 * scale10 / 16384) / 10000,
|
||||
d: Math.round(10000 * scaleY / 16384) / 10000,
|
||||
e: arg1,
|
||||
f: arg2
|
||||
};
|
||||
}
|
||||
else {
|
||||
g.points = [arg1, arg2];
|
||||
g.transform = {
|
||||
a: Math.round(10000 * scaleX / 16384) / 10000,
|
||||
b: Math.round(10000 * scale01 / 16384) / 10000,
|
||||
c: Math.round(10000 * scale10 / 16384) / 10000,
|
||||
d: Math.round(10000 * scaleY / 16384) / 10000,
|
||||
e: 0,
|
||||
f: 0
|
||||
};
|
||||
}
|
||||
|
||||
glyf.glyfs.push(g);
|
||||
|
||||
} while (componentFlag.MORE_COMPONENTS & flags);
|
||||
|
||||
if (componentFlag.WE_HAVE_INSTRUCTIONS & flags) {
|
||||
const length = reader.readUint16();
|
||||
if (length < MAX_INSTRUCTION_LENGTH) {
|
||||
const instructions = [];
|
||||
for (let i = 0; i < length; ++i) {
|
||||
instructions.push(reader.readUint8());
|
||||
}
|
||||
glyf.instructions = instructions;
|
||||
}
|
||||
else {
|
||||
console.warn(length);
|
||||
}
|
||||
}
|
||||
|
||||
return glyf;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 解析glyf轮廓
|
||||
*
|
||||
* @param {Reader} reader 读取器
|
||||
* @param {Object} ttf ttf对象
|
||||
* @param {number=} offset 偏移
|
||||
* @return {Object} glyf对象
|
||||
*/
|
||||
export default function parseGlyf(reader, ttf, offset) {
|
||||
|
||||
if (null != offset) {
|
||||
reader.seek(offset);
|
||||
}
|
||||
|
||||
const glyf = {};
|
||||
let i;
|
||||
let length;
|
||||
let instructions;
|
||||
|
||||
// 边界值
|
||||
const numberOfContours = reader.readInt16();
|
||||
glyf.xMin = reader.readInt16();
|
||||
glyf.yMin = reader.readInt16();
|
||||
glyf.xMax = reader.readInt16();
|
||||
glyf.yMax = reader.readInt16();
|
||||
|
||||
// 读取简单字形
|
||||
if (numberOfContours >= 0) {
|
||||
// endPtsOfConturs
|
||||
glyf.endPtsOfContours = [];
|
||||
if (numberOfContours > 0) {
|
||||
for (i = 0; i < numberOfContours; i++) {
|
||||
glyf.endPtsOfContours.push(reader.readUint16());
|
||||
}
|
||||
}
|
||||
else {
|
||||
delete glyf.xMin;
|
||||
delete glyf.yMin;
|
||||
delete glyf.xMax;
|
||||
delete glyf.yMax;
|
||||
}
|
||||
|
||||
// instructions
|
||||
length = reader.readUint16();
|
||||
if (length) {
|
||||
// range错误
|
||||
if (length < MAX_INSTRUCTION_LENGTH) {
|
||||
instructions = [];
|
||||
for (i = 0; i < length; ++i) {
|
||||
instructions.push(reader.readUint8());
|
||||
}
|
||||
glyf.instructions = instructions;
|
||||
}
|
||||
else {
|
||||
console.warn(length);
|
||||
}
|
||||
}
|
||||
|
||||
parseSimpleGlyf(reader, glyf);
|
||||
delete glyf.endPtsOfContours;
|
||||
}
|
||||
else {
|
||||
parseCompoundGlyf(reader, glyf);
|
||||
}
|
||||
|
||||
return glyf;
|
||||
}
|
||||
257
vendor/fonteditor-core/src/ttf/table/glyf/sizeof.js
vendored
257
vendor/fonteditor-core/src/ttf/table/glyf/sizeof.js
vendored
@ -1,257 +0,0 @@
|
||||
/**
|
||||
* @file 获取glyf的大小,同时对glyf写入进行预处理
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
import glyFlag from '../../enum/glyFlag';
|
||||
|
||||
/**
|
||||
* 获取glyf的大小
|
||||
*
|
||||
* @param {Object} glyf glyf对象
|
||||
* @param {Object} glyfSupport glyf相关统计
|
||||
* @param {boolean} hinting 是否保留hints
|
||||
* @param {boolean} writeZeroContoursGlyfData 是否写空轮廓 glyph
|
||||
* @return {number} size大小
|
||||
*/
|
||||
function sizeofSimple(glyf, glyfSupport, hinting, writeZeroContoursGlyfData) {
|
||||
if (!writeZeroContoursGlyfData && (!glyf.contours || !glyf.contours.length)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// fixed header + endPtsOfContours
|
||||
let result = 12
|
||||
+ (glyf.contours || []).length * 2
|
||||
+ (glyfSupport.flags || []).length;
|
||||
|
||||
(glyfSupport.xCoord || []).forEach((x) => {
|
||||
result += 0 <= x && x <= 0xFF ? 1 : 2;
|
||||
});
|
||||
|
||||
(glyfSupport.yCoord || []).forEach((y) => {
|
||||
result += 0 <= y && y <= 0xFF ? 1 : 2;
|
||||
});
|
||||
|
||||
return result + (hinting && glyf.instructions ? glyf.instructions.length : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 复合图元size
|
||||
*
|
||||
* @param {Object} glyf glyf对象
|
||||
* @param {boolean} hinting 是否保留hints, compound 图元暂时不做hinting
|
||||
* @return {number} size大小
|
||||
*/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function sizeofCompound(glyf, hinting) {
|
||||
let size = 10;
|
||||
let transform;
|
||||
glyf.glyfs.forEach((g) => {
|
||||
transform = g.transform;
|
||||
// flags + glyfIndex
|
||||
size += 4;
|
||||
|
||||
// a, b, c, d, e
|
||||
// xy values or points
|
||||
if (transform.e < 0 || transform.e > 0x7F || transform.f < 0 || transform.f > 0x7F) {
|
||||
size += 4;
|
||||
}
|
||||
else {
|
||||
size += 2;
|
||||
}
|
||||
|
||||
// 01 , 10
|
||||
if (transform.b || transform.c) {
|
||||
size += 8;
|
||||
}
|
||||
// scale
|
||||
else if (transform.a !== 1 || transform.d !== 1) {
|
||||
size += transform.a === transform.d ? 2 : 4;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取flags
|
||||
*
|
||||
* @param {Object} glyf glyf对象
|
||||
* @param {Object} glyfSupport glyf相关统计
|
||||
* @return {Array}
|
||||
*/
|
||||
function getFlags(glyf, glyfSupport) {
|
||||
|
||||
if (!glyf.contours || 0 === glyf.contours.length) {
|
||||
return glyfSupport;
|
||||
}
|
||||
|
||||
const flags = [];
|
||||
const xCoord = [];
|
||||
const yCoord = [];
|
||||
|
||||
const contours = glyf.contours;
|
||||
let contour;
|
||||
let prev;
|
||||
let first = true;
|
||||
|
||||
for (let j = 0, cl = contours.length; j < cl; j++) {
|
||||
contour = contours[j];
|
||||
|
||||
for (let i = 0, l = contour.length; i < l; i++) {
|
||||
|
||||
const point = contour[i];
|
||||
if (first) {
|
||||
xCoord.push(point.x);
|
||||
yCoord.push(point.y);
|
||||
first = false;
|
||||
}
|
||||
else {
|
||||
xCoord.push(point.x - prev.x);
|
||||
yCoord.push(point.y - prev.y);
|
||||
}
|
||||
flags.push(point.onCurve ? glyFlag.ONCURVE : 0);
|
||||
prev = point;
|
||||
}
|
||||
}
|
||||
|
||||
// compress
|
||||
const flagsC = [];
|
||||
const xCoordC = [];
|
||||
const yCoordC = [];
|
||||
let x;
|
||||
let y;
|
||||
let prevFlag;
|
||||
let repeatPoint = -1;
|
||||
|
||||
flags.forEach((flag, index) => {
|
||||
|
||||
x = xCoord[index];
|
||||
y = yCoord[index];
|
||||
|
||||
// 第一个
|
||||
if (index === 0) {
|
||||
|
||||
if (-0xFF <= x && x <= 0xFF) {
|
||||
flag += glyFlag.XSHORT;
|
||||
if (x >= 0) {
|
||||
flag += glyFlag.XSAME;
|
||||
}
|
||||
|
||||
x = Math.abs(x);
|
||||
}
|
||||
|
||||
if (-0xFF <= y && y <= 0xFF) {
|
||||
flag += glyFlag.YSHORT;
|
||||
if (y >= 0) {
|
||||
flag += glyFlag.YSAME;
|
||||
}
|
||||
|
||||
y = Math.abs(y);
|
||||
}
|
||||
|
||||
flagsC.push(prevFlag = flag);
|
||||
xCoordC.push(x);
|
||||
yCoordC.push(y);
|
||||
}
|
||||
// 后续
|
||||
else {
|
||||
|
||||
if (x === 0) {
|
||||
flag += glyFlag.XSAME;
|
||||
}
|
||||
else {
|
||||
if (-0xFF <= x && x <= 0xFF) {
|
||||
flag += glyFlag.XSHORT;
|
||||
if (x > 0) {
|
||||
flag += glyFlag.XSAME;
|
||||
}
|
||||
|
||||
x = Math.abs(x);
|
||||
}
|
||||
|
||||
xCoordC.push(x);
|
||||
}
|
||||
|
||||
if (y === 0) {
|
||||
flag += glyFlag.YSAME;
|
||||
}
|
||||
else {
|
||||
if (-0xFF <= y && y <= 0xFF) {
|
||||
flag += glyFlag.YSHORT;
|
||||
if (y > 0) {
|
||||
flag += glyFlag.YSAME;
|
||||
}
|
||||
y = Math.abs(y);
|
||||
}
|
||||
yCoordC.push(y);
|
||||
}
|
||||
|
||||
// repeat
|
||||
if (flag === prevFlag) {
|
||||
// 记录重复个数
|
||||
if (-1 === repeatPoint) {
|
||||
repeatPoint = flagsC.length - 1;
|
||||
flagsC[repeatPoint] |= glyFlag.REPEAT;
|
||||
flagsC.push(1);
|
||||
}
|
||||
else {
|
||||
++flagsC[repeatPoint + 1];
|
||||
}
|
||||
}
|
||||
else {
|
||||
repeatPoint = -1;
|
||||
flagsC.push(prevFlag = flag);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
glyfSupport.flags = flagsC;
|
||||
glyfSupport.xCoord = xCoordC;
|
||||
glyfSupport.yCoord = yCoordC;
|
||||
|
||||
return glyfSupport;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对glyf数据进行预处理,获取大小
|
||||
*
|
||||
* @param {Object} ttf ttf对象
|
||||
* @return {number} 大小
|
||||
*/
|
||||
export default function sizeof(ttf) {
|
||||
ttf.support.glyf = [];
|
||||
let tableSize = 0;
|
||||
const hinting = ttf.writeOptions ? ttf.writeOptions.hinting : false;
|
||||
const writeZeroContoursGlyfData = ttf.writeOptions ? ttf.writeOptions.writeZeroContoursGlyfData : false;
|
||||
ttf.glyf.forEach((glyf) => {
|
||||
let glyfSupport = {};
|
||||
glyfSupport = glyf.compound ? glyfSupport : getFlags(glyf, glyfSupport);
|
||||
|
||||
const glyfSize = glyf.compound
|
||||
? sizeofCompound(glyf, hinting)
|
||||
: sizeofSimple(glyf, glyfSupport, hinting, writeZeroContoursGlyfData);
|
||||
let size = glyfSize;
|
||||
|
||||
// 4字节对齐
|
||||
if (size % 4) {
|
||||
size += 4 - size % 4;
|
||||
}
|
||||
|
||||
glyfSupport.glyfSize = glyfSize;
|
||||
glyfSupport.size = size;
|
||||
|
||||
ttf.support.glyf.push(glyfSupport);
|
||||
|
||||
tableSize += size;
|
||||
});
|
||||
|
||||
ttf.support.glyf.tableSize = tableSize;
|
||||
|
||||
// 写header的indexToLocFormat
|
||||
ttf.head.indexToLocFormat = tableSize > 65536 ? 1 : 0;
|
||||
|
||||
return ttf.support.glyf.tableSize;
|
||||
}
|
||||
163
vendor/fonteditor-core/src/ttf/table/glyf/write.js
vendored
163
vendor/fonteditor-core/src/ttf/table/glyf/write.js
vendored
@ -1,163 +0,0 @@
|
||||
/**
|
||||
* @file 写glyf数据
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
import componentFlag from '../../enum/componentFlag';
|
||||
|
||||
/**
|
||||
* 写glyf
|
||||
*
|
||||
* @param {Object} writer 写入器
|
||||
* @param {Object} ttf ttf对象
|
||||
* @return {Object} 写入器
|
||||
*/
|
||||
export default function write(writer, ttf) {
|
||||
const hinting = ttf.writeOptions ? ttf.writeOptions.hinting : false;
|
||||
const writeZeroContoursGlyfData = ttf.writeOptions ? ttf.writeOptions.writeZeroContoursGlyfData : false;
|
||||
ttf.glyf.forEach((glyf, index) => {
|
||||
// 非复合图元没有轮廓则不写
|
||||
if (!glyf.compound && !writeZeroContoursGlyfData && (!glyf.contours || !glyf.contours.length)) {
|
||||
return;
|
||||
}
|
||||
// header
|
||||
writer.writeInt16(glyf.compound ? -1 : (glyf.contours || []).length);
|
||||
writer.writeInt16(glyf.xMin);
|
||||
writer.writeInt16(glyf.yMin);
|
||||
writer.writeInt16(glyf.xMax);
|
||||
writer.writeInt16(glyf.yMax);
|
||||
|
||||
let i;
|
||||
let l;
|
||||
let flags;
|
||||
|
||||
// 复合图元
|
||||
if (glyf.compound) {
|
||||
|
||||
for (i = 0, l = glyf.glyfs.length; i < l; i++) {
|
||||
const g = glyf.glyfs[i];
|
||||
|
||||
flags = g.points
|
||||
? 0 : (componentFlag.ARGS_ARE_XY_VALUES + componentFlag.ROUND_XY_TO_GRID); // xy values
|
||||
|
||||
// more components
|
||||
if (i < l - 1) {
|
||||
flags += componentFlag.MORE_COMPONENTS;
|
||||
}
|
||||
|
||||
|
||||
// use my metrics
|
||||
flags += g.useMyMetrics ? componentFlag.USE_MY_METRICS : 0;
|
||||
// overlap compound
|
||||
flags += g.overlapCompound ? componentFlag.OVERLAP_COMPOUND : 0;
|
||||
|
||||
const transform = g.transform;
|
||||
const a = transform.a;
|
||||
const b = transform.b;
|
||||
const c = transform.c;
|
||||
const d = transform.d;
|
||||
const e = g.points ? g.points[0] : transform.e;
|
||||
const f = g.points ? g.points[1] : transform.f;
|
||||
|
||||
// xy values or points
|
||||
// int 8 放不下,则用int16放
|
||||
if (e < 0 || e > 0x7F || f < 0 || f > 0x7F) {
|
||||
flags += componentFlag.ARG_1_AND_2_ARE_WORDS;
|
||||
}
|
||||
|
||||
if (b || c) {
|
||||
flags += componentFlag.WE_HAVE_A_TWO_BY_TWO;
|
||||
}
|
||||
else if ((a !== 1 || d !== 1) && a === d) {
|
||||
flags += componentFlag.WE_HAVE_A_SCALE;
|
||||
}
|
||||
else if (a !== 1 || d !== 1) {
|
||||
flags += componentFlag.WE_HAVE_AN_X_AND_Y_SCALE;
|
||||
}
|
||||
|
||||
writer.writeUint16(flags);
|
||||
writer.writeUint16(g.glyphIndex);
|
||||
|
||||
if (componentFlag.ARG_1_AND_2_ARE_WORDS & flags) {
|
||||
writer.writeInt16(e);
|
||||
writer.writeInt16(f);
|
||||
|
||||
}
|
||||
else {
|
||||
writer.writeUint8(e);
|
||||
writer.writeUint8(f);
|
||||
}
|
||||
|
||||
if (componentFlag.WE_HAVE_A_SCALE & flags) {
|
||||
writer.writeInt16(Math.round(a * 16384));
|
||||
}
|
||||
else if (componentFlag.WE_HAVE_AN_X_AND_Y_SCALE & flags) {
|
||||
writer.writeInt16(Math.round(a * 16384));
|
||||
writer.writeInt16(Math.round(d * 16384));
|
||||
}
|
||||
else if (componentFlag.WE_HAVE_A_TWO_BY_TWO & flags) {
|
||||
writer.writeInt16(Math.round(a * 16384));
|
||||
writer.writeInt16(Math.round(b * 16384));
|
||||
writer.writeInt16(Math.round(c * 16384));
|
||||
writer.writeInt16(Math.round(d * 16384));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
let endPtsOfContours = -1;
|
||||
(glyf.contours || []).forEach((contour) => {
|
||||
endPtsOfContours += contour.length;
|
||||
writer.writeUint16(endPtsOfContours);
|
||||
});
|
||||
|
||||
// instruction
|
||||
if (hinting && glyf.instructions) {
|
||||
const instructions = glyf.instructions;
|
||||
writer.writeUint16(instructions.length);
|
||||
for (i = 0, l = instructions.length; i < l; i++) {
|
||||
writer.writeUint8(instructions[i]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
writer.writeUint16(0);
|
||||
}
|
||||
|
||||
|
||||
// 获取暂存中的flags
|
||||
flags = ttf.support.glyf[index].flags || [];
|
||||
for (i = 0, l = flags.length; i < l; i++) {
|
||||
writer.writeUint8(flags[i]);
|
||||
}
|
||||
|
||||
const xCoord = ttf.support.glyf[index].xCoord || [];
|
||||
for (i = 0, l = xCoord.length; i < l; i++) {
|
||||
if (0 <= xCoord[i] && xCoord[i] <= 0xFF) {
|
||||
writer.writeUint8(xCoord[i]);
|
||||
}
|
||||
else {
|
||||
writer.writeInt16(xCoord[i]);
|
||||
}
|
||||
}
|
||||
|
||||
const yCoord = ttf.support.glyf[index].yCoord || [];
|
||||
for (i = 0, l = yCoord.length; i < l; i++) {
|
||||
if (0 <= yCoord[i] && yCoord[i] <= 0xFF) {
|
||||
writer.writeUint8(yCoord[i]);
|
||||
}
|
||||
else {
|
||||
writer.writeInt16(yCoord[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4字节对齐
|
||||
const glyfSize = ttf.support.glyf[index].glyfSize;
|
||||
|
||||
if (glyfSize % 4) {
|
||||
writer.writeEmpty(4 - glyfSize % 4);
|
||||
}
|
||||
});
|
||||
|
||||
return writer;
|
||||
}
|
||||
30
vendor/fonteditor-core/src/ttf/table/head.js
vendored
30
vendor/fonteditor-core/src/ttf/table/head.js
vendored
@ -1,30 +0,0 @@
|
||||
/**
|
||||
* @file head表
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
import table from './table';
|
||||
import struct from './struct';
|
||||
|
||||
export default table.create(
|
||||
'head',
|
||||
[
|
||||
['version', struct.Fixed],
|
||||
['fontRevision', struct.Fixed],
|
||||
['checkSumAdjustment', struct.Uint32],
|
||||
['magickNumber', struct.Uint32],
|
||||
['flags', struct.Uint16],
|
||||
['unitsPerEm', struct.Uint16],
|
||||
['created', struct.LongDateTime],
|
||||
['modified', struct.LongDateTime],
|
||||
['xMin', struct.Int16],
|
||||
['yMin', struct.Int16],
|
||||
['xMax', struct.Int16],
|
||||
['yMax', struct.Int16],
|
||||
['macStyle', struct.Uint16],
|
||||
['lowestRecPPEM', struct.Uint16],
|
||||
['fontDirectionHint', struct.Int16],
|
||||
['indexToLocFormat', struct.Int16],
|
||||
['glyphDataFormat', struct.Int16]
|
||||
]
|
||||
);
|
||||
31
vendor/fonteditor-core/src/ttf/table/hhea.js
vendored
31
vendor/fonteditor-core/src/ttf/table/hhea.js
vendored
@ -1,31 +0,0 @@
|
||||
/**
|
||||
* @file hhea 表
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*
|
||||
* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6hhea.html
|
||||
*/
|
||||
|
||||
import table from './table';
|
||||
import struct from './struct';
|
||||
export default table.create(
|
||||
'hhea',
|
||||
[
|
||||
['version', struct.Fixed],
|
||||
['ascent', struct.Int16],
|
||||
['descent', struct.Int16],
|
||||
['lineGap', struct.Int16],
|
||||
['advanceWidthMax', struct.Uint16],
|
||||
['minLeftSideBearing', struct.Int16],
|
||||
['minRightSideBearing', struct.Int16],
|
||||
['xMaxExtent', struct.Int16],
|
||||
['caretSlopeRise', struct.Int16],
|
||||
['caretSlopeRun', struct.Int16],
|
||||
['caretOffset', struct.Int16],
|
||||
['reserved0', struct.Int16],
|
||||
['reserved1', struct.Int16],
|
||||
['reserved2', struct.Int16],
|
||||
['reserved3', struct.Int16],
|
||||
['metricDataFormat', struct.Int16],
|
||||
['numOfLongHorMetrics', struct.Uint16]
|
||||
]
|
||||
);
|
||||
85
vendor/fonteditor-core/src/ttf/table/hmtx.js
vendored
85
vendor/fonteditor-core/src/ttf/table/hmtx.js
vendored
@ -1,85 +0,0 @@
|
||||
/**
|
||||
* @file hmtx 表
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*
|
||||
* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6hmtx.html
|
||||
*/
|
||||
|
||||
import table from './table';
|
||||
|
||||
export default table.create(
|
||||
'hmtx',
|
||||
[],
|
||||
{
|
||||
|
||||
read(reader, ttf) {
|
||||
const offset = this.offset;
|
||||
reader.seek(offset);
|
||||
|
||||
const numOfLongHorMetrics = ttf.hhea.numOfLongHorMetrics;
|
||||
const hMetrics = [];
|
||||
let i;
|
||||
let hMetric;
|
||||
for (i = 0; i < numOfLongHorMetrics; ++i) {
|
||||
hMetric = {};
|
||||
hMetric.advanceWidth = reader.readUint16();
|
||||
hMetric.leftSideBearing = reader.readInt16();
|
||||
hMetrics.push(hMetric);
|
||||
}
|
||||
|
||||
// 最后一个宽度
|
||||
const advanceWidth = hMetrics[numOfLongHorMetrics - 1].advanceWidth;
|
||||
const numOfLast = ttf.maxp.numGlyphs - numOfLongHorMetrics;
|
||||
|
||||
// 获取后续的hmetrics
|
||||
for (i = 0; i < numOfLast; ++i) {
|
||||
hMetric = {};
|
||||
hMetric.advanceWidth = advanceWidth;
|
||||
hMetric.leftSideBearing = reader.readInt16();
|
||||
hMetrics.push(hMetric);
|
||||
}
|
||||
|
||||
return hMetrics;
|
||||
|
||||
},
|
||||
|
||||
write(writer, ttf) {
|
||||
let i;
|
||||
const numOfLongHorMetrics = ttf.hhea.numOfLongHorMetrics;
|
||||
for (i = 0; i < numOfLongHorMetrics; ++i) {
|
||||
writer.writeUint16(ttf.glyf[i].advanceWidth);
|
||||
writer.writeInt16(ttf.glyf[i].leftSideBearing);
|
||||
}
|
||||
|
||||
// 最后一个宽度
|
||||
const numOfLast = ttf.glyf.length - numOfLongHorMetrics;
|
||||
|
||||
for (i = 0; i < numOfLast; ++i) {
|
||||
writer.writeInt16(ttf.glyf[numOfLongHorMetrics + i].leftSideBearing);
|
||||
}
|
||||
|
||||
return writer;
|
||||
},
|
||||
|
||||
size(ttf) {
|
||||
|
||||
// 计算同最后一个advanceWidth相等的元素个数
|
||||
let numOfLast = 0;
|
||||
// 最后一个advanceWidth
|
||||
const advanceWidth = ttf.glyf[ttf.glyf.length - 1].advanceWidth;
|
||||
|
||||
for (let i = ttf.glyf.length - 2; i >= 0; i--) {
|
||||
if (advanceWidth === ttf.glyf[i].advanceWidth) {
|
||||
numOfLast++;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ttf.hhea.numOfLongHorMetrics = ttf.glyf.length - numOfLast;
|
||||
|
||||
return 4 * ttf.hhea.numOfLongHorMetrics + 2 * numOfLast;
|
||||
}
|
||||
}
|
||||
);
|
||||
30
vendor/fonteditor-core/src/ttf/table/kern.js
vendored
30
vendor/fonteditor-core/src/ttf/table/kern.js
vendored
@ -1,30 +0,0 @@
|
||||
/**
|
||||
* @file kern
|
||||
* @author fr33z00(https://github.com/fr33z00)
|
||||
*
|
||||
* @reference: https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6kern.html
|
||||
*/
|
||||
|
||||
import table from './table';
|
||||
|
||||
export default table.create(
|
||||
'kern',
|
||||
[],
|
||||
{
|
||||
|
||||
read(reader, ttf) {
|
||||
const length = ttf.tables.kern.length;
|
||||
return reader.readBytes(this.offset, length);
|
||||
},
|
||||
|
||||
write(writer, ttf) {
|
||||
if (ttf.kern) {
|
||||
writer.writeBytes(ttf.kern, ttf.kern.length);
|
||||
}
|
||||
},
|
||||
|
||||
size(ttf) {
|
||||
return ttf.kern ? ttf.kern.length : 0;
|
||||
}
|
||||
}
|
||||
);
|
||||
30
vendor/fonteditor-core/src/ttf/table/kerx.js
vendored
30
vendor/fonteditor-core/src/ttf/table/kerx.js
vendored
@ -1,30 +0,0 @@
|
||||
/**
|
||||
* @file kerx
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*
|
||||
* @reference: https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6kerx.html
|
||||
*/
|
||||
|
||||
import table from './table';
|
||||
|
||||
export default table.create(
|
||||
'kerx',
|
||||
[],
|
||||
{
|
||||
|
||||
read(reader, ttf) {
|
||||
const length = ttf.tables.kerx.length;
|
||||
return reader.readBytes(this.offset, length);
|
||||
},
|
||||
|
||||
write(writer, ttf) {
|
||||
if (ttf.kerx) {
|
||||
writer.writeBytes(ttf.kerx, ttf.kerx.length);
|
||||
}
|
||||
},
|
||||
|
||||
size(ttf) {
|
||||
return ttf.kerx ? ttf.kerx.length : 0;
|
||||
}
|
||||
}
|
||||
);
|
||||
67
vendor/fonteditor-core/src/ttf/table/loca.js
vendored
67
vendor/fonteditor-core/src/ttf/table/loca.js
vendored
@ -1,67 +0,0 @@
|
||||
/**
|
||||
* @file loca表
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
import table from './table';
|
||||
import struct from './struct';
|
||||
|
||||
export default table.create(
|
||||
'loca',
|
||||
[],
|
||||
{
|
||||
|
||||
read(reader, ttf) {
|
||||
let offset = this.offset;
|
||||
const indexToLocFormat = ttf.head.indexToLocFormat;
|
||||
// indexToLocFormat有2字节和4字节的区别
|
||||
const type = struct.names[(indexToLocFormat === 0) ? struct.Uint16 : struct.Uint32];
|
||||
const size = (indexToLocFormat === 0) ? 2 : 4; // 字节大小
|
||||
const sizeRatio = (indexToLocFormat === 0) ? 2 : 1; // 真实地址偏移
|
||||
const wordOffset = [];
|
||||
|
||||
reader.seek(offset);
|
||||
|
||||
const numGlyphs = ttf.maxp.numGlyphs;
|
||||
for (let i = 0; i < numGlyphs; ++i) {
|
||||
wordOffset.push(reader.read(type, offset, false) * sizeRatio);
|
||||
offset += size;
|
||||
}
|
||||
|
||||
return wordOffset;
|
||||
},
|
||||
|
||||
write(writer, ttf) {
|
||||
const glyfSupport = ttf.support.glyf;
|
||||
let offset = ttf.support.glyf.offset || 0;
|
||||
const indexToLocFormat = ttf.head.indexToLocFormat;
|
||||
const sizeRatio = (indexToLocFormat === 0) ? 0.5 : 1;
|
||||
const numGlyphs = ttf.glyf.length;
|
||||
|
||||
for (let i = 0; i < numGlyphs; ++i) {
|
||||
if (indexToLocFormat) {
|
||||
writer.writeUint32(offset);
|
||||
}
|
||||
else {
|
||||
writer.writeUint16(offset);
|
||||
}
|
||||
offset += glyfSupport[i].size * sizeRatio;
|
||||
}
|
||||
|
||||
// write extra
|
||||
if (indexToLocFormat) {
|
||||
writer.writeUint32(offset);
|
||||
}
|
||||
else {
|
||||
writer.writeUint16(offset);
|
||||
}
|
||||
|
||||
return writer;
|
||||
},
|
||||
|
||||
size(ttf) {
|
||||
const locaCount = ttf.glyf.length + 1;
|
||||
return ttf.head.indexToLocFormat ? locaCount * 4 : locaCount * 2;
|
||||
}
|
||||
}
|
||||
);
|
||||
39
vendor/fonteditor-core/src/ttf/table/maxp.js
vendored
39
vendor/fonteditor-core/src/ttf/table/maxp.js
vendored
@ -1,39 +0,0 @@
|
||||
/**
|
||||
* @file maxp 表
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
import table from './table';
|
||||
import struct from './struct';
|
||||
|
||||
export default table.create(
|
||||
'maxp',
|
||||
[
|
||||
['version', struct.Fixed],
|
||||
['numGlyphs', struct.Uint16],
|
||||
['maxPoints', struct.Uint16],
|
||||
['maxContours', struct.Uint16],
|
||||
['maxCompositePoints', struct.Uint16],
|
||||
['maxCompositeContours', struct.Uint16],
|
||||
['maxZones', struct.Uint16],
|
||||
['maxTwilightPoints', struct.Uint16],
|
||||
['maxStorage', struct.Uint16],
|
||||
['maxFunctionDefs', struct.Uint16],
|
||||
['maxInstructionDefs', struct.Uint16],
|
||||
['maxStackElements', struct.Uint16],
|
||||
['maxSizeOfInstructions', struct.Uint16],
|
||||
['maxComponentElements', struct.Uint16],
|
||||
['maxComponentDepth', struct.Int16]
|
||||
],
|
||||
{
|
||||
|
||||
write(writer, ttf) {
|
||||
table.write.call(this, writer, ttf.support);
|
||||
return writer;
|
||||
},
|
||||
|
||||
size() {
|
||||
return 32;
|
||||
}
|
||||
}
|
||||
);
|
||||
166
vendor/fonteditor-core/src/ttf/table/name.js
vendored
166
vendor/fonteditor-core/src/ttf/table/name.js
vendored
@ -1,166 +0,0 @@
|
||||
/**
|
||||
* @file name表
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
import table from './table';
|
||||
import nameIdTbl from '../enum/nameId';
|
||||
import string from '../util/string';
|
||||
import platformTbl from '../enum/platform';
|
||||
import {mac, win} from '../enum/encoding';
|
||||
|
||||
export default table.create(
|
||||
'name',
|
||||
[],
|
||||
{
|
||||
|
||||
read(reader) {
|
||||
let offset = this.offset;
|
||||
reader.seek(offset);
|
||||
|
||||
const nameTbl = {};
|
||||
nameTbl.format = reader.readUint16();
|
||||
nameTbl.count = reader.readUint16();
|
||||
nameTbl.stringOffset = reader.readUint16();
|
||||
|
||||
const nameRecordTbl = [];
|
||||
const count = nameTbl.count;
|
||||
let i;
|
||||
let nameRecord;
|
||||
|
||||
for (i = 0; i < count; ++i) {
|
||||
nameRecord = {};
|
||||
nameRecord.platform = reader.readUint16();
|
||||
nameRecord.encoding = reader.readUint16();
|
||||
nameRecord.language = reader.readUint16();
|
||||
nameRecord.nameId = reader.readUint16();
|
||||
nameRecord.length = reader.readUint16();
|
||||
nameRecord.offset = reader.readUint16();
|
||||
nameRecordTbl.push(nameRecord);
|
||||
}
|
||||
|
||||
offset += nameTbl.stringOffset;
|
||||
|
||||
// 读取字符名字
|
||||
for (i = 0; i < count; ++i) {
|
||||
nameRecord = nameRecordTbl[i];
|
||||
nameRecord.name = reader.readBytes(offset + nameRecord.offset, nameRecord.length);
|
||||
}
|
||||
|
||||
const names = {};
|
||||
|
||||
// mac 下的english name
|
||||
let platform = platformTbl.Macintosh;
|
||||
let encoding = mac.Default;
|
||||
let language = 0;
|
||||
|
||||
// 如果有windows 下的 english,则用windows下的 name
|
||||
if (nameRecordTbl.some((record) => record.platform === platformTbl.Microsoft
|
||||
&& record.encoding === win.UCS2
|
||||
&& record.language === 1033)) {
|
||||
platform = platformTbl.Microsoft;
|
||||
encoding = win.UCS2;
|
||||
language = 1033;
|
||||
}
|
||||
|
||||
for (i = 0; i < count; ++i) {
|
||||
nameRecord = nameRecordTbl[i];
|
||||
if (nameRecord.platform === platform
|
||||
&& nameRecord.encoding === encoding
|
||||
&& nameRecord.language === language
|
||||
&& nameIdTbl[nameRecord.nameId]) {
|
||||
names[nameIdTbl[nameRecord.nameId]] = language === 0
|
||||
? string.getUTF8String(nameRecord.name)
|
||||
: string.getUCS2String(nameRecord.name);
|
||||
}
|
||||
}
|
||||
|
||||
return names;
|
||||
},
|
||||
|
||||
write(writer, ttf) {
|
||||
const nameRecordTbl = ttf.support.name;
|
||||
|
||||
writer.writeUint16(0); // format
|
||||
writer.writeUint16(nameRecordTbl.length); // count
|
||||
writer.writeUint16(6 + nameRecordTbl.length * 12); // string offset
|
||||
|
||||
// write name tbl header
|
||||
let offset = 0;
|
||||
nameRecordTbl.forEach((nameRecord) => {
|
||||
writer.writeUint16(nameRecord.platform);
|
||||
writer.writeUint16(nameRecord.encoding);
|
||||
writer.writeUint16(nameRecord.language);
|
||||
writer.writeUint16(nameRecord.nameId);
|
||||
writer.writeUint16(nameRecord.name.length);
|
||||
writer.writeUint16(offset); // offset
|
||||
offset += nameRecord.name.length;
|
||||
});
|
||||
|
||||
// write name tbl strings
|
||||
nameRecordTbl.forEach((nameRecord) => {
|
||||
writer.writeBytes(nameRecord.name);
|
||||
});
|
||||
|
||||
return writer;
|
||||
},
|
||||
|
||||
size(ttf) {
|
||||
const names = ttf.name;
|
||||
let nameRecordTbl = [];
|
||||
|
||||
// 写入name信息
|
||||
// 这里为了简化书写,仅支持英文编码字符,
|
||||
// 中文编码字符将被转化成url encode
|
||||
let size = 6;
|
||||
Object.keys(names).forEach((name) => {
|
||||
const id = nameIdTbl.names[name];
|
||||
|
||||
const utf8Bytes = string.toUTF8Bytes(names[name]);
|
||||
const usc2Bytes = string.toUCS2Bytes(names[name]);
|
||||
|
||||
if (undefined !== id) {
|
||||
// mac
|
||||
nameRecordTbl.push({
|
||||
nameId: id,
|
||||
platform: 1,
|
||||
encoding: 0,
|
||||
language: 0,
|
||||
name: utf8Bytes
|
||||
});
|
||||
|
||||
// windows
|
||||
nameRecordTbl.push({
|
||||
nameId: id,
|
||||
platform: 3,
|
||||
encoding: 1,
|
||||
language: 1033,
|
||||
name: usc2Bytes
|
||||
});
|
||||
|
||||
// 子表大小
|
||||
size += 12 * 2 + utf8Bytes.length + usc2Bytes.length;
|
||||
}
|
||||
});
|
||||
|
||||
const namingOrder = ['platform', 'encoding', 'language', 'nameId'];
|
||||
nameRecordTbl = nameRecordTbl.sort((a, b) => {
|
||||
let l = 0;
|
||||
namingOrder.some(name => {
|
||||
const o = a[name] - b[name];
|
||||
if (o) {
|
||||
l = o;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return l;
|
||||
});
|
||||
|
||||
// 保存预处理信息
|
||||
ttf.support.name = nameRecordTbl;
|
||||
|
||||
return size;
|
||||
}
|
||||
}
|
||||
);
|
||||
154
vendor/fonteditor-core/src/ttf/table/post.js
vendored
154
vendor/fonteditor-core/src/ttf/table/post.js
vendored
@ -1,154 +0,0 @@
|
||||
/**
|
||||
* @file post 表
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*
|
||||
* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6post.html
|
||||
*/
|
||||
|
||||
import table from './table';
|
||||
import struct from './struct';
|
||||
import string from '../util/string';
|
||||
import unicodeName from '../enum/unicodeName';
|
||||
|
||||
const Posthead = table.create(
|
||||
'posthead',
|
||||
[
|
||||
['format', struct.Fixed],
|
||||
['italicAngle', struct.Fixed],
|
||||
['underlinePosition', struct.Int16],
|
||||
['underlineThickness', struct.Int16],
|
||||
['isFixedPitch', struct.Uint32],
|
||||
['minMemType42', struct.Uint32],
|
||||
['maxMemType42', struct.Uint32],
|
||||
['minMemType1', struct.Uint32],
|
||||
['maxMemType1', struct.Uint32]
|
||||
]
|
||||
);
|
||||
|
||||
export default table.create(
|
||||
'post',
|
||||
[],
|
||||
{
|
||||
|
||||
read(reader, ttf) {
|
||||
const format = reader.readFixed(this.offset);
|
||||
// 读取表头
|
||||
const tbl = new Posthead(this.offset).read(reader, ttf);
|
||||
|
||||
// format2
|
||||
if (format === 2) {
|
||||
const numberOfGlyphs = reader.readUint16();
|
||||
const glyphNameIndex = [];
|
||||
|
||||
for (let i = 0; i < numberOfGlyphs; ++i) {
|
||||
glyphNameIndex.push(reader.readUint16());
|
||||
}
|
||||
|
||||
const pascalStringOffset = reader.offset;
|
||||
const pascalStringLength = ttf.tables.post.length - (pascalStringOffset - this.offset);
|
||||
const pascalStringBytes = reader.readBytes(reader.offset, pascalStringLength);
|
||||
|
||||
tbl.nameIndex = glyphNameIndex; // 设置glyf名字索引
|
||||
tbl.names = string.getPascalString(pascalStringBytes); // glyf名字数组
|
||||
}
|
||||
// deprecated
|
||||
else if (format === 2.5) {
|
||||
tbl.format = 3;
|
||||
}
|
||||
|
||||
return tbl;
|
||||
},
|
||||
|
||||
write(writer, ttf) {
|
||||
|
||||
|
||||
const post = ttf.post || {
|
||||
format: 3
|
||||
};
|
||||
|
||||
// write header
|
||||
writer.writeFixed(post.format); // format
|
||||
writer.writeFixed(post.italicAngle || 0); // italicAngle
|
||||
writer.writeInt16(post.underlinePosition || 0); // underlinePosition
|
||||
writer.writeInt16(post.underlineThickness || 0); // underlineThickness
|
||||
writer.writeUint32(post.isFixedPitch || 0); // isFixedPitch
|
||||
writer.writeUint32(post.minMemType42 || 0); // minMemType42
|
||||
writer.writeUint32(post.maxMemType42 || 0); // maxMemType42
|
||||
writer.writeUint32(post.minMemType1 || 0); // minMemType1
|
||||
writer.writeUint32(post.maxMemType1 || 0); // maxMemType1
|
||||
|
||||
// version 3 不设置post信息
|
||||
if (post.format === 2) {
|
||||
const numberOfGlyphs = ttf.glyf.length;
|
||||
writer.writeUint16(numberOfGlyphs); // numberOfGlyphs
|
||||
// write glyphNameIndex
|
||||
const nameIndex = ttf.support.post.nameIndex;
|
||||
for (let i = 0, l = nameIndex.length; i < l; i++) {
|
||||
writer.writeUint16(nameIndex[i]);
|
||||
}
|
||||
|
||||
// write names
|
||||
ttf.support.post.names.forEach((name) => {
|
||||
writer.writeBytes(name);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
size(ttf) {
|
||||
|
||||
const numberOfGlyphs = ttf.glyf.length;
|
||||
ttf.post = ttf.post || {};
|
||||
ttf.post.format = ttf.post.format || 3;
|
||||
ttf.post.maxMemType1 = numberOfGlyphs;
|
||||
|
||||
// version 3 不设置post信息
|
||||
if (ttf.post.format === 3 || ttf.post.format === 1) {
|
||||
return 32;
|
||||
}
|
||||
|
||||
// version 2
|
||||
let size = 34 + numberOfGlyphs * 2; // header + numberOfGlyphs + numberOfGlyphs * 2
|
||||
const glyphNames = [];
|
||||
const nameIndexArr = [];
|
||||
let nameIndex = 0;
|
||||
|
||||
// 获取 name的大小
|
||||
for (let i = 0; i < numberOfGlyphs; i++) {
|
||||
// .notdef
|
||||
if (i === 0) {
|
||||
nameIndexArr.push(0);
|
||||
}
|
||||
else {
|
||||
const glyf = ttf.glyf[i];
|
||||
const unicode = glyf.unicode ? glyf.unicode[0] : 0;
|
||||
const unicodeNameIndex = unicodeName[unicode];
|
||||
if (undefined !== unicodeNameIndex) {
|
||||
nameIndexArr.push(unicodeNameIndex);
|
||||
}
|
||||
else {
|
||||
// 这里需要注意,"" 有可能是"\3" length不为0,但是是空字符串
|
||||
const name = glyf.name;
|
||||
if (!name || name.charCodeAt(0) < 32) {
|
||||
nameIndexArr.push(258 + nameIndex++);
|
||||
glyphNames.push([0]);
|
||||
size++;
|
||||
}
|
||||
else {
|
||||
nameIndexArr.push(258 + nameIndex++);
|
||||
const bytes = string.toPascalStringBytes(name); // pascal string bytes
|
||||
glyphNames.push(bytes);
|
||||
size += bytes.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ttf.support.post = {
|
||||
nameIndex: nameIndexArr,
|
||||
names: glyphNames
|
||||
};
|
||||
|
||||
return size;
|
||||
}
|
||||
}
|
||||
);
|
||||
30
vendor/fonteditor-core/src/ttf/table/prep.js
vendored
30
vendor/fonteditor-core/src/ttf/table/prep.js
vendored
@ -1,30 +0,0 @@
|
||||
/**
|
||||
* @file prep表
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*
|
||||
* @reference: http://www.microsoft.com/typography/otspec140/prep.htm
|
||||
*/
|
||||
|
||||
import table from './table';
|
||||
|
||||
export default table.create(
|
||||
'prep',
|
||||
[],
|
||||
{
|
||||
|
||||
read(reader, ttf) {
|
||||
const length = ttf.tables.prep.length;
|
||||
return reader.readBytes(this.offset, length);
|
||||
},
|
||||
|
||||
write(writer, ttf) {
|
||||
if (ttf.prep) {
|
||||
writer.writeBytes(ttf.prep, ttf.prep.length);
|
||||
}
|
||||
},
|
||||
|
||||
size(ttf) {
|
||||
return ttf.prep ? ttf.prep.length : 0;
|
||||
}
|
||||
}
|
||||
);
|
||||
38
vendor/fonteditor-core/src/ttf/table/struct.js
vendored
38
vendor/fonteditor-core/src/ttf/table/struct.js
vendored
@ -1,38 +0,0 @@
|
||||
/**
|
||||
* @file ttf基本数据结构
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*
|
||||
* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6.html
|
||||
*/
|
||||
|
||||
const struct = {
|
||||
Int8: 1,
|
||||
Uint8: 2,
|
||||
Int16: 3,
|
||||
Uint16: 4,
|
||||
Int32: 5,
|
||||
Uint32: 6,
|
||||
Fixed: 7, // 32-bit signed fixed-point number (16.16)
|
||||
FUnit: 8, // Smallest measurable distance in the em space
|
||||
// 16-bit signed fixed number with the low 14 bits of fraction
|
||||
F2Dot14: 11,
|
||||
// The long internal format of a date in seconds since 12:00 midnight,
|
||||
// January 1, 1904. It is represented as a signed 64-bit integer.
|
||||
LongDateTime: 12,
|
||||
|
||||
// extend data type
|
||||
Char: 13,
|
||||
String: 14,
|
||||
Bytes: 15,
|
||||
Uint24: 20
|
||||
};
|
||||
|
||||
// 反转名字查找
|
||||
const names = {};
|
||||
Object.keys(struct).forEach((key) => {
|
||||
names[struct[key]] = key;
|
||||
});
|
||||
|
||||
struct.names = names;
|
||||
|
||||
export default struct;
|
||||
@ -1,30 +0,0 @@
|
||||
/**
|
||||
* @file otf字体格式支持的表
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
import head from './head';
|
||||
import maxp from './maxp';
|
||||
import cmap from './cmap';
|
||||
import name from './name';
|
||||
import hhea from './hhea';
|
||||
import hmtx from './hmtx';
|
||||
import post from './post';
|
||||
import OS2 from './OS2';
|
||||
import CFF from './CFF';
|
||||
import GPOS from './GPOS';
|
||||
import kern from './kern';
|
||||
|
||||
export default {
|
||||
head,
|
||||
maxp,
|
||||
cmap,
|
||||
name,
|
||||
hhea,
|
||||
hmtx,
|
||||
post,
|
||||
'OS/2': OS2,
|
||||
CFF,
|
||||
GPOS,
|
||||
kern
|
||||
};
|
||||
42
vendor/fonteditor-core/src/ttf/table/support.js
vendored
42
vendor/fonteditor-core/src/ttf/table/support.js
vendored
@ -1,42 +0,0 @@
|
||||
/**
|
||||
* @file ttf读取和写入支持的表
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
import head from './head';
|
||||
import maxp from './maxp';
|
||||
import loca from './loca';
|
||||
import cmap from './cmap';
|
||||
import glyf from './glyf';
|
||||
import name from './name';
|
||||
import hhea from './hhea';
|
||||
import hmtx from './hmtx';
|
||||
import post from './post';
|
||||
import OS2 from './OS2';
|
||||
import fpgm from './fpgm';
|
||||
import cvt from './cvt';
|
||||
import prep from './prep';
|
||||
import gasp from './gasp';
|
||||
import GPOS from './GPOS';
|
||||
import kern from './kern';
|
||||
import kerx from './kerx';
|
||||
|
||||
export default {
|
||||
head,
|
||||
maxp,
|
||||
loca,
|
||||
cmap,
|
||||
glyf,
|
||||
name,
|
||||
hhea,
|
||||
hmtx,
|
||||
post,
|
||||
'OS/2': OS2,
|
||||
fpgm,
|
||||
cvt,
|
||||
prep,
|
||||
gasp,
|
||||
GPOS,
|
||||
kern,
|
||||
kerx
|
||||
};
|
||||
224
vendor/fonteditor-core/src/ttf/table/table.js
vendored
224
vendor/fonteditor-core/src/ttf/table/table.js
vendored
@ -1,224 +0,0 @@
|
||||
/**
|
||||
* @file ttf表基类
|
||||
* @author mengke01(kekee000@gmail.com)
|
||||
*/
|
||||
|
||||
import struct from './struct';
|
||||
import error from '../error';
|
||||
/* eslint-disable no-invalid-this */
|
||||
/**
|
||||
* 读取表结构
|
||||
*
|
||||
* @param {Reader} reader reader对象
|
||||
* @return {Object} 当前对象
|
||||
*/
|
||||
function read(reader) {
|
||||
|
||||
const offset = this.offset;
|
||||
|
||||
if (undefined !== offset) {
|
||||
reader.seek(offset);
|
||||
}
|
||||
|
||||
const me = this;
|
||||
|
||||
this.struct.forEach((item) => {
|
||||
const name = item[0];
|
||||
const type = item[1];
|
||||
let typeName = null;
|
||||
switch (type) {
|
||||
case struct.Int8:
|
||||
case struct.Uint8:
|
||||
case struct.Int16:
|
||||
case struct.Uint16:
|
||||
case struct.Int32:
|
||||
case struct.Uint32:
|
||||
typeName = struct.names[type];
|
||||
me[name] = reader.read(typeName);
|
||||
break;
|
||||
|
||||
case struct.Fixed:
|
||||
me[name] = reader.readFixed();
|
||||
break;
|
||||
|
||||
case struct.LongDateTime:
|
||||
me[name] = reader.readLongDateTime();
|
||||
break;
|
||||
|
||||
case struct.Bytes:
|
||||
me[name] = reader.readBytes(reader.offset, item[2] || 0);
|
||||
break;
|
||||
|
||||
case struct.Char:
|
||||
me[name] = reader.readChar();
|
||||
break;
|
||||
|
||||
case struct.String:
|
||||
me[name] = reader.readString(reader.offset, item[2] || 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
error.raise(10003, name, type);
|
||||
}
|
||||
});
|
||||
|
||||
return this.valueOf();
|
||||
}
|
||||
|
||||
/**
|
||||
* 写表结构
|
||||
*
|
||||
* @param {Object} writer writer对象
|
||||
* @param {Object} ttf 已解析的ttf对象
|
||||
*
|
||||
* @return {Writer} 返回writer对象
|
||||
*/
|
||||
function write(writer, ttf) {
|
||||
const table = ttf[this.name];
|
||||
|
||||
if (!table) {
|
||||
error.raise(10203, this.name);
|
||||
}
|
||||
|
||||
this.struct.forEach((item) => {
|
||||
const name = item[0];
|
||||
const type = item[1];
|
||||
let typeName = null;
|
||||
switch (type) {
|
||||
case struct.Int8:
|
||||
case struct.Uint8:
|
||||
case struct.Int16:
|
||||
case struct.Uint16:
|
||||
case struct.Int32:
|
||||
case struct.Uint32:
|
||||
typeName = struct.names[type];
|
||||
writer.write(typeName, table[name]);
|
||||
break;
|
||||
|
||||
case struct.Fixed:
|
||||
writer.writeFixed(table[name]);
|
||||
break;
|
||||
|
||||
case struct.LongDateTime:
|
||||
writer.writeLongDateTime(table[name]);
|
||||
break;
|
||||
|
||||
case struct.Bytes:
|
||||
writer.writeBytes(table[name], item[2] || 0);
|
||||
break;
|
||||
|
||||
case struct.Char:
|
||||
writer.writeChar(table[name]);
|
||||
break;
|
||||
|
||||
case struct.String:
|
||||
writer.writeString(table[name], item[2] || 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
error.raise(10003, name, type);
|
||||
}
|
||||
});
|
||||
|
||||
return writer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取ttf表的size大小
|
||||
*
|
||||
* @param {string} name 表名
|
||||
* @return {number} 表大小
|
||||
*/
|
||||
function size() {
|
||||
|
||||
let sz = 0;
|
||||
this.struct.forEach((item) => {
|
||||
const type = item[1];
|
||||
switch (type) {
|
||||
case struct.Int8:
|
||||
case struct.Uint8:
|
||||
sz += 1;
|
||||
break;
|
||||
|
||||
case struct.Int16:
|
||||
case struct.Uint16:
|
||||
sz += 2;
|
||||
break;
|
||||
|
||||
case struct.Int32:
|
||||
case struct.Uint32:
|
||||
case struct.Fixed:
|
||||
sz += 4;
|
||||
break;
|
||||
|
||||
case struct.LongDateTime:
|
||||
sz += 8;
|
||||
break;
|
||||
|
||||
case struct.Bytes:
|
||||
sz += item[2] || 0;
|
||||
break;
|
||||
|
||||
case struct.Char:
|
||||
sz += 1;
|
||||
break;
|
||||
|
||||
case struct.String:
|
||||
sz += item[2] || 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
error.raise(10003, name, type);
|
||||
}
|
||||
});
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象的值
|
||||
*
|
||||
* @return {*} 当前对象的值
|
||||
*/
|
||||
function valueOf() {
|
||||
const val = {};
|
||||
const me = this;
|
||||
this.struct.forEach(item => {
|
||||
val[item[0]] = me[item[0]];
|
||||
});
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
export default {
|
||||
read,
|
||||
write,
|
||||
size,
|
||||
valueOf,
|
||||
|
||||
/**
|
||||
* 创建一个表结构
|
||||
*
|
||||
* @param {string} name 表名
|
||||
* @param {Array<[string, number]>} struct 表结构
|
||||
* @param {Object} proto 原型
|
||||
* @return {Function} 表构造函数
|
||||
*/
|
||||
create(name, struct, proto) {
|
||||
class Table {
|
||||
constructor(offset) {
|
||||
this.name = name;
|
||||
this.struct = struct;
|
||||
this.offset = offset;
|
||||
}
|
||||
}
|
||||
|
||||
Table.prototype.read = read;
|
||||
Table.prototype.write = write;
|
||||
Table.prototype.size = size;
|
||||
Table.prototype.valueOf = valueOf;
|
||||
Object.assign(Table.prototype, proto);
|
||||
return Table;
|
||||
}
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user