UNPKG

satie

Version:

A sheet music renderer for the web

369 lines (334 loc) 12.3 kB
/** * This file is part of Satie music engraver <https://github.com/jnetterf/satie>. * Copyright (C) Joshua Netterfield <joshua.ca> 2015 - present. * * Satie is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * Satie is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with Satie. If not, see <http://www.gnu.org/licenses/>. */ /** * @file engine/scoreHeader.ts holds default header information as well * as convienience utilites for score headers. */ import {ScoreHeader, Credit, Identification, Defaults, NormalItalic, NormalBold, OddEvenBoth, Work, PartList, LeftCenterRight, serializeScoreHeader} from "musicxml-interfaces"; import {forEach, some, defaultsDeep} from "lodash"; import {getPageMargins} from "./private_print"; import {distances, bravura} from "./private_smufl"; import {mmToTenths, defaultStaveHeight, defaultPageSize, defaultMargins} from "./private_renderUtil"; /** * A header is a child of parts, and includes the title and other basic * information. */ class ScoreHeaderModel implements ScoreHeader { /*---- ScoreHeader --------------------------------------------------------------------------*/ credits: Credit[] = []; identification: Identification = { creators: [], encoding: { encodingDescriptions: [], encodingDate: null, supports: {}, encoders: [], softwares: [] }, miscellaneous: null, relations: [], rights: [], source: null }; defaults: Defaults = { appearance: { distances: { hyphen: { tenths: 10 * distances.hyphen, type: "hyphen" }, beam: { tenths: 10 * distances.beam, type: "beam" } }, lineWidths: { staff: { "tenths": 10 * bravura.engravingDefaults.staffLineThickness, "type": "staff" }, wedge: { "tenths": 10 * bravura.engravingDefaults.hairpinThickness, "type": "wedge" }, ending: { "tenths": 10 * bravura.engravingDefaults.repeatEndingLineThickness, "type": "ending" }, "heavy barline": { "tenths": 10 * bravura.engravingDefaults.thickBarlineThickness, "type": "heavy barline" }, leger: { "tenths": 10 * bravura.engravingDefaults.legerLineThickness, "type": "leger" }, stem: { "tenths": 10 * bravura.engravingDefaults.stemThickness, "type": "stem" }, "tuplet bracket": { "tenths": 10 * bravura.engravingDefaults.tupletBracketThickness, "type": "tuplet bracket" }, beam: { "tenths": 10 * bravura.engravingDefaults.beamThickness, "type": "beam" }, "light barline": { "tenths": 10 * bravura.engravingDefaults.thinBarlineThickness, "type": "light barline" }, enclosure: { "tenths": 10 * bravura.engravingDefaults.textEnclosureThickness, "type": "enclosure" } }, noteSizes: { 1: { // Grace "type": 1, "size": 60 // Not sure what 60 refers to. Our grace notes are 1.9 spaces }, 0: { // Cue "type": 0, "size": 60 // Not sure what 60 refers to. Our cue notes are 1.9 spaces. } }, otherAppearances: [] }, lyricFonts: [], lyricLanguages: [], musicFont: { fontSize: "20.5", // This value is completely ignored. See "scaling" fontFamily: "Bravura, Maestro, engraved", fontStyle: NormalItalic.Normal, fontWeight: NormalBold.Normal }, pageLayout: { pageHeight: mmToTenths( defaultStaveHeight, defaultPageSize().height), pageWidth: mmToTenths( defaultStaveHeight, defaultPageSize().width), pageMargins: [ { bottomMargin: mmToTenths( defaultStaveHeight, defaultMargins.bottom), leftMargin: mmToTenths( defaultStaveHeight, defaultMargins.left), rightMargin: mmToTenths( defaultStaveHeight, defaultMargins.right), topMargin: mmToTenths( defaultStaveHeight, defaultMargins.top), type: OddEvenBoth.Both } ] }, scaling: { millimeters: defaultStaveHeight, tenths: 40 }, staffLayouts: [], systemLayout: { systemDistance: 131, systemDividers: null, systemMargins: { leftMargin: 0, rightMargin: 0 }, topSystemDistance: 70 }, wordFont: { fontSize: "12", fontFamily: "Alegreya, Times New Roman, serif", fontStyle: NormalItalic.Normal, fontWeight: NormalBold.Normal } }; work: Work = { opus: null, workNumber: "", workTitle: "" }; movementTitle: string = ""; movementNumber: string = ""; partList: PartList = []; get composer() { return this._getIdentificationOrCredit("composer"); } set composer(composer: string) { this._setIdentification("composer", composer); this._setCredits("composer", composer, LeftCenterRight.Right, "12px", 20); } get arranger() { return this._getIdentificationOrCredit("arranger"); } set arranger(arranger: string) { this._setIdentification("arranger", arranger); this._setCredits("arranger", arranger, LeftCenterRight.Right, "12px", 35); } get lyricist() { return this._getIdentificationOrCredit("lyricist"); } set lyricist(lyricist: string) { this._setIdentification("lyricist", lyricist); this._setCredits("lyricist", lyricist, LeftCenterRight.Right, "12px", 50); } get title() { return this.movementTitle; } set title(title: string) { // Set meta-data this.movementTitle = title; this._setCredits("title", title, LeftCenterRight.Center, "18px", 10); } /*---- Extensions ---------------------------------------------------------------------------*/ constructor(spec: ScoreHeader) { if (spec) { defaultsDeep(spec, this); } for (let key in spec) { if (spec.hasOwnProperty(key) && typeof key === "string" && !!(<any>spec)[key]) { (<any>this)[key] = (<any>spec)[key]; } } } toXML(): string { return serializeScoreHeader(this); } inspect() { return this.toXML(); } overwriteEncoding() { let date = new Date; this.identification = this.identification || (new ScoreHeaderModel(null)).identification; this.identification.encoding = { encodingDescriptions: [], encodingDate: { month: date.getMonth() + 1, day: date.getDate(), year: date.getFullYear() }, supports: { "satie-collaboration": { element: "satie-collaboration", value: null, type: true, attribute: null } }, encoders: [], softwares: [ "Songhaus Satie" ] }; } private _getIdentificationOrCredit(type: string) { if (this.identification && (this.identification.creators || []).length) { let idComposer = this.identification.creators .filter(c => c.type === type) .map(c => c.creator) .join(", "); if (idComposer) { return idComposer; } } return this.credits.filter(c => c.creditTypes.indexOf(type) !== -1) .map(m => m.creditWords) .map(w => w.map(w => w.words).join(", ")) .join(", "); } private _setIdentification(type: string, val: string) { this.identification = this.identification || { miscellaneous: [], creators: [], encoding: [], relations: [], rights: [], source: null }; this.identification.creators = this.identification.creators || []; forEach(this.identification.creators, c => { if (c.type === type) { c.creator = val; } }); if (!some(this.identification.creators, c => c.type === type)) { // ...or add a val this.identification.creators.push({ creator: val, type: type }); } } private _setCredits(type: string, val: string, justification: LeftCenterRight, fontSize: string, top: number) { const mm = this.defaults.scaling.millimeters; const pageLayout = this.defaults.pageLayout; this.credits = this.credits || []; forEach(this.credits, (c, idx) => { if (!c.creditWords) { return false; } // Replace a credit... let isComposer = c.creditTypes.indexOf(type) !== -1; if (isComposer) { if (!c.creditWords.length) { delete this.credits[idx]; } else { c.creditWords.length = 1; c.creditWords[0].words = val; } } }); if (!some(this.credits, c => Boolean(c.creditWords) && c.creditTypes.indexOf(type) !== -1)) { let defaultX = NaN; let margins = getPageMargins(this.defaults.pageLayout.pageMargins, 1); // TODO: Throughout this file, use own instead of default values switch (justification) { case LeftCenterRight.Center: defaultX = (margins.leftMargin - margins.rightMargin + pageLayout.pageWidth) / 2; break; case LeftCenterRight.Right: defaultX = pageLayout.pageWidth - margins.rightMargin; break; case LeftCenterRight.Left: defaultX = margins.leftMargin; break; default: defaultX = margins.leftMargin; break; }; this.credits.push({ // ... or add a credit creditImage: null, creditTypes: [type], creditWords: [{ words: val, defaultX: defaultX, justify: justification, defaultY: pageLayout.pageHeight - mmToTenths(mm, top), fontSize: fontSize }], page: 1 }); } } } export default ScoreHeaderModel;