Browse Source

Merge branch 'feat/PrintToPdf' into develop

# Conflicts:
#	src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetCalculator.ts

Fixed Cursor (at least for first page)
Matthias Uiberacker 5 năm trước cách đây
mục cha
commit
f4f890739a

+ 15 - 0
demo/index.html

@@ -126,6 +126,21 @@
             </div>
         </div>
     </div>
+    <div class="column">
+        <h3 class="ui header">Page size:</h3>
+        <select class="ui selection dropdown" id="selectPageSize">
+            <option value="endless">endless</option>
+            <option value="A3 P">A3 Portrait</option>
+            <option value="A3 L">A3 Landscape</option>
+            <option value="A4 P">A4 Portrait</option>
+            <option value="A4 L">A4 Landscape</option>
+            <option value="A5 P">A5 Portrait</option>
+            <option value="A5 L">A5 Landscape</option>
+            <option value="Letter P">Letter Portrait</option>
+            <option value="Letter L">Letter Landscape</option>
+        </select>
+        <div class="ui button" id="print-pdf-btn">Print to Pdf</div>
+    </div>
 </div>
 <div id="optionalControls"></div>
 <table cellspacing="0" style="max-width:700px;">

+ 24 - 2
demo/index.js

@@ -70,7 +70,9 @@ import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMus
         hideCursorBtn,
         backendSelect,
         debugReRenderBtn,
-        debugClearBtn;
+        debugClearBtn,
+        selectPageSize,
+        printPdfBtn;
 
     // manage option setting and resetting for specific samples, e.g. in the autobeam sample autobeam is set to true, otherwise reset to previous state
     // TODO design a more elegant option state saving & restoring system, though that requires saving the options state in OSMD
@@ -142,6 +144,8 @@ import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMus
         backendSelect = document.getElementById("backend-select");
         debugReRenderBtn = document.getElementById("debug-re-render-btn");
         debugClearBtn = document.getElementById("debug-clear-btn");
+        selectPageSize = document.getElementById("selectPageSize");
+        printPdfBtn = document.getElementById("print-pdf-btn");
 
         if (!showControls) {
             divControls.style.display = 'none';
@@ -181,6 +185,20 @@ import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMus
             selectBounding.onchange = selectBoundingOnChange;
         }
 
+        if (selectPageSize) {
+            selectPageSize.onchange = function (evt) {
+                var value = evt.target.value;
+                openSheetMusicDisplay.setPageFormat(value);
+                openSheetMusicDisplay.render();
+            };
+        }
+
+        if (printPdfBtn) {
+            printPdfBtn.onclick = function () {
+                openSheetMusicDisplay.createPdf();
+            }
+        }
+
         // Pre-select default music piece
 
         custom.appendChild(document.createTextNode("Custom"));
