Skip to content

Commit

Permalink
Preserve file extension
Browse files Browse the repository at this point in the history
  • Loading branch information
GarboMuffin committed Aug 16, 2023
1 parent fe41d2b commit 744e8cf
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 41 deletions.
20 changes: 5 additions & 15 deletions src/engine/tw-font-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,24 +74,13 @@ class FontManager extends EventEmitter {
/**
* @param {string} family
* @param {string} fallback
* @param {Uint8Array|Asset} data Binary data or scratch-storage asset
* @param {Asset} asset scratch-storage asset
*/
addCustomFont (family, fallback, data) {
addCustomFont (family, fallback, asset) {
if (!this.isValidFamily(family)) {
throw new Error('Invalid family');
}

const storage = this.runtime.storage;
const asset = data instanceof storage.Asset ?
data :
storage.createAsset(
storage.AssetType.Font,
storage.DataFormat.TTF,
data,
null,
true
);

this.fonts.push({
system: false,
family,
Expand All @@ -104,14 +93,15 @@ class FontManager extends EventEmitter {
}

/**
* @returns {Array<{system: boolean; name: string; family: string; data: Uint8Array | null;}>}
* @returns {Array<{system: boolean; name: string; family: string; data: Uint8Array | null; format: string | null}>}
*/
getFonts () {
return this.fonts.map(font => ({
system: font.system,
name: font.family,
family: `"${font.family}", ${font.fallback}`,
data: font.asset ? font.asset.data : null
data: font.asset ? font.asset.data : null,
format: font.asset ? font.asset.dataFormat : null
}));
}

Expand Down
94 changes: 68 additions & 26 deletions test/integration/tw_font_manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ test('system font', t => {
system: true,
name: 'Noto Sans Mono',
family: '"Noto Sans Mono", monospace',
data: null
data: null,
format: null
}
]);
t.same(fontManager.serializeJSON(), [
Expand All @@ -84,13 +85,15 @@ test('system font', t => {
system: true,
name: 'Noto Sans Mono',
family: '"Noto Sans Mono", monospace',
data: null
data: null,
format: null
},
{
system: true,
name: 'Lobster',
family: '"Lobster", fantasy, sans-serif',
data: null
data: null,
format: null
}
]);
t.same(fontManager.serializeJSON(), [
Expand Down Expand Up @@ -130,7 +133,7 @@ test('clear', t => {
const rt = new Runtime();
rt.attachStorage(makeTestStorage());
rt.attachRenderer(mockRenderer);
const {fontManager} = rt;
const {fontManager, storage} = rt;

fontManager.addSystemFont('Arial', 'sans-serif');
t.equal(fontManager.getFonts().length, 1);
Expand All @@ -144,7 +147,13 @@ test('clear', t => {
t.equal(fontManager.getFonts().length, 0, 'removed font');
t.same(setCustomFontsCalls, [], 'clear() does not call setCustomFonts() if only system fonts');

fontManager.addCustomFont('Wingdings', 'monospace', new Uint8Array([11, 12, 13]));
fontManager.addCustomFont('Wingdings', 'monospace', storage.createAsset(
storage.AssetType.Font,
'ttf',
new Uint8Array([11, 12, 13]),
null,
true
));
changed = false;
setCustomFontsCalls.length = 0;
fontManager.clear();
Expand All @@ -166,22 +175,29 @@ test('custom fonts', t => {
const rt = new Runtime();
rt.attachRenderer(mockRenderer);
rt.attachStorage(makeTestStorage());
const {fontManager} = rt;
const {fontManager, storage} = rt;

let changed = false;
fontManager.on('change', () => {
changed = true;
});

fontManager.addCustomFont('Arial', 'sans-serif', new Uint8Array([1, 2, 3]));
fontManager.addCustomFont('Arial', 'sans-serif', storage.createAsset(
storage.AssetType.Font,
storage.DataFormat.TTF,
new Uint8Array([1, 2, 3]),
null,
true
));
t.ok(changed, 'addCustomFont() emits change');
t.ok(fontManager.hasFont('Arial'), 'updated hasFont()');
t.same(fontManager.getFonts(), [
{
system: false,
name: 'Arial',
family: '"Arial", sans-serif',
data: new Uint8Array([1, 2, 3])
data: new Uint8Array([1, 2, 3]),
format: 'ttf'
}
]);
t.same(fontManager.serializeJSON(), [
Expand All @@ -205,9 +221,9 @@ test('custom fonts', t => {

changed = false;
setCustomFontsCalls.length = 0;
const asset = rt.storage.createAsset(
rt.storage.AssetType.Font,
rt.storage.DataFormat.TTF,
const asset = storage.createAsset(
storage.AssetType.Font,
'woff2',
new Uint8Array([4, 5, 6]),
null,
true
Expand All @@ -220,13 +236,15 @@ test('custom fonts', t => {
system: false,
name: 'Arial',
family: '"Arial", sans-serif',
data: new Uint8Array([1, 2, 3])
data: new Uint8Array([1, 2, 3]),
format: 'ttf'
},
{
system: false,
name: 'Comic Sans MS',
family: '"Comic Sans MS", serif',
data: new Uint8Array([4, 5, 6])
data: new Uint8Array([4, 5, 6]),
format: 'woff2'
}
]);
t.same(fontManager.serializeJSON(), [
Expand All @@ -240,7 +258,7 @@ test('custom fonts', t => {
system: false,
family: 'Comic Sans MS',
fallback: 'serif',
md5ext: 'b4a3ba90641372b4e4eaa841a5a400ec.ttf'
md5ext: 'b4a3ba90641372b4e4eaa841a5a400ec.woff2'
}
]);
t.same(setCustomFontsCalls, [
Expand All @@ -263,10 +281,16 @@ test('custom fonts', t => {
test('custom font validation', t => {
const rt = new Runtime();
rt.attachStorage(makeTestStorage());
const {fontManager} = rt;
const {fontManager, storage} = rt;

t.throws(() => {
fontManager.addCustomFont('family;', 'sans-serif', new Uint8Array([1]));
fontManager.addCustomFont('family;', 'sans-serif', storage.createAsset(
storage.DataFormat.Font,
storage.DataFormat.TTF,
new Uint8Array([1]),
null,
true
));
});

t.end();
Expand All @@ -275,10 +299,16 @@ test('custom font validation', t => {
test('deleteFont', t => {
const rt = new Runtime();
rt.attachStorage(makeTestStorage());
const {fontManager} = rt;
const {fontManager, storage} = rt;

fontManager.addSystemFont('Liberation Mono', 'monospace');
fontManager.addCustomFont('Noto Sans Mono', 'monospace', new Uint8Array([17, 18, 19]));
fontManager.addCustomFont('Noto Sans Mono', 'monospace', storage.createAsset(
storage.AssetType.Font,
storage.DataFormat.TTF,
new Uint8Array([17, 18, 19]),
null,
true
));

t.ok(fontManager.hasFont('Liberation Mono'), 'has font initially');
t.ok(fontManager.hasFont('Noto Sans Mono'), 'has font initially');
Expand Down Expand Up @@ -307,7 +337,8 @@ test('deleteFont', t => {
system: true,
name: 'Liberation Mono',
family: '"Liberation Mono", monospace',
data: null
data: null,
format: null
}
], 'updated getFonts() after deleting');

Expand Down Expand Up @@ -415,13 +446,15 @@ test('serialization and deserialization roundtrip - project', t => {
system: true,
name: 'Ubuntu Mono',
family: '"Ubuntu Mono", monospace',
data: null
data: null,
format: null
},
{
system: false,
name: 'Inter',
family: '"Inter", sans-serif',
data: new Uint8Array([20, 21, 22, 23, 24])
data: new Uint8Array([20, 21, 22, 23, 24]),
format: 'ttf'
}
], 'preserved in getFonts()');
t.same(newFontManager.serializeJSON(), [
Expand Down Expand Up @@ -450,7 +483,7 @@ test('serialization and deserialization roundtrip - project', t => {
test('serialization and deserialization roundtrip - target', t => {
const originalVM = new VirtualMachine();
originalVM.attachStorage(makeTestStorage());
const {fontManager} = originalVM.runtime;
const {fontManager, storage} = originalVM.runtime;

originalVM.loadProject(fs.readFileSync(emptyProjectFixture)).then(() => {
// The fixture we use only contains a stage. We'll convert it to a sprite so we can
Expand All @@ -461,7 +494,13 @@ test('serialization and deserialization roundtrip - target', t => {
const noFontsJSON = JSON.parse(originalVM.toJSON(sprite.id));
t.notOk('customFonts' in noFontsJSON, 'does not serialize fonts in target if no fonts');

fontManager.addCustomFont('Noto Sans Traditional Chinese', 'sans-serif', new Uint8Array([97, 98, 99]));
fontManager.addCustomFont('Noto Sans Traditional Chinese', 'sans-serif', storage.createAsset(
storage.AssetType.Font,
storage.DataFormat.TTF,
new Uint8Array([97, 98, 99]),
null,
true
));
fontManager.addSystemFont('FreeSans', 'sans-serif');

const spriteJSON = JSON.parse(originalVM.toJSON(sprite.id));
Expand Down Expand Up @@ -503,19 +542,22 @@ test('serialization and deserialization roundtrip - target', t => {
system: true,
name: 'Liberation Sans',
family: '"Liberation Sans", sans-serif',
data: null
data: null,
format: null
},
{
system: true,
name: 'FreeSans',
family: '"FreeSans", monospace',
data: null
data: null,
format: null
},
{
system: false,
name: 'Noto Sans Traditional Chinese',
family: '"Noto Sans Traditional Chinese", sans-serif',
data: new Uint8Array([97, 98, 99])
data: new Uint8Array([97, 98, 99]),
format: 'ttf'
}
], 'imported fonts from sprite');

Expand Down

0 comments on commit 744e8cf

Please sign in to comment.