Преглед на файлове

Refactored page layout code: Page creation and linking system to pages was too early and not usable for correct layout. Moved page creation to final positioning of systems.

Matthias Uiberacker преди 5 години
родител
ревизия
7ad7870267

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

@@ -201,7 +201,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");

+ 6 - 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;
@@ -243,14 +242,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
@@ -559,12 +557,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;
     }
@@ -589,11 +581,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;

+ 125 - 246
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
@@ -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);
                         }
                     }
                 }
@@ -1412,7 +1317,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();
@@ -1669,21 +1574,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);
                             }
                         }
                     }
@@ -2156,11 +2058,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();
             }
         }
     }
@@ -2173,18 +2073,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);
                     }
                 }
             }
@@ -2192,21 +2089,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);
                             }
                         }
                     }
@@ -2216,26 +2110,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);
                                 }
                             }
                         }
@@ -2246,18 +2137,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);
                     }
                 }
             }
@@ -2324,26 +2212,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);
                             }
                         }
                     }
@@ -2362,28 +2247,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));
             }
         }
     }

+ 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 - 47
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 = this.rules.PageTopMargin + this.rules.TitleTopDistance;
         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,17 +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;
-        } else {
-            this.currentMusicPage = this.createMusicPage();
-            this.currentPageHeight = this.rules.PageTopMargin;
-        }
         this.currentSystemParams = new SystemBuildParameters();
         if (this.measureListIndex < this.measureList.length) {
             this.currentSystemParams.currentSystem = this.initMusicSystem();
-            this.layoutSystemStaves();
-            this.addSystemLabels();
         }
     }
 
@@ -222,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;
     }
 
@@ -261,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;
@@ -327,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;
@@ -921,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[] = [];

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

@@ -24,7 +24,6 @@ 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 {

+ 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);
     }
 
     /**

+ 99 - 177
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 {
@@ -689,12 +674,10 @@ 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 staffLine of musicSystem.StaffLines) {
-                (<VexFlowStaffLine>staffLine).AlignmentManager.alignDynamicExpressions();
-                staffLine.AbstractExpressions.forEach(ae => ae.updateSkyBottomLine());
-            }
+    for (const musicSystem of this.musicSystems) {
+        for (const staffLine of musicSystem.StaffLines) {
+            (<VexFlowStaffLine>staffLine).AlignmentManager.alignDynamicExpressions();
+            staffLine.AbstractExpressions.forEach(ae => ae.updateSkyBottomLine());
         }
     }
   }
@@ -850,178 +833,117 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
     }
     */
 
-    for (const gmPage of this.graphicalMusicSheet.MusicPages) {
-      for (const musicSystem of gmPage.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)
-          const openGraphicalSlurs: GraphicalSlur[] = openSlursDict[staffLine.ParentStaff.idInMusicSheet];
-          for (let slurIndex: number = 0; slurIndex < openGraphicalSlurs.length; slurIndex++) {
-            const oldGSlur: GraphicalSlur = openGraphicalSlurs[slurIndex];
-            const newGSlur: GraphicalSlur = new GraphicalSlur(oldGSlur.slur); //Graphicalslur.createFromSlur(oldSlur);
-            staffLine.addSlurToStaffline(newGSlur); // every VFSlur is added to the array in the VFStaffline!
-            openGraphicalSlurs[slurIndex] = newGSlur;
-          }
+    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)
+        const openGraphicalSlurs: GraphicalSlur[] = openSlursDict[staffLine.ParentStaff.idInMusicSheet];
+        for (let slurIndex: number = 0; slurIndex < openGraphicalSlurs.length; slurIndex++) {
+          const oldGSlur: GraphicalSlur = openGraphicalSlurs[slurIndex];
+          const newGSlur: GraphicalSlur = new GraphicalSlur(oldGSlur.slur); //Graphicalslur.createFromSlur(oldSlur);
+          staffLine.addSlurToStaffline(newGSlur); // every VFSlur is added to the array in the VFStaffline!
+          openGraphicalSlurs[slurIndex] = newGSlur;
+        }
 
