Skip to content

Commit

Permalink
Improve glyph inspector.
Browse files Browse the repository at this point in the history
- Support all fonts.
- Support both OTF and extracted outlines.
Add more documentation.
Other small fixes.
  • Loading branch information
ronyeh committed Nov 4, 2021
1 parent 628aa69 commit 2e05120
Show file tree
Hide file tree
Showing 27 changed files with 136 additions and 295 deletions.
26 changes: 26 additions & 0 deletions demos/fonts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Fonts

## all.html

The full `vexflow.js` includes the following music engraving fonts: Bravura, Petaluma, Gonville, and Custom. In this case, `Vex.Flow.setMusicFont('Bravura', ...);` is a synchronous function that changes the current font stack.

## bravura.html, gonville.html, petaluma.html

Each of these files depends on `vexflow-core.js` and the individual font JS files:

```
vexflow-font-bravura.js
vexflow-font-gonville.js
vexflow-font-petaluma.js
vexflow-font-custom.js
```

They use the await keyword to call an async function that returns once the requested font is successfully loaded:

```
await Vex.Flow.setMusicFont('Petaluma');`
```

## promise.html

The async `Vex.Flow.setMusicFont(...)` returns a Promise. This demo shows how to use a `.then(callback)` to request a font stack, and initialize VexFlow once the fonts are ready.
2 changes: 0 additions & 2 deletions demos/fonts/gonville.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
const factory = new Vex.Flow.Factory({ renderer: { elementId: 'output', width: 500, height: 200 } });
const score = factory.EasyScore();
const system = factory.System();

system
.addStave({
voices: [
Expand All @@ -22,7 +21,6 @@
})
.addClef('treble')
.addTimeSignature('4/4');

factory.draw();
</script>
</body>
Expand Down
2 changes: 0 additions & 2 deletions demos/fonts/petaluma.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
const factory = new Vex.Flow.Factory({ renderer: { elementId: 'output', width: 500, height: 200 } });
const score = factory.EasyScore();
const system = factory.System();

system
.addStave({
voices: [
Expand All @@ -22,7 +21,6 @@
})
.addClef('treble')
.addTimeSignature('4/4');

factory.draw();
</script>
</body>
Expand Down
34 changes: 34 additions & 0 deletions demos/fonts/promise.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<!DOCTYPE html>
<html>
<body>
<div id="output"></div>
<!-- The vexflow core build includes no music fonts. -->
<script src="../../build/vexflow-core.js"></script>
<script>
// Vex.Flow.setMusicFont(...fontNames) is an async function that triggers
// the loading of the fonts (stored in vexflow-font-xxxx.js).
// It returns a Promise that resolves to the loaded music font(s).
Vex.Flow.setMusicFont('Petaluma', 'Bravura', 'Gonville', 'Custom').then(onFontsLoaded);

function onFontsLoaded(musicFonts) {
console.log('Successfully loaded this music font stack:');
const [bravura, gonville, petaluma, custom] = musicFonts;
console.log(bravura, gonville, petaluma, custom);

const factory = new Vex.Flow.Factory({ renderer: { elementId: 'output', width: 500, height: 200 } });
const score = factory.EasyScore();
const system = factory.System();
system
.addStave({
voices: [
score.voice(score.notes('C#5/q, B4, A4, G#4', { stem: 'up' })),
score.voice(score.notes('C#4/h, C#4', { stem: 'down' })),
],
})
.addClef('treble')
.addTimeSignature('4/4');
factory.draw();
}
</script>
</body>
</html>
1 change: 0 additions & 1 deletion src/accidental.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
// @author Mohit Cheppudira
// @author Greg Ristow (modifications)

import { Flow } from './flow';
import { Fraction } from './fraction';
import { Glyph } from './glyph';
import { Modifier } from './modifier';
Expand Down
2 changes: 1 addition & 1 deletion src/annotation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// MIT License

import { Element } from './element';
import { Font, FontInfo } from './font';
import { FontInfo } from './font';
import { Modifier } from './modifier';
import { ModifierContextState } from './modifiercontext';
import { StemmableNote } from './stemmablenote';
Expand Down
1 change: 0 additions & 1 deletion src/articulation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
// MIT License

import { Builder } from './easyscore';
import { Flow } from './flow';
import { Glyph } from './glyph';
import { Modifier } from './modifier';
import { ModifierContextState } from './modifiercontext';
Expand Down
9 changes: 7 additions & 2 deletions src/chordsymbol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
// See `tests/chordsymbol_tests.ts` for usage examples.

import { Font, FontInfo, FontStyle, FontWeight } from './font';
import { Flow } from './flow';
import { Glyph } from './glyph';
import { Modifier } from './modifier';
import { ModifierContextState } from './modifiercontext';
Expand Down Expand Up @@ -376,7 +375,13 @@ export class ChordSymbol extends Modifier {
getDefaultFont(): Required<FontInfo> {
let family = 'Roboto Slab, Times, serif';
if (Tables.currentMusicFont().getName() === 'Petaluma') {
family = 'PetalumaScript, Arial, sans-serif';
// Fixes Issue #1180
// https://github.com/0xfe/vexflow/issues/1180
// family = 'PetalumaScript, Arial, sans-serif';

// RONYEH-FONT: A mismatched font family results in loading Roboto Slab's metrics instead.
// DELETE THE FOLLOWING LINE AND RESTORE THE ONE ABOVE.
family = 'petalumaScript,Arial';
}
return {
family,
Expand Down
37 changes: 37 additions & 0 deletions src/flow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,43 @@ export const Flow = {
BUILD: '',
VERSION: '',

/**
* `Flow.setMusicFont(...fontNames)` behaves differently depending on how you use VexFlow.
*
* **CASE 1**: You are using `vexflow.js`, which includes all music fonts (Bravura, Gonville, Petaluma, Custom).
* In this case, calling this method is optional, since VexFlow already defaults to a music font stack of:
* 'Bravura', 'Gonville', 'Custom'. This method is synchronous.
*
* Examples:
* ```
* Vex.Flow.setMusicFont('Petaluma');
* Vex.Flow.setMusicFont('Bravura', 'Gonville');
* ```
*
* **CASE 2**: You are using the lighter weight `vexflow-core.js` to take advantage of lazy loading for fonts.
* In this case, you MUST call this method at the beginning, since the default music font stack is empty.
* This method is replaced by an async function, so you must use `await` or a Promise to wait for the fonts
* to load before proceeding. See `demos/fonts/` for examples. See `src/fonts/async.ts` for implementation details.
*
* Example:
* ```
* await Vex.Flow.setMusicFont('Petaluma');
* ... (do VexFlow stuff) ...
* ```
* @returns CASE 1: a `Font` or an array of `Font` objects corresponding to the provided `fontNames`.
* @returns CASE 2: Promise<Font[]> that resolves to the same array of `Font` objects as above.
*/
setMusicFont: (...fontNames: string[]): Font | Font[] => {
// Convert the array of font names into an array of Font objects.
const fonts = fontNames.map((fontName) => Font.load(fontName));
Flow.setMusicFontStack(fonts);
if (fonts.length === 1) {
return fonts[0];
} else {
return fonts;
}
},

/**
* Use Flow.setMusicFont(...fontNames).
*
Expand Down
9 changes: 6 additions & 3 deletions src/font.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,10 @@ export class Font {
return 'Font';
}

/** Customize this field to specify a different CDN for delivering web fonts. */
/**
* Customize this field to specify a different CDN for delivering web fonts.
* https://cdn.jsdelivr.net/npm/vexflow-fonts@1.0.3/
*/
static FONT_HOST = 'https://unpkg.com/vexflow-fonts@1.0.3/';

/** Default sans-serif font family. */
Expand Down Expand Up @@ -355,7 +358,7 @@ export class Font {
*/
// eslint-disable-next-line
lookupMetric(key: string, defaultValue?: Record<string, any> | number): any {
// console.log('lookupMetric:', key);
// console.log('lookupMetric: ', key);

const keyParts = key.split('.');

Expand All @@ -373,7 +376,7 @@ export class Font {
}

// After checking every part of the key (i.e., the loop completed), return the most recently retrieved value.
// console.log('found:', key, currObj);
// console.log('found: ', key, currObj);
return currObj;
}

Expand Down
4 changes: 0 additions & 4 deletions src/fonts/custom_glyphs.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
export const CustomFont = {
resolution: 1000,
<<<<<<< HEAD
familyName: 'VexFlowCustom',
=======
fontFamily: 'VexflowCustom',
>>>>>>> Improve API for Flow and Tables.
glyphs: {
noteheadSquareWhite: {
x_min: 0,
Expand Down
23 changes: 13 additions & 10 deletions src/fonts/loadDynamic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,20 @@

import { Flow } from '../flow';
import { Font } from '../font';
import { RuntimeError } from '../util';

export function setupMusicFonts(): void {
Flow.setMusicFont = async (...fontNames: string[]) => {
// Make sure each individual font is loaded before we proceed to call Flow.setMusicFontStack(...).
export function loadMusicFonts(): void {
// Keep the synchronous function around so we can call it later.
const setMusicFontSync = Flow.setMusicFont;

// Replace Flow.setMusicFont(...fontNames) with this async function.
// @ts-ignore override with a function that has a different return type.
Flow.setMusicFont = async (...fontNames: string[]): Promise<Font | Font[]> => {
// Make sure each individual font is loaded before we proceed.
for (const fontName of fontNames) {
const font = Font.load(fontName);
// Check if the font data has already been loaded.
if (font.hasData()) {
// This font has been loaded before.
console.log('We have already loaded: ' + fontName);
console.log(font.getData());
// TODO/RONYEH REMOVE!
continue;
}

Expand All @@ -41,12 +44,12 @@ export function setupMusicFonts(): void {
break;
}
default: {
console.log('Unknown music font: ' + fontName);
throw new RuntimeError('UnknownMusicFont', `Music font ${fontName} does not exist.`);
}
}
}

// Convert an array of font names into an array of Font objects.
Flow.setMusicFontStack(fontNames.map((fontName) => Font.load(fontName)));
// The fonts are ready! Now it's time to call the synchronous function.
return setMusicFontSync(...fontNames);
};
}
2 changes: 1 addition & 1 deletion src/fonts/loadStatic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ export function loadMusicFonts(): void {
// Populate our font "database" with all our music engraving fonts.
const bravuraFont = Font.load('Bravura', Bravura.data, Bravura.metrics);
const gonvilleFont = Font.load('Gonville', Gonville.data, Gonville.metrics);
Font.load('Petaluma', Petaluma.data, Petaluma.metrics);
const customFont = Font.load('Custom', Custom.data, Custom.metrics);
Font.load('Petaluma', Petaluma.data, Petaluma.metrics);

// vexflow.js uses the following default font stack:
Flow.setMusicFontStack([bravuraFont, gonvilleFont, customFont]);
Expand Down
4 changes: 2 additions & 2 deletions src/fonts/loadTextFonts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ export function loadTextFonts() {
Font.load(fontFamily, fontData);
// H isn't actually the tallest or the widest.
// Interestingly, the lowercase b is the tallest.
const maxSizeGlyph = 'H';
// const maxSizeGlyph = 'b'; // TODO/RONYEH: introduces a visual diff.
const maxSizeGlyph = 'H'; // Two lines of lyrics touch each other because we don't allow enough vertical space.
// const maxSizeGlyph = 'b'; // RONYEH: introduces a visual diff, but the spacing is better.
TextFormatter.registerInfo({
family: fontFamily,
resolution,
Expand Down
1 change: 0 additions & 1 deletion src/formatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

import { Beam } from './beam';
import { BoundingBox } from './boundingbox';
import { Flow } from './flow';
import { Font } from './font';
import { Fraction } from './fraction';
import { ModifierContext } from './modifiercontext';
Expand Down
1 change: 0 additions & 1 deletion src/multimeasurerest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
// This class implements multiple measure rests.

import { Element } from './element';
import { Flow } from './flow';
import { Glyph } from './glyph';
import { NoteHead } from './notehead';
import { RenderContext } from './rendercontext';
Expand Down
1 change: 0 additions & 1 deletion src/notehead.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

import { BoundingBox } from './boundingbox';
import { ElementStyle } from './element';
import { Flow } from './flow';
import { Glyph, GlyphProps } from './glyph';
import { Note, NoteStruct } from './note';
import { RenderContext } from './rendercontext';
Expand Down
1 change: 0 additions & 1 deletion src/ornament.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// Author: Cyril Silverman
// MIT License

import { Flow } from './flow';
import { Glyph } from './glyph';
import { Modifier } from './modifier';
import { ModifierContextState } from './modifiercontext';
Expand Down
1 change: 0 additions & 1 deletion src/stave.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import { Tables } from './tables';
import { TimeSignature } from './timesignature';
import { isBarline } from './typeguard';
import { RuntimeError } from './util';
import { Flow } from './flow';

export interface StaveLineConfig {
visible?: boolean;
Expand Down
1 change: 0 additions & 1 deletion src/stavenote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import { Beam } from './beam';
import { BoundingBox } from './boundingbox';
import { Dot } from './dot';
import { ElementStyle } from './element';
import { Flow } from './flow';
import { Modifier } from './modifier';
import { ModifierContextState } from './modifiercontext';
import { Note, NoteStruct } from './note';
Expand Down
1 change: 0 additions & 1 deletion src/stem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

import { BoundingBox } from './boundingbox';
import { Element } from './element';
import { Flow } from './flow';
import { Tables } from './tables';
import { log, RuntimeError } from './util';

Expand Down
1 change: 0 additions & 1 deletion src/stemmablenote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
// `StemmableNote` is an abstract interface for notes with optional stems.
// Examples of stemmable notes are `StaveNote` and `TabNote`

import { Flow } from './flow';
import { Glyph } from './glyph';
import { GlyphProps } from './glyph';
import { Note, NoteStruct } from './note';
Expand Down
1 change: 0 additions & 1 deletion tests/textformatter_tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
// TextFormatter Tests

import { TextFormatter } from 'textformatter';
import { TestOptions, VexFlowTests } from './vexflow_test_helpers';

const TextFormatterTests = {
Start(): void {
Expand Down
15 changes: 7 additions & 8 deletions tools/fonts/inspector/glyphs.html
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,13 @@ <h1 id="title">Glyph Inspector</h1>
function chooseFont(fontName) {
document.getElementById('title').innerText = 'Glyph Inspector – ' + fontName;

/* RONYEH 3.0.9 BEGIN */
const font = Vex.Flow.Fonts[fontName]();
Vex.Flow.DEFAULT_FONT_STACK = [font];
/* RONYEH 3.0.9 END */

/*
const font = Vex.Flow.setMusicFont(fontName); // RONYEH 4.0.0
*/
/* Version 4.0.0 */
const font = Vex.Flow.setMusicFont(fontName);

/* Version 3.0.9 */
// const font = Vex.Flow.Fonts[fontName]();
// Vex.Flow.DEFAULT_FONT_STACK = [font];

const glyphs = font.getGlyphs();

// Calculate the canvas height programmatically based on the number of glyphs.
Expand Down
Loading

0 comments on commit 2e05120

Please sign in to comment.