Wait for custom fonts to load before continuing (#401)

This commit is contained in:
Thomas Weber authored and GitHub committed 2026-10-31 15:31:42 -05:00
1 parent ce39f140de
commit cecd467dd2
2 files changed
+58 -4

No files matched your search

+12 -4
View File
@@ -160,6 +160,7 @@ class FontManager extends EventEmitter {
* @param {string} family
* @param {string} fallback
* @param {Asset} asset scratch-storage asset
* @returns {Promise<unknown>} Resolves when the font is ready.
*/
addCustomFont (family, fallback, asset) {
if (!this.isValidCustomFont(family)) {
@@ -171,8 +172,9 @@ class FontManager extends EventEmitter {
fallback,
asset
});
this.updateRenderer();
const promise = this.updateRenderer();
this.changed();
return promise;
}
/**
@@ -208,9 +210,12 @@ class FontManager extends EventEmitter {
this.changed();
}
/**
* @returns {Promise<unknown>} Resolves when fonts are ready.
*/
updateRenderer () {
if (!this.runtime.renderer || !this.runtime.renderer.setCustomFonts) {
return;
return Promise.resolve();
}
const fontfaces = {};
@@ -222,7 +227,7 @@ class FontManager extends EventEmitter {
fontfaces[family] = fontface;
}
}
this.runtime.renderer.setCustomFonts(fontfaces);
return this.runtime.renderer.setCustomFonts(fontfaces);
}
/**
@@ -273,6 +278,7 @@ class FontManager extends EventEmitter {
return;
}
const fontPromises = [];
for (const font of json) {
if (!font || typeof font !== 'object') {
continue;
@@ -305,12 +311,14 @@ class FontManager extends EventEmitter {
this.runtime.storage.AssetType.Font,
md5ext
);
this.addCustomFont(family, fallback, asset);
fontPromises.push(this.addCustomFont(family, fallback, asset));
}
} catch (e) {
log.error('could not add font', e);
}
}
await Promise.all(fontPromises);
}
}
+46
View File
@@ -5,6 +5,7 @@ const assert = require('node:assert');
const _makeTestStorage = require('../fixtures/make-test-storage');
const Runtime = require('../../src/engine/runtime');
const VirtualMachine = require('../../src/virtual-machine');
const JSZip = require('@turbowarp/jszip');
const emptyProjectFixture = path.join(__dirname, '..', 'fixtures', 'tw-empty-project.sb3');
@@ -731,3 +732,48 @@ test('overriding existing fonts', () => {
assert.strictEqual(setCustomFontsCalls, 3);
assert.deepEqual(fontManager.getFonts().map(i => i.name), ['TESTFONT']);
});
test('deserialize waits for renderer to load fonts', async () => {
const zip = new JSZip();
zip.file('8e0b5e4ad4e3b2b9a8b4e9bd0ec4de2c.ttf', new Uint8Array([1, 2, 3]));
let resolveSetCustomFontsCalled;
const setCustomFontsCalled = new Promise(resolve => {
resolveSetCustomFontsCalled = resolve;
});
let resolveFontsLoaded;
const mockRenderer = {
setLayerGroupOrdering: () => {},
setCustomFonts: () => {
resolveSetCustomFontsCalled();
return new Promise(resolve => {
resolveFontsLoaded = resolve;
});
}
};
const rt = new Runtime();
rt.attachRenderer(mockRenderer);
rt.attachStorage(makeTestStorage());
let deserialized = false;
const promise = rt.fontManager.deserialize([
{
system: false,
family: 'TestFont',
fallback: 'sans-serif',
md5ext: '8e0b5e4ad4e3b2b9a8b4e9bd0ec4de2c.ttf'
}
], zip, false).then(() => {
deserialized = true;
});
await setCustomFontsCalled;
await new Promise(resolve => setTimeout(resolve));
assert.strictEqual(deserialized, false, 'still waiting for fonts');
resolveFontsLoaded();
await promise;
assert.strictEqual(deserialized, true);
assert.deepEqual(rt.fontManager.getFonts().map(i => i.name), ['TestFont']);
});