@@ -277,7 +295,11 @@ import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMus
             }
         }
         hideCursorBtn.addEventListener("click", function () {
-            openSheetMusicDisplay.cursor.hide();
+            if (openSheetMusicDisplay.cursor) {
+                openSheetMusicDisplay.cursor.hide();
+            } else {
+                console.info("Can't hide cursor, as it was disabled (e.g. by drawingParameters).");
+            }
         });
         showCursorBtn.addEventListener("click", function () {
             if (openSheetMusicDisplay.cursor) {

+ 2 - 1
package.json

@@ -48,11 +48,12 @@
   },
   "homepage": "http://opensheetmusicdisplay.org",
   "dependencies": {
+    "@types/vexflow": "^1.2.33",
     "es6-promise": "^4.2.5",
     "jszip": "^3.0.0",
     "loglevel": "^1.5.0",
+    "svg2pdf.js": "^1.5.0",
     "typescript-collections": "^1.1.2",
-    "@types/vexflow": "^1.2.33",
     "vexflow": "^1.2.89"
   },
   "devDependencies": {

+ 8 - 0
src/MusicalScore/Graphical/BoundingBox.ts

@@ -203,7 +203,15 @@ export class BoundingBox {
     }
 
     public set Parent(value: BoundingBox) {
+        if (this.parent !== undefined) {
+            // remove from old parent
+            const index: number = this.parent.ChildElements.indexOf(this, 0);
+            if (index > -1) {
+                this.parent.ChildElements.splice(index, 1);
+            }
+        }
         this.parent = value;
+        // add to new parent
         if (this.parent.ChildElements.indexOf(this) > -1) {
             log.error("BoundingBox of " + (this.dataObject.constructor as any).name +
             " already in children list of " + (this.parent.dataObject.constructor as any).name + "'s BoundingBox");

+ 26 - 14
src/MusicalScore/Graphical/EngravingRules.ts

@@ -30,14 +30,13 @@ export class EngravingRules {
     private pageRightMargin: number;
     private titleTopDistance: number;
     private titleBottomDistance: number;
-    private systemDistance: number;
     private systemLeftMargin: number;
     private systemRightMargin: number;
     private firstSystemMargin: number;
     private systemLabelsRightMargin: number;
     private systemComposerDistance: number;
     private instrumentLabelTextHeight: number;
-    private minimumAllowedDistanceBetweenSystems: number;
+    private minimumDistanceBetweenSystems: number;
     private lastSystemMaxScalingFactor: number;
     private staffDistance: number;
     private betweenStaffDistance: number;
@@ -214,6 +213,7 @@ export class EngravingRules {
     /** Position of fingering label in relation to corresponding note (left, right supported, above, below experimental) */
     private fingeringPosition: PlacementEnum;
     private fingeringInsideStafflines: boolean;
+    private pageFormat: PageFormat;
 
     private fixStafflineBoundingBox: boolean; // TODO temporary workaround
 
@@ -245,14 +245,13 @@ export class EngravingRules {
         // System Sizing and Label Variables
         this.staffHeight = 4.0;
         this.betweenStaffLinesDistance = EngravingRules.unit;
-        this.systemDistance = 10.0;
         this.systemLeftMargin = 0.0;
         this.systemRightMargin = 0.0;
         this.firstSystemMargin = 15.0;
         this.systemLabelsRightMargin = 2.0;
         this.systemComposerDistance = 2.0;
         this.instrumentLabelTextHeight = 2;
-        this.minimumAllowedDistanceBetweenSystems = 3.0;
+        this.minimumDistanceBetweenSystems = 4.0;
         this.lastSystemMaxScalingFactor = 1.4;
 
         // autoBeam options
@@ -563,12 +562,6 @@ export class EngravingRules {
     public set InstrumentLabelTextHeight(value: number) {
         this.instrumentLabelTextHeight = value;
     }
-    public get SystemDistance(): number {
-        return this.systemDistance;
-    }
-    public set SystemDistance(value: number) {
-        this.systemDistance = value;
-    }
     public get SystemLeftMargin(): number {
         return this.systemLeftMargin;
     }
@@ -593,11 +586,11 @@ export class EngravingRules {
     public set SystemLabelsRightMargin(value: number) {
         this.systemLabelsRightMargin = value;
     }
-    public get MinimumAllowedDistanceBetweenSystems(): number {
-        return this.minimumAllowedDistanceBetweenSystems;
+    public get MinimumDistanceBetweenSystems(): number {
+        return this.minimumDistanceBetweenSystems;
     }
-    public set MinimumAllowedDistanceBetweenSystems(value: number) {
-        this.minimumAllowedDistanceBetweenSystems = value;
+    public set MinimumDistanceBetweenSystems(value: number) {
+        this.minimumDistanceBetweenSystems = value;
     }
     public get LastSystemMaxScalingFactor(): number {
         return this.lastSystemMaxScalingFactor;
@@ -1558,6 +1551,13 @@ export class EngravingRules {
         return this.fixStafflineBoundingBox;
     }
 
+    public get PageFormat(): PageFormat {
+        return this.pageFormat;
+    }
+    public set PageFormat(value: PageFormat) {
+        this.pageFormat = value;
+    }
+
     /**
      * This method maps NoteDurations to Distances and DistancesScalingFactors.
      */
@@ -1619,3 +1619,15 @@ export class EngravingRules {
         }
     }
 }
+
+export class PageFormat {
+    constructor(width: number, height: number) {
+        this.width = width;
+        this.height = height;
+    }
+    public width: number;
+    public height: number;
+    public get aspectRatio(): number {
+        return this.width / this.height;
+    }
+}

+ 23 - 20
src/MusicalScore/Graphical/GraphicalMusicPage.ts

@@ -48,26 +48,29 @@ export class GraphicalMusicPage extends GraphicalObject {
      * @returns {PointF2D}
      */
     public setMusicPageAbsolutePosition(pageIndex: number, rules: EngravingRules): PointF2D {
-        if (rules.PagePlacement === PagePlacementEnum.Down) {
-            return new PointF2D(0.0, pageIndex * rules.PageHeight);
-        } else if (rules.PagePlacement === PagePlacementEnum.Right) {
-            return new PointF2D(pageIndex * this.parent.ParentMusicSheet.pageWidth, 0.0);
-        } else {
-            // placement RightDown
-            if (pageIndex % 2 === 0) {
-                if (pageIndex === 0) {
-                    return new PointF2D(0.0, pageIndex * rules.PageHeight);
-                } else {
-                    return new PointF2D(0.0, (pageIndex - 1) * rules.PageHeight);
-                }
-            } else {
-                if (pageIndex === 1) {
-                    return new PointF2D(this.parent.ParentMusicSheet.pageWidth, (pageIndex - 1) * rules.PageHeight);
-                } else {
-                    return new PointF2D(this.parent.ParentMusicSheet.pageWidth, (pageIndex - 2) * rules.PageHeight);
-                }
-            }
-        }
+        return new PointF2D(0.0, 0.0);
+
+        // use this code if pages are rendered on only one canvas:
+        // if (rules.PagePlacement === PagePlacementEnum.Down) {
+        //     return new PointF2D(0.0, pageIndex * rules.PageHeight);
+        // } else if (rules.PagePlacement === PagePlacementEnum.Right) {
+        //     return new PointF2D(pageIndex * this.parent.ParentMusicSheet.pageWidth, 0.0);
+        // } else {
+        //     // placement RightDown
+        //     if (pageIndex % 2 === 0) {
+        //         if (pageIndex === 0) {
+        //             return new PointF2D(0.0, pageIndex * rules.PageHeight);
+        //         } else {
+        //             return new PointF2D(0.0, (pageIndex - 1) * rules.PageHeight);
+        //         }
+        //     } else {
+        //         if (pageIndex === 1) {
+        //             return new PointF2D(this.parent.ParentMusicSheet.pageWidth, (pageIndex - 1) * rules.PageHeight);
+        //         } else {
+        //             return new PointF2D(this.parent.ParentMusicSheet.pageWidth, (pageIndex - 2) * rules.PageHeight);
+        //         }
+        //     }
+        // }
     }
 }
 

+ 126 - 247
src/MusicalScore/Graphical/MusicSheetCalculator.ts

@@ -85,7 +85,7 @@ export abstract class MusicSheetCalculator {
 
     protected graphicalMusicSheet: GraphicalMusicSheet;
     protected rules: EngravingRules;
-    //protected symbolFactory: IGraphicalSymbolFactory;
+    protected musicSystems: MusicSystem[];
 
     public static get TextMeasurer(): ITextMeasurer {
         return MusicSheetCalculator.textMeasurer;
@@ -172,6 +172,8 @@ export abstract class MusicSheetCalculator {
      * The main method for the Calculator.
      */
     public calculate(): void {
+        this.musicSystems = [];
+
         this.clearSystemsAndMeasures();
 
         // delete graphicalObjects (currently: ties) that will be recalculated, newly create GraphicalObjects streching over a single StaffEntry
@@ -190,7 +192,7 @@ export abstract class MusicSheetCalculator {
         this.calculateMusicSystems();
 
         // Add some white space at the end of the piece:
-        this.graphicalMusicSheet.MusicPages[0].PositionAndShape.BorderMarginBottom += 9;
+        //this.graphicalMusicSheet.MusicPages[0].PositionAndShape.BorderMarginBottom += 9;
 
         // transform Relative to Absolute Positions
         GraphicalMusicSheet.transformRelativeToAbsolutePosition(this.graphicalMusicSheet);
@@ -228,55 +230,6 @@ export abstract class MusicSheetCalculator {
         throw new Error("abstract, not implemented");
     }
 
-    /** Calculates the relative Positions of all MusicSystems.
-     *
-     */
-    protected calculateMusicSystemsRelativePositions(graphicalMusicPage: GraphicalMusicPage): void {
-        // xPosition is always fixed
-        let relativePosition: PointF2D = new PointF2D(this.rules.PageLeftMargin + this.rules.SystemLeftMargin, 0);
-
-        if (EngravingRules.Rules.CompactMode) {
-            relativePosition.y += EngravingRules.Rules.PageTopMarginNarrow;
-        } else {
-            relativePosition.y += EngravingRules.Rules.PageTopMargin;
-        }
-
-        // first System is handled extra
-        const firstMusicSystem: MusicSystem = graphicalMusicPage.MusicSystems[0];
-        if (graphicalMusicPage === graphicalMusicPage.Parent.MusicPages[0]) {
-            if (EngravingRules.Rules.RenderTitle) {
-                relativePosition.y += this.rules.TitleTopDistance + this.rules.SheetTitleHeight +
-                    this.rules.TitleBottomDistance;
-            }
-        } else {
-            if (EngravingRules.Rules.RenderTitle) {
-                relativePosition.y += this.rules.PageTopMargin + this.rules.TitleTopDistance;
-            } else {
-                relativePosition.y = this.rules.PageTopMargin;
-            }
-        }
-
-        firstMusicSystem.PositionAndShape.RelativePosition = relativePosition;
-
-        for (let i: number = 1; i < graphicalMusicPage.MusicSystems.length; i++) {
-            const musicSystem: MusicSystem = graphicalMusicPage.MusicSystems[i];
-            relativePosition = new PointF2D(this.rules.PageLeftMargin + this.rules.SystemLeftMargin, 0);
-
-            // find optimum distance between Systems
-            const previousSystem: MusicSystem = graphicalMusicPage.MusicSystems[i - 1];
-            const lastPreviousStaffLine: StaffLine = previousSystem.StaffLines[previousSystem.StaffLines.length - 1];
-            const distance: number = (lastPreviousStaffLine.SkyBottomLineCalculator.getBottomLineMax() - this.rules.StaffHeight) +
-                Math.abs(musicSystem.StaffLines[0].SkyBottomLineCalculator.getSkyLineMin()) +
-                this.rules.MinimumAllowedDistanceBetweenSystems;
-
-            relativePosition.y = previousSystem.PositionAndShape.RelativePosition.y +
-                lastPreviousStaffLine.PositionAndShape.RelativePosition.y +
-                this.rules.StaffHeight + Math.max(this.rules.SystemDistance, distance);
-
-            musicSystem.PositionAndShape.RelativePosition = relativePosition;
-        }
-    }
-
     /**
      * Calculates the x layout of the staff entries within the staff measures belonging to one source measure.
      * All staff entries are x-aligned throughout all the measures.
@@ -287,47 +240,6 @@ export abstract class MusicSheetCalculator {
     }
 
     /**
-     * This method checks the distances between two System's StaffLines and if needed, shifts the lower down.
-     * @param musicSystem
-     */
-    protected optimizeDistanceBetweenStaffLines(musicSystem: MusicSystem): void {
-        musicSystem.PositionAndShape.calculateAbsolutePositionsRecursive(0, 0);
-
-        // don't perform any y-spacing in case of a StaffEntryLink (in both StaffLines)
-        if (!musicSystem.checkStaffEntriesForStaffEntryLink()) {
-            for (let i: number = 0; i < musicSystem.StaffLines.length - 1; i++) {
-                const upperBottomLine: number = musicSystem.StaffLines[i].SkyBottomLineCalculator.getBottomLineMax();
-                // TODO: Lower skyline should add to offset when there are items above the line. Currently no test
-                // file available
-                // const lowerSkyLine: number = Math.min(...musicSystem.StaffLines[i + 1].SkyLine);
-                if (Math.abs(upperBottomLine) > this.rules.MinimumStaffLineDistance) {
-                    // Remove staffheight from offset. As it results in huge distances
-                    const offset: number = Math.abs(upperBottomLine) + this.rules.MinimumStaffLineDistance - this.rules.StaffHeight;
-                    this.updateStaffLinesRelativePosition(musicSystem, i + 1, offset);
-                }
-            }
-        }
-    }
-
-    /**
-     * This method updates the System's StaffLine's RelativePosition (starting from the given index).
-     * @param musicSystem
-     * @param index
-     * @param value
-     */
-    protected updateStaffLinesRelativePosition(musicSystem: MusicSystem, index: number, value: number): void {
-        for (let i: number = index; i < musicSystem.StaffLines.length; i++) {
-            musicSystem.StaffLines[i].PositionAndShape.RelativePosition.y += value;
-        }
-
-        musicSystem.PositionAndShape.BorderBottom += value;
-    }
-
-    protected calculateSystemYLayout(): void {
-        throw new Error("abstract, not implemented");
-    }
-
-    /**
      * Called for every source measure when generating the list of staff measures for it.
      */
     protected initGraphicalMeasuresCreation(): void {
@@ -456,7 +368,7 @@ export abstract class MusicSheetCalculator {
         let end: number = relativeX - graphicalLabel.PositionAndShape.BorderLeft + graphicalLabel.PositionAndShape.BorderMarginRight;
 
         // take into account the InstrumentNameLabel's at the beginning of the first MusicSystem
-        if (staffLine === musicSystem.StaffLines[0] && musicSystem === musicSystem.Parent.MusicSystems[0]) {
+        if (staffLine === musicSystem.StaffLines[0] && musicSystem === this.musicSystems[0]) {
             start -= staffLine.PositionAndShape.RelativePosition.x;
             end -= staffLine.PositionAndShape.RelativePosition.x;
         }
@@ -716,7 +628,7 @@ export abstract class MusicSheetCalculator {
         // build the MusicSystems
         const musicSystemBuilder: MusicSystemBuilder = new MusicSystemBuilder();
         musicSystemBuilder.initialize(this.graphicalMusicSheet, visibleMeasureList, numberOfStaffLines);
-        musicSystemBuilder.buildMusicSystems();
+        this.musicSystems = musicSystemBuilder.buildMusicSystems();
 
         this.formatMeasures();
 
@@ -742,12 +654,9 @@ export abstract class MusicSheetCalculator {
 
         // calculate MeasureNumbers
         if (EngravingRules.Rules.RenderMeasureNumbers) {
-            for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
-                const graphicalMusicPage: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
-                for (let idx2: number = 0, len2: number = graphicalMusicPage.MusicSystems.length; idx2 < len2; ++idx2) {
-                    const musicSystem: MusicSystem = graphicalMusicPage.MusicSystems[idx2];
-                    this.calculateMeasureNumberPlacement(musicSystem);
-                }
+            for (let idx: number = 0, len: number = this.musicSystems.length; idx < len; ++idx) {
+                const musicSystem: MusicSystem = this.musicSystems[idx];
+                this.calculateMeasureNumberPlacement(musicSystem);
             }
         }
         // calculate Slurs
@@ -785,19 +694,16 @@ export abstract class MusicSheetCalculator {
 
         // update all StaffLine's Borders
         // create temporary Object, just to call the methods (in order to avoid declaring them static)
-        for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
-            const graphicalMusicPage: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
-            for (let idx2: number = 0, len2: number = graphicalMusicPage.MusicSystems.length; idx2 < len2; ++idx2) {
-                const musicSystem: MusicSystem = graphicalMusicPage.MusicSystems[idx2];
-                for (let idx3: number = 0, len3: number = musicSystem.StaffLines.length; idx3 < len3; ++idx3) {
-                    const staffLine: StaffLine = musicSystem.StaffLines[idx3];
-                    this.updateStaffLineBorders(staffLine);
-                }
+        for (let idx2: number = 0, len2: number = this.musicSystems.length; idx2 < len2; ++idx2) {
+            const musicSystem: MusicSystem = this.musicSystems[idx2];
+            for (let idx3: number = 0, len3: number = musicSystem.StaffLines.length; idx3 < len3; ++idx3) {
+                const staffLine: StaffLine = musicSystem.StaffLines[idx3];
+                this.updateStaffLineBorders(staffLine);
             }
         }
 
-        // Y-spacing
-        this.calculateSystemYLayout();
+        // calculate Y-spacing -> MusicPages are created here
+        musicSystemBuilder.calculateSystemYLayout();
         // calculate Comments for each Staffline
         this.calculateComments();
         // calculate marked Areas for Systems
@@ -809,16 +715,17 @@ export abstract class MusicSheetCalculator {
         for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
             const graphicalMusicPage: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
             for (let idx2: number = 0, len2: number = graphicalMusicPage.MusicSystems.length; idx2 < len2; ++idx2) {
+                const isFirstSystem: boolean = idx === 0 && idx2 === 0;
                 const musicSystem: MusicSystem = graphicalMusicPage.MusicSystems[idx2];
                 musicSystem.setMusicSystemLabelsYPosition();
                 if (!this.leadSheet) {
                     musicSystem.setYPositionsToVerticalLineObjectsAndCreateLines(this.rules);
-                    musicSystem.createSystemLeftLine(this.rules.SystemThinLineWidth, this.rules.SystemLabelsRightMargin);
+                    musicSystem.createSystemLeftLine(this.rules.SystemThinLineWidth, this.rules.SystemLabelsRightMargin, isFirstSystem);
                     musicSystem.createInstrumentBrackets(this.graphicalMusicSheet.ParentMusicSheet.Instruments, this.rules.StaffHeight);
                     musicSystem.createGroupBrackets(this.graphicalMusicSheet.ParentMusicSheet.InstrumentalGroups, this.rules.StaffHeight, 0);
                     musicSystem.alignBeginInstructions();
                 } else if (musicSystem === musicSystem.Parent.MusicSystems[0]) {
-                    musicSystem.createSystemLeftLine(this.rules.SystemThinLineWidth, this.rules.SystemLabelsRightMargin);
+                    musicSystem.createSystemLeftLine(this.rules.SystemThinLineWidth, this.rules.SystemLabelsRightMargin, isFirstSystem);
                 }
                 musicSystem.calculateBorders(this.rules);
             }
@@ -863,22 +770,20 @@ export abstract class MusicSheetCalculator {
     }
 
     protected calculateChordSymbols(): void {
-        for (const musicPage of this.graphicalMusicSheet.MusicPages) {
-            for (const musicSystem of musicPage.MusicSystems) {
-                for (const staffLine of musicSystem.StaffLines) {
-                    const sbc: SkyBottomLineCalculator = staffLine.SkyBottomLineCalculator;
-                    for (const measure of staffLine.Measures) {
-                        for (const staffEntry of measure.staffEntries) {
-                            if (!staffEntry.graphicalChordContainers || staffEntry.graphicalChordContainers.length === 0) {
-                                continue;
-                            }
-                            for (const graphicalChordContainer of staffEntry.graphicalChordContainers) {
-                                const sps: BoundingBox = staffEntry.PositionAndShape;
-                                const gps: BoundingBox = graphicalChordContainer.PositionAndShape;
-                                const start: number = gps.BorderMarginLeft + sps.AbsolutePosition.x;
-                                const end: number = gps.BorderMarginRight + sps.AbsolutePosition.x;
-                                sbc.updateSkyLineInRange(start, end, sps.BorderMarginTop);
-                            }
+        for (const musicSystem of this.musicSystems) {
+            for (const staffLine of musicSystem.StaffLines) {
+                const sbc: SkyBottomLineCalculator = staffLine.SkyBottomLineCalculator;
+                for (const measure of staffLine.Measures) {
+                    for (const staffEntry of measure.staffEntries) {
+                        if (!staffEntry.graphicalChordContainers || staffEntry.graphicalChordContainers.length === 0) {
+                            continue;
+                        }
+                        for (const graphicalChordContainer of staffEntry.graphicalChordContainers) {
+                            const sps: BoundingBox = staffEntry.PositionAndShape;
+                            const gps: BoundingBox = graphicalChordContainer.PositionAndShape;
+                            const start: number = gps.BorderMarginLeft + sps.AbsolutePosition.x;
+                            const end: number = gps.BorderMarginRight + sps.AbsolutePosition.x;
+                            sbc.updateSkyLineInRange(start, end, sps.BorderMarginTop);
                         }
                     }
                 }
@@ -1421,7 +1326,7 @@ export abstract class MusicSheetCalculator {
                 // check if MusicSystem is first MusicSystem
                 if (staffLine.Measures[0].staffEntries.length > 0 &&
                     Math.abs(relative.x - staffLine.Measures[0].staffEntries[0].PositionAndShape.RelativePosition.x) === 0 &&
-                    staffLine.ParentMusicSystem === staffLine.ParentMusicSystem.Parent.MusicSystems[0]) {
+                    staffLine.ParentMusicSystem === this.musicSystems[0]) {
                     const firstInstructionEntry: GraphicalStaffEntry = staffLine.Measures[0].FirstInstructionStaffEntry;
                     if (firstInstructionEntry) {
                         const lastInstruction: AbstractGraphicalInstruction = firstInstructionEntry.GraphicalInstructions.last();
@@ -1678,21 +1583,18 @@ export abstract class MusicSheetCalculator {
     }
 
     protected checkMeasuresForWholeRestNotes(): void {
-        for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
-            const musicPage: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
-            for (let idx2: number = 0, len2: number = musicPage.MusicSystems.length; idx2 < len2; ++idx2) {
-                const musicSystem: MusicSystem = musicPage.MusicSystems[idx2];
-                for (let idx3: number = 0, len3: number = musicSystem.StaffLines.length; idx3 < len3; ++idx3) {
-                    const staffLine: StaffLine = musicSystem.StaffLines[idx3];
-                    for (let idx4: number = 0, len4: number = staffLine.Measures.length; idx4 < len4; ++idx4) {
-                        const measure: GraphicalMeasure = staffLine.Measures[idx4];
-                        if (measure.staffEntries.length === 1) {
-                            const gse: GraphicalStaffEntry = measure.staffEntries[0];
-                            if (gse.graphicalVoiceEntries.length > 0 && gse.graphicalVoiceEntries[0].notes.length === 1) {
-                                const graphicalNote: GraphicalNote = gse.graphicalVoiceEntries[0].notes[0];
-                                if (graphicalNote.sourceNote.Pitch === undefined && (new Fraction(1, 2)).lt(graphicalNote.sourceNote.Length)) {
-                                    this.layoutMeasureWithWholeRest(graphicalNote, gse, measure);
-                                }
+        for (let idx2: number = 0, len2: number = this.musicSystems.length; idx2 < len2; ++idx2) {
+            const musicSystem: MusicSystem = this.musicSystems[idx2];
+            for (let idx3: number = 0, len3: number = musicSystem.StaffLines.length; idx3 < len3; ++idx3) {
+                const staffLine: StaffLine = musicSystem.StaffLines[idx3];
+                for (let idx4: number = 0, len4: number = staffLine.Measures.length; idx4 < len4; ++idx4) {
+                    const measure: GraphicalMeasure = staffLine.Measures[idx4];
+                    if (measure.staffEntries.length === 1) {
+                        const gse: GraphicalStaffEntry = measure.staffEntries[0];
+                        if (gse.graphicalVoiceEntries.length > 0 && gse.graphicalVoiceEntries[0].notes.length === 1) {
+                            const graphicalNote: GraphicalNote = gse.graphicalVoiceEntries[0].notes[0];
+                            if (graphicalNote.sourceNote.Pitch === undefined && (new Fraction(1, 2)).lt(graphicalNote.sourceNote.Length)) {
+                                this.layoutMeasureWithWholeRest(graphicalNote, gse, measure);
                             }
                         }
                     }
@@ -2165,11 +2067,9 @@ export abstract class MusicSheetCalculator {
     }
 
     private calculateSkyBottomLines(): void {
-        for (const graphicalMusicPage of this.graphicalMusicSheet.MusicPages) {
-            for (const musicSystem of graphicalMusicPage.MusicSystems) {
-                for (const staffLine of musicSystem.StaffLines) {
-                    staffLine.SkyBottomLineCalculator.calculateLines();
-                }
+        for (const musicSystem of this.musicSystems) {
+            for (const staffLine of musicSystem.StaffLines) {
+                staffLine.SkyBottomLineCalculator.calculateLines();
             }
         }
     }
@@ -2182,18 +2082,15 @@ export abstract class MusicSheetCalculator {
     }
 
     private calculateBeams(): void {
-        for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
-            const musicPage: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
-            for (let idx2: number = 0, len2: number = musicPage.MusicSystems.length; idx2 < len2; ++idx2) {
-                const musicSystem: MusicSystem = musicPage.MusicSystems[idx2];
-                for (let idx3: number = 0, len3: number = musicSystem.StaffLines.length; idx3 < len3; ++idx3) {
-                    const staffLine: StaffLine = musicSystem.StaffLines[idx3];
-                    for (let idx4: number = 0, len4: number = staffLine.Measures.length; idx4 < len4; ++idx4) {
-                        const measure: GraphicalMeasure = staffLine.Measures[idx4];
-                        for (let idx5: number = 0, len5: number = measure.staffEntries.length; idx5 < len5; ++idx5) {
-                            const staffEntry: GraphicalStaffEntry = measure.staffEntries[idx5];
-                            this.layoutBeams(staffEntry);
-                        }
+        for (let idx2: number = 0, len2: number = this.musicSystems.length; idx2 < len2; ++idx2) {
+            const musicSystem: MusicSystem = this.musicSystems[idx2];
+            for (let idx3: number = 0, len3: number = musicSystem.StaffLines.length; idx3 < len3; ++idx3) {
+                const staffLine: StaffLine = musicSystem.StaffLines[idx3];
+                for (let idx4: number = 0, len4: number = staffLine.Measures.length; idx4 < len4; ++idx4) {
+                    const measure: GraphicalMeasure = staffLine.Measures[idx4];
+                    for (let idx5: number = 0, len5: number = measure.staffEntries.length; idx5 < len5; ++idx5) {
+                        const staffEntry: GraphicalStaffEntry = measure.staffEntries[idx5];
+                        this.layoutBeams(staffEntry);
                     }
                 }
             }
@@ -2201,21 +2098,18 @@ export abstract class MusicSheetCalculator {
     }
 
     private calculateStaffEntryArticulationMarks(): void {
-        for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
-            const page: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
-            for (let idx2: number = 0, len2: number = page.MusicSystems.length; idx2 < len2; ++idx2) {
-                const system: MusicSystem = page.MusicSystems[idx2];
-                for (let idx3: number = 0, len3: number = system.StaffLines.length; idx3 < len3; ++idx3) {
-                    const line: StaffLine = system.StaffLines[idx3];
-                    for (let idx4: number = 0, len4: number = line.Measures.length; idx4 < len4; ++idx4) {
-                        const measure: GraphicalMeasure = line.Measures[idx4];
-                        for (let idx5: number = 0, len5: number = measure.staffEntries.length; idx5 < len5; ++idx5) {
-                            const graphicalStaffEntry: GraphicalStaffEntry = measure.staffEntries[idx5];
-                            for (let idx6: number = 0, len6: number = graphicalStaffEntry.sourceStaffEntry.VoiceEntries.length; idx6 < len6; ++idx6) {
-                                const voiceEntry: VoiceEntry = graphicalStaffEntry.sourceStaffEntry.VoiceEntries[idx6];
-                                if (voiceEntry.Articulations.length > 0) {
-                                    this.layoutArticulationMarks(voiceEntry.Articulations, voiceEntry, graphicalStaffEntry);
-                                }
+        for (let idx2: number = 0, len2: number = this.musicSystems.length; idx2 < len2; ++idx2) {
+            const system: MusicSystem = this.musicSystems[idx2];
+            for (let idx3: number = 0, len3: number = system.StaffLines.length; idx3 < len3; ++idx3) {
+                const line: StaffLine = system.StaffLines[idx3];
+                for (let idx4: number = 0, len4: number = line.Measures.length; idx4 < len4; ++idx4) {
+                    const measure: GraphicalMeasure = line.Measures[idx4];
+                    for (let idx5: number = 0, len5: number = measure.staffEntries.length; idx5 < len5; ++idx5) {
+                        const graphicalStaffEntry: GraphicalStaffEntry = measure.staffEntries[idx5];
+                        for (let idx6: number = 0, len6: number = graphicalStaffEntry.sourceStaffEntry.VoiceEntries.length; idx6 < len6; ++idx6) {
+                            const voiceEntry: VoiceEntry = graphicalStaffEntry.sourceStaffEntry.VoiceEntries[idx6];
+                            if (voiceEntry.Articulations.length > 0) {
+                                this.layoutArticulationMarks(voiceEntry.Articulations, voiceEntry, graphicalStaffEntry);
                             }
                         }
                     }
@@ -2225,26 +2119,23 @@ export abstract class MusicSheetCalculator {
     }
 
     private calculateOrnaments(): void {
-        for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
-            const page: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
-            for (let idx2: number = 0, len2: number = page.MusicSystems.length; idx2 < len2; ++idx2) {
-                const system: MusicSystem = page.MusicSystems[idx2];
-                for (let idx3: number = 0, len3: number = system.StaffLines.length; idx3 < len3; ++idx3) {
-                    const line: StaffLine = system.StaffLines[idx3];
-                    for (let idx4: number = 0, len4: number = line.Measures.length; idx4 < len4; ++idx4) {
-                        const measure: GraphicalMeasure = line.Measures[idx4];
-                        for (let idx5: number = 0, len5: number = measure.staffEntries.length; idx5 < len5; ++idx5) {
-                            const graphicalStaffEntry: GraphicalStaffEntry = measure.staffEntries[idx5];
-                            for (let idx6: number = 0, len6: number = graphicalStaffEntry.sourceStaffEntry.VoiceEntries.length; idx6 < len6; ++idx6) {
-                                const voiceEntry: VoiceEntry = graphicalStaffEntry.sourceStaffEntry.VoiceEntries[idx6];
-                                if (voiceEntry.OrnamentContainer !== undefined) {
-                                    if (voiceEntry.hasTie() && !graphicalStaffEntry.relInMeasureTimestamp.Equals(voiceEntry.Timestamp)) {
-                                        continue;
-                                    }
-                                    this.layoutOrnament(voiceEntry.OrnamentContainer, voiceEntry, graphicalStaffEntry);
-                                    if (!(this.staffEntriesWithOrnaments.indexOf(graphicalStaffEntry) !== -1)) {
-                                        this.staffEntriesWithOrnaments.push(graphicalStaffEntry);
-                                    }
+        for (let idx2: number = 0, len2: number = this.musicSystems.length; idx2 < len2; ++idx2) {
+            const system: MusicSystem = this.musicSystems[idx2];
+            for (let idx3: number = 0, len3: number = system.StaffLines.length; idx3 < len3; ++idx3) {
+                const line: StaffLine = system.StaffLines[idx3];
+                for (let idx4: number = 0, len4: number = line.Measures.length; idx4 < len4; ++idx4) {
+                    const measure: GraphicalMeasure = line.Measures[idx4];
+                    for (let idx5: number = 0, len5: number = measure.staffEntries.length; idx5 < len5; ++idx5) {
+                        const graphicalStaffEntry: GraphicalStaffEntry = measure.staffEntries[idx5];
+                        for (let idx6: number = 0, len6: number = graphicalStaffEntry.sourceStaffEntry.VoiceEntries.length; idx6 < len6; ++idx6) {
+                            const voiceEntry: VoiceEntry = graphicalStaffEntry.sourceStaffEntry.VoiceEntries[idx6];
+                            if (voiceEntry.OrnamentContainer !== undefined) {
+                                if (voiceEntry.hasTie() && !graphicalStaffEntry.relInMeasureTimestamp.Equals(voiceEntry.Timestamp)) {
+                                    continue;
+                                }
+                                this.layoutOrnament(voiceEntry.OrnamentContainer, voiceEntry, graphicalStaffEntry);
+                                if (!(this.staffEntriesWithOrnaments.indexOf(graphicalStaffEntry) !== -1)) {
+                                    this.staffEntriesWithOrnaments.push(graphicalStaffEntry);
                                 }
                             }
                         }
@@ -2255,18 +2146,15 @@ export abstract class MusicSheetCalculator {
     }
 
     private optimizeRestPlacement(): void {
-        for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
-            const page: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
-            for (let idx2: number = 0, len2: number = page.MusicSystems.length; idx2 < len2; ++idx2) {
-                const system: MusicSystem = page.MusicSystems[idx2];
-                for (let idx3: number = 0, len3: number = system.StaffLines.length; idx3 < len3; ++idx3) {
-                    const line: StaffLine = system.StaffLines[idx3];
-                    for (let idx4: number = 0, len4: number = line.Measures.length; idx4 < len4; ++idx4) {
-                        const measure: GraphicalMeasure = line.Measures[idx4];
-                        for (let idx5: number = 0, len5: number = measure.staffEntries.length; idx5 < len5; ++idx5) {
-                            const graphicalStaffEntry: GraphicalStaffEntry = measure.staffEntries[idx5];
-                            this.optimizeRestNotePlacement(graphicalStaffEntry, measure);
-                        }
+        for (let idx2: number = 0, len2: number = this.musicSystems.length; idx2 < len2; ++idx2) {
+            const system: MusicSystem = this.musicSystems[idx2];
+            for (let idx3: number = 0, len3: number = system.StaffLines.length; idx3 < len3; ++idx3) {
+                const line: StaffLine = system.StaffLines[idx3];
+                for (let idx4: number = 0, len4: number = line.Measures.length; idx4 < len4; ++idx4) {
+                    const measure: GraphicalMeasure = line.Measures[idx4];
+                    for (let idx5: number = 0, len5: number = measure.staffEntries.length; idx5 < len5; ++idx5) {
+                        const graphicalStaffEntry: GraphicalStaffEntry = measure.staffEntries[idx5];
+                        this.optimizeRestNotePlacement(graphicalStaffEntry, measure);
                     }
                 }
             }
@@ -2333,26 +2221,23 @@ export abstract class MusicSheetCalculator {
     }
 
     private calculateTieCurves(): void {
-        for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
-            const graphicalMusicPage: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
-            for (let idx2: number = 0, len2: number = graphicalMusicPage.MusicSystems.length; idx2 < len2; ++idx2) {
-                const musicSystem: MusicSystem = graphicalMusicPage.MusicSystems[idx2];
-                for (let idx3: number = 0, len3: number = musicSystem.StaffLines.length; idx3 < len3; ++idx3) {
-                    const staffLine: StaffLine = musicSystem.StaffLines[idx3];
-                    for (let idx4: number = 0, len5: number = staffLine.Measures.length; idx4 < len5; ++idx4) {
-                        const measure: GraphicalMeasure = staffLine.Measures[idx4];
-                        for (let idx6: number = 0, len6: number = measure.staffEntries.length; idx6 < len6; ++idx6) {
-                            const staffEntry: GraphicalStaffEntry = measure.staffEntries[idx6];
-                            const graphicalTies: GraphicalTie[] = staffEntry.GraphicalTies;
-                            for (let idx7: number = 0, len7: number = graphicalTies.length; idx7 < len7; ++idx7) {
-                                const graphicalTie: GraphicalTie = graphicalTies[idx7];
-                                if (graphicalTie.StartNote !== undefined && graphicalTie.StartNote.parentVoiceEntry.parentStaffEntry === staffEntry) {
-                                    const tieIsAtSystemBreak: boolean = (
-                                        graphicalTie.StartNote.parentVoiceEntry.parentStaffEntry.parentMeasure.ParentStaffLine !==
-                                        graphicalTie.EndNote.parentVoiceEntry.parentStaffEntry.parentMeasure.ParentStaffLine
-                                    );
-                                    this.layoutGraphicalTie(graphicalTie, tieIsAtSystemBreak);
-                                }
+        for (let idx2: number = 0, len2: number = this.musicSystems.length; idx2 < len2; ++idx2) {
+            const musicSystem: MusicSystem = this.musicSystems[idx2];
+            for (let idx3: number = 0, len3: number = musicSystem.StaffLines.length; idx3 < len3; ++idx3) {
+                const staffLine: StaffLine = musicSystem.StaffLines[idx3];
+                for (let idx4: number = 0, len5: number = staffLine.Measures.length; idx4 < len5; ++idx4) {
+                    const measure: GraphicalMeasure = staffLine.Measures[idx4];
+                    for (let idx6: number = 0, len6: number = measure.staffEntries.length; idx6 < len6; ++idx6) {
+                        const staffEntry: GraphicalStaffEntry = measure.staffEntries[idx6];
+                        const graphicalTies: GraphicalTie[] = staffEntry.GraphicalTies;
+                        for (let idx7: number = 0, len7: number = graphicalTies.length; idx7 < len7; ++idx7) {
+                            const graphicalTie: GraphicalTie = graphicalTies[idx7];
+                            if (graphicalTie.StartNote !== undefined && graphicalTie.StartNote.parentVoiceEntry.parentStaffEntry === staffEntry) {
+                                const tieIsAtSystemBreak: boolean = (
+                                    graphicalTie.StartNote.parentVoiceEntry.parentStaffEntry.parentMeasure.ParentStaffLine !==
+                                    graphicalTie.EndNote.parentVoiceEntry.parentStaffEntry.parentMeasure.ParentStaffLine
+                                );
+                                this.layoutGraphicalTie(graphicalTie, tieIsAtSystemBreak);
                             }
                         }
                     }
@@ -2371,28 +2256,22 @@ export abstract class MusicSheetCalculator {
             }
         }
         // first calc lyrics text positions
-        for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
-            const graphicalMusicPage: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
-            for (let idx2: number = 0, len2: number = graphicalMusicPage.MusicSystems.length; idx2 < len2; ++idx2) {
-                const musicSystem: MusicSystem = graphicalMusicPage.MusicSystems[idx2];
-                for (let idx3: number = 0, len3: number = musicSystem.StaffLines.length; idx3 < len3; ++idx3) {
-                    const staffLine: StaffLine = musicSystem.StaffLines[idx3];
-                    const lyricsStaffEntries: GraphicalStaffEntry[] =
-                        this.calculateSingleStaffLineLyricsPosition(staffLine, staffLine.ParentStaff.ParentInstrument.LyricVersesNumbers);
-                    lyricStaffEntriesDict.setValue(staffLine, lyricsStaffEntries);
-                    this.calculateLyricsExtendsAndDashes(lyricStaffEntriesDict.getValue(staffLine));
-                }
+        for (let idx2: number = 0, len2: number = this.musicSystems.length; idx2 < len2; ++idx2) {
+            const musicSystem: MusicSystem = this.musicSystems[idx2];
+            for (let idx3: number = 0, len3: number = musicSystem.StaffLines.length; idx3 < len3; ++idx3) {
+                const staffLine: StaffLine = musicSystem.StaffLines[idx3];
+                const lyricsStaffEntries: GraphicalStaffEntry[] =
+                    this.calculateSingleStaffLineLyricsPosition(staffLine, staffLine.ParentStaff.ParentInstrument.LyricVersesNumbers);
+                lyricStaffEntriesDict.setValue(staffLine, lyricsStaffEntries);
+                this.calculateLyricsExtendsAndDashes(lyricStaffEntriesDict.getValue(staffLine));
             }
         }
         // then fill in the lyric word dashes and lyrics extends/underscores
-        for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
-            const graphicalMusicPage: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
-            for (let idx2: number = 0, len2: number = graphicalMusicPage.MusicSystems.length; idx2 < len2; ++idx2) {
-                const musicSystem: MusicSystem = graphicalMusicPage.MusicSystems[idx2];
-                for (let idx3: number = 0, len3: number = musicSystem.StaffLines.length; idx3 < len3; ++idx3) {
-                    const staffLine: StaffLine = musicSystem.StaffLines[idx3];
-                    this.calculateLyricsExtendsAndDashes(lyricStaffEntriesDict.getValue(staffLine));
-                }
+        for (let idx2: number = 0, len2: number = this.musicSystems.length; idx2 < len2; ++idx2) {
+            const musicSystem: MusicSystem = this.musicSystems[idx2];
+            for (let idx3: number = 0, len3: number = musicSystem.StaffLines.length; idx3 < len3; ++idx3) {
+                const staffLine: StaffLine = musicSystem.StaffLines[idx3];
+                this.calculateLyricsExtendsAndDashes(lyricStaffEntriesDict.getValue(staffLine));
             }
         }
     }

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

@@ -464,7 +464,7 @@ export abstract class MusicSheetDrawer {
         this.graphicalMusicSheet.LeadSheet = value;
     }
 
-    private drawPage(page: GraphicalMusicPage): void {
+    protected drawPage(page: GraphicalMusicPage): void {
         if (!this.isVisible(page.PositionAndShape)) {
             return;
         }

+ 68 - 66
src/MusicalScore/Graphical/MusicSystem.ts

@@ -43,15 +43,12 @@ export abstract class MusicSystem extends GraphicalObject {
     protected graphicalMarkedAreas: GraphicalMarkedArea[] = [];
     protected graphicalComments: GraphicalComment[] = [];
     protected systemLines: SystemLine[] = [];
-    protected rules: EngravingRules;
 
-    constructor(parent: GraphicalMusicPage, id: number) {
+    constructor(id: number) {
         super();
-        this.parent = parent;
         this.id = id;
-        this.boundingBox = new BoundingBox(this, parent.PositionAndShape);
+        this.boundingBox = new BoundingBox(this);
         this.maxLabelLength = 0.0;
-        this.rules = this.parent.Parent.ParentMusicSheet.Rules;
     }
 
     public get Parent(): GraphicalMusicPage {
@@ -59,7 +56,16 @@ export abstract class MusicSystem extends GraphicalObject {
     }
 
     public set Parent(value: GraphicalMusicPage) {
+        // remove from old page
+        if (this.parent !== undefined) {
+            const index: number = this.parent.MusicSystems.indexOf(this, 0);
+            if (index > -1) {
+                this.parent.MusicSystems.splice(index, 1);
+            }
+        }
+
         this.parent = value;
+        this.boundingBox.Parent = value.PositionAndShape;
     }
 
     public get NextSystem(): MusicSystem {
@@ -116,9 +122,9 @@ export abstract class MusicSystem extends GraphicalObject {
      * @param lineWidth
      * @param systemLabelsRightMargin
      */
-    public createSystemLeftLine(lineWidth: number, systemLabelsRightMargin: number): void {
+    public createSystemLeftLine(lineWidth: number, systemLabelsRightMargin: number, isFirstSystem: boolean): void {
         let xPosition: number = -lineWidth / 2;
-        if (this === this.parent.MusicSystems[0] && this.parent === this.parent.Parent.MusicPages[0]) {
+        if (isFirstSystem) {
             xPosition = this.maxLabelLength + systemLabelsRightMargin - lineWidth / 2;
         }
         const top: GraphicalMeasure = this.staffLines[0].Measures[0];
@@ -279,79 +285,75 @@ export abstract class MusicSystem extends GraphicalObject {
      * @param systemLabelsRightMargin
      * @param labelMarginBorderFactor
      */
-    public createMusicSystemLabel(instrumentLabelTextHeight: number, systemLabelsRightMargin: number, labelMarginBorderFactor: number): void {
-        if (this.parent === this.parent.Parent.MusicPages[0]) {
-            const instruments: Instrument[] = this.parent.Parent.ParentMusicSheet.getVisibleInstruments();
-            for (let idx: number = 0, len: number = instruments.length; idx < len; ++idx) {
-                const instrument: Instrument = instruments[idx];
-                let instrNameLabel: Label;
-                if (this !== this.parent.MusicSystems[0]) {
-                    if (!EngravingRules.Rules.RenderPartAbbreviations
-                        // don't render part abbreviations if there's only one instrument/part (could be an option in the future)
-                        || this.Parent.Parent.ParentMusicSheet.Instruments.length === 1
-                        || !instrument.PartAbbreviation
-                        || instrument.PartAbbreviation === "") {
-                        return;
-                    }
-                    const labelText: string = instrument.PartAbbreviation;
-                    // const labelText: string = instrument.NameLabel.text[0] + ".";
-                    instrNameLabel = new Label(labelText, instrument.NameLabel.textAlignment, instrument.NameLabel.font);
-                } else {
-                    instrNameLabel = instrument.NameLabel;
-                    if (!EngravingRules.Rules.RenderPartNames) {
-                        instrNameLabel = new Label("", instrument.NameLabel.textAlignment, instrument.NameLabel.font);
-                        systemLabelsRightMargin = 0; // might affect lyricist/tempo placement. but without this there's still some extra x-spacing.
-                    }
+    public createMusicSystemLabel(  instrumentLabelTextHeight: number, systemLabelsRightMargin: number,
+                                    labelMarginBorderFactor: number, isFirstSystem: boolean = false): void {
+        for (let idx: number = 0, len: number = this.staffLines.length; idx < len; ++idx) {
+            const instrument: Instrument = this.staffLines[idx].ParentStaff.ParentInstrument;
+            let instrNameLabel: Label;
+            if (isFirstSystem) {
+                instrNameLabel = instrument.NameLabel;
+                if (!EngravingRules.Rules.RenderPartNames) {
+                    instrNameLabel = new Label("", instrument.NameLabel.textAlignment, instrument.NameLabel.font);
+                    systemLabelsRightMargin = 0; // might affect lyricist/tempo placement. but without this there's still some extra x-spacing.
+                }
+            } else {
+                if (!EngravingRules.Rules.RenderPartAbbreviations
+                    // don't render part abbreviations if there's only one instrument/part (could be an option in the future)
+                    || this.staffLines.length === 1
+                    || !instrument.PartAbbreviation
+                    || instrument.PartAbbreviation === "") {
+                    return;
                 }
-                const graphicalLabel: GraphicalLabel = new GraphicalLabel(
-                    instrNameLabel, instrumentLabelTextHeight, TextAlignmentEnum.LeftCenter, this.boundingBox
-                );
-                graphicalLabel.setLabelPositionAndShapeBorders();
-                this.labels.setValue(instrument, graphicalLabel);
-                // X-Position will be 0 (Label starts at the same PointF_2D with MusicSystem)
-                // Y-Position will be calculated after the y-Spacing
-                // graphicalLabel.PositionAndShape.RelativePosition = new PointF2D(0.0, 0.0);
+                const labelText: string = instrument.PartAbbreviation;
+                // const labelText: string = instrument.NameLabel.text[0] + ".";
+                instrNameLabel = new Label(labelText, instrument.NameLabel.textAlignment, instrument.NameLabel.font);
             }
+            const graphicalLabel: GraphicalLabel = new GraphicalLabel(
+                instrNameLabel, instrumentLabelTextHeight, TextAlignmentEnum.LeftCenter, this.boundingBox
+            );
+            graphicalLabel.setLabelPositionAndShapeBorders();
+            this.labels.setValue(instrument, graphicalLabel);
+            // X-Position will be 0 (Label starts at the same PointF_2D with MusicSystem)
+            // Y-Position will be calculated after the y-Spacing
+            // graphicalLabel.PositionAndShape.RelativePosition = new PointF2D(0.0, 0.0);
+        }
 
-            // calculate maxLabelLength (needed for X-Spacing)
-            this.maxLabelLength = 0.0;
-            const labels: GraphicalLabel[] = this.labels.values();
-            for (let idx: number = 0, len: number = labels.length; idx < len; ++idx) {
-                const label: GraphicalLabel = labels[idx];
-                if (label.PositionAndShape.Size.width > this.maxLabelLength) {
-                    this.maxLabelLength = label.PositionAndShape.Size.width;
-                }
+        // calculate maxLabelLength (needed for X-Spacing)
+        this.maxLabelLength = 0.0;
+        const labels: GraphicalLabel[] = this.labels.values();
+        for (let idx: number = 0, len: number = labels.length; idx < len; ++idx) {
+            const label: GraphicalLabel = labels[idx];
+            if (label.PositionAndShape.Size.width > this.maxLabelLength) {
+                this.maxLabelLength = label.PositionAndShape.Size.width;
             }
-            this.updateMusicSystemStaffLineXPosition(systemLabelsRightMargin);
         }
+        this.updateMusicSystemStaffLineXPosition(systemLabelsRightMargin);
     }
 
     /**
      * Set the Y-Positions for the MusicSystem's Labels.
      */
     public setMusicSystemLabelsYPosition(): void {
-        if (this.parent === this.parent.Parent.MusicPages[0]) {
-            this.labels.forEach((key: Instrument, value: GraphicalLabel): void => {
-                let ypositionSum: number = 0;
-                let staffCounter: number = 0;
-                for (let i: number = 0; i < this.staffLines.length; i++) {
-                    if (this.staffLines[i].ParentStaff.ParentInstrument === key) {
-                        for (let j: number = i; j < this.staffLines.length; j++) {
-                            const staffLine: StaffLine = this.staffLines[j];
-                            if (staffLine.ParentStaff.ParentInstrument !== key) {
-                                break;
-                            }
-                            ypositionSum += staffLine.PositionAndShape.RelativePosition.y;
-                            staffCounter++;
+        this.labels.forEach((key: Instrument, value: GraphicalLabel): void => {
+            let ypositionSum: number = 0;
+            let staffCounter: number = 0;
+            for (let i: number = 0; i < this.staffLines.length; i++) {
+                if (this.staffLines[i].ParentStaff.ParentInstrument === key) {
+                    for (let j: number = i; j < this.staffLines.length; j++) {
+                        const staffLine: StaffLine = this.staffLines[j];
+                        if (staffLine.ParentStaff.ParentInstrument !== key) {
+                            break;
                         }
-                        break;
+                        ypositionSum += staffLine.PositionAndShape.RelativePosition.y;
+                        staffCounter++;
                     }
+                    break;
                 }
-                if (staffCounter > 0) {
-                    value.PositionAndShape.RelativePosition = new PointF2D(0.0, ypositionSum / staffCounter + 2.0);
-                }
-            });
-        }
+            }
+            if (staffCounter > 0) {
+                value.PositionAndShape.RelativePosition = new PointF2D(0.0, ypositionSum / staffCounter + 2.0);
+            }
+        });
     }
 
     /**

+ 150 - 54
src/MusicalScore/Graphical/MusicSystemBuilder.ts

@@ -24,12 +24,11 @@ import {SystemLinePosition} from "./SystemLinePosition";
 export class MusicSystemBuilder {
     private measureList: GraphicalMeasure[][];
     private graphicalMusicSheet: GraphicalMusicSheet;
-    private currentMusicPage: GraphicalMusicPage;
-    private currentPageHeight: number;
     private currentSystemParams: SystemBuildParameters;
     private numberOfVisibleStaffLines: number;
     private rules: EngravingRules;
     private measureListIndex: number;
+    private musicSystems: MusicSystem[] = [];
 
     /**
      * Does the mapping from the currently visible staves to the global staff-list of the music sheet.
@@ -47,8 +46,6 @@ export class MusicSystemBuilder {
         this.graphicalMusicSheet = graphicalMusicSheet;
         this.rules = this.graphicalMusicSheet.ParentMusicSheet.rules;
         this.measureList = measureList;
-        this.currentMusicPage = this.createMusicPage();
-        this.currentPageHeight = 0.0;
         this.numberOfVisibleStaffLines = numberOfStaffLines;
         this.activeRhythm = new Array(this.numberOfVisibleStaffLines);
         this.activeKeys = new Array(this.numberOfVisibleStaffLines);
@@ -56,7 +53,7 @@ export class MusicSystemBuilder {
         this.initializeActiveInstructions(this.measureList[0]);
     }
 
-    public buildMusicSystems(): void {
+    public buildMusicSystems(): MusicSystem[] {
         let previousMeasureEndsSystem: boolean = false;
         const systemMaxWidth: number = this.getFullPageSystemWidth();
         this.measureListIndex = 0;
@@ -64,9 +61,6 @@ export class MusicSystemBuilder {
 
         // the first System - create also its Labels
         this.currentSystemParams.currentSystem = this.initMusicSystem();
-        this.layoutSystemStaves();
-        this.addSystemLabels();
-        this.currentPageHeight += this.currentSystemParams.currentSystem.PositionAndShape.RelativePosition.y;
 
         let numberOfMeasures: number = 0;
         for (let idx: number = 0, len: number = this.measureList.length; idx < len; ++idx) {
@@ -134,6 +128,20 @@ export class MusicSystemBuilder {
             previousMeasureEndsSystem = sourceMeasureEndsSystem;
         }
         this.finalizeCurrentAndCreateNewSystem(this.measureList[this.measureList.length - 1], true);
+        return this.musicSystems;
+    }
+
+    /**
+     * calculates the y positions of the staff lines within a system and
+     * furthermore the y positions of the systems themselves.
+     */
+    public calculateSystemYLayout(): void {
+        for (const musicSystem of this.musicSystems) {
+            this.optimizeDistanceBetweenStaffLines(musicSystem);
+        }
+
+        // set y positions of systems using the previous system and a fixed distance.
+        this.calculateMusicSystemsRelativePositions();
     }
 
     /**
@@ -168,24 +176,9 @@ export class MusicSystemBuilder {
             this.checkAndCreateExtraInstructionMeasure(measures);
         }
         this.stretchMusicSystem(isPartEndingSystem);
-        if (this.currentPageHeight + this.currentSystemParams.currentSystem.PositionAndShape.Size.height + this.rules.SystemDistance <= this.rules.PageHeight) {
-            this.currentPageHeight += this.currentSystemParams.currentSystem.PositionAndShape.Size.height + this.rules.SystemDistance;
-            if (
-                this.currentPageHeight + this.currentSystemParams.currentSystem.PositionAndShape.Size.height
-                + this.rules.SystemDistance >= this.rules.PageHeight
-            ) {
-                this.currentMusicPage = this.createMusicPage();
-                this.currentPageHeight = this.rules.PageTopMargin + this.rules.TitleTopDistance;
-            }
-        } else {
-            this.currentMusicPage = this.createMusicPage();
-            this.currentPageHeight = this.rules.PageTopMargin + this.rules.TitleTopDistance;
-        }
         this.currentSystemParams = new SystemBuildParameters();
         if (this.measureListIndex < this.measureList.length) {
             this.currentSystemParams.currentSystem = this.initMusicSystem();
-            this.layoutSystemStaves();
-            this.addSystemLabels();
         }
     }
 
@@ -229,37 +222,20 @@ export class MusicSystemBuilder {
         this.currentSystemParams.systemMeasureIndex++;
     }
 
-    private addSystemLabels(): void {
-        this.currentSystemParams.currentSystem.createMusicSystemLabel(
-            this.rules.InstrumentLabelTextHeight,
-            this.rules.SystemLabelsRightMargin,
-            this.rules.LabelMarginBorderFactor
-        );
-    }
-
-    /**
-     * Create a new [[GraphicalMusicPage]]
-     * (for now only one long page is used per music sheet, as we scroll down and have no page flips)
-     * @returns {GraphicalMusicPage}
-     */
-    private createMusicPage(): GraphicalMusicPage {
-        const page: GraphicalMusicPage = new GraphicalMusicPage(this.graphicalMusicSheet);
-        this.graphicalMusicSheet.MusicPages.push(page);
-        page.PositionAndShape.BorderLeft = 0.0;
-        page.PositionAndShape.BorderRight = this.graphicalMusicSheet.ParentMusicSheet.pageWidth;
-        page.PositionAndShape.BorderTop = 0.0;
-        page.PositionAndShape.BorderBottom = this.rules.PageHeight;
-        page.PositionAndShape.RelativePosition = new PointF2D(0.0, 0.0);
-        return page;
-    }
-
     /**
      * Initialize a new [[MusicSystem]].
      * @returns {MusicSystem}
      */
     private initMusicSystem(): MusicSystem {
-        const musicSystem: MusicSystem = MusicSheetCalculator.symbolFactory.createMusicSystem(this.currentMusicPage, this.globalSystemIndex++);
-        this.currentMusicPage.MusicSystems.push(musicSystem);
+        const musicSystem: MusicSystem = MusicSheetCalculator.symbolFactory.createMusicSystem(this.globalSystemIndex++);
+        this.musicSystems.push(musicSystem);
+        this.layoutSystemStaves(musicSystem);
+        musicSystem.createMusicSystemLabel(
+            this.rules.InstrumentLabelTextHeight,
+            this.rules.SystemLabelsRightMargin,
+            this.rules.LabelMarginBorderFactor,
+            this.musicSystems.length === 1
+        );
         return musicSystem;
     }
 
@@ -268,13 +244,12 @@ export class MusicSystemBuilder {
      * @returns {number}
      */
     private getFullPageSystemWidth(): number {
-        return this.currentMusicPage.PositionAndShape.Size.width - this.rules.PageLeftMargin
+        return this.graphicalMusicSheet.ParentMusicSheet.pageWidth - this.rules.PageLeftMargin
             - this.rules.PageRightMargin - this.rules.SystemLeftMargin - this.rules.SystemRightMargin;
     }
 
-    private layoutSystemStaves(): void {
+    private layoutSystemStaves(musicSystem: MusicSystem): void {
         const systemWidth: number = this.getFullPageSystemWidth();
-        const musicSystem: MusicSystem = this.currentSystemParams.currentSystem;
         const boundingBox: BoundingBox = musicSystem.PositionAndShape;
         boundingBox.BorderLeft = 0.0;
         boundingBox.BorderRight = systemWidth;
@@ -334,8 +309,7 @@ export class MusicSystemBuilder {
             musicSystem.StaffLines.push(staffLine);
             const boundingBox: BoundingBox = staffLine.PositionAndShape;
             const relativePosition: PointF2D = new PointF2D();
-            if (musicSystem.Parent.MusicSystems[0] === musicSystem &&
-                musicSystem.Parent === musicSystem.Parent.Parent.MusicPages[0] &&
+            if (musicSystem === this.musicSystems[0] &&
                 !EngravingRules.Rules.CompactMode) {
                 relativePosition.x = this.rules.FirstSystemMargin;
                 boundingBox.BorderRight = musicSystem.PositionAndShape.Size.width - this.rules.FirstSystemMargin;
@@ -928,7 +902,129 @@ export class MusicSystemBuilder {
         }
         currentSystem.PositionAndShape.BorderRight = width + this.currentSystemParams.maxLabelLength + this.rules.SystemLabelsRightMargin;
     }
+
+    /**
+     * This method checks the distances between two System's StaffLines and if needed, shifts the lower down.
+     * @param musicSystem
+     */
+    private optimizeDistanceBetweenStaffLines(musicSystem: MusicSystem): void {
+        // don't perform any y-spacing in case of a StaffEntryLink (in both StaffLines)
+        if (!musicSystem.checkStaffEntriesForStaffEntryLink()) {
+            for (let i: number = 0; i < musicSystem.StaffLines.length - 1; i++) {
+                const upperBottomLine: number = musicSystem.StaffLines[i].SkyBottomLineCalculator.getBottomLineMax();
+                // TODO: Lower skyline should add to offset when there are items above the line. Currently no test
+                // file available
+                // const lowerSkyLine: number = Math.min(...musicSystem.StaffLines[i + 1].SkyLine);
+                if (Math.abs(upperBottomLine) > this.rules.MinimumStaffLineDistance) {
+                    // Remove staffheight from offset. As it results in huge distances
+                    const offset: number = Math.abs(upperBottomLine) + this.rules.MinimumStaffLineDistance - this.rules.StaffHeight;
+                    this.updateStaffLinesRelativePosition(musicSystem, i + 1, offset);
+                }
+            }
+        }
+        const firstStaffLine: StaffLine = musicSystem.StaffLines[0];
+        musicSystem.PositionAndShape.BorderTop = firstStaffLine.PositionAndShape.RelativePosition.y + firstStaffLine.PositionAndShape.BorderTop;
+        const lastStaffLine: StaffLine = musicSystem.StaffLines[musicSystem.StaffLines.length - 1];
+        musicSystem.PositionAndShape.BorderBottom = lastStaffLine.PositionAndShape.RelativePosition.y + lastStaffLine.PositionAndShape.BorderBottom;
+    }
+
+    /**
+     * This method updates the System's StaffLine's RelativePosition (starting from the given index).
+     * @param musicSystem
+     * @param index
+     * @param value
+     */
+    private updateStaffLinesRelativePosition(musicSystem: MusicSystem, index: number, value: number): void {
+        for (let i: number = index; i < musicSystem.StaffLines.length; i++) {
+            musicSystem.StaffLines[i].PositionAndShape.RelativePosition.y += value;
+        }
+
+        musicSystem.PositionAndShape.BorderBottom += value;
+    }
+
+    /**
+     * Create a new [[GraphicalMusicPage]]
+     * (for now only one long page is used per music sheet, as we scroll down and have no page flips)
+     * @returns {GraphicalMusicPage}
+     */
+    private createMusicPage(): GraphicalMusicPage {
+        const page: GraphicalMusicPage = new GraphicalMusicPage(this.graphicalMusicSheet);
+        this.graphicalMusicSheet.MusicPages.push(page);
+        page.PositionAndShape.BorderLeft = 0.0;
+        page.PositionAndShape.BorderRight = this.graphicalMusicSheet.ParentMusicSheet.pageWidth;
+        page.PositionAndShape.BorderTop = 0.0;
+        page.PositionAndShape.BorderBottom = this.rules.PageHeight;
+        page.PositionAndShape.RelativePosition = new PointF2D(0.0, 0.0);
+        return page;
+    }
+
+    private addSystemToPage(page: GraphicalMusicPage, system: MusicSystem): void {
+        page.MusicSystems.push(system);
+        system.Parent = page;
+    }
+
+    /** Calculates the relative Positions of all MusicSystems.
+     *
+     */
+    private calculateMusicSystemsRelativePositions(): void {
+        let currentPage: GraphicalMusicPage = this.createMusicPage();
+        let currentYPosition: number = 0;
+        // xPosition is always fixed
+        let currentSystem: MusicSystem = this.musicSystems[0];
+
+        for (let i: number = 0; i < this.musicSystems.length; i++) {
+            currentSystem = this.musicSystems[i];
+            if (currentPage.MusicSystems.length === 0) {
+                // first system on the page:
+                this.addSystemToPage(currentPage, currentSystem);
+                if (EngravingRules.Rules.CompactMode) {
+                    currentYPosition = EngravingRules.Rules.PageTopMarginNarrow;
+                } else {
+                    currentYPosition = EngravingRules.Rules.PageTopMargin;
+                }
+
+                // Handle Title for first System on the first page
+                if (this.graphicalMusicSheet.MusicPages.length === 1 &&
+                    EngravingRules.Rules.RenderTitle) {
+                    currentYPosition +=   this.rules.TitleTopDistance + this.rules.SheetTitleHeight +
+                                            this.rules.TitleBottomDistance;
+                }
+                currentYPosition += -currentSystem.PositionAndShape.BorderTop;
+                const relativePosition: PointF2D = new PointF2D(this.rules.PageLeftMargin + this.rules.SystemLeftMargin,
+                                                                currentYPosition);
+                currentSystem.PositionAndShape.RelativePosition = relativePosition;
+                currentYPosition += currentSystem.PositionAndShape.BorderBottom;
+
+            } else {
+                // if this is not the first system on the page:
+                // find optimum distance between Systems
+                const previousSystem: MusicSystem = this.musicSystems[i - 1];
+                const previousStaffLineBB: BoundingBox = previousSystem.StaffLines[previousSystem.StaffLines.length - 1].PositionAndShape;
+                const currentStaffLineBB: BoundingBox = currentSystem.StaffLines[0].PositionAndShape;
+                let distance: number =  currentStaffLineBB.RelativePosition.y + previousStaffLineBB.BorderTop -
+                                        (previousStaffLineBB.RelativePosition.y + previousStaffLineBB.BorderBottom);
+                distance = Math.max(this.rules.MinimumDistanceBetweenSystems, distance);
+                const neededHeight: number = distance - currentSystem.PositionAndShape.BorderTop + currentSystem.PositionAndShape.BorderBottom;
+                if (currentYPosition + neededHeight <
+                    this.rules.PageHeight - this.rules.PageBottomMargin) {
+                    // enough space on this page:
+                    this.addSystemToPage(currentPage, currentSystem);
+                    const relativePosition: PointF2D = new PointF2D(this.rules.PageLeftMargin + this.rules.SystemLeftMargin,
+                                                                    currentYPosition + distance - currentSystem.PositionAndShape.BorderTop);
+                    currentSystem.PositionAndShape.RelativePosition = relativePosition;
+                    currentYPosition += neededHeight;
+                } else {
+                    // new page needed:
+                    currentPage = this.createMusicPage();
+                    // re-check this system again:
+                    i -= 1;
+                    continue;
+                }
+            }
+        }
+    }
 }
+
 export class SystemBuildParameters {
     public currentSystem: MusicSystem;
     public systemMeasures: MeasureBuildParameters[] = [];

+ 4 - 1
src/MusicalScore/Graphical/VexFlow/SvgVexFlowBackend.ts

@@ -24,13 +24,16 @@ export class SvgVexFlowBackend extends VexFlowBackend {
         container.appendChild(this.inner);
         this.renderer = new Vex.Flow.Renderer(this.canvas, this.getBackendType());
         this.ctx = <Vex.Flow.SVGContext>this.renderer.getContext();
-
     }
 
     public getContext(): Vex.Flow.SVGContext {
         return this.ctx;
     }
 
+    public getSvgElement(): SVGElement {
+        return this.ctx.svg;
+    }
+
     public clear(): void {
         if (!this.ctx) {
             return;

+ 5 - 1
src/MusicalScore/Graphical/VexFlow/VexFlowBackend.ts

@@ -28,7 +28,11 @@ export abstract class VexFlowBackend {
     return this.renderer;
   }
 
-  public abstract getContext(): Vex.IRenderContext;
+  public removeFromContainer(container: HTMLElement): void {
+    container.removeChild(this.canvas);
+  }
+
+public abstract getContext(): Vex.IRenderContext;
 
   // public abstract setWidth(width: number): void;
   // public abstract setHeight(height: number): void;

+ 2 - 3
src/MusicalScore/Graphical/VexFlow/VexFlowGraphicalSymbolFactory.ts

@@ -1,6 +1,5 @@
 import Vex = require("vexflow");
 import {IGraphicalSymbolFactory} from "../../Interfaces/IGraphicalSymbolFactory";
-import {GraphicalMusicPage} from "../GraphicalMusicPage";
 import {MusicSystem} from "../MusicSystem";
 import {VexFlowMusicSystem} from "./VexFlowMusicSystem";
 import {Staff} from "../../VoiceData/Staff";
@@ -36,8 +35,8 @@ export class VexFlowGraphicalSymbolFactory implements IGraphicalSymbolFactory {
      * @param systemIndex
      * @returns {VexFlowMusicSystem}
      */
-    public createMusicSystem(page: GraphicalMusicPage, systemIndex: number): MusicSystem {
-        return new VexFlowMusicSystem(page, systemIndex);
+    public createMusicSystem(systemIndex: number): MusicSystem {
+        return new VexFlowMusicSystem(systemIndex);
     }
 
     /**

+ 4 - 82
src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetCalculator.ts

@@ -395,21 +395,6 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
   }
 
   /**
-   * calculates the y positions of the staff lines within a system and
-   * furthermore the y positions of the systems themselves.
-   */
-  protected calculateSystemYLayout(): void {
-    for (const graphicalMusicPage of this.graphicalMusicSheet.MusicPages) {
-      for (const musicSystem of graphicalMusicPage.MusicSystems) {
-        this.optimizeDistanceBetweenStaffLines(musicSystem);
-      }
-
-      // set y positions of systems using the previous system and a fixed distance.
-      this.calculateMusicSystemsRelativePositions(graphicalMusicPage);
-    }
-  }
-
-  /**
    * Is called at the begin of the method for creating the vertically aligned staff measures belonging to one source measure.
    */
   protected initGraphicalMeasuresCreation(): void {
@@ -695,8 +680,7 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
    * Re-adjust the x positioning of expressions. Update the skyline afterwards
    */
   protected calculateExpressionAlignements(): void {
-    for (const graphicalMusicPage of this.graphicalMusicSheet.MusicPages) {
-        for (const musicSystem of graphicalMusicPage.MusicSystems) {
+    for (const musicSystem of this.musicSystems) {
             for (const staffLine of musicSystem.StaffLines) {
               try {
                 (<VexFlowStaffLine>staffLine).AlignmentManager.alignDynamicExpressions();
@@ -704,11 +688,10 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
               } catch (e) {
                 // TODO still necessary when calculation of expression fails, see calculateDynamicExpressionsForMultiExpression()
                 //   see calculateGraphicalContinuousDynamic(), also in MusicSheetCalculator.
-              }
-            }
         }
     }
   }
+  }
 
   /**
    * Check if the tied graphical note belongs to any beams or tuplets and react accordingly.
@@ -861,8 +844,7 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
     }
     */
 
-    for (const gmPage of this.graphicalMusicSheet.MusicPages) {
-      for (const musicSystem of gmPage.MusicSystems) {
+    for (const musicSystem of this.musicSystems) {
         for (const staffLine of musicSystem.StaffLines) {
           // if a graphical slur reaches out of the last musicsystem, we have to create another graphical slur reaching into this musicsystem
           // (one slur needs 2 graphical slurs)
@@ -889,34 +871,6 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
           // add reference of slur array to the VexFlowStaffline class
           for (const graphicalMeasure of staffLine.Measures) {
             for (const graphicalStaffEntry of graphicalMeasure.staffEntries) {
-              // for (var idx5: number = 0, len5 = graphicalStaffEntry.GraceStaffEntriesBefore.Count; idx5 < len5; ++idx5) {
-              //     var graceStaffEntry: GraphicalStaffEntry = graphicalStaffEntry.GraceStaffEntriesBefore[idx5];
-              //     if (graceStaffEntry.Notes[0][0].SourceNote.NoteSlurs.Count > 0) {
-              //         var graceNote: Note = graceStaffEntry.Notes[0][0].SourceNote;
-              //         graceStaffEntry.RelInMeasureTimestamp = Fraction.createFromFraction(graphicalStaffEntry.RelInMeasureTimestamp);
-              //         for (var idx6: number = 0, len6 = graceNote.NoteSlurs.Count; idx6 < len6; ++idx6) {
-              //             var graceNoteSlur: Slur = graceNote.NoteSlurs[idx6];
-              //             if (graceNoteSlur.StartNote == graceNote) {
-              //                 var vfSlur: VexFlowSlur = new VexFlowSlur(graceNoteSlur);
-              //                 vfSlur.GraceStart = true;
-              //                 staffLine.GraphicalSlurs.Add(vfSlur);
-              //                 openGraphicalSlurs[i].Add(vfSlur);
-              //                 for (var j: number = graphicalStaffEntry.GraceStaffEntriesBefore.IndexOf(graceStaffEntry);
-              //                     j < graphicalStaffEntry.GraceStaffEntriesBefore.Count; j++)
-              //                        vfSlur.StaffEntries.Add(<PsStaffEntry>graphicalStaffEntry.GraceStaffEntriesBefore[j]);
-              //             }
-              //             if (graceNote == graceNoteSlur.EndNote) {
-              //                 var vfSlur: VexFlowSlur = findGraphicalSlurFromSlur(openGraphicalSlurs[i], graceNoteSlur);
-              //                 if (vfSlur != null) {
-              //                     vfSlur.GraceEnd = true;
-              //                     openGraphicalSlurs[i].Remove(vfSlur);
-              //                     for (var j: number = 0; j <= graphicalStaffEntry.GraceStaffEntriesBefore.IndexOf(graceStaffEntry); j++)
-              //                         vfSlur.StaffEntries.Add(<PsStaffEntry>graphicalStaffEntry.GraceStaffEntriesBefore[j]);
-              //                 }
-              //             }
-              //         }
-              //     }
-              // }
               // loop over "normal" notes (= no gracenotes)
               for (const graphicalVoiceEntry of graphicalStaffEntry.graphicalVoiceEntries) {
                 for (const graphicalNote of graphicalVoiceEntry.notes) {
@@ -973,35 +927,6 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
                   }
                 }
               }
-              // for (var idx5: number = 0, len5 = graphicalStaffEntry.GraceStaffEntriesAfter.Count; idx5 < len5; ++idx5) {
-              //     var graceStaffEntry: GraphicalStaffEntry = graphicalStaffEntry.GraceStaffEntriesAfter[idx5];
-              //     if (graceStaffEntry.Notes[0][0].SourceNote.NoteSlurs.Count > 0) {
-              //         var graceNote: Note = graceStaffEntry.Notes[0][0].SourceNote;
-              //         graceStaffEntry.RelInMeasureTimestamp = Fraction.createFromFraction(graphicalStaffEntry.RelInMeasureTimestamp);
-              //         for (var idx6: number = 0, len6 = graceNote.NoteSlurs.Count; idx6 < len6; ++idx6) {
-              //             var graceNoteSlur: Slur = graceNote.NoteSlurs[idx6];
-              //             if (graceNoteSlur.StartNote == graceNote) {
-              //                 var vfSlur: VexFlowSlur = new VexFlowSlur(graceNoteSlur);
-              //                 vfSlur.GraceStart = true;
-              //                 staffLine.GraphicalSlurs.Add(vfSlur);
-              //                 openGraphicalSlurs[i].Add(vfSlur);
-              //                 for (var j: number = graphicalStaffEntry.GraceStaffEntriesAfter.IndexOf(graceStaffEntry);
-              //                      j < graphicalStaffEntry.GraceStaffEntriesAfter.Count; j++)
-              //                        vfSlur.StaffEntries.Add(<PsStaffEntry>graphicalStaffEntry.GraceStaffEntriesAfter[j]);
-              //             }
-              //             if (graceNote == graceNoteSlur.EndNote) {
-              //                 var vfSlur: VexFlowSlur = findGraphicalSlurFromSlur(openGraphicalSlurs[i], graceNoteSlur);
-              //                 if (vfSlur != null) {
-              //                     vfSlur.GraceEnd = true;
-              //                     openGraphicalSlurs[i].Remove(vfSlur);
-              //                     vfSlur.StaffEntries.Add(<PsStaffEntry>graphicalStaffEntry);
-              //                     for (var j: number = 0; j <= graphicalStaffEntry.GraceStaffEntriesAfter.IndexOf(graceStaffEntry); j++)
-              //                         vfSlur.StaffEntries.Add(<PsStaffEntry>graphicalStaffEntry.GraceStaffEntriesAfter[j]);
-              //                 }
-              //             }
-              //         }
-              //     }
-              // }
 
               //add the present Staffentry to all open slurs that don't contain this Staffentry already
               for (const gSlur of openGraphicalSlurs) {
@@ -1016,11 +941,9 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
         // Attach vfSlur array to the vfStaffline to be drawn
         //vfStaffLine.SlursInVFStaffLine = vfSlurs;
       } // loop over MusicSystems
-    } // loop over MusicPages
 
     // order slurs that were saved to the Staffline
-    for (const graphicalMusicPage of this.graphicalMusicSheet.MusicPages) {
-        for (const musicSystem of graphicalMusicPage.MusicSystems) {
+    for (const musicSystem of this.musicSystems) {
           for (const staffLine of musicSystem.StaffLines) {
             // Sort all gSlurs in the staffline using the Compare function in class GraphicalSlurSorter
             const sortedGSlurs: GraphicalSlur[] = staffLine.GraphicalSlurs.sort(GraphicalSlur.Compare);
@@ -1035,4 +958,3 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
         }
       }
   }
-}

+ 32 - 22
src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetDrawer.ts

@@ -26,6 +26,8 @@ import log = require("loglevel");
 import { GraphicalContinuousDynamicExpression } from "../GraphicalContinuousDynamicExpression";
 import { VexFlowContinuousDynamicExpression } from "./VexFlowContinuousDynamicExpression";
 import { DrawingParameters } from "../DrawingParameters";
+import { GraphicalMusicPage } from "../GraphicalMusicPage";
+import { GraphicalMusicSheet } from "../GraphicalMusicSheet";
 
 /**
  * This is a global constant which denotes the height in pixels of the space between two lines of the stave
@@ -36,39 +38,47 @@ export const unitInPixels: number = 10;
 
 export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
     private backend: VexFlowBackend;
+    private backends: VexFlowBackend[] = [];
     private zoom: number = 1.0;
+    private pageIdx: number = 0;
 
-    constructor(element: HTMLElement,
-                backend: VexFlowBackend,
-                drawingParameters: DrawingParameters = new DrawingParameters()) {
+    constructor(drawingParameters: DrawingParameters = new DrawingParameters()) {
         super(new VexFlowTextMeasurer(), drawingParameters);
-        this.backend = backend;
     }
 
-    public clear(): void {
-        this.backend.clear();
+    public get Backends(): VexFlowBackend[] {
+        return this.backends;
     }
 
-    /**
-     * Zoom the rendering areas
-     * @param k is the zoom factor
-     */
-    public scale(k: number): void {
-        this.zoom = k;
-        this.backend.scale(this.zoom);
+    public drawSheet(graphicalMusicSheet: GraphicalMusicSheet): void {
+        this.pageIdx = 0;
+        for (const {} of graphicalMusicSheet.MusicPages) {
+            const backend: VexFlowBackend = this.backends[this.pageIdx];
+            backend.scale(this.zoom);
+            //backend.resize(graphicalMusicSheet.ParentMusicSheet.pageWidth * unitInPixels * this.zoom,
+            //               EngravingRules.Rules.PageHeight * unitInPixels * this.zoom);
+            this.pageIdx += 1;
+        }
+
+        this.pageIdx = 0;
+        this.backend = this.backends[0];
+        super.drawSheet(graphicalMusicSheet);
     }
 
-    /**
-     * Resize the rendering areas
-     * @param x
-     * @param y
-     */
-    public resize(x: number, y: number): void {
-        this.backend.resize(x, y);
+    protected drawPage(page: GraphicalMusicPage): void {
+        super.drawPage(page);
+        this.pageIdx += 1;
+        this.backend = this.backends[this.pageIdx];
+    }
+
+    public clear(): void {
+        for (const backend of this.backends) {
+            backend.clear();
+        }
     }
 
-    public translate(x: number, y: number): void {
-        this.backend.translate(x, y);
+    public setZoom(zoom: number): void {
+        this.zoom = zoom;
     }
 
     /**

+ 2 - 3
src/MusicalScore/Graphical/VexFlow/VexFlowMusicSystem.ts

@@ -1,5 +1,4 @@
 import {MusicSystem} from "../MusicSystem";
-import {GraphicalMusicPage} from "../GraphicalMusicPage";
 import {SystemLinesEnum} from "../SystemLinesEnum";
 import {SystemLinePosition} from "../SystemLinePosition";
 import {GraphicalMeasure} from "../GraphicalMeasure";
@@ -14,8 +13,8 @@ import { VexFlowInstrumentBrace } from "./VexFlowInstrumentBrace";
 import { SkyBottomLineCalculator } from "../SkyBottomLineCalculator";
 
 export class VexFlowMusicSystem extends MusicSystem {
-    constructor(parent: GraphicalMusicPage, id: number) {
-        super(parent, id);
+    constructor(id: number) {
+        super(id);
 
     }
 

+ 1 - 2
src/MusicalScore/Interfaces/IGraphicalSymbolFactory.ts

@@ -1,6 +1,5 @@
 import {ClefInstruction} from "../VoiceData/Instructions/ClefInstruction";
 import {Fraction} from "../../Common/DataObjects/Fraction";
-import {GraphicalMusicPage} from "../Graphical/GraphicalMusicPage";
 import {GraphicalNote} from "../Graphical/GraphicalNote";
 import {GraphicalStaffEntry} from "../Graphical/GraphicalStaffEntry";
 import {MusicSystem} from "../Graphical/MusicSystem";
@@ -18,7 +17,7 @@ import { VoiceEntry } from "../VoiceData/VoiceEntry";
 
 export interface IGraphicalSymbolFactory {
 
-    createMusicSystem(page: GraphicalMusicPage, systemIndex: number): MusicSystem;
+    createMusicSystem(systemIndex: number): MusicSystem;
 
     createStaffLine(parentSystem: MusicSystem, parentStaff: Staff): StaffLine;
 

+ 3 - 0
src/OpenSheetMusicDisplay/OSMDOptions.ts

@@ -1,4 +1,5 @@
 import { DrawingParametersEnum, ColoringModes } from "../MusicalScore/Graphical/DrawingParameters";
+import { PageFormat } from "../MusicalScore/Graphical/EngravingRules";
 
 /** Possible options for the OpenSheetMusicDisplay constructor and osmd.setOptions(). None are mandatory.
  *  Note that after using setOptions(), you have to call osmd.render() again to make changes visible.
@@ -110,6 +111,8 @@ export interface IOSMDOptions {
     tripletsBracketed?: boolean;
     /** Whether to draw hidden/invisible notes (print-object="no" in XML). Default false. Not yet supported. */ // TODO
     drawHiddenNotes?: boolean;
+
+    pageFormat?: PageFormat;
 }
 
 export enum AlignRestOption {

+ 149 - 32
src/OpenSheetMusicDisplay/OpenSheetMusicDisplay.ts

@@ -15,11 +15,13 @@ import { AJAX } from "./AJAX";
 import * as log from "loglevel";
 import { DrawingParametersEnum, DrawingParameters, ColoringModes } from "../MusicalScore/Graphical/DrawingParameters";
 import { IOSMDOptions, OSMDOptions, AutoBeamOptions } from "./OSMDOptions";
-import { EngravingRules } from "../MusicalScore/Graphical/EngravingRules";
+import { EngravingRules, PageFormat } from "../MusicalScore/Graphical/EngravingRules";
 import { AbstractExpression } from "../MusicalScore/VoiceData/Expressions/AbstractExpression";
 import { Dictionary } from "typescript-collections";
 import { NoteEnum } from "..";
 import { AutoColorSet } from "../MusicalScore";
+import jspdf = require("jspdf-yworks/dist/jspdf.min");
+import svg2pdf = require("svg2pdf.js/dist/svg2pdf.min");
 
 /**
  * The main class and control point of OpenSheetMusicDisplay.<br>
@@ -63,9 +65,7 @@ export class OpenSheetMusicDisplay {
     public zoom: number = 1.0;
 
     private container: HTMLElement;
-    private canvas: HTMLElement;
-    private backend: VexFlowBackend;
-    private innerElement: HTMLElement;
+    private backendType: any;
     private sheet: MusicSheet;
     private drawer: VexFlowMusicSheetDrawer;
     private graphic: GraphicalMusicSheet;
@@ -178,6 +178,12 @@ export class OpenSheetMusicDisplay {
         // Set page width
         const width: number = this.container.offsetWidth;
         this.sheet.pageWidth = width / this.zoom / 10.0;
+        if (EngravingRules.Rules.PageFormat) {
+            EngravingRules.Rules.PageHeight = this.sheet.pageWidth / EngravingRules.Rules.PageFormat.aspectRatio;
+        } else {
+            EngravingRules.Rules.PageHeight = 100001.0;
+        }
+
         // Before introducing the following optimization (maybe irrelevant), tests
         // have to be modified to ensure that width is > 0 when executed
         //if (isNaN(width) || width === 0) {
@@ -186,21 +192,41 @@ export class OpenSheetMusicDisplay {
 
         // Calculate again
         this.graphic.reCalculate();
-        const height: number = this.graphic.MusicPages[0].PositionAndShape.BorderBottom * 10.0 * this.zoom;
+
         if (this.drawingParameters.drawCursors) {
             this.graphic.Cursors.length = 0;
         }
-        // Update Sheet Page
-        this.drawer.resize(width, height);
-        this.drawer.scale(this.zoom);
+
+        // Remove old backends
+        for (const backend of this.drawer.Backends) {
+            backend.removeFromContainer(this.container);
+        }
+        this.drawer.Backends.clear();
+
+        // create new backends
+        for (const page of this.graphic.MusicPages) {
+            const backend: VexFlowBackend = this.createBackend(this.backendType);
+            if (EngravingRules.Rules.PageFormat) {
+                backend.resize(width, width / EngravingRules.Rules.PageFormat.aspectRatio);
+            } else {
+                backend.resize(width, (page.PositionAndShape.Size.height + 15) * this.zoom * 10.0);
+            }
+            this.drawer.Backends.push(backend);
+        }
+        this.drawer.setZoom(this.zoom);
         // Finally, draw
         this.drawer.drawSheet(this.graphic);
+
+        this.enableOrDisableCursor(this.drawingParameters.drawCursors);
+
         if (this.drawingParameters.drawCursors && this.cursor) {
             // Update the cursor position
             this.cursor.update();
         }
     }
 
+
+
     /** States whether the render() function can be safely called. */
     public IsReadyToRender(): boolean {
         return this.graphic !== undefined;
@@ -230,29 +256,30 @@ export class OpenSheetMusicDisplay {
                 (<any>DrawingParametersEnum)[options.drawingParameters.toLowerCase()];
         }
 
-        const updateExistingBackend: boolean = this.backend !== undefined;
-        if (options.backend !== undefined || this.backend === undefined) {
-            if (updateExistingBackend) {
-                // TODO doesn't work yet, still need to create a new OSMD object
+        this.backendType = options.backend;
+        // const updateExistingBackend: boolean = this.backend !== undefined;
+        // if (options.backend !== undefined || this.backend === undefined) {
+        //     if (updateExistingBackend) {
+        //         // TODO doesn't work yet, still need to create a new OSMD object
 
-                this.drawer.clear();
+        //         this.drawer.clear();
 
-                // musicSheetCalculator.clearSystemsAndMeasures() // maybe? don't have reference though
-                // musicSheetCalculator.clearRecreatedObjects();
-            }
-            if (options.backend === undefined || options.backend.toLowerCase() === "svg") {
-                this.backend = new SvgVexFlowBackend();
-            } else {
-                this.backend = new CanvasVexFlowBackend();
+        //         // musicSheetCalculator.clearSystemsAndMeasures() // maybe? don't have reference though
+        //         // musicSheetCalculator.clearRecreatedObjects();
+        //     }
+        // }
+
+        // Create the drawer
+        if (this.drawer) {
+            // Remove old backends
+            for (const backend of this.drawer.Backends) {
+                backend.removeFromContainer(this.container);
             }
-            this.backend.initialize(this.container);
-            this.canvas = this.backend.getCanvas();
-            this.innerElement = this.backend.getInnerElement();
-            this.enableOrDisableCursor(this.drawingParameters.drawCursors);
-            // Create the drawer
-            this.drawer = new VexFlowMusicSheetDrawer(this.canvas, this.backend, this.drawingParameters);
+            this.drawer.Backends.clear();
         }
 
+        this.drawer = new VexFlowMusicSheetDrawer(this.drawingParameters);
+
         // individual drawing parameters options
         if (options.autoBeam !== undefined) {
             EngravingRules.Rules.AutoBeamNotes = options.autoBeam;
@@ -286,8 +313,8 @@ export class OpenSheetMusicDisplay {
         }
         if (options.disableCursor) {
             this.drawingParameters.drawCursors = false;
-            this.enableOrDisableCursor(this.drawingParameters.drawCursors);
         }
+
         // alternative to if block: this.drawingsParameters.drawCursors = options.drawCursors !== false. No if, but always sets drawingParameters.
         // note that every option can be undefined, which doesn't mean the option should be set to false.
         if (options.drawHiddenNotes) {
@@ -386,6 +413,9 @@ export class OpenSheetMusicDisplay {
             this.autoResizeEnabled = false;
             // we could remove the window EventListener here, but not necessary.
         }
+
+        // no if -> shall also be set to undefined:
+        EngravingRules.Rules.PageFormat = options.pageFormat;
     }
 
     public setColoringMode(options: IOSMDOptions): void {
@@ -551,11 +581,9 @@ export class OpenSheetMusicDisplay {
     public enableOrDisableCursor(enable: boolean): void {
         this.drawingParameters.drawCursors = enable;
         if (enable) {
-            if (!this.cursor) {
-                this.cursor = new Cursor(this.innerElement, this);
-                if (this.sheet && this.graphic) { // else init is called in load()
-                    this.cursor.init(this.sheet.MusicPartManager, this.graphic);
-                }
+            this.cursor = new Cursor(this.drawer.Backends[0].getInnerElement(), this);
+            if (this.sheet && this.graphic) { // else init is called in load()
+                this.cursor.init(this.sheet.MusicPartManager, this.graphic);
             }
         } else { // disable cursor
             if (!this.cursor) {
@@ -568,6 +596,95 @@ export class OpenSheetMusicDisplay {
         }
     }
 
+    public createBackend(type: any): VexFlowBackend {
+        let backend: VexFlowBackend;
+        if (type === undefined || type.toLowerCase() === "svg") {
+            backend = new SvgVexFlowBackend();
+        } else {
+            backend = new CanvasVexFlowBackend();
+        }
+        backend.initialize(this.container);
+        return backend;
+    }
+
+    public static PageFormatStandards: {[type: string]: PageFormat} = {
+        "A3 L": new PageFormat(420, 297),
+        "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),
+        "Letter L": new PageFormat(279.4, 215.9),
+        "Letter P": new PageFormat(215.9, 279.4)
+    };
+
+    public setPageFormat(formatId: string): void {
+        let f: PageFormat = undefined;
+        if (OpenSheetMusicDisplay.PageFormatStandards.hasOwnProperty(formatId)) {
+            f = OpenSheetMusicDisplay.PageFormatStandards[formatId];
+        }
+        const options: IOSMDOptions = {
+            pageFormat: f,
+        };
+        this.setOptions(options);
+    }
+
+    public setCustomPageFormat(width: number, height: number): void {
+        if (width > 0 && height > 0) {
+            const f: PageFormat = new PageFormat(width, height);
+            const options: IOSMDOptions = {
+                pageFormat: f,
+            };
+            this.setOptions(options);
+        }
+    }
+
+    /**
+     * Creates a Pdf of the currently rendered MusicXML
+     * @param pdfName if no name is given, the composer and title of the piece will be used
+     */
+    public createPdf(pdfName: string = undefined): void {
+
+        if (pdfName === undefined) {
+            pdfName = this.sheet.FullNameString + ".pdf";
+        }
+
+        const backends: VexFlowBackend[] =  this.drawer.Backends;
+        let svgElement: SVGElement = (<SvgVexFlowBackend>backends[0]).getSvgElement();
+
+        let pageWidth: number = 210;
+        let pageHeight: number = 297;
+        if (EngravingRules.Rules.PageFormat) {
+            pageWidth = EngravingRules.Rules.PageFormat.width;
+            pageHeight = EngravingRules.Rules.PageFormat.height;
+        } else {
+            pageHeight = pageWidth * svgElement.clientHeight / svgElement.clientWidth;
+        }
+
+        const orientation: string = pageHeight > pageWidth ? "p" : "l";
+        // create a new jsPDF instance
+        const pdf: any = new jspdf(orientation, "mm", [pageWidth, pageHeight]);
+        const scale: number = pageWidth / svgElement.clientWidth;
+        for (let idx: number = 0, len: number = backends.length; idx < len; ++idx) {
+            if (idx > 0) {
+                pdf.addPage();
+            }
+            svgElement = (<SvgVexFlowBackend>backends[idx]).getSvgElement();
+
+            // render the svg element
+            svg2pdf(svgElement, pdf, {
+                scale: scale,
+                xOffset: 0,
+                yOffset: 0
+            });
+        }
+
+        // simply save the created pdf
+        pdf.save(pdfName);
+    }
+
     //#region GETTER / SETTER
     public set DrawSkyLine(value: boolean) {
         if (this.drawer) {

+ 4 - 2
test/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetDrawer_Test.ts

@@ -28,7 +28,8 @@ describe("VexFlow Music Sheet Drawer", () => {
         const canvas: HTMLCanvasElement = document.createElement("canvas");
         const backend: VexFlowBackend = new CanvasVexFlowBackend();
         backend.initialize(canvas);
-        const drawer: VexFlowMusicSheetDrawer = new VexFlowMusicSheetDrawer(canvas, backend);
+        const drawer: VexFlowMusicSheetDrawer = new VexFlowMusicSheetDrawer();
+        drawer.Backends.push(backend);
         drawer.drawSheet(gms);
         done();
     });
@@ -48,7 +49,8 @@ describe("VexFlow Music Sheet Drawer", () => {
         const canvas: HTMLCanvasElement = document.createElement("canvas");
         const backend: VexFlowBackend = new CanvasVexFlowBackend();
         backend.initialize(canvas);
-        const drawer: VexFlowMusicSheetDrawer = new VexFlowMusicSheetDrawer(canvas, backend, new DrawingParameters());
+        const drawer: VexFlowMusicSheetDrawer = new VexFlowMusicSheetDrawer(new DrawingParameters());
+        drawer.Backends.push(backend);
         drawer.drawSheet(gms);
         done();
     });