VexFlowConverter.ts 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667
  1. import Vex = require("vexflow");
  2. import {ClefEnum} from "../../VoiceData/Instructions/ClefInstruction";
  3. import {ClefInstruction} from "../../VoiceData/Instructions/ClefInstruction";
  4. import {Pitch} from "../../../Common/DataObjects/Pitch";
  5. import {Fraction} from "../../../Common/DataObjects/Fraction";
  6. import {RhythmInstruction} from "../../VoiceData/Instructions/RhythmInstruction";
  7. import {RhythmSymbolEnum} from "../../VoiceData/Instructions/RhythmInstruction";
  8. import {KeyInstruction} from "../../VoiceData/Instructions/KeyInstruction";
  9. import {KeyEnum} from "../../VoiceData/Instructions/KeyInstruction";
  10. import {AccidentalEnum} from "../../../Common/DataObjects/Pitch";
  11. import {NoteEnum} from "../../../Common/DataObjects/Pitch";
  12. import {VexFlowGraphicalNote} from "./VexFlowGraphicalNote";
  13. import {GraphicalNote} from "../GraphicalNote";
  14. import {SystemLinesEnum} from "../SystemLinesEnum";
  15. import {FontStyles} from "../../../Common/Enums/FontStyles";
  16. import {Fonts} from "../../../Common/Enums/Fonts";
  17. import {OutlineAndFillStyleEnum, OUTLINE_AND_FILL_STYLE_DICT} from "../DrawingEnums";
  18. import * as log from "loglevel";
  19. import { ArticulationEnum, StemDirectionType } from "../../VoiceData/VoiceEntry";
  20. import { SystemLinePosition } from "../SystemLinePosition";
  21. import { GraphicalVoiceEntry } from "../GraphicalVoiceEntry";
  22. import { OrnamentEnum, OrnamentContainer } from "../../VoiceData/OrnamentContainer";
  23. /**
  24. * Helper class, which contains static methods which actually convert
  25. * from OSMD objects to VexFlow objects.
  26. */
  27. export class VexFlowConverter {
  28. /**
  29. * Mapping from numbers of alterations on the key signature to major keys
  30. * @type {[alterationsNo: number]: string; }
  31. */
  32. private static majorMap: {[_: number]: string; } = {
  33. "-1": "F", "-2": "Bb", "-3": "Eb", "-4": "Ab", "-5": "Db", "-6": "Gb", "-7": "Cb", "-8": "Fb",
  34. "0": "C", "1": "G", "2": "D", "3": "A", "4": "E", "5": "B", "6": "F#", "7": "C#", "8": "G#"
  35. };
  36. /**
  37. * Mapping from numbers of alterations on the key signature to minor keys
  38. * @type {[alterationsNo: number]: string; }
  39. */
  40. private static minorMap: {[_: number]: string; } = {
  41. "-1": "D", "-2": "G", "-3": "C", "-4": "F", "-5": "Bb", "-6": "Eb", "-7": "Ab", "-8": "Db",
  42. "0": "A", "1": "E", "2": "B", "3": "F#", "4": "C#", "5": "G#", "6": "D#", "7": "A#", "8": "E#"
  43. };
  44. /**
  45. * Convert a fraction to a string which represents a duration in VexFlow
  46. * @param fraction a fraction representing the duration of a note
  47. * @returns {string}
  48. */
  49. public static duration(fraction: Fraction, isTuplet: boolean): string {
  50. const dur: number = fraction.RealValue;
  51. if (dur >= 1) {
  52. return "w";
  53. } else if (dur < 1 && dur >= 0.5) {
  54. // change to the next higher straight note to get the correct note display type
  55. if (isTuplet) {
  56. return "w";
  57. }
  58. return "h";
  59. } else if (dur < 0.5 && dur >= 0.25) {
  60. // change to the next higher straight note to get the correct note display type
  61. if (isTuplet && dur > 0.25) {
  62. return "h";
  63. }
  64. return "q";
  65. } else if (dur < 0.25 && dur >= 0.125) {
  66. // change to the next higher straight note to get the correct note display type
  67. if (isTuplet && dur > 0.125) {
  68. return "q";
  69. }
  70. return "8";
  71. } else if (dur < 0.125 && dur >= 0.0625) {
  72. // change to the next higher straight note to get the correct note display type
  73. if (isTuplet && dur > 0.0625) {
  74. return "8";
  75. }
  76. return "16";
  77. } else if (dur < 0.0625 && dur >= 0.03125) {
  78. // change to the next higher straight note to get the correct note display type
  79. if (isTuplet && dur > 0.03125) {
  80. return "16";
  81. }
  82. return "32";
  83. } else if (dur < 0.03125 && dur >= 0.015625) {
  84. // change to the next higher straight note to get the correct note display type
  85. if (isTuplet && dur > 0.015625) {
  86. return "32";
  87. }
  88. return "64";
  89. }
  90. if (isTuplet) {
  91. return "64";
  92. }
  93. return "128";
  94. }
  95. /**
  96. * Takes a Pitch and returns a string representing a VexFlow pitch,
  97. * which has the form "b/4", plus its alteration (accidental)
  98. * @param pitch
  99. * @returns {string[]}
  100. */
  101. public static pitch(pitch: Pitch, clef: ClefInstruction): [string, string, ClefInstruction] {
  102. const fund: string = NoteEnum[pitch.FundamentalNote].toLowerCase();
  103. // The octave seems to need a shift of three FIXME?
  104. const octave: number = pitch.Octave - clef.OctaveOffset + 3;
  105. const acc: string = VexFlowConverter.accidental(pitch.Accidental);
  106. return [fund + "n/" + octave, acc, clef];
  107. }
  108. /**
  109. * Converts AccidentalEnum to a string which represents an accidental in VexFlow
  110. * @param accidental
  111. * @returns {string}
  112. */
  113. public static accidental(accidental: AccidentalEnum): string {
  114. let acc: string;
  115. switch (accidental) {
  116. case AccidentalEnum.NATURAL:
  117. acc = "n";
  118. break;
  119. case AccidentalEnum.FLAT:
  120. acc = "b";
  121. break;
  122. case AccidentalEnum.SHARP:
  123. acc = "#";
  124. break;
  125. case AccidentalEnum.DOUBLESHARP:
  126. acc = "##";
  127. break;
  128. case AccidentalEnum.TRIPLESHARP:
  129. acc = "++";
  130. break;
  131. case AccidentalEnum.DOUBLEFLAT:
  132. acc = "bb";
  133. break;
  134. case AccidentalEnum.TRIPLEFLAT:
  135. acc = "bbs"; // there is no "bbb" in VexFlow yet, unfortunately.
  136. break;
  137. case AccidentalEnum.QUARTERTONESHARP:
  138. acc = "+";
  139. break;
  140. case AccidentalEnum.QUARTERTONEFLAT:
  141. acc = "d";
  142. break;
  143. default:
  144. }
  145. return acc;
  146. }
  147. public static GhostNote(frac: Fraction): Vex.Flow.GhostNote {
  148. // const frac: Fraction = notes[0].graphicalNoteLength;
  149. return new Vex.Flow.GhostNote({
  150. duration: VexFlowConverter.duration(frac, false),
  151. });
  152. }
  153. /**
  154. * Convert a GraphicalVoiceEntry to a VexFlow StaveNote
  155. * @param gve the GraphicalVoiceEntry which can hold a note or a chord on the staff belonging to one voice
  156. * @returns {Vex.Flow.StaveNote}
  157. */
  158. public static StaveNote(gve: GraphicalVoiceEntry): Vex.Flow.StaveNote {
  159. // VexFlow needs the notes ordered vertically in the other direction:
  160. const notes: GraphicalNote[] = gve.notes.reverse();
  161. const baseNote: GraphicalNote = notes[0];
  162. let keys: string[] = [];
  163. const accidentals: string[] = [];
  164. const frac: Fraction = baseNote.graphicalNoteLength;
  165. const isTuplet: boolean = baseNote.sourceNote.NoteTuplet !== undefined;
  166. let duration: string = VexFlowConverter.duration(frac, isTuplet);
  167. let vfClefType: string = undefined;
  168. let numDots: number = baseNote.numberOfDots;
  169. let alignCenter: boolean = false;
  170. let xShift: number = 0;
  171. for (const note of notes) {
  172. if (numDots < note.numberOfDots) {
  173. numDots = note.numberOfDots;
  174. }
  175. // if it is a rest:
  176. if (note.sourceNote.isRest()) {
  177. keys = ["b/4"];
  178. // if it is a full measure rest:
  179. if (note.parentVoiceEntry.parentStaffEntry.parentMeasure.parentSourceMeasure.Duration.RealValue <= frac.RealValue) {
  180. keys = ["d/5"];
  181. duration = "w";
  182. numDots = 0;
  183. // If it's a whole rest we want it smack in the middle. Apparently there is still an issue in vexflow:
  184. // https://github.com/0xfe/vexflow/issues/579 The author reports that he needs to add some negative x shift
  185. // if the measure has no modifiers.
  186. alignCenter = true;
  187. xShift = -25; // TODO: Either replace by EngravingRules entry or find a way to make it dependent on the modifiers
  188. }
  189. duration += "r";
  190. break;
  191. }
  192. const pitch: [string, string, ClefInstruction] = (note as VexFlowGraphicalNote).vfpitch;
  193. keys.push(pitch[0]);
  194. accidentals.push(pitch[1]);
  195. if (!vfClefType) {
  196. const vfClef: {type: string, annotation: string} = VexFlowConverter.Clef(pitch[2]);
  197. vfClefType = vfClef.type;
  198. }
  199. }
  200. for (let i: number = 0, len: number = numDots; i < len; ++i) {
  201. duration += "d";
  202. }
  203. let vfnote: Vex.Flow.StaveNote;
  204. const vfnoteStruct: Object = {
  205. align_center: alignCenter,
  206. auto_stem: true,
  207. clef: vfClefType,
  208. duration: duration,
  209. keys: keys,
  210. slash: gve.parentVoiceEntry.GraceNoteSlash
  211. };
  212. if (!gve.parentVoiceEntry.IsGrace) {
  213. vfnote = new Vex.Flow.StaveNote(vfnoteStruct);
  214. } else {
  215. vfnote = new Vex.Flow.GraceNote(vfnoteStruct);
  216. }
  217. vfnote.x_shift = xShift;
  218. if (gve.parentVoiceEntry !== undefined) {
  219. const wantedStemDirection: StemDirectionType = gve.parentVoiceEntry.StemDirection;
  220. switch (wantedStemDirection) {
  221. case(StemDirectionType.Up):
  222. vfnote.setStemDirection(Vex.Flow.Stem.UP);
  223. break;
  224. case (StemDirectionType.Down):
  225. vfnote.setStemDirection(Vex.Flow.Stem.DOWN);
  226. break;
  227. default:
  228. break;
  229. }
  230. }
  231. for (let i: number = 0, len: number = notes.length; i < len; i += 1) {
  232. (notes[i] as VexFlowGraphicalNote).setIndex(vfnote, i);
  233. if (accidentals[i]) {
  234. if (accidentals[i] === "++") {
  235. vfnote.addAccidental(i, new Vex.Flow.Accidental("##"));
  236. vfnote.addAccidental(i, new Vex.Flow.Accidental("#"));
  237. continue;
  238. } else if (accidentals[i] === "bbs") {
  239. vfnote.addAccidental(i, new Vex.Flow.Accidental("bb"));
  240. vfnote.addAccidental(i, new Vex.Flow.Accidental("b"));
  241. continue;
  242. }
  243. vfnote.addAccidental(i, new Vex.Flow.Accidental(accidentals[i]));
  244. }
  245. }
  246. for (let i: number = 0, len: number = numDots; i < len; ++i) {
  247. vfnote.addDotToAll();
  248. }
  249. return vfnote;
  250. }
  251. public static generateArticulations(vfnote: Vex.Flow.StemmableNote, articulations: ArticulationEnum[]): void {
  252. // Articulations:
  253. let vfArtPosition: number = Vex.Flow.Modifier.Position.ABOVE;
  254. if (vfnote.getStemDirection() === Vex.Flow.Stem.UP) {
  255. vfArtPosition = Vex.Flow.Modifier.Position.BELOW;
  256. }
  257. for (const articulation of articulations) {
  258. // tslint:disable-next-line:switch-default
  259. let vfArt: Vex.Flow.Articulation = undefined;
  260. switch (articulation) {
  261. case ArticulationEnum.accent: {
  262. vfArt = new Vex.Flow.Articulation("a>");
  263. break;
  264. }
  265. case ArticulationEnum.downbow: {
  266. vfArt = new Vex.Flow.Articulation("am");
  267. break;
  268. }
  269. case ArticulationEnum.fermata: {
  270. vfArt = new Vex.Flow.Articulation("a@a");
  271. vfArtPosition = Vex.Flow.Modifier.Position.ABOVE;
  272. break;
  273. }
  274. case ArticulationEnum.invertedfermata: {
  275. vfArt = new Vex.Flow.Articulation("a@u");
  276. vfArtPosition = Vex.Flow.Modifier.Position.BELOW;
  277. break;
  278. }
  279. case ArticulationEnum.lefthandpizzicato: {
  280. vfArt = new Vex.Flow.Articulation("a+");
  281. break;
  282. }
  283. case ArticulationEnum.snappizzicato: {
  284. vfArt = new Vex.Flow.Articulation("ao");
  285. break;
  286. }
  287. case ArticulationEnum.staccatissimo: {
  288. vfArt = new Vex.Flow.Articulation("av");
  289. break;
  290. }
  291. case ArticulationEnum.staccato: {
  292. vfArt = new Vex.Flow.Articulation("a.");
  293. break;
  294. }
  295. case ArticulationEnum.tenuto: {
  296. vfArt = new Vex.Flow.Articulation("a-");
  297. break;
  298. }
  299. case ArticulationEnum.upbow: {
  300. vfArt = new Vex.Flow.Articulation("a|");
  301. break;
  302. }
  303. case ArticulationEnum.strongaccent: {
  304. vfArt = new Vex.Flow.Articulation("a^");
  305. break;
  306. }
  307. default: {
  308. break;
  309. }
  310. }
  311. if (vfArt !== undefined) {
  312. vfArt.setPosition(vfArtPosition);
  313. vfnote.addModifier(0, vfArt);
  314. }
  315. }
  316. }
  317. public static generateOrnaments(vfnote: Vex.Flow.StemmableNote, oContainer: OrnamentContainer): void {
  318. let vfPosition: number = Vex.Flow.Modifier.Position.ABOVE;
  319. if (vfnote.getStemDirection() === Vex.Flow.Stem.UP) {
  320. vfPosition = Vex.Flow.Modifier.Position.BELOW;
  321. }
  322. let vfOrna: Vex.Flow.Ornament = undefined;
  323. switch (oContainer.GetOrnament) {
  324. case OrnamentEnum.DelayedInvertedTurn: {
  325. vfOrna = new Vex.Flow.Ornament("turn_inverted");
  326. vfOrna.setDelayed(true);
  327. break;
  328. }
  329. case OrnamentEnum.DelayedTurn: {
  330. vfOrna = new Vex.Flow.Ornament("turn");
  331. vfOrna.setDelayed(true);
  332. break;
  333. }
  334. case OrnamentEnum.InvertedMordent: {
  335. vfOrna = new Vex.Flow.Ornament("mordent_inverted");
  336. vfOrna.setDelayed(false);
  337. break;
  338. }
  339. case OrnamentEnum.InvertedTurn: {
  340. vfOrna = new Vex.Flow.Ornament("turn_inverted");
  341. vfOrna.setDelayed(false);
  342. break;
  343. }
  344. case OrnamentEnum.Mordent: {
  345. vfOrna = new Vex.Flow.Ornament("mordent");
  346. vfOrna.setDelayed(false);
  347. break;
  348. }
  349. case OrnamentEnum.Trill: {
  350. vfOrna = new Vex.Flow.Ornament("tr");
  351. vfOrna.setDelayed(false);
  352. break;
  353. }
  354. case OrnamentEnum.Turn: {
  355. vfOrna = new Vex.Flow.Ornament("turn");
  356. vfOrna.setDelayed(false);
  357. break;
  358. }
  359. default: {
  360. log.warn("unhandled OrnamentEnum type: " + oContainer.GetOrnament);
  361. return;
  362. }
  363. }
  364. if (vfOrna !== undefined) {
  365. if (oContainer.AccidentalBelow !== AccidentalEnum.NONE) {
  366. vfOrna.setLowerAccidental(this.accidental(oContainer.AccidentalBelow));
  367. }
  368. if (oContainer.AccidentalAbove !== AccidentalEnum.NONE) {
  369. vfOrna.setUpperAccidental(this.accidental(oContainer.AccidentalAbove));
  370. }
  371. vfOrna.setPosition(vfPosition);
  372. vfnote.addModifier(0, vfOrna);
  373. }
  374. }
  375. /**
  376. * Convert a ClefInstruction to a string represention of a clef type in VexFlow.
  377. *
  378. * @param clef The OSMD object to be converted representing the clef
  379. * @param size The VexFlow size to be used. Can be `default` or `small`. As soon as
  380. * #118 is done, this parameter will be dispensable.
  381. * @returns A string representation of a VexFlow clef
  382. * @see https://github.com/0xfe/vexflow/blob/master/src/clef.js
  383. * @see https://github.com/0xfe/vexflow/blob/master/tests/clef_tests.js
  384. */
  385. public static Clef(clef: ClefInstruction, size: string = "default"): { type: string, size: string, annotation: string } {
  386. let type: string;
  387. let annotation: string;
  388. // Make sure size is either "default" or "small"
  389. if (size !== "default" && size !== "small") {
  390. log.warn(`Invalid VexFlow clef size "${size}" specified. Using "default".`);
  391. size = "default";
  392. }
  393. /*
  394. * For all of the following conversions, OSMD uses line numbers 1-5 starting from
  395. * the bottom, while VexFlow uses 0-4 starting from the top.
  396. */
  397. switch (clef.ClefType) {
  398. // G Clef
  399. case ClefEnum.G:
  400. switch (clef.Line) {
  401. case 1:
  402. type = "french"; // VexFlow line 4
  403. break;
  404. case 2:
  405. type = "treble"; // VexFlow line 3
  406. break;
  407. default:
  408. type = "treble";
  409. log.error(`Clef ${ClefEnum[clef.ClefType]} on line ${clef.Line} not supported by VexFlow. Using default value "${type}".`);
  410. }
  411. break;
  412. // F Clef
  413. case ClefEnum.F:
  414. switch (clef.Line) {
  415. case 4:
  416. type = "bass"; // VexFlow line 1
  417. break;
  418. case 3:
  419. type = "baritone-f"; // VexFlow line 2
  420. break;
  421. case 5:
  422. type = "subbass"; // VexFlow line 0
  423. break;
  424. default:
  425. type = "bass";
  426. log.error(`Clef ${ClefEnum[clef.ClefType]} on line ${clef.Line} not supported by VexFlow. Using default value "${type}".`);
  427. }
  428. break;
  429. // C Clef
  430. case ClefEnum.C:
  431. switch (clef.Line) {
  432. case 3:
  433. type = "alto"; // VexFlow line 2
  434. break;
  435. case 4:
  436. type = "tenor"; // VexFlow line 1
  437. break;
  438. case 1:
  439. type = "soprano"; // VexFlow line 4
  440. break;
  441. case 2:
  442. type = "mezzo-soprano"; // VexFlow line 3
  443. break;
  444. default:
  445. type = "alto";
  446. log.error(`Clef ${ClefEnum[clef.ClefType]} on line ${clef.Line} not supported by VexFlow. Using default value "${type}".`);
  447. }
  448. break;
  449. // Percussion Clef
  450. case ClefEnum.percussion:
  451. type = "percussion";
  452. break;
  453. // TAB Clef
  454. case ClefEnum.TAB:
  455. type = "tab";
  456. break;
  457. default:
  458. }
  459. // annotations in vexflow don't allow bass and 8va. No matter the offset :(
  460. if (clef.OctaveOffset === 1 && type !== "bass" ) {
  461. annotation = "8va";
  462. } else if (clef.OctaveOffset === -1) {
  463. annotation = "8vb";
  464. }
  465. return { type, size, annotation };
  466. }
  467. /**
  468. * Convert a RhythmInstruction to a VexFlow TimeSignature object
  469. * @param rhythm
  470. * @returns {Vex.Flow.TimeSignature}
  471. * @constructor
  472. */
  473. public static TimeSignature(rhythm: RhythmInstruction): Vex.Flow.TimeSignature {
  474. let timeSpec: string;
  475. switch (rhythm.SymbolEnum) {
  476. case RhythmSymbolEnum.NONE:
  477. timeSpec = rhythm.Rhythm.Numerator + "/" + rhythm.Rhythm.Denominator;
  478. break;
  479. case RhythmSymbolEnum.COMMON:
  480. timeSpec = "C";
  481. break;
  482. case RhythmSymbolEnum.CUT:
  483. timeSpec = "C|";
  484. break;
  485. default:
  486. }
  487. return new Vex.Flow.TimeSignature(timeSpec);
  488. }
  489. /**
  490. * Convert a KeyInstruction to a string representing in VexFlow a key
  491. * @param key
  492. * @returns {string}
  493. */
  494. public static keySignature(key: KeyInstruction): string {
  495. if (key === undefined) {
  496. return undefined;
  497. }
  498. let ret: string;
  499. switch (key.Mode) {
  500. case KeyEnum.minor:
  501. ret = VexFlowConverter.minorMap[key.Key] + "m";
  502. break;
  503. case KeyEnum.major:
  504. ret = VexFlowConverter.majorMap[key.Key];
  505. break;
  506. // some XMLs don't have the mode set despite having a key signature.
  507. case KeyEnum.none:
  508. ret = VexFlowConverter.majorMap[key.Key];
  509. break;
  510. default:
  511. ret = "C";
  512. }
  513. return ret;
  514. }
  515. /**
  516. * Converts a lineType to a VexFlow StaveConnector type
  517. * @param lineType
  518. * @returns {any}
  519. */
  520. public static line(lineType: SystemLinesEnum, linePosition: SystemLinePosition): any {
  521. switch (lineType) {
  522. case SystemLinesEnum.SingleThin:
  523. if (linePosition === SystemLinePosition.MeasureBegin) {
  524. return Vex.Flow.StaveConnector.type.SINGLE;
  525. }
  526. return Vex.Flow.StaveConnector.type.SINGLE_RIGHT;
  527. case SystemLinesEnum.DoubleThin:
  528. return Vex.Flow.StaveConnector.type.DOUBLE;
  529. case SystemLinesEnum.ThinBold:
  530. return Vex.Flow.StaveConnector.type.BOLD_DOUBLE_RIGHT;
  531. case SystemLinesEnum.BoldThinDots:
  532. return Vex.Flow.StaveConnector.type.BOLD_DOUBLE_LEFT;
  533. case SystemLinesEnum.DotsThinBold:
  534. return Vex.Flow.StaveConnector.type.BOLD_DOUBLE_RIGHT;
  535. case SystemLinesEnum.DotsBoldBoldDots:
  536. return Vex.Flow.StaveConnector.type.BOLD_DOUBLE_RIGHT;
  537. case SystemLinesEnum.None:
  538. return Vex.Flow.StaveConnector.type.NONE;
  539. default:
  540. }
  541. }
  542. /**
  543. * Construct a string which can be used in a CSS font property
  544. * @param fontSize
  545. * @param fontStyle
  546. * @param font
  547. * @returns {string}
  548. */
  549. public static font(fontSize: number, fontStyle: FontStyles = FontStyles.Regular, font: Fonts = Fonts.TimesNewRoman): string {
  550. let style: string = "normal";
  551. let weight: string = "normal";
  552. const family: string = "'Times New Roman'";
  553. switch (fontStyle) {
  554. case FontStyles.Bold:
  555. weight = "bold";
  556. break;
  557. case FontStyles.Italic:
  558. style = "italic";
  559. break;
  560. case FontStyles.BoldItalic:
  561. style = "italic";
  562. weight = "bold";
  563. break;
  564. case FontStyles.Underlined:
  565. // TODO
  566. break;
  567. default:
  568. break;
  569. }
  570. switch (font) {
  571. case Fonts.Kokila:
  572. // TODO Not Supported
  573. break;
  574. default:
  575. }
  576. return style + " " + weight + " " + Math.floor(fontSize) + "px " + family;
  577. }
  578. /**
  579. * Converts the style into a string that VexFlow RenderContext can understand
  580. * as the weight of the font
  581. */
  582. public static fontStyle(style: FontStyles): string {
  583. switch (style) {
  584. case FontStyles.Bold:
  585. return "bold";
  586. case FontStyles.Italic:
  587. return "italic";
  588. case FontStyles.BoldItalic:
  589. return "italic bold";
  590. default:
  591. return "normal";
  592. }
  593. }
  594. /**
  595. * Convert OutlineAndFillStyle to CSS properties
  596. * @param styleId
  597. * @returns {string}
  598. */
  599. public static style(styleId: OutlineAndFillStyleEnum): string {
  600. const ret: string = OUTLINE_AND_FILL_STYLE_DICT.getValue(styleId);
  601. return ret;
  602. }
  603. }
  604. export enum VexFlowRepetitionType {
  605. NONE = 1, // no coda or segno
  606. CODA_LEFT = 2, // coda at beginning of stave
  607. CODA_RIGHT = 3, // coda at end of stave
  608. SEGNO_LEFT = 4, // segno at beginning of stave
  609. SEGNO_RIGHT = 5, // segno at end of stave
  610. DC = 6, // D.C. at end of stave
  611. DC_AL_CODA = 7, // D.C. al coda at end of stave
  612. DC_AL_FINE = 8, // D.C. al Fine end of stave
  613. DS = 9, // D.S. at end of stave
  614. DS_AL_CODA = 10, // D.S. al coda at end of stave
  615. DS_AL_FINE = 11, // D.S. al Fine at end of stave
  616. FINE = 12, // Fine at end of stave
  617. }
  618. export enum VexFlowBarlineType {
  619. SINGLE = 1,
  620. DOUBLE = 2,
  621. END = 3,
  622. REPEAT_BEGIN = 4,
  623. REPEAT_END = 5,
  624. REPEAT_BOTH = 6,
  625. NONE = 7,
  626. }