Build tweaks for server-side, fixes - WIP

This commit is contained in:
Marcel Mraz 2024-09-02 11:43:44 +02:00
parent 6ce17a80c4
commit 1e19a446b8
11 changed files with 87 additions and 86 deletions

View File

@ -62,10 +62,8 @@ export class ExcalidrawFontFace implements IExcalidrawFontFace {
return new Promise<string>((resolve) => { return new Promise<string>((resolve) => {
this.getContent(codePoints).then((content) => { this.getContent(codePoints).then((content) => {
resolve(`@font-face { resolve(`
font-family: ${this.fontFace.family}; @font-face { font-family: ${this.fontFace.family}; src: url(${content}); }`);
src: url(${content});
}`);
}); });
}); });
} }

View File

@ -15,27 +15,23 @@ const load = (): Promise<{
}> => { }> => {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
try { try {
WebAssembly.instantiate(binary).then((module) => { const module = await WebAssembly.instantiate(binary);
const harfbuzzJsWasm = module.instance.exports; const harfbuzzJsWasm = module.instance.exports;
// @ts-expect-error since `.buffer` is custom prop // @ts-expect-error since `.buffer` is custom prop
const heapu8 = new Uint8Array(harfbuzzJsWasm.memory.buffer); const heapu8 = new Uint8Array(harfbuzzJsWasm.memory.buffer);
const hbSubset = { const hbSubset = {
subset: ( subset: (fontBuffer: ArrayBuffer, codePoints: ReadonlySet<number>) => {
fontBuffer: ArrayBuffer, return bindings.subset(
codePoints: ReadonlySet<number>, harfbuzzJsWasm,
) => { heapu8,
return bindings.subset( fontBuffer,
harfbuzzJsWasm, codePoints,
heapu8, );
fontBuffer, },
codePoints, };
);
},
};
resolve(hbSubset); resolve(hbSubset);
});
} catch (e) { } catch (e) {
reject(e); reject(e);
} }

View File

@ -47,6 +47,7 @@ const Module = (function () {
moduleOverrides[key] = Module[key]; moduleOverrides[key] = Module[key];
} }
} }
let arguments_ = []; let arguments_ = [];
let thisProgram = "./this.program"; let thisProgram = "./this.program";
let quit_ = function (status, toThrow) { let quit_ = function (status, toThrow) {
@ -3917,6 +3918,7 @@ const Module = (function () {
let calledRun; let calledRun;
Module.then = function (func) { Module.then = function (func) {
if (calledRun) { if (calledRun) {
throw new Error("1 This error should be silently swallowed");
func(Module); func(Module);
} else { } else {
const old = Module.onRuntimeInitialized; const old = Module.onRuntimeInitialized;
@ -3924,6 +3926,7 @@ const Module = (function () {
if (old) { if (old) {
old(); old();
} }
throw new Error("This error should be silently swallowed 1");
func(Module); func(Module);
}; };
} }
@ -4046,3 +4049,5 @@ const Module = (function () {
})(); })();
export default Module; export default Module;

View File

@ -15,37 +15,36 @@ const load = (): Promise<{
}> => { }> => {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
try { try {
// re-map from internal vector into byte array
function convertFromVecToUint8Array(vector: Vector): Uint8Array {
const arr = [];
for (let i = 0, l = vector.size(); i < l; i++) {
arr.push(vector.get(i));
}
return new Uint8Array(arr);
}
// TODO_CHINESE: bindings implements .then, but rejections will still likely be not caught
// initializing the module manually, so that we could pass in the wasm binary // initializing the module manually, so that we could pass in the wasm binary
bindings({ wasmBinary: binary }).then( const module: {
(module: { woff2Enc: (buffer: ArrayBuffer, byteLength: number) => Vector;
woff2Enc: (buffer: ArrayBuffer, byteLength: number) => Vector; woff2Dec: (buffer: ArrayBuffer, byteLength: number) => Vector;
woff2Dec: (buffer: ArrayBuffer, byteLength: number) => Vector; } = await bindings({ wasmBinary: binary });
}) => {
// re-map from internal vector into byte array
function convertFromVecToUint8Array(vector: Vector): Uint8Array {
const arr = [];
for (let i = 0, l = vector.size(); i < l; i++) {
arr.push(vector.get(i));
}
return new Uint8Array(arr); // re-exporting only compress and decompress functions (also avoids infinite loop inside emscripten bindings)
} const woff2 = {
compress: (buffer: ArrayBuffer) =>
convertFromVecToUint8Array(
module.woff2Enc(buffer, buffer.byteLength),
),
decompress: (buffer: ArrayBuffer) =>
convertFromVecToUint8Array(
module.woff2Dec(buffer, buffer.byteLength),
),
};
// re-exporting only compress and decompress functions (also avoids infinite loop inside emscripten bindings) resolve(woff2);
const woff2 = {
compress: (buffer: ArrayBuffer) =>
convertFromVecToUint8Array(
module.woff2Enc(buffer, buffer.byteLength),
),
decompress: (buffer: ArrayBuffer) =>
convertFromVecToUint8Array(
module.woff2Dec(buffer, buffer.byteLength),
),
};
resolve(woff2);
},
);
} catch (e) { } catch (e) {
reject(e); reject(e);
} }

View File

@ -31,8 +31,8 @@ export class WorkerPool<T, R> {
}, },
) { ) {
this.workerUrl = workerUrl; this.workerUrl = workerUrl;
// by default, active & idle workers will be terminated after 10 seconds of inactivity // by default, active & idle workers will be terminated after 5 seconds of inactivity
this.workerTTL = options.ttl || 10_000; this.workerTTL = options.ttl || 5_000;
this.initWorker = options.initWorker; this.initWorker = options.initWorker;
} }

View File

@ -82,7 +82,6 @@ const rawConfig = {
entryPoints: ["index.ts"], entryPoints: ["index.ts"],
bundle: true, bundle: true,
format: "esm", format: "esm",
packages: "external",
}; };
// const BASE_PATH = `${path.resolve(`${__dirname}/..`)}`; // const BASE_PATH = `${path.resolve(`${__dirname}/..`)}`;

View File

@ -0,0 +1,6 @@
# 1. Created "XiaolaiNotoEmoji" through FontForge
- Merged `XiaolaiSC-Regular.ttf` with `NotoEmoji-Regular.ttf` (kept in the codebasefor future reference)
- Adjusted glyphs of the merged font to fit the Em box
- Added merged copyright & license
- Generated versions for different Em sizes 1000 and 2048 (otherwise pyftmerge throws)

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -47,7 +47,7 @@ module.exports.woff2BrowserPlugin = () => {
* 2. convert all the imported fonts (including those from cdn) at build time into .ttf (since Resvg does not support woff2, neither inlined dataurls - https://github.com/RazrFalcon/resvg/issues/541) * 2. convert all the imported fonts (including those from cdn) at build time into .ttf (since Resvg does not support woff2, neither inlined dataurls - https://github.com/RazrFalcon/resvg/issues/541)
* - merging multiple woff2 into one ttf (for same families with different unicode ranges) * - merging multiple woff2 into one ttf (for same families with different unicode ranges)
* - deduplicating glyphs due to the merge process * - deduplicating glyphs due to the merge process
* - merging emoji font for each * - merging fallback font for each
* - printing out font metrics * - printing out font metrics
* *
* @returns {import("esbuild").Plugin} * @returns {import("esbuild").Plugin}
@ -176,6 +176,11 @@ module.exports.woff2ServerPlugin = (options = {}) => {
// for now we are interested in the regular families only // for now we are interested in the regular families only
for (const [family, { Regular }] of sortedFonts) { for (const [family, { Regular }] of sortedFonts) {
if (family === "Xiaolai SC") {
// don't generate ttf for Xiaolai SC, as we have it hardcoded
continue;
}
const baseFont = Regular[0]; const baseFont = Regular[0];
const tempFilePaths = Regular.map((_, index) => const tempFilePaths = Regular.map((_, index) =>
@ -192,41 +197,34 @@ module.exports.woff2ServerPlugin = (options = {}) => {
fs.writeFileSync(tempFilePaths[index], font.write({ type: "ttf" })); fs.writeFileSync(tempFilePaths[index], font.write({ type: "ttf" }));
} }
const emojiFilePath = path.resolve( const fallbackFilePath2048 = path.resolve(
__dirname, __dirname,
"./assets/NotoEmoji-Regular.ttf", "./assets/XiaolaiNotoEmoji-2048.ttf",
); );
const fallbackFilePath1000 = path.resolve(
const emojiBuffer = fs.readFileSync(emojiFilePath); __dirname,
const emojiFont = Font.create(emojiBuffer, { type: "ttf" }); "./assets/XiaolaiNotoEmoji-1000.ttf",
// hack so that:
// - emoji font has same metrics as the base font, otherwise pyftmerge throws due to different unitsPerEm
// - emoji font glyphs are adjusted based to the base font glyphs, otherwise the glyphs don't match
const patchedEmojiFont = Font.create({
...baseFont.data,
glyf: baseFont.find({ unicode: [65] }), // adjust based on the "A" glyph (does not have to be first)
}).merge(emojiFont, { adjustGlyf: true });
const emojiTempFilePath = path.resolve(
outputDir,
`temp_${family}_Emoji.ttf`,
);
fs.writeFileSync(
emojiTempFilePath,
patchedEmojiFont.write({ type: "ttf" }),
); );
const fallbackBuffer = fs.readFileSync(fallbackFilePath2048);
const fallbackFont = Font.create(fallbackBuffer, { type: "ttf" });
const mergedFontPath = path.resolve(outputDir, `${family}.ttf`); const mergedFontPath = path.resolve(outputDir, `${family}.ttf`);
execSync( if (baseFont.data.head.unitsPerEm !== 1000) {
`pyftmerge --output-file="${mergedFontPath}" "${tempFilePaths.join( execSync(
'" "', `pyftmerge --output-file="${mergedFontPath}" "${tempFilePaths.join(
)}" "${emojiTempFilePath}"`, '" "',
); )}" "${fallbackFilePath2048}"`,
);
} else {
execSync(
`pyftmerge --output-file="${mergedFontPath}" "${tempFilePaths.join(
'" "',
)}" "${fallbackFilePath1000}"`,
);
}
// cleanup // cleanup
fs.rmSync(emojiTempFilePath);
for (const path of tempFilePaths) { for (const path of tempFilePaths) {
fs.rmSync(path); fs.rmSync(path);
} }
@ -243,8 +241,8 @@ module.exports.woff2ServerPlugin = (options = {}) => {
...mergedFont.data, ...mergedFont.data,
name: { name: {
...mergedFont.data.name, ...mergedFont.data.name,
copyright: `${baseFont.data.name.copyright} & ${emojiFont.data.name.copyright}`, copyright: `${baseFont.data.name.copyright} & ${fallbackFont.data.name.copyright}`,
licence: `${baseFont.data.name.licence} & ${emojiFont.data.name.licence}`, licence: `${baseFont.data.name.licence} & ${fallbackFont.data.name.licence}`,
}, },
}); });
@ -255,7 +253,7 @@ module.exports.woff2ServerPlugin = (options = {}) => {
console.info(`Generated "${family}"`); console.info(`Generated "${family}"`);
if (Regular.length > 1) { if (Regular.length > 1) {
console.info( console.info(
`- by merging ${Regular.length} woff2 files and 1 emoji ttf file`, `- by merging ${Regular.length} woff2 files and 1 fallback ttf file`,
); );
} }
console.info( console.info(