Bladeren bron

Merge branch 'develop' into feat/export-png-svg-and-diff-images

# Conflicts:
#	.eslintrc.yml
#	.gitignore
#	package.json
sschmid 5 jaren geleden
bovenliggende
commit
ed075c7281

+ 1 - 1
.eslintrc.yml

@@ -1,5 +1,5 @@
 parserOptions: {
-        ecmaVersion: 8, // 8 = ECMA2017 necessary for promises. The ecmaVersion isn't set in stone, for now mostly adapted for eslint.
+        ecmaVersion: 8, // 8 = ECMA2017 necessary for promises/async. The ecmaVersion isn't set in stone, for now mostly adapted for eslint.
     }
 extends: standard
 rules:

+ 7 - 7
package.json

@@ -57,11 +57,11 @@
     "jszip": "^3.0.0",
     "loglevel": "^1.5.0",
     "svg2pdf.js": "^1.5.0",
-    "typescript-collections": "^1.1.2",
+    "typescript-collections": "^1.3.3",
     "vexflow": "1.2.89"
   },
   "devDependencies": {
-    "@types/chai": "^4.0.3",
+    "@types/chai": "^4.2.9",
     "@types/mocha": "^7.0.1",
     "@types/node": "^13.1.0",
     "chai": "^4.1.0",
@@ -69,11 +69,11 @@
     "cross-blob": "^1.2.0",
     "cross-env": "^7.0.0",
     "cz-conventional-changelog": "^3.0.0",
-    "eslint": "^6.2.2",
+    "eslint": "^6.8.0",
     "eslint-config-standard": "^14.1.0",
-    "eslint-plugin-import": "^2.16.0",
+    "eslint-plugin-import": "^2.20.1",
     "eslint-plugin-node": "^11.0.0",
-    "eslint-plugin-promise": "^4.0.1",
+    "eslint-plugin-promise": "^4.2.1",
     "eslint-plugin-standard": "^4.0.0",
     "html-webpack-plugin": "^3.2.0",
     "jquery": "^3.4.1",
@@ -92,11 +92,11 @@
     "puppeteer": "^2.1.1",
     "ts-loader": "^4.1.0",
     "tslint": "^5.14.0",
-    "tslint-loader": "^3.5.3",
+    "tslint-loader": "^3.5.4",
     "typedoc": "^0.16.0",
     "typescript": "^2.6.1",
     "webpack": "^4.39.3",
-    "webpack-cli": "^3.3.2",
+    "webpack-cli": "^3.3.11",
     "webpack-dev-server": "^3.8.0",
     "webpack-merge": "^4.1.2",
     "webpack-visualizer-plugin": "^0.1.11"

+ 10 - 1
src/MusicalScore/Graphical/EngravingRules.ts

@@ -1632,12 +1632,14 @@ export class EngravingRules {
 
 // TODO maybe this should be moved to OSMDOptions. Also see OpenSheetMusicDisplay.PageFormatStandards
 export class PageFormat {
-    constructor(width: number, height: number) {
+    constructor(width: number, height: number, idString: string = "noIdStringGiven") {
         this.width = width;
         this.height = height;
+        this.idString = idString;
     }
     public width: number;
     public height: number;
+    public idString: string;
     public get aspectRatio(): number {
         if (!this.IsUndefined) {
             return this.width / this.height;
@@ -1653,4 +1655,11 @@ export class PageFormat {
     public static get UndefinedPageFormat(): PageFormat {
         return new PageFormat(0, 0);
     }
+
+    public Equals(otherPageFormat: PageFormat): boolean {
+        if (!otherPageFormat) {
+            return false;
+        }
+        return otherPageFormat.width === this.width && otherPageFormat.height === this.height;
+    }
 }

+ 2 - 1
src/MusicalScore/Graphical/MusicSheetCalculator.ts

@@ -2338,7 +2338,8 @@ export abstract class MusicSheetCalculator {
             this.calculateDashes(startStaffLine, startX, endX, y);
 
             // calculate Dashes for the second StaffLine (only if endStaffEntry isn't the first StaffEntry of the StaffLine)
-            if (nextStaffLine &&
+            if (nextStaffLine && // check for undefined objects e.g. when drawingRange given
+                nextStaffLine.Measures[0] &&
                 endStaffentry.parentMeasure.ParentStaffLine &&
                 !(endStaffentry === endStaffentry.parentMeasure.staffEntries[0] &&
                 endStaffentry.parentMeasure === endStaffentry.parentMeasure.ParentStaffLine.Measures[0])) {

+ 0 - 1
src/MusicalScore/Graphical/MusicSheetDrawer.ts

@@ -484,7 +484,6 @@ export abstract class MusicSheetDrawer {
         if (this.drawableBoundingBoxElement) {
             this.drawBoundingBoxes(page.PositionAndShape, 0, this.drawableBoundingBoxElement);
         }
-
     }
 
     /**

+ 14 - 1
src/MusicalScore/Graphical/MusicSystemBuilder.ts

@@ -972,6 +972,7 @@ export class MusicSystemBuilder {
         let currentYPosition: number = 0;
         // xPosition is always fixed
         let currentSystem: MusicSystem = this.musicSystems[0];
+        let timesPageCouldntFitSingleSystem: number = 0;
 
         for (let i: number = 0; i < this.musicSystems.length; i++) {
             currentSystem = this.musicSystems[i];
@@ -995,7 +996,16 @@ export class MusicSystemBuilder {
                                                                 currentYPosition);
                 currentSystem.PositionAndShape.RelativePosition = relativePosition;
                 currentYPosition += currentSystem.PositionAndShape.BorderBottom;
-
+                if (currentYPosition > this.rules.PageHeight - this.rules.PageBottomMargin) { // can't fit single system on page, maybe PageFormat too small
+                    timesPageCouldntFitSingleSystem++;
+                    if (timesPageCouldntFitSingleSystem <= 4) { // only warn once with detailed info
+                        console.log(`warning: could not fit a single system on page ${currentPage.PageNumber}` +
+                            ` and measure number ${currentSystem.GraphicalMeasures[0][0].MeasureNumber}.
+                            The PageFormat may be too small for this sheet."
+                            Will not give further warnings for all pages, only total.`
+                        );
+                    }
+                }
             } else {
                 // if this is not the first system on the page:
                 // find optimum distance between Systems
@@ -1023,6 +1033,9 @@ export class MusicSystemBuilder {
                 }
             }
         }
+        if (timesPageCouldntFitSingleSystem > 0) {
+            console.log(`total amount of pages that couldn't fit a single music system: ${timesPageCouldntFitSingleSystem} of ${currentPage.PageNumber}`);
+        }
     }
 }
 

+ 2 - 1
src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetDrawer.ts

@@ -40,7 +40,7 @@ export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
     private backend: VexFlowBackend;
     private backends: VexFlowBackend[] = [];
     private zoom: number = 1.0;
-    private pageIdx: number = 0;
+    private pageIdx: number = 0; // this is a bad solution, should use MusicPage.PageNumber instead.
 
     constructor(drawingParameters: DrawingParameters = new DrawingParameters()) {
         super(new VexFlowTextMeasurer(), drawingParameters);
@@ -67,6 +67,7 @@ export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
     }
 
     protected drawPage(page: GraphicalMusicPage): void {
+        this.backend = this.backends[page.PageNumber - 1]; // TODO we may need to set this in a couple of other places. this.pageIdx is a bad solution
         super.drawPage(page);
         this.pageIdx += 1;
         this.backend = this.backends[this.pageIdx];

+ 13 - 12
src/MusicalScore/Graphical/VexFlow/VexFlowVoiceEntry.ts

@@ -49,12 +49,14 @@ export class VexFlowVoiceEntry extends GraphicalVoiceEntry {
         const defaultColorStem: string = EngravingRules.Rules.DefaultColorStem;
         const transparentColor: string = "#00000000"; // transparent color in vexflow
         let noteheadColor: string; // if null: no noteheadcolor to set (stays black)
+        let sourceNoteNoteheadColor: string;
 
         const vfStaveNote: any = (<VexFlowVoiceEntry>(this as any)).vfStaveNote;
         for (let i: number = 0; i < this.notes.length; i++) {
             const note: GraphicalNote = this.notes[i];
 
-            noteheadColor = note.sourceNote.NoteheadColor;
+            sourceNoteNoteheadColor = note.sourceNote.NoteheadColor;
+            noteheadColor = sourceNoteNoteheadColor;
             // Switch between XML colors and automatic coloring
             if (EngravingRules.Rules.ColoringMode === ColoringModes.AutoColoring ||
                 EngravingRules.Rules.ColoringMode === ColoringModes.CustomColorSet) {
@@ -83,7 +85,7 @@ export class VexFlowVoiceEntry extends GraphicalVoiceEntry {
                     ", in measure #" + measureNumber);
             }*/
 
-            if (!noteheadColor) {
+            if (!sourceNoteNoteheadColor && EngravingRules.Rules.ColoringMode === ColoringModes.XML) {
                 if (!note.sourceNote.isRest() && defaultColorNotehead) {
                     noteheadColor = defaultColorNotehead;
                 } else if (note.sourceNote.isRest() && defaultColorRest) {
@@ -91,17 +93,17 @@ export class VexFlowVoiceEntry extends GraphicalVoiceEntry {
                 }
             }
             if (noteheadColor && note.sourceNote.PrintObject) {
-                note.sourceNote.NoteheadColor = noteheadColor;
+                note.sourceNote.NoteheadColorCurrentlyRendered = noteheadColor;
             } else if (!noteheadColor) {
                 continue;
             }
 
             // color notebeam if all noteheads have same color and stem coloring enabled
-            if (EngravingRules.Rules.ColoringEnabled && note.sourceNote.NoteBeam && EngravingRules.Rules.ColorStemsLikeNoteheads) {
+            if (EngravingRules.Rules.ColoringEnabled && note.sourceNote.NoteBeam && EngravingRules.Rules.ColorBeams) {
                 const beamNotes: Note[] = note.sourceNote.NoteBeam.Notes;
                 let colorBeam: boolean = true;
                 for (let j: number = 0; j < beamNotes.length; j++) {
-                    if (beamNotes[j].NoteheadColor !== noteheadColor) {
+                    if (beamNotes[j].NoteheadColorCurrentlyRendered !== noteheadColor) {
                         colorBeam = false;
                     }
                 }
@@ -123,17 +125,16 @@ export class VexFlowVoiceEntry extends GraphicalVoiceEntry {
         }
 
         // color stems
-        let stemColor: string = EngravingRules.Rules.DefaultColorStem; // reset to black/default when coloring was disabled. maybe needed elsewhere too
+        let stemColor: string = defaultColorStem; // reset to black/default when coloring was disabled. maybe needed elsewhere too
         if (EngravingRules.Rules.ColoringEnabled) {
             stemColor = this.parentVoiceEntry.StemColor; // TODO: once coloringSetCustom gets stem color, respect it
-            if (!stemColor || EngravingRules.Rules.ColorStemsLikeNoteheads
+            if (!stemColor
                 || stemColor === "#000000") { // see above, noteheadColor === "#000000"
+                stemColor = defaultColorStem;
+            }
+            if (EngravingRules.Rules.ColorStemsLikeNoteheads && noteheadColor) {
                 // condition could be even more fine-grained by only recoloring if there was no custom StemColor set. will be more complex though
-                if (noteheadColor) {
-                    stemColor = noteheadColor;
-                } else if (defaultColorStem) {
-                    stemColor = defaultColorStem;
-                }
+                stemColor = noteheadColor;
             }
         }
         let stemTransparent: boolean = true;

+ 9 - 1
src/MusicalScore/VoiceData/Note.ts

@@ -77,11 +77,12 @@ export class Note {
      * because Note.Notehead is undefined for normal Noteheads to save space and time.
      */
     private noteheadColorXml: string;
-    /** Color of the notehead currently set. RGB Hexadecimal, like #00FF00.
+    /** Color of the notehead currently set/desired for next render. RGB Hexadecimal, like #00FF00.
      * Needs to be stored here and not in Note.Notehead,
      * because Note.Notehead is undefined for normal Noteheads to save space and time.
      */
     private noteheadColor: string;
+    private noteheadColorCurrentlyRendered: string;
 
     public get ParentVoiceEntry(): VoiceEntry {
         return this.voiceEntry;
@@ -200,12 +201,19 @@ export class Note {
     public set NoteheadColorXml(value: string) {
         this.noteheadColorXml = value;
     }
+    /** The desired notehead color for the next render. */
     public get NoteheadColor(): string {
         return this.noteheadColor;
     }
     public set NoteheadColor(value: string) {
         this.noteheadColor = value;
     }
+    public get NoteheadColorCurrentlyRendered(): string {
+        return this.noteheadColorCurrentlyRendered;
+    }
+    public set NoteheadColorCurrentlyRendered(value: string) {
+        this.noteheadColorCurrentlyRendered = value;
+    }
 
     public isRest(): boolean {
         return this.Pitch === undefined;

+ 36 - 17
src/OpenSheetMusicDisplay/OpenSheetMusicDisplay.ts

@@ -70,6 +70,9 @@ export class OpenSheetMusicDisplay {
     private needBackendUpdate: boolean;
     private sheet: MusicSheet;
     private drawer: VexFlowMusicSheetDrawer;
+    private drawBoundingBox: string;
+    private drawSkyLine: boolean;
+    private drawBottomLine: boolean;
     private graphic: GraphicalMusicSheet;
     private drawingParameters: DrawingParameters;
     private autoResizeEnabled: boolean;
@@ -153,6 +156,7 @@ export class OpenSheetMusicDisplay {
         }
         log.info(`[OSMD] Loaded sheet ${this.sheet.TitleString} successfully.`);
 
+        this.needBackendUpdate = true;
         this.updateGraphic();
 
         return Promise.resolve({});
@@ -189,7 +193,7 @@ export class OpenSheetMusicDisplay {
         if (EngravingRules.Rules.PageFormat && !EngravingRules.Rules.PageFormat.IsUndefined) {
             EngravingRules.Rules.PageHeight = this.sheet.pageWidth / EngravingRules.Rules.PageFormat.aspectRatio;
         } else {
-            EngravingRules.Rules.PageHeight = 100001; // infinite page height // TODO Number.MAX_SAFE_INTEGER ?
+            EngravingRules.Rules.PageHeight = 100001; // infinite page height // TODO maybe Number.MAX_VALUE or Math.pow(10, 20)?
         }
 
         // Before introducing the following optimization (maybe irrelevant), tests
@@ -205,7 +209,10 @@ export class OpenSheetMusicDisplay {
             this.graphic.Cursors.length = 0;
         }
 
-        if (this.needBackendUpdate) {
+        // needBackendUpdate is well intentioned, but we need to cover all cases.
+        //   backends also need an update when this.zoom was set from outside, which unfortunately doesn't have a setter method to set this in.
+        //   so just for compatibility, we need to assume users set osmd.zoom, so we'd need to check whether it was changed compared to last time.
+        if (true || this.needBackendUpdate) {
             this.createOrRefreshRenderBackend();
             this.needBackendUpdate = false;
         }
@@ -233,7 +240,10 @@ export class OpenSheetMusicDisplay {
             this.drawer.Backends.clear();
         }
         // Create the drawer
-        this.drawer = new VexFlowMusicSheetDrawer(this.drawingParameters);
+        this.drawer = new VexFlowMusicSheetDrawer(this.drawingParameters); // note that here the drawer.drawableBoundingBoxElement is lost. now saved in OSMD.
+        this.drawer.drawableBoundingBoxElement = this.DrawBoundingBox;
+        this.drawer.bottomLineVisible = this.drawBottomLine;
+        this.drawer.skyLineVisible = this.drawSkyLine;
 
         // Set page width
         const width: number = this.container.offsetWidth;
@@ -623,20 +633,24 @@ export class OpenSheetMusicDisplay {
 
     /** Standard page format options like A4 or Letter, in portrait and landscape. E.g. PageFormatStandards["A4_P"] or PageFormatStandards["Letter_L"]. */
     public static PageFormatStandards: {[type: string]: PageFormat} = {
-        "A3_L": new PageFormat(420, 297), // id strings should use underscores instead of white spaces to facilitate use as URL parameters.
-        "A3_P": new PageFormat(297, 420),
-        "A4_L": new PageFormat(297, 210),
-        "A4_P": new PageFormat(210, 297),
-        "A5_L": new PageFormat(210, 148),
-        "A5_P": new PageFormat(148, 210),
-        "A6_L": new PageFormat(148, 105),
-        "A6_P": new PageFormat(105, 148),
+        "A3_L": new PageFormat(420, 297, "A3_L"), // id strings should use underscores instead of white spaces to facilitate use as URL parameters.
+        "A3_P": new PageFormat(297, 420, "A3_P"),
+        "A4_L": new PageFormat(297, 210, "A4_L"),
+        "A4_P": new PageFormat(210, 297, "A4_P"),
+        "A5_L": new PageFormat(210, 148, "A5_L"),
+        "A5_P": new PageFormat(148, 210, "A5_P"),
+        "A6_L": new PageFormat(148, 105, "A6_L"),
+        "A6_P": new PageFormat(105, 148, "A6_P"),
         "Endless": PageFormat.UndefinedPageFormat,
-        "Letter_L": new PageFormat(279.4, 215.9),
-        "Letter_P": new PageFormat(215.9, 279.4)
+        "Letter_L": new PageFormat(279.4, 215.9, "Letter_L"),
+        "Letter_P": new PageFormat(215.9, 279.4, "Letter_P")
     };
 
     public static StringToPageFormat(formatId: string): PageFormat {
+        formatId = formatId.replace(" ", "_");
+        formatId = formatId.replace("Landscape", "L");
+        formatId = formatId.replace("Portrait", "P");
+        //console.log("change format to: " + formatId);
         let f: PageFormat = PageFormat.UndefinedPageFormat; // default: 'endless' page height, take canvas/container width
         if (OpenSheetMusicDisplay.PageFormatStandards.hasOwnProperty(formatId)) {
             f = OpenSheetMusicDisplay.PageFormatStandards[formatId];
@@ -646,7 +660,9 @@ export class OpenSheetMusicDisplay {
 
     /** Sets page format by string. Alternative to setOptions({pageFormat: PageFormatStandards.Endless}) for example. */
     public setPageFormat(formatId: string): void {
-        EngravingRules.Rules.PageFormat = OpenSheetMusicDisplay.StringToPageFormat(formatId);
+        const newPageFormat: PageFormat = OpenSheetMusicDisplay.StringToPageFormat(formatId);
+        this.needBackendUpdate = !(newPageFormat.Equals(EngravingRules.Rules.PageFormat));
+        EngravingRules.Rules.PageFormat = newPageFormat;
     }
 
     public setCustomPageFormat(width: number, height: number): void {
@@ -708,6 +724,7 @@ export class OpenSheetMusicDisplay {
 
     //#region GETTER / SETTER
     public set DrawSkyLine(value: boolean) {
+        this.drawSkyLine = value;
         if (this.drawer) {
             this.drawer.skyLineVisible = value;
             this.render();
@@ -718,6 +735,7 @@ export class OpenSheetMusicDisplay {
     }
 
     public set DrawBottomLine(value: boolean) {
+        this.drawBottomLine = value;
         if (this.drawer) {
             this.drawer.bottomLineVisible = value;
             this.render();
@@ -728,11 +746,12 @@ export class OpenSheetMusicDisplay {
     }
 
     public set DrawBoundingBox(value: string) {
-        this.drawer.drawableBoundingBoxElement = value;
-        this.render();
+        this.drawBoundingBox = value;
+        this.drawer.drawableBoundingBoxElement = value; // drawer is sometimes created anew, losing this value, so it's saved in OSMD now.
+        this.render(); // may create new Drawer.
     }
     public get DrawBoundingBox(): string {
-        return this.drawer.drawableBoundingBoxElement;
+        return this.drawBoundingBox;
     }
 
     public get AutoResizeEnabled(): boolean {