-          /* VexFlow Version - for later use
-          const vfOpenSlurs: VexFlowSlur[] = vfOpenSlursDict[staffLine.ParentStaff.idInMusicSheet];
-          const vfStaffLine: VexFlowStaffLine = <VexFlowStaffLine> staffLine;
-          for (let slurIndex: number = 0; slurIndex < vfOpenSlurs.length; slurIndex++) {
-              const oldVFSlur: VexFlowSlur = vfOpenSlurs[slurIndex];
-              const newVFSlur: VexFlowSlur = VexFlowSlur.createFromVexflowSlur(oldVFSlur);
-              newVFSlur.vfStartNote = undefined;
-              vfStaffLine.addVFSlurToVFStaffline(newVFSlur); // every VFSlur is added to the array in the VFStaffline!
-              vfOpenSlurs[slurIndex] = newVFSlur;
-          }
-          */
-
-          // 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) {
-                  for (const slur of graphicalNote.sourceNote.NoteSlurs) {
-                    // extra check for some MusicSheets that have openSlurs (because only the first Page is available -> Recordare files)
-                    if (slur.EndNote === undefined || slur.StartNote === undefined) {
-                      continue;
-                    }
-                    // add new VexFlowSlur to List
-                    if (slur.StartNote === graphicalNote.sourceNote) {
-                      if (graphicalNote.sourceNote.NoteTie !== undefined) {
-                        if (graphicalNote.parentVoiceEntry.parentStaffEntry.getAbsoluteTimestamp() !==
-                          graphicalNote.sourceNote.NoteTie.StartNote.getAbsoluteTimestamp()) {
-                          break;
-                        }
+        /* VexFlow Version - for later use
+        const vfOpenSlurs: VexFlowSlur[] = vfOpenSlursDict[staffLine.ParentStaff.idInMusicSheet];
+        const vfStaffLine: VexFlowStaffLine = <VexFlowStaffLine> staffLine;
+        for (let slurIndex: number = 0; slurIndex < vfOpenSlurs.length; slurIndex++) {
+            const oldVFSlur: VexFlowSlur = vfOpenSlurs[slurIndex];
+            const newVFSlur: VexFlowSlur = VexFlowSlur.createFromVexflowSlur(oldVFSlur);
+            newVFSlur.vfStartNote = undefined;
+            vfStaffLine.addVFSlurToVFStaffline(newVFSlur); // every VFSlur is added to the array in the VFStaffline!
+            vfOpenSlurs[slurIndex] = newVFSlur;
+        }
+        */
+
+        // add reference of slur array to the VexFlowStaffline class
+        for (const graphicalMeasure of staffLine.Measures) {
+          for (const graphicalStaffEntry of graphicalMeasure.staffEntries) {
+            // loop over "normal" notes (= no gracenotes)
+            for (const graphicalVoiceEntry of graphicalStaffEntry.graphicalVoiceEntries) {
+              for (const graphicalNote of graphicalVoiceEntry.notes) {
+                for (const slur of graphicalNote.sourceNote.NoteSlurs) {
+                  // extra check for some MusicSheets that have openSlurs (because only the first Page is available -> Recordare files)
+                  if (slur.EndNote === undefined || slur.StartNote === undefined) {
+                    continue;
+                  }
+                  // add new VexFlowSlur to List
+                  if (slur.StartNote === graphicalNote.sourceNote) {
+                    if (graphicalNote.sourceNote.NoteTie !== undefined) {
+                      if (graphicalNote.parentVoiceEntry.parentStaffEntry.getAbsoluteTimestamp() !==
+                        graphicalNote.sourceNote.NoteTie.StartNote.getAbsoluteTimestamp()) {
+                        break;
                       }
-
-                      // Add a Graphical Slur to the staffline, if the recent note is the Startnote of a slur
-                      const gSlur: GraphicalSlur = new GraphicalSlur(slur);
-                      openGraphicalSlurs.push(gSlur);
-                      staffLine.addSlurToStaffline(gSlur);
-
-                      /* VexFlow Version - for later use
-                      const vfSlur: VexFlowSlur = new VexFlowSlur(slur);
-                      vfOpenSlurs.push(vfSlur); //add open... adding / removing is JUST DONE in the open... array
-                      vfSlur.vfStartNote = (graphicalVoiceEntry as VexFlowVoiceEntry).vfStaveNote;
-                      vfStaffLine.addVFSlurToVFStaffline(vfSlur); // every VFSlur is added to the array in the VFStaffline!
-                      */
                     }
-                    if (slur.EndNote === graphicalNote.sourceNote) {
-                      // Remove the Graphical Slur from the staffline if the note is the Endnote of a slur
-                      const index: number = this.findIndexGraphicalSlurFromSlur(openGraphicalSlurs, slur);
-                      if (index >= 0) {
-                        // save Voice Entry in VFSlur and then remove it from array of open VFSlurs
-                        const gSlur: GraphicalSlur = openGraphicalSlurs[index];
-                        if (gSlur.staffEntries.indexOf(graphicalStaffEntry) === -1) {
-                          gSlur.staffEntries.push(graphicalStaffEntry);
-                        }
 
-                        openGraphicalSlurs.splice(index, 1);
+                    // Add a Graphical Slur to the staffline, if the recent note is the Startnote of a slur
+                    const gSlur: GraphicalSlur = new GraphicalSlur(slur);
+                    openGraphicalSlurs.push(gSlur);
+                    staffLine.addSlurToStaffline(gSlur);
+
+                    /* VexFlow Version - for later use
+                    const vfSlur: VexFlowSlur = new VexFlowSlur(slur);
+                    vfOpenSlurs.push(vfSlur); //add open... adding / removing is JUST DONE in the open... array
+                    vfSlur.vfStartNote = (graphicalVoiceEntry as VexFlowVoiceEntry).vfStaveNote;
+                    vfStaffLine.addVFSlurToVFStaffline(vfSlur); // every VFSlur is added to the array in the VFStaffline!
+                    */
+                  }
+                  if (slur.EndNote === graphicalNote.sourceNote) {
+                    // Remove the Graphical Slur from the staffline if the note is the Endnote of a slur
+                    const index: number = this.findIndexGraphicalSlurFromSlur(openGraphicalSlurs, slur);
+                    if (index >= 0) {
+                      // save Voice Entry in VFSlur and then remove it from array of open VFSlurs
+                      const gSlur: GraphicalSlur = openGraphicalSlurs[index];
+                      if (gSlur.staffEntries.indexOf(graphicalStaffEntry) === -1) {
+                        gSlur.staffEntries.push(graphicalStaffEntry);
                       }
 
-                      /* VexFlow Version - for later use
-                      const vfIndex: number = this.findIndexVFSlurFromSlur(vfOpenSlurs, slur);
-                      if (vfIndex !== undefined) {
-                          // save Voice Entry in VFSlur and then remove it from array of open VFSlurs
-                          const vfSlur: VexFlowSlur = vfOpenSlurs[vfIndex];
-                          vfSlur.vfEndNote = (graphicalVoiceEntry as VexFlowVoiceEntry).vfStaveNote;
-                          vfSlur.createVexFlowCurve();
-                          vfOpenSlurs.splice(vfIndex, 1);
-                      }
-                      */
+                      openGraphicalSlurs.splice(index, 1);
+                    }
+
+                    /* VexFlow Version - for later use
+                    const vfIndex: number = this.findIndexVFSlurFromSlur(vfOpenSlurs, slur);
+                    if (vfIndex !== undefined) {
+                        // save Voice Entry in VFSlur and then remove it from array of open VFSlurs
+                        const vfSlur: VexFlowSlur = vfOpenSlurs[vfIndex];
+                        vfSlur.vfEndNote = (graphicalVoiceEntry as VexFlowVoiceEntry).vfStaveNote;
+                        vfSlur.createVexFlowCurve();
+                        vfOpenSlurs.splice(vfIndex, 1);
                     }
+                    */
                   }
                 }
               }
-              // 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) {
-                if (gSlur.staffEntries.indexOf(graphicalStaffEntry) === -1) {
-                  gSlur.staffEntries.push(graphicalStaffEntry);
-                }
+            }
+
+            //add the present Staffentry to all open slurs that don't contain this Staffentry already
+            for (const gSlur of openGraphicalSlurs) {
+              if (gSlur.staffEntries.indexOf(graphicalStaffEntry) === -1) {
+                gSlur.staffEntries.push(graphicalStaffEntry);
               }
-            } // loop over StaffEntries
-          } // loop over Measures
-        } // loop over StaffLines
+            }
+          } // loop over StaffEntries
+        } // loop over Measures
+      } // loop over StaffLines
 
-        // Attach vfSlur array to the vfStaffline to be drawn
-        //vfStaffLine.SlursInVFStaffLine = vfSlurs;
-      } // loop over MusicSystems
-    } // loop over MusicPages
+      // Attach vfSlur array to the vfStaffline to be drawn
+      //vfStaffLine.SlursInVFStaffLine = vfSlurs;
+    } // loop over MusicSystems
 
     // order slurs that were saved to the Staffline
