Skip to content

Commit 59c8b9d

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

File tree

6 files changed

+98
-36
lines changed

6 files changed

+98
-36
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/index.node.test.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ async function createTextImage(
3232
maxHeight?: number;
3333
x?: number;
3434
y?: number;
35-
},
35+
}
3636
) {
3737
const loadedFont = await loadFont(font);
3838
const image = new Jimp({ width, height, color: 0xffffffff });
@@ -76,7 +76,7 @@ describe("Write text over image", function () {
7676
});
7777

7878
test("Jimp preset SANS_16_BLACK bitmap font positioned", async () => {
79-
const font = await loadFont(fonts.SANS_16_BLACK);
79+
const font = await loadFont(fonts.SANS_12_BLACK);
8080
const image = new Jimp({ width: 300, height: 100, color: 0xff8800ff });
8181
const output = await image
8282
.print({
@@ -93,7 +93,7 @@ describe("Write text over image", function () {
9393

9494
test("Jimp loads font from URL", async () => {
9595
const font = await loadFont(
96-
"https://raw.githubusercontent.com/jimp-dev/jimp/main/plugins/plugin-print/fonts/open-sans/open-sans-16-black/open-sans-16-black.fnt",
96+
"https://raw.githubusercontent.com/jimp-dev/jimp/main/plugins/plugin-print/fonts/open-sans/open-sans-16-black/open-sans-16-black.fnt"
9797
);
9898
const image = new Jimp({ width: 300, height: 100, color: 0xff8800ff });
9999
const output = await image
@@ -111,7 +111,7 @@ describe("Write text over image", function () {
111111

112112
test("Max width works without spaces", async () => {
113113
const font = await loadFont(
114-
"https://raw.githubusercontent.com/jimp-dev/jimp/main/plugins/plugin-print/fonts/open-sans/open-sans-16-black/open-sans-16-black.fnt",
114+
"https://raw.githubusercontent.com/jimp-dev/jimp/main/plugins/plugin-print/fonts/open-sans/open-sans-16-black/open-sans-16-black.fnt"
115115
);
116116
const image = new Jimp({ width: 300, height: 100, color: 0xff8800ff });
117117
const output = await image

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

+75-11
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@ 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+
const CharacterJimp = createJimp({ formats: [png] });
611
const HEADER = Buffer.from([66, 77, 70, 3]);
712

813
function isBinary(buf: Buffer | string) {
@@ -20,17 +25,16 @@ function isBinary(buf: Buffer | string) {
2025
);
2126
}
2227

23-
function parseFont(
24-
file: string,
25-
data: Buffer | string,
26-
): {
28+
export interface LoadedFont {
2729
chars: BmCharacter[];
2830
kernings: BmKerning[];
2931
common: BmCommonProps;
3032
// eslint-disable-next-line @typescript-eslint/no-explicit-any
3133
info: Record<string, any>;
3234
pages: string[];
33-
} {
35+
}
36+
37+
function parseFont(file: string, data: Buffer | string): LoadedFont {
3438
if (isBinary(data)) {
3539
if (typeof data === "string") {
3640
data = Buffer.from(data, "binary");
@@ -52,13 +56,70 @@ function parseFont(
5256
return parseASCII(data);
5357
}
5458

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

@@ -69,11 +130,9 @@ async function loadBitmapFontData(bufferOrUrl: string | Buffer) {
69130
}
70131

71132
type RawFont = Awaited<ReturnType<typeof loadBitmapFontData>>;
133+
export type ResolveBmFont = Omit<BmFont, "pages"> & Pick<RawFont, "pages">;
72134

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

@@ -94,5 +153,10 @@ export async function loadBitmapFont(
94153
...font,
95154
chars,
96155
kernings,
156+
pages: await Promise.all(
157+
font.pages.map(async (page) =>
158+
CharacterJimp.read(path.join(path.dirname(file), page))
159+
)
160+
),
97161
};
98162
}

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

+5-17
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
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 { loadBitmapFontData, processBitmapFont } from "./load-bitmap-font.js";
72

83
/**
94
* Loads a Bitmap Font from a file.
@@ -21,24 +16,17 @@ const CharacterJimp = createJimp({ formats: [png] });
2116
* ```
2217
*/
2318
export async function loadFont(file: string) {
19+
const isWebWorker = self.document === undefined;
2420
let fileOrBuffer: string | Buffer = file;
2521

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

2925
if (existsSync(file)) {
3026
fileOrBuffer = await fs.readFile(file);
3127
}
3228
}
3329

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-
};
30+
const data = await loadBitmapFontData(fileOrBuffer);
31+
return processBitmapFont(file, data);
4432
}

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)