Skip to content

Commit 1f63422

Browse files
authored
feat: Add option for custom smufl font sources (#2114)
1 parent 4aff072 commit 1f63422

File tree

15 files changed

+318
-100
lines changed

15 files changed

+318
-100
lines changed

src.compiler/typescript/Serializer.setProperty.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,9 +321,10 @@ function generateSetPropertyBody(serializable: TypeSchema, importer: (name: stri
321321

322322
// obj.fieldName = TypeName.fromJson(value)!
323323
// return true;
324+
const notNull = prop.type.isNullable || prop.type.isOptional ? '' : '!';
324325
caseStatements.push(
325326
createNodeFromSource<ts.ExpressionStatement>(
326-
`obj.${fieldName} = ${prop.type.typeAsString}.fromJson(v)!;`,
327+
`obj.${fieldName} = ${prop.type.typeAsString}.fromJson(v)${notNull};`,
327328
ts.SyntaxKind.ExpressionStatement
328329
)
329330
);

src.compiler/typescript/Serializer.toJson.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ function generateToJsonBody(serializable: TypeSchema, importer: (name: string, m
235235
}
236236
} else if (prop.type.isJsonImmutable) {
237237
importer(prop.type.typeAsString, prop.type.modulePath);
238-
const notNull = !prop.type.isNullable ? '!' : '';
238+
const notNull = prop.type.isNullable || prop.type.isOptional ? '' : '!';
239239
propertyStatements.push(
240240
createNodeFromSource<ts.ExpressionStatement>(
241241
`

src/CoreSettings.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,42 @@ import { LogLevel } from '@src/LogLevel';
33
// biome-ignore lint/correctness/noUnusedImports: https://github.com/biomejs/biome/issues/4677
44
import type { BoundsLookup } from '@src/rendering/utils/BoundsLookup';
55

6+
/**
7+
* Lists the known file formats for font files.
8+
* @target web
9+
*/
10+
export enum FontFileFormat {
11+
/**
12+
* .eot
13+
*/
14+
EmbeddedOpenType = 0,
15+
16+
/**
17+
* .woff
18+
*/
19+
Woff = 1,
20+
21+
/**
22+
* .woff2
23+
*/
24+
Woff2 = 2,
25+
26+
/**
27+
* .otf
28+
*/
29+
OpenType = 3,
30+
31+
/**
32+
* .ttf
33+
*/
34+
TrueType = 4,
35+
36+
/**
37+
* .svg
38+
*/
39+
Svg = 5
40+
}
41+
642
/**
743
* All main settings of alphaTab controlling rather general aspects of its behavior.
844
* @json
@@ -31,13 +67,45 @@ export class CoreSettings {
3167
* where the Web Font files of [Bravura](https://github.com/steinbergmedia/bravura) are. Normally alphaTab expects
3268
* them to be in a `font` subfolder beside the script file. If this is not the case, this setting must be used to configure the path.
3369
* Alternatively also a global variable `ALPHATAB_FONT` can be set on the page before initializing alphaTab.
70+
*
71+
* Use {@link smuflFontSources} for more flexible font configuration.
3472
* @defaultValue `"${AlphaTabScriptFolder}/font/"`
3573
* @category Core - JavaScript Specific
3674
* @target web
3775
* @since 0.9.6
3876
*/
3977
public fontDirectory: string | null = null;
4078

79+
/**
80+
* Defines the URLs from which to load the SMuFL compliant font files.
81+
* @remarks
82+
* These sources will be used to load and register the webfonts on the page so
83+
* they are available for rendering the music sheet. The sources can be set to any
84+
* CSS compatible URL which can be passed into `url()`.
85+
* See https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/src#url
86+
* @defaultValue Bravura files located at {@link fontDirectory} .
87+
* @category Core - JavaScript Specific
88+
* @target web
89+
* @since 1.6.0
90+
*/
91+
public smuflFontSources: Map<FontFileFormat, string> | null = null;
92+
93+
/**
94+
* Builds the default SMuFL font sources for the usage with alphaTab in cases
95+
* where no custom {@link smuflFontSources} are provided.
96+
* @param fontDirectory The {@link fontDirectory} configured.
97+
* @target web
98+
*/
99+
public static buildDefaultSmuflFontSources(fontDirectory: string | null): Map<FontFileFormat, string> {
100+
const map = new Map<FontFileFormat, string>();
101+
// WOFF, WOFF2 and OTF should cover all our platform needs
102+
const prefix = fontDirectory ?? '';
103+
map.set(FontFileFormat.Woff2, `${prefix}Bravura.woff2`);
104+
map.set(FontFileFormat.Woff, `${prefix}Bravura.woff`);
105+
map.set(FontFileFormat.OpenType, `${prefix}Bravura.otf`);
106+
return map;
107+
}
108+
41109
/**
42110
* The full URL to the input file to be loaded.
43111
* @remarks

src/Environment.ts

Lines changed: 6 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ import type { ScoreLayout } from '@src/rendering/layout/ScoreLayout';
4747
import { ScoreBarRendererFactory } from '@src/rendering/ScoreBarRendererFactory';
4848
import type { ScoreRenderer } from '@src/rendering/ScoreRenderer';
4949
import { TabBarRendererFactory } from '@src/rendering/TabBarRendererFactory';
50-
import { FontLoadingChecker } from '@src/util/FontLoadingChecker';
5150
import { Logger } from '@src/Logger';
5251
import { LeftHandTapEffectInfo } from '@src/rendering/effects/LeftHandTapEffectInfo';
5352
import { CapellaImporter } from '@src/importer/CapellaImporter';
@@ -140,62 +139,6 @@ export class Environment {
140139
*/
141140
public static HighDpiFactor = 1;
142141

143-
/**
144-
* @target web
145-
* @internal
146-
*/
147-
public static createStyleElement(elementDocument: HTMLDocument, fontDirectory: string | null) {
148-
let styleElement: HTMLStyleElement = elementDocument.getElementById('alphaTabStyle') as HTMLStyleElement;
149-
if (!styleElement) {
150-
if (!fontDirectory) {
151-
Logger.error('AlphaTab', 'Font directory could not be detected, cannot create style element');
152-
return;
153-
}
154-
155-
styleElement = elementDocument.createElement('style');
156-
styleElement.id = 'alphaTabStyle';
157-
const css: string = `
158-
@font-face {
159-
font-display: block;
160-
font-family: 'alphaTab';
161-
src: url('${fontDirectory}Bravura.eot');
162-
src: url('${fontDirectory}Bravura.eot?#iefix') format('embedded-opentype')
163-
, url('${fontDirectory}Bravura.woff') format('woff')
164-
, url('${fontDirectory}Bravura.otf') format('opentype')
165-
, url('${fontDirectory}Bravura.svg#Bravura') format('svg');
166-
font-weight: normal;
167-
font-style: normal;
168-
}
169-
.at-surface * {
170-
cursor: default;
171-
vertical-align: top;
172-
overflow: visible;
173-
}
174-
.at-surface-svg text {
175-
dominant-baseline: central;
176-
white-space:pre;
177-
}
178-
.at {
179-
font-family: 'alphaTab';
180-
speak: none;
181-
font-style: normal;
182-
font-weight: normal;
183-
font-variant: normal;
184-
text-transform: none;
185-
line-height: 1;
186-
line-height: 1;
187-
-webkit-font-smoothing: antialiased;
188-
-moz-osx-font-smoothing: grayscale;
189-
font-size: ${Environment.MusicFontSize}px;
190-
overflow: visible !important;
191-
}`;
192-
193-
styleElement.innerHTML = css;
194-
elementDocument.getElementsByTagName('head').item(0)!.appendChild(styleElement);
195-
Environment.bravuraFontChecker.checkForFontAvailability();
196-
}
197-
}
198-
199142
/**
200143
* @target web
201144
*/
@@ -255,12 +198,6 @@ export class Environment {
255198
*/
256199
public static readonly fontDirectory: string | null = Environment.detectFontDirectory();
257200

258-
/**
259-
* @target web
260-
* @internal
261-
*/
262-
public static readonly bravuraFontChecker: FontLoadingChecker = new FontLoadingChecker(['alphaTab']);
263-
264201
/**
265202
* @target web
266203
*/
@@ -327,8 +264,12 @@ export class Environment {
327264
}
328265

329266
// normal browser include as <script>
330-
if ('document' in Environment.globalThis && document.currentScript) {
331-
return (document.currentScript as HTMLScriptElement).src;
267+
if (
268+
'document' in Environment.globalThis &&
269+
document.currentScript &&
270+
document.currentScript instanceof HTMLScriptElement
271+
) {
272+
return document.currentScript.src;
332273
}
333274

334275
return null;

src/RenderingResources.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,17 @@ export class RenderingResources {
1111
private static sansFont: string = 'Arial, sans-serif';
1212
private static serifFont: string = 'Georgia, serif';
1313

14+
/**
15+
* The SMuFL Font to use for rendering music symbols.
16+
* @remarks
17+
* This is only meant for internal passing of font family information between components.
18+
* Setting this manually can lead to unexpected side effects.
19+
* @defaultValue `alphaTab`
20+
* @since 0.9.6
21+
* @internal
22+
*/
23+
public smuflFont?: Font;
24+
1425
/**
1526
* The font to use for displaying the songs copyright information in the header of the music sheet.
1627
* @defaultValue `bold 12px Arial, sans-serif`

src/alphaTab.core.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import '@src/alphaTab.polyfills';
22

3-
export { CoreSettings } from '@src/CoreSettings';
3+
export { CoreSettings, FontFileFormat } from '@src/CoreSettings';
44
export { DisplaySettings, SystemsLayoutMode } from '@src/DisplaySettings';
55
export { LayoutMode } from '@src/LayoutMode';
66
export { StaveProfile } from '@src/StaveProfile';

src/generated/CoreSettingsJson.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// Changes to this file may cause incorrect behavior and will be lost if
44
// the code is regenerated.
55
// </auto-generated>
6+
import { FontFileFormat } from "@src/CoreSettings";
67
import { LogLevel } from "@src/LogLevel";
78
/**
89
* All main settings of alphaTab controlling rather general aspects of its behavior.
@@ -32,12 +33,27 @@ export interface CoreSettingsJson {
3233
* where the Web Font files of [Bravura](https://github.com/steinbergmedia/bravura) are. Normally alphaTab expects
3334
* them to be in a `font` subfolder beside the script file. If this is not the case, this setting must be used to configure the path.
3435
* Alternatively also a global variable `ALPHATAB_FONT` can be set on the page before initializing alphaTab.
36+
*
37+
* Use {@link smuflFontSources} for more flexible font configuration.
3538
* @defaultValue `"${AlphaTabScriptFolder}/font/"`
3639
* @category Core - JavaScript Specific
3740
* @target web
3841
* @since 0.9.6
3942
*/
4043
fontDirectory?: string | null;
44+
/**
45+
* Defines the URLs from which to load the SMuFL compliant font files.
46+
* @remarks
47+
* These sources will be used to load and register the webfonts on the page so
48+
* they are available for rendering the music sheet. The sources can be set to any
49+
* CSS compatible URL which can be passed into `url()`.
50+
* See https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/src#url
51+
* @defaultValue Bravura files located at {@link fontDirectory} .
52+
* @category Core - JavaScript Specific
53+
* @target web
54+
* @since 1.6.0
55+
*/
56+
smuflFontSources?: Map<FontFileFormat | keyof typeof FontFileFormat | Lowercase<keyof typeof FontFileFormat>, string>;
4157
/**
4258
* The full URL to the input file to be loaded.
4359
* @remarks

src/generated/CoreSettingsSerializer.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// </auto-generated>
66
import { CoreSettings } from "@src/CoreSettings";
77
import { JsonHelper } from "@src/io/JsonHelper";
8+
import { FontFileFormat } from "@src/CoreSettings";
89
import { LogLevel } from "@src/LogLevel";
910
export class CoreSettingsSerializer {
1011
public static fromJson(obj: CoreSettings, m: unknown): void {
@@ -23,6 +24,14 @@ export class CoreSettingsSerializer {
2324
/*@target web*/
2425
o.set("fontdirectory", obj.fontDirectory);
2526
/*@target web*/
27+
if (obj.smuflFontSources !== null) {
28+
const m = new Map<string, unknown>();
29+
o.set("smuflfontsources", m);
30+
for (const [k, v] of obj.smuflFontSources!) {
31+
m.set(k.toString(), v);
32+
}
33+
}
34+
/*@target web*/
2635
o.set("file", obj.file);
2736
/*@target web*/
2837
o.set("tex", obj.tex);
@@ -46,6 +55,13 @@ export class CoreSettingsSerializer {
4655
obj.fontDirectory = v as string | null;
4756
return true;
4857
/*@target web*/
58+
case "smuflfontsources":
59+
obj.smuflFontSources = new Map<FontFileFormat, string>();
60+
JsonHelper.forEach(v, (v, k) => {
61+
obj.smuflFontSources!.set(JsonHelper.parseEnum<FontFileFormat>(k, FontFileFormat)!, v as string);
62+
});
63+
return true;
64+
/*@target web*/
4965
case "file":
5066
obj.file = v as string | null;
5167
return true;

src/generated/RenderingResourcesJson.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,16 @@ import { ColorJson } from "@src/model/Color";
1212
* @target web
1313
*/
1414
export interface RenderingResourcesJson {
15+
/**
16+
* The SMuFL Font to use for rendering music symbols.
17+
* @remarks
18+
* This is only meant for internal passing of font family information between components.
19+
* Setting this manually can lead to unexpected side effects.
20+
* @defaultValue `alphaTab`
21+
* @since 0.9.6
22+
* @internal
23+
*/
24+
smuflFont?: FontJson;
1525
/**
1626
* The font to use for displaying the songs copyright information in the header of the music sheet.
1727
* @defaultValue `bold 12px Arial, sans-serif`

src/generated/RenderingResourcesSerializer.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export class RenderingResourcesSerializer {
1919
return null;
2020
}
2121
const o = new Map<string, unknown>();
22+
o.set("smuflfont", Font.toJson(obj.smuflFont));
2223
o.set("copyrightfont", Font.toJson(obj.copyrightFont)!);
2324
o.set("titlefont", Font.toJson(obj.titleFont)!);
2425
o.set("subtitlefont", Font.toJson(obj.subTitleFont)!);
@@ -45,6 +46,9 @@ export class RenderingResourcesSerializer {
4546
}
4647
public static setProperty(obj: RenderingResources, property: string, v: unknown): boolean {
4748
switch (property) {
49+
case "smuflfont":
50+
obj.smuflFont = Font.fromJson(v);
51+
return true;
4852
case "copyrightfont":
4953
obj.copyrightFont = Font.fromJson(v)!;
5054
return true;

0 commit comments

Comments
 (0)