-    for (const graphicalMusicPage of this.graphicalMusicSheet.MusicPages) {
-        for (const musicSystem of graphicalMusicPage.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);
-            for (const gSlur of sortedGSlurs) {
-                // crossed slurs will be handled later:
-                if (gSlur.slur.isCrossed()) {
-                    continue;
-                }
-                gSlur.calculateCurve(this.rules);
+    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);
+        for (const gSlur of sortedGSlurs) {
+            // crossed slurs will be handled later:
+            if (gSlur.slur.isCrossed()) {
+                continue;
             }
-          }
+            gSlur.calculateCurve(this.rules);
         }
       }
+    }
   }
 }

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

@@ -55,8 +55,8 @@ export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
         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);
+            //backend.resize(graphicalMusicSheet.ParentMusicSheet.pageWidth * unitInPixels * this.zoom,
+            //               EngravingRules.Rules.PageHeight * unitInPixels * this.zoom);
             this.pageIdx += 1;
         }
 

+ 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;
 

+ 14 - 7
src/OpenSheetMusicDisplay/OpenSheetMusicDisplay.ts

@@ -192,10 +192,17 @@ export class OpenSheetMusicDisplay {
             this.graphic.Cursors.length = 0;
         }
 
-        // Update Sheet Page
+        // Remove old backends
+        for (const backend of this.drawer.Backends) {
+            backend.removeFromContainer(this.container);
+        }
         this.drawer.Backends.clear();
