Skip to content

Commit

Permalink
Text Font feature
Browse files Browse the repository at this point in the history
  • Loading branch information
AaronDavidNewman committed Oct 31, 2020
1 parent 6d0b27e commit 9420147
Show file tree
Hide file tree
Showing 6 changed files with 263 additions and 18 deletions.
8 changes: 8 additions & 0 deletions src/factory.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import { BarNote } from './barnote';
import { TabNote } from './tabnote';
import { TabStave } from './tabstave';
import { TextNote } from './textnote';
import { TextFont } from './textFont';

// To enable logging for this class. Set `Vex.Flow.Factory.DEBUG` to `true`.
function L(...args) { if (Factory.DEBUG) Vex.L('Vex.Flow.Factory', args); }
Expand Down Expand Up @@ -599,6 +600,13 @@ export class Factory {
return group;
}

TextFont(params = {}) {
params.factory = this;
const textFont = new TextFont(params);
return textFont;
}


draw() {
this.systems.forEach(i => i.setContext(this.context).format());
this.staves.forEach(i => i.setContext(this.context).draw());
Expand Down
16 changes: 8 additions & 8 deletions src/fonts/bravura_metrics.js
Original file line number Diff line number Diff line change
Expand Up @@ -317,13 +317,13 @@ export const BravuraMetrics = {
superSubRatio: 0.66
},
csymDiminished: {
scale: 1,
scale: 0.8,
leftSideBearing: -32,
advanceWidth: 506,
yOffset: 0
},
csymHalfDiminished: {
scale: 1,
scale: 0.8,
leftSideBearing: -32,
advanceWidth: 506,
yOffset: 0
Expand Down Expand Up @@ -360,13 +360,13 @@ export const BravuraMetrics = {
},
csymParensLeftVeryTall: {
scale: 0.9,
leftSideBearing: 0,
advanceWidth: 101,
leftSideBearing: 50,
advanceWidth: 121,
yOffset: 350
},
csymParensRightVeryTall: {
scale: 0.9,
leftSideBearing: 50,
leftSideBearing: 0,
advanceWidth: 111,
yOffset: 350
},
Expand All @@ -377,14 +377,14 @@ export const BravuraMetrics = {
yOffset: 0
},
csymMinor: {
scale: 1,
scale: 0.8,
leftSideBearing: 0,
advanceWidth: 482,
yOffset: 0
},
csymMajorSeventh: {
scale: 1,
leftSideBearing: 0,
scale: 0.9,
leftSideBearing: 200,
yOffset: 0,
advanceWidth: 600
},
Expand Down
12 changes: 6 additions & 6 deletions src/fonts/gonville_metrics.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,13 +179,13 @@ export const GonvilleMetrics = {
superSubRatio: 0.66
},
csymDiminished: {
scale: 1,
scale: 0.8,
leftSideBearing: 0,
advanceWidth: 506,
yOffset: 0
},
csymHalfDiminished: {
scale: 1,
scale: 0.8,
leftSideBearing: -32,
advanceWidth: 506,
yOffset: 0
Expand Down Expand Up @@ -223,7 +223,7 @@ export const GonvilleMetrics = {
csymParensLeftVeryTall: {
scale: 0.9,
leftSideBearing: 0,
advanceWidth: 101,
advanceWidth: 121,
yOffset: 350
},
csymParensRightVeryTall: {
Expand All @@ -239,14 +239,14 @@ export const GonvilleMetrics = {
yOffset: 0
},
csymMinor: {
scale: 1,
scale: 0.8,
leftSideBearing: 0,
advanceWidth: 482,
yOffset: 0
},
csymMajorSeventh: {
scale: 1,
leftSideBearing: 0,
scale: 0.9,
leftSideBearing: 200,
yOffset: 0,
advanceWidth: 600
},
Expand Down
8 changes: 4 additions & 4 deletions src/fonts/petaluma_metrics.js
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ export const PetalumaMetrics = {
csymParensLeftVeryTall: {
scale: 0.95,
leftSideBearing: 0,
advanceWidth: 200,
advanceWidth: 210,
yOffset: 250
},
csymParensRightVeryTall: {
Expand All @@ -404,14 +404,14 @@ export const PetalumaMetrics = {
yOffset: 0
},
csymMinor: {
scale: 0.8,
scale: 0.7,
leftSideBearing: 0,
advanceWidth: 482,
yOffset: 0
},
csymMajorSeventh: {
scale: 1,
leftSideBearing: 0,
scale: 0.8,
leftSideBearing: 100,
yOffset: 0,
advanceWidth: 600
},
Expand Down
7 changes: 7 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ import { Registry } from './registry';
import { StaveText } from './stavetext';
import { GlyphNote } from './glyphnote';
import { RepeatNote } from './repeatnote';
import { TextFont } from './textFont';
import { PetalumaScriptMetrics } from './fonts/petalumaScript_metrics';
import { RobotoSlabMetrics } from './fonts/robotoSlab_metrics';


import { Font, Fonts, DefaultFontStack } from './smufl';

Expand Down Expand Up @@ -155,6 +159,9 @@ Vex.Flow.RepeatNote = RepeatNote;

Vex.Flow.Font = Font;
Vex.Flow.Fonts = Fonts;
Vex.Flow.TextFont = TextFont;
Vex.Flow.DefaultFontStack = DefaultFontStack;
Vex.Flow.PetalumaScriptMetrics = PetalumaScriptMetrics;
Vex.Flow.RobotoSlabMetrics = RobotoSlabMetrics;

export default Vex;
230 changes: 230 additions & 0 deletions src/textFont.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
// [VexFlow](http://vexflow.com) - Copyright (c) Mohit Muthanna 2010.
//
// ## Description
//
// This file handles a registry of text font metric information, so all
// VEX modules can take advantage of font metrics in a uniform way.
//

import { Vex } from './vex';
import { PetalumaScriptMetrics } from './fonts/petalumaScript_metrics';
import { RobotoSlabMetrics } from './fonts/robotoSlab_metrics';

// To enable logging for this class. Set `Vex.Flow.TextFont.DEBUG` to `true`.
function L(...args) { if (TextFont.DEBUG) Vex.L('Vex.Flow.TextFont', args); }

export class TextFont {
static get CATEGORY() { return 'textFont'; }

static get DEBUG() {
return TextFont.debug;
}
static set DEBUG(val) {
TextFont.debug = val;
}

// ### fontRegistry
// Getter of an array of available fonts. Applications may register their
// own fonts and the metrics for those fonts will be available to the
// application.
static get fontRegistry() {
if (!TextFont.registryInstance) {
TextFont.registryInstance = [];
TextFont.registryInstance.push({
name: 'RobotoSlab',
resolution: RobotoSlabMetrics.resolution,
glyphs: RobotoSlabMetrics.glyphs,
family: RobotoSlabMetrics.fontFamily,
serifs: true,
monospaced: false,
italic: false,
bold: false,
maxSizeGlyph: 'H',
superscriptOffset: 0.66,
subscriptOffset: 0.66,
description: 'Default serif text font to pair with Bravura/Gonville engraving font',
});
TextFont.registryInstance.push({
name: 'PetalumaScript',
resolution: PetalumaScriptMetrics.resolution,
glyphs: PetalumaScriptMetrics.glyphs,
family: PetalumaScriptMetrics.fontFamily,
serifs: false,
monospaced: false,
italic: false,
bold: false,
maxSizeGlyph: 'H',
superscriptOffset: 0.66,
subscriptOffset: 0.66,
description: 'Default sans-serif text font to pair with Petaluma engraving font',
});
}
return TextFont.registryInstance;
}

// ### getFontFamilies
// Web font files are generally distributed per weight and style (bold, italic).
// return the family with the attributes that are available for that font.
// We assume descriptions are the same for different weights/styles.
static getFontFamilies() {
const hash = {};
const rv = [];
TextFont.fontRegistry.forEach((font) => {
if (!hash[font.family]) {
hash[font.family] = { family: font.family, description: font.description,
bold: font.bold, serifs: font.serifs, italic: font.italic };
} else {
['bold', 'italic', 'monospaced', 'serifs'].forEach((attr) => {
if (font[attr]) {
hash[font.family][attr] = true;
}
});
}
});
const keys = Object.keys(hash);
keys.forEach((key) => {
rv.push(hash[key]);
});
return rv;
}

// ### fontWeightToBold
// return true if the font weight indicates we desire a 'bold'
// used in getTextFontFromVexFontData
static fontWeightToBold(fw) {
if (!fw) {
return false;
}
if (isNaN(parseInt(fw, 10))) {
return fw.toLowerCase() === 'bold';
}
// very subjective...
return parseInt(fw, 10) >= 600;
}

// ### fontStyleToItalic
// return true if the font style indicates we desire 'italic' style
// used in getTextFontFromVexFontData
static fontStyleToItalic(fs) {
return (fs && typeof(fs) === 'string' && fs.toLowerCase() === 'italic');
}

// ### getTextFontFromVexFontData
// Find the font that most closely matches the parameters from the given font data.
// Primarily we look for font family, also bold and italic attributes. This
// method will always return a fallback font if there are no matches.
static getTextFontFromVexFontData(fd) {
let i = 0;
const fallback = TextFont.fontRegistry[0];
let candidates = [];
const families = fd.family.split(',');
for (i = 0; i < families.length; ++i) {
const famliy = families[i];
candidates = TextFont.fontRegistry.filter((font) => font.family === famliy);
if (candidates.length) {
break;
}
}
if (candidates.length === 0) {
return new TextFont(fallback);
}
if (candidates.length === 1) {
return new TextFont(candidates[0]);
}
const bold = TextFont.fontWeightToBold(fd.weight);
const italic = TextFont.fontStyleToItalic(fd.style);

const perfect = candidates.find((font) => font.bold === bold && font.italic === italic);
if (perfect) {
return new TextFont(perfect);
}
const ok = candidates.find((font) => font.italic === italic || font.bold === bold);
if (ok) {
return new TextFont(ok);
}
return new TextFont(candidates[0]);
}

static getFontDataByName(fontName) {
return TextFont.fontRegistry.find((fd) => fd.name === fontName);
}

// ### registerFont
// Applications may register their own fonts and the metrics, and those metrics
// will be available to the application for formatting. See fontRegistry
// for format of font metrics. Metrics can be generated from any font file
// using font_fontgen.js in the tools/smufl directory.
static registerFont(fontData, overwrite) {
// Get via external reference to make sure initial object is created
const reg = TextFont.fontRegistry;
const exists = reg.find((td) => fontData.name === td.name);
if (exists && overwrite) {
TextFont.registryInstance = TextFont.fontRegistry.filter((fd) => fd.name !== exists.name);
}
if (!exists) {
L('registering font ' + fontData.name);
TextFont.registryInstance.push(fontData);
}
}

// ## Prototype Methods
//
// create a font instance.
// The preferred method for returning an instance of this class is via
// getTextFontFromVexFontData
constructor(params) {
this.attrs = { 'type': 'TextFont' };
if (!params.name) {
Vex.RERR('BadArgument', 'Font constructor must specify a name');
}
const fontData = params.glyphs ? params : TextFont.getFontDataByName(params.name);
if (!fontData) {
if (params.glyphs && params.resolution) {
TextFont.registerFont(params);
} else {
Vex.RERR('BadArgument', 'Unknown font, must have glyph metrics and resolution');
}
} else {
Vex.Merge(this, fontData);
}
Vex.Merge(this, params);

if (!this.size) {
this.size = 14;
}
if (!this.maxSizeGlyph) {
this.maxSizeGlyph = 'H';
}
}

getMetricForCharacter(c) {
if (this.glyphs[c]) {
return this.glyphs[c];
}
return this.glyphs[this.maxSizeGlyph];
}

get maxHeight() {
const glyph = this.getMetricForCharacter(this.maxSizeGlyph);
return (glyph.ha / this.resolution) * this.pointsToPixels;
}

getWidthForCharacter(c) {
const metric = this.getMetricForCharacter(c);
if (!metric) {
return 0.65 * this.pointsToPixels;
}
return (metric.advanceWidth / this.resolution) * this.pointsToPixels;
}

// ### pointsToPixels
// The font size is specified in points, convert to 'pixels' in the svg space
get pointsToPixels() {
return (this.size / 72) / (1 / 96);
}

setFontSize(size) {
this.size = size;
return this;
}
}

0 comments on commit 9420147

Please sign in to comment.