Skip to content

Commit e67e39e

Browse files
Fix loading fonts in web-worker
1 parent e1010dd commit e67e39e

File tree

5 files changed

+98
-32
lines changed

5 files changed

+98
-32
lines changed

packages/types/src/index.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export interface RGBAColor {
3838

3939
export const JimpClassSchema = z.object({
4040
bitmap: z.object({
41-
data: z.instanceof(Buffer),
41+
data: z.union([z.instanceof(Buffer), z.instanceof(Uint8Array)]),
4242
width: z.number(),
4343
height: z.number(),
4444
}),
@@ -60,7 +60,7 @@ export interface JimpClass {
6060
w: number,
6161
h: number,
6262
// eslint-disable-next-line @typescript-eslint/no-explicit-any
63-
cb: (x: number, y: number, idx: number) => any,
63+
cb: (x: number, y: number, idx: number) => any
6464
): JimpClass;
6565
scan(
6666
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -69,6 +69,6 @@ export interface JimpClass {
6969
w?: number,
7070
h?: number,
7171
// eslint-disable-next-line @typescript-eslint/no-explicit-any
72-
f?: (x: number, y: number, idx: number) => any,
72+
f?: (x: number, y: number, idx: number) => any
7373
): JimpClass;
7474
}

plugins/plugin-print/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
"parse-bmfont-ascii": "^1.0.6",
8383
"parse-bmfont-binary": "^1.0.6",
8484
"parse-bmfont-xml": "^1.1.6",
85+
"simple-xml-to-json": "^1.2.2",
8586
"zod": "^3.23.8"
8687
},
8788
"publishConfig": {

plugins/plugin-print/src/load-bitmap-font.ts

+76-11
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,15 @@ import parseASCII from "parse-bmfont-ascii";
22
import parseXML from "parse-bmfont-xml";
33
import readBinary from "parse-bmfont-binary";
44
import { BmCharacter, BmKerning, BmFont, BmCommonProps } from "./types.js";
5+
import png from "@jimp/js-png";
6+
import { createJimp } from "@jimp/core";
7+
import path from "path";
8+
import { convertXML } from "simple-xml-to-json";
59

10+
export const isWebWorker =
11+
typeof self !== "undefined" && self.document === undefined;
12+
13+
const CharacterJimp = createJimp({ formats: [png] });
614
const HEADER = Buffer.from([66, 77, 70, 3]);
715

816
function isBinary(buf: Buffer | string) {
@@ -20,17 +28,16 @@ function isBinary(buf: Buffer | string) {
2028
);
2129
}
2230

23-
function parseFont(
24-
file: string,
25-
data: Buffer | string,
26-
): {
31+
export interface LoadedFont {
2732
chars: BmCharacter[];
2833
kernings: BmKerning[];
2934
common: BmCommonProps;
3035
// eslint-disable-next-line @typescript-eslint/no-explicit-any
3136
info: Record<string, any>;
3237
pages: string[];
33-
} {
38+
}
39+
40+
function parseFont(file: string, data: Buffer | string): LoadedFont {
3441
if (isBinary(data)) {
3542
if (typeof data === "string") {
3643
data = Buffer.from(data, "binary");
@@ -52,13 +59,68 @@ function parseFont(
5259
return parseASCII(data);
5360
}
5461

62+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
63+
function parseNumbersInObject<T extends Record<string, any>>(obj: T) {
64+
for (const key in obj) {
65+
try {
66+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
67+
(obj as any)[key] = parseInt(obj[key], 10);
68+
} catch {
69+
// do nothing
70+
}
71+
72+
if (typeof obj[key] === "object") {
73+
parseNumbersInObject(obj[key]);
74+
}
75+
}
76+
77+
return obj;
78+
}
79+
5580
/**
5681
*
5782
* @param bufferOrUrl A URL to a file or a buffer
5883
* @returns
5984
*/
60-
async function loadBitmapFontData(bufferOrUrl: string | Buffer) {
61-
if (typeof bufferOrUrl === "string") {
85+
export async function loadBitmapFontData(
86+
bufferOrUrl: string | Buffer
87+
): Promise<LoadedFont> {
88+
if (isWebWorker && typeof bufferOrUrl === "string") {
89+
const res = await fetch(bufferOrUrl);
90+
const text = await res.text();
91+
const json = convertXML(text);
92+
93+
const font = json.font.children.reduce(
94+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
95+
(acc: Record<string, any>, i: any) => ({ ...acc, ...i }),
96+
{}
97+
);
98+
const pages: LoadedFont["pages"] = [];
99+
const chars: LoadedFont["chars"] = [];
100+
const kernings: LoadedFont["kernings"] = [];
101+
102+
for (let i = 0; i < font.pages.children.length; i++) {
103+
const p = font.pages.children[i].page;
104+
const id = parseInt(p.id, 10);
105+
pages[id] = parseNumbersInObject(p.file);
106+
}
107+
108+
for (let i = 0; i < font.chars.children.length; i++) {
109+
chars.push(parseNumbersInObject(font.chars.children[i].char));
110+
}
111+
112+
for (let i = 0; i < font.kernings.children.length; i++) {
113+
kernings.push(parseNumbersInObject(font.kernings.children[i].kerning));
114+
}
115+
116+
return {
117+
info: font.info,
118+
common: font.common,
119+
pages,
120+
chars,
121+
kernings,
122+
} satisfies LoadedFont;
123+
} else if (typeof bufferOrUrl === "string") {
62124
const res = await fetch(bufferOrUrl);
63125
const text = await res.text();
64126

@@ -69,11 +131,9 @@ async function loadBitmapFontData(bufferOrUrl: string | Buffer) {
69131
}
70132

71133
type RawFont = Awaited<ReturnType<typeof loadBitmapFontData>>;
134+
export type ResolveBmFont = Omit<BmFont, "pages"> & Pick<RawFont, "pages">;
72135

73-
export async function loadBitmapFont(
74-
bufferOrUrl: string | Buffer,
75-
): Promise<Omit<BmFont, "pages"> & Pick<RawFont, "pages">> {
76-
const font = await loadBitmapFontData(bufferOrUrl);
136+
export async function processBitmapFont(file: string, font: LoadedFont) {
77137
const chars: Record<string, BmCharacter> = {};
78138
const kernings: Record<string, BmKerning> = {};
79139

@@ -94,5 +154,10 @@ export async function loadBitmapFont(
94154
...font,
95155
chars,
96156
kernings,
157+
pages: await Promise.all(
158+
font.pages.map(async (page) =>
159+
CharacterJimp.read(path.join(path.dirname(file), page))
160+
)
161+
),
97162
};
98163
}

plugins/plugin-print/src/load-font.ts

+8-17
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
import { loadBitmapFont } from "./load-bitmap-font.js";
2-
import { createJimp } from "@jimp/core";
3-
import png from "@jimp/js-png";
4-
import path from "path";
5-
6-
const CharacterJimp = createJimp({ formats: [png] });
1+
import {
2+
isWebWorker,
3+
loadBitmapFontData,
4+
processBitmapFont,
5+
} from "./load-bitmap-font.js";
76

87
/**
98
* Loads a Bitmap Font from a file.
@@ -23,22 +22,14 @@ const CharacterJimp = createJimp({ formats: [png] });
2322
export async function loadFont(file: string) {
2423
let fileOrBuffer: string | Buffer = file;
2524

26-
if (typeof window === "undefined") {
25+
if (typeof window === "undefined" && !isWebWorker) {
2726
const { existsSync, promises: fs } = await import("fs");
2827

2928
if (existsSync(file)) {
3029
fileOrBuffer = await fs.readFile(file);
3130
}
3231
}
3332

34-
const font = await loadBitmapFont(fileOrBuffer);
35-
36-
return {
37-
...font,
38-
pages: await Promise.all(
39-
font.pages.map(async (page) =>
40-
CharacterJimp.read(path.join(path.dirname(file), page)),
41-
),
42-
),
43-
};
33+
const data = await loadBitmapFontData(fileOrBuffer);
34+
return processBitmapFont(file, data);
4435
}

pnpm-lock.yaml

+10-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)