+
+        // create new backends
         for (const {} of this.graphic.MusicPages) {
-            this.drawer.Backends.push(this.createBackend(this.backendType));
+            const backend: VexFlowBackend = this.createBackend(this.backendType);
+            backend.resize(width, width * 1.41);
+            this.drawer.Backends.push(backend);
         }
         this.drawer.setZoom(this.zoom);
         // Finally, draw
@@ -580,12 +587,12 @@ export class OpenSheetMusicDisplay {
         return backend;
     }
 
-    public createPdf(pdfName: string, backends: VexFlowBackend[]): void {
+    public createPdf(pdfName: string, backends: VexFlowBackend[], pageWidth: number = 210, pageHeight: number = 297): void {
         let svgElement: SVGElement = (<SvgVexFlowBackend>backends[0]).getSvgElement();
-
+        const orientation: string = pageHeight > pageWidth ? "p" : "l";
         // create a new jsPDF instance
-        const pdf: any = new jspdf("p", "pt", [svgElement.clientWidth, svgElement.clientWidth * 1.41]);
-
+        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();
@@ -594,7 +601,7 @@ export class OpenSheetMusicDisplay {
 
             // render the svg element
             svg2pdf(svgElement, pdf, {
-                scale: 1,
+                scale: scale,
                 xOffset: 0,
                 yOffset: 0
             });