Просмотр исходного кода

Merge branch 'develop' of https://github.com/opensheetmusicdisplay/opensheetmusicdisplay into fix/slur/rendering

Justin Litten 5 лет назад
Родитель
Сommit
7073212bce

+ 3 - 0
.gitignore

@@ -62,3 +62,6 @@ node_modules/
 bin/gh_pages_deploy_key
 bin/gh_pages_deploy_key.pub
 package-lock.json
+
+#local env files
+webpack.local.js

+ 1 - 1
README.md

@@ -1,4 +1,4 @@
-<img alt="OSMD logo" src="https://osmd.org/wp-content/uploads/2016/05/OSMD_3_icon_only.svg" width="200"/>
+<img alt="OSMD logo" src="https://opensheetmusicdisplay.org/wp-content/uploads/2016/05/OSMD_3_icon_only.svg" width="200"/>
 <!--img alt="Brought to you by PhonicScore" src="https://phonicscore.com/neu/wp-content/uploads/2018/06/phonicscore_brown.svg"/-->
 
 # OpenSheetMusicDisplay

+ 2 - 0
demo/index.js

@@ -36,6 +36,7 @@ import * as svg2pdf from '../node_modules/svg2pdf.js/dist/svg2pdf.min';
             "OSMD Function Test - Expressions": "OSMD_function_test_expressions.musicxml",
             "OSMD Function Test - Expressions Overlap": "OSMD_function_test_expressions_overlap.musicxml",
             "OSMD Function Test - Grace Notes": "OSMD_function_test_GraceNotes.xml",
+            "OSMD Function Test - Multiple Rest Measures": "OSMD_function_test_multiple_rest_measures.musicxml",
             "OSMD Function Test - Invisible Notes": "OSMD_function_test_invisible_notes.musicxml",
             "OSMD Function Test - Notehead Shapes": "OSMD_function_test_noteheadShapes.musicxml",
             "OSMD Function Test - Ornaments": "OSMD_function_test_Ornaments.xml",
@@ -43,6 +44,7 @@ import * as svg2pdf from '../node_modules/svg2pdf.js/dist/svg2pdf.min';
             "OSMD Function Test - System and Page Breaks": "OSMD_Function_Test_System_and_Page_Breaks_4_pages.mxl",
             "OSMD Function Test - Tabulature": "OSMD_Function_Test_Tabulature_hayden_study_1.mxl",
             "OSMD Function Test - Tremolo": "OSMD_Function_Test_Tremolo_2bars.musicxml",
+            "OSMD Function Test - Labels": "OSMD_Function_Test_Labels.musicxml",
             "OSMD Function Test - High Slur Test": "Slurtest_highNotes.musicxml",
             "Schubert, F. - An Die Musik": "Schubert_An_die_Musik.xml",
             "Actor, L. - Prelude (Large Sample, loading time)": "ActorPreludeSample.xml",

+ 5 - 4
package.json

@@ -18,6 +18,7 @@
     "build:webpack-dev": "webpack --progress --colors --config webpack.dev.js",
     "build:webpack-sourcemap": "webpack --progress --colors --config webpack.sourcemap.js",
     "start": "webpack-dev-server --progress --colors --config webpack.dev.js",
+    "start:local": "webpack-dev-server --progress --colors --config webpack.local.js",
     "generatePNG": "node ./test/Util/generateImages_browserless.js ../../build ./test/data ./export 1000 1600 allSmall --debug",
     "generatePNG:single": "node ./test/Util/generateImages_browserless.js ../../build ./test/data ./export 0 0 ^Beethoven",
     "generatePNG:legacyslow": "node ./test/Util/generateDiffImagesPuppeteerLocalhost.js ./test/data ./export 0 0 all",
@@ -26,10 +27,10 @@
     "generatePNG:paged:single": "node ./test/Util/generateImages_browserless.js ../../build ./test/data ./export 0 0 ^Beethoven",
     "generate:current": "node ./test/Util/generateImages_browserless.js ../../build ./test/data ./visual_regression/current 0 0 allSmall --osmdtesting",
     "generate:current:debug": "node ./test/Util/generateImages_browserless.js ../../build ./test/data ./visual_regression/current 0 0 allSmall --debugosmdtesting",
-    "generate:current:singletest": "node test/Util/generateImages_browserless.js ../../build ./test/data ./visual_regression/current 0 0 .*function_test_all.* --osmdtesting",
+    "generate:current:singletest": "node test/Util/generateImages_browserless.js ../../build ./test/data ./visual_regression/current 0 0 ^Beethoven --osmdtesting",
     "generate:blessed": "node ./test/Util/generateImages_browserless.js ../../build ./test/data ./visual_regression/blessed 0 0 allSmall --osmdtesting",
     "test:visual": "sh ./test/Util/visual_regression.sh ./visual_regression",
-    "test:visual:singletest": "sh ./test/Util/visual_regression.sh ./visual_regression OSMD_function_test_all",
+    "test:visual:singletest": "sh ./test/Util/visual_regression.sh ./visual_regression Beethoven",
     "fix-memory-limit": "cross-env NODE_OPTIONS=--max_old_space_size=4096"
   },
   "pre-commit": [
@@ -60,7 +61,7 @@
   },
   "homepage": "http://opensheetmusicdisplay.org",
   "dependencies": {
-    "@types/vexflow": "^1.2.33",
+    "@types/vexflow": "^1.2.36",
     "jszip": "3.4.0",
     "loglevel": "^1.6.8",
     "typescript-collections": "^1.3.3",
@@ -103,7 +104,7 @@
     "tslint-loader": "^3.5.4",
     "svg2pdf.js": "^1.5.0",
     "typedoc": "^0.17.3",
-    "typescript": "^3.7.4",
+    "typescript": "^3.9.5",
     "webpack": "^4.43.0",
     "webpack-cli": "^3.3.11",
     "webpack-dev-server": "^3.10.3",

+ 33 - 0
src/MusicalScore/Graphical/EngravingRules.ts

@@ -167,6 +167,7 @@ export class EngravingRules {
     private systemBoldLineWidth: number;
     private systemRepetitionEndingLineWidth: number;
     private systemDotWidth: number;
+    private multipleRestMeasureDefaultWidth: number;
     private distanceBetweenVerticalSystemLines: number;
     private distanceBetweenDotAndLine: number;
     private octaveShiftLineWidth: number;
@@ -183,6 +184,7 @@ export class EngravingRules {
     private metronomeMarksDrawn: boolean;
     private metronomeMarkXShift: number;
     private metronomeMarkYShift: number;
+    private softmaxFactorVexFlow: number;
     private maxInstructionsConstValue: number;
     private noteDistances: number[] = [1.0, 1.0, 1.3, 1.6, 2.0, 2.5, 3.0, 4.0];
     private noteDistancesScalingFactors: number[] = [1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0];
@@ -207,6 +209,7 @@ export class EngravingRules {
     private defaultColorTitle: string;
     private defaultFontFamily: string;
     private defaultFontStyle: FontStyles;
+    private defaultVexFlowNoteFont: string;
     private maxMeasureToDrawIndex: number;
     private minMeasureToDrawIndex: number;
     /** Whether to render a label for the composer of the piece at the top of the sheet. */
@@ -219,6 +222,7 @@ export class EngravingRules {
     private renderFingerings: boolean;
     private renderMeasureNumbers: boolean;
     private renderLyrics: boolean;
+    private renderMultipleRestMeasures: boolean;
     private renderTimeSignatures: boolean;
     private dynamicExpressionMaxDistance: number;
     private dynamicExpressionSpacer: number;
@@ -425,6 +429,8 @@ export class EngravingRules {
         this.octaveShiftVerticalLineLength = EngravingRules.unit;
         this.graceLineWidth = this.staffLineWidth * this.GraceNoteScalingFactor;
 
+        this.multipleRestMeasureDefaultWidth = 4;
+
         // Line Widths
         this.minimumCrossedBeamDifferenceMargin = 0.0001;
 
@@ -437,6 +443,7 @@ export class EngravingRules {
         this.metronomeMarksDrawn = true;
         this.metronomeMarkXShift = -6; // our unit, is taken * unitInPixels
         this.metronomeMarkYShift = -0.5;
+        this.softmaxFactorVexFlow = 5;
 
         // Render options (whether to render specific or invisible elements)
         this.alignRests = AlignRestOption.Never; // 0 = false, 1 = true, 2 = auto
@@ -456,6 +463,7 @@ export class EngravingRules {
         this.defaultColorTitle = this.defaultColorNotehead;
         this.defaultFontFamily = "Times New Roman"; // what OSMD was initially optimized for
         this.defaultFontStyle = FontStyles.Regular;
+        this.defaultVexFlowNoteFont = "gonville"; // was the default vexflow font up to vexflow 1.2.93, now it's Bravura, which is more cursive/bold
         this.maxMeasureToDrawIndex = Number.MAX_VALUE;
         this.minMeasureToDrawIndex = 0;
         this.renderComposer = true;
@@ -467,6 +475,7 @@ export class EngravingRules {
         this.renderFingerings = true;
         this.renderMeasureNumbers = true;
         this.renderLyrics = true;
+        this.renderMultipleRestMeasures = true;
         this.renderTimeSignatures = true;
         this.fingeringPosition = PlacementEnum.Left; // easier to get bounding box, and safer for vertical layout
         this.fingeringInsideStafflines = false;
@@ -1345,6 +1354,12 @@ export class EngravingRules {
     public set SystemDotWidth(value: number) {
         this.systemDotWidth = value;
     }
+    public get MultipleRestMeasureDefaultWidth(): number {
+        return this.multipleRestMeasureDefaultWidth;
+    }
+    public set MultipleRestMeasureDefaultWidth(value: number) {
+        this.multipleRestMeasureDefaultWidth = value;
+    }
     public get DistanceBetweenVerticalSystemLines(): number {
         return this.distanceBetweenVerticalSystemLines;
     }
@@ -1441,6 +1456,12 @@ export class EngravingRules {
     public set MetronomeMarkYShift(value: number) {
         this.metronomeMarkYShift = value;
     }
+    public get SoftmaxFactorVexFlow(): number {
+        return this.softmaxFactorVexFlow;
+    }
+    public set SoftmaxFactorVexFlow(value: number) {
+        this.softmaxFactorVexFlow = value;
+    }
     public get MaxInstructionsConstValue(): number {
         return this.maxInstructionsConstValue;
     }
@@ -1574,6 +1595,12 @@ export class EngravingRules {
     public set DefaultFontStyle(value: FontStyles) {
         this.defaultFontStyle = value;
     }
+    public get DefaultVexFlowNoteFont(): string {
+        return this.defaultVexFlowNoteFont;
+    }
+    public set DefaultVexFlowNoteFont(value: string) {
+        this.defaultVexFlowNoteFont = value;
+    }
     public get MaxMeasureToDrawIndex(): number {
         return this.maxMeasureToDrawIndex;
     }
@@ -1643,6 +1670,12 @@ export class EngravingRules {
     public set RenderLyrics(value: boolean) {
         this.renderLyrics = value;
     }
+    public get RenderMultipleRestMeasures(): boolean {
+        return this.renderMultipleRestMeasures;
+    }
+    public set RenderMultipleRestMeasures(value: boolean) {
+        this.renderMultipleRestMeasures = value;
+    }
     public get RenderTimeSignatures(): boolean {
         return this.renderTimeSignatures;
     }

+ 26 - 16
src/MusicalScore/Graphical/GraphicalLabel.ts

@@ -11,6 +11,7 @@ import { MusicSheetCalculator } from "./MusicSheetCalculator";
 export class GraphicalLabel extends Clickable {
     private label: Label;
     private rules: EngravingRules;
+    public TextLines: string[];
 
     /**
      * Creates a new GraphicalLabel from a Label
@@ -46,65 +47,74 @@ export class GraphicalLabel extends Clickable {
         }
         const labelMarginBorderFactor: number = this.rules?.LabelMarginBorderFactor ?? 0.1;
 
-        const widthToHeightRatio: number =
+        this.TextLines = this.Label.text.split(/[\n\r]+/g);
+        const numOfLines: number = this.TextLines.length;
+        let maxWidth: number = 0;
+        for (let i: number = 0; i < numOfLines; i++) {
+            this.TextLines[i] = this.TextLines[i].trim();
+            const line: string = this.TextLines[i];
+            const widthToHeightRatio: number =
             MusicSheetCalculator.TextMeasurer.computeTextWidthToHeightRatio(
-                this.Label.text, this.Label.font, this.Label.fontStyle, this.label.fontFamily);
-        const height: number = this.Label.fontHeight;
-        const width: number = height * widthToHeightRatio;
+               line, this.Label.font, this.Label.fontStyle, this.label.fontFamily);
+            const currWidth: number = this.Label.fontHeight * widthToHeightRatio;
+            maxWidth = Math.max(maxWidth, currWidth);
+        }
+
+        const height: number = this.Label.fontHeight * numOfLines;
         const bbox: BoundingBox = this.PositionAndShape;
 
         switch (this.Label.textAlignment) {
             case TextAlignmentEnum.CenterBottom:
                 bbox.BorderTop = -height;
-                bbox.BorderLeft = -width / 2;
+                bbox.BorderLeft = -maxWidth / 2;
                 bbox.BorderBottom = 0;
-                bbox.BorderRight = width / 2;
+                bbox.BorderRight = maxWidth / 2;
                 break;
             case TextAlignmentEnum.CenterCenter:
                 bbox.BorderTop = -height / 2;
-                bbox.BorderLeft = -width / 2;
+                bbox.BorderLeft = -maxWidth / 2;
                 bbox.BorderBottom = height / 2;
-                bbox.BorderRight = width / 2;
+                bbox.BorderRight = maxWidth / 2;
                 break;
             case TextAlignmentEnum.CenterTop:
                 bbox.BorderTop = 0;
-                bbox.BorderLeft = -width / 2;
+                bbox.BorderLeft = -maxWidth / 2;
                 bbox.BorderBottom = height;
-                bbox.BorderRight = width / 2;
+                bbox.BorderRight = maxWidth / 2;
                 break;
             case TextAlignmentEnum.LeftBottom:
                 bbox.BorderTop = -height;
                 bbox.BorderLeft = 0;
                 bbox.BorderBottom = 0;
-                bbox.BorderRight = width;
+                bbox.BorderRight = maxWidth;
                 break;
             case TextAlignmentEnum.LeftCenter:
                 bbox.BorderTop = -height / 2;
                 bbox.BorderLeft = 0;
                 bbox.BorderBottom = height / 2;
-                bbox.BorderRight = width;
+                bbox.BorderRight = maxWidth;
                 break;
             case TextAlignmentEnum.LeftTop:
                 bbox.BorderTop = 0;
                 bbox.BorderLeft = 0;
                 bbox.BorderBottom = height;
-                bbox.BorderRight = width;
+                bbox.BorderRight = maxWidth;
                 break;
             case TextAlignmentEnum.RightBottom:
                 bbox.BorderTop = -height;
-                bbox.BorderLeft = -width;
+                bbox.BorderLeft = -maxWidth;
                 bbox.BorderBottom = 0;
                 bbox.BorderRight = 0;
                 break;
             case TextAlignmentEnum.RightCenter:
                 bbox.BorderTop = -height / 2;
-                bbox.BorderLeft = -width;
+                bbox.BorderLeft = -maxWidth;
                 bbox.BorderBottom = height / 2;
                 bbox.BorderRight = 0;
                 break;
             case TextAlignmentEnum.RightTop:
                 bbox.BorderTop = 0;
-                bbox.BorderLeft = -width;
+                bbox.BorderLeft = -maxWidth;
                 bbox.BorderBottom = height;
                 bbox.BorderRight = 0;
                 break;

+ 3 - 0
src/MusicalScore/Graphical/GraphicalMusicSheet.ts

@@ -213,6 +213,9 @@ export class GraphicalMusicSheet {
     public findGraphicalStaffEntryFromMeasureList(staffIndex: number, measureIndex: number, sourceStaffEntry: SourceStaffEntry): GraphicalStaffEntry {
         for (let i: number = measureIndex; i < this.measureList.length; i++) {
             const graphicalMeasure: GraphicalMeasure = this.measureList[i][staffIndex];
+            if (!graphicalMeasure) {
+                continue;
+            }
             for (let idx: number = 0, len: number = graphicalMeasure.staffEntries.length; idx < len; ++idx) {
                 const graphicalStaffEntry: GraphicalStaffEntry = graphicalMeasure.staffEntries[idx];
                 if (graphicalStaffEntry.sourceStaffEntry === sourceStaffEntry) {

+ 62 - 4
src/MusicalScore/Graphical/MusicSheetCalculator.ts

@@ -105,7 +105,9 @@ export abstract class MusicSheetCalculator {
     protected static setMeasuresMinStaffEntriesWidth(measures: GraphicalMeasure[], minimumStaffEntriesWidth: number): void {
         for (let idx: number = 0, len: number = measures.length; idx < len; ++idx) {
             const measure: GraphicalMeasure = measures[idx];
-            measure.minimumStaffEntriesWidth = minimumStaffEntriesWidth;
+            if (measure) {
+                measure.minimumStaffEntriesWidth = minimumStaffEntriesWidth;
+            }
         }
     }
 
@@ -165,6 +167,16 @@ export abstract class MusicSheetCalculator {
                 activeClefs
             );
             measureList.push(graphicalMeasures);
+            if (sourceMeasure.multipleRestMeasures && this.rules.RenderMultipleRestMeasures) {
+                const measuresToSkip: number = sourceMeasure.multipleRestMeasures - 1;
+                // console.log(`skipping ${measuresToSkip} measures for measure #${sourceMeasure.MeasureNumber}.`);
+                idx += measuresToSkip;
+                for (let idx2: number = 0; idx2 < measuresToSkip; idx2++) {
+                    measureList.push([undefined]);
+                    // TODO we could push an object here or push nothing entirely,
+                    //   but then the index doesn't correspond to measure numbers anymore.
+                }
+            }
         }
 
         const staffIsPercussionArray: Array<boolean> =
@@ -659,7 +671,7 @@ export abstract class MusicSheetCalculator {
             for (let idx2: number = 0, len2: number = graphicalMeasures.length; idx2 < len2; ++idx2) {
                 const graphicalMeasure: GraphicalMeasure = allMeasures[idx][idx2];
 
-                if (graphicalMeasure.isVisible()) {
+                if (graphicalMeasure?.isVisible()) {
                     visiblegraphicalMeasures.push(graphicalMeasure);
 
                     if (this.rules.ColoringEnabled) {
@@ -1835,6 +1847,9 @@ export abstract class MusicSheetCalculator {
             subtitle.PositionAndShape.RelativePosition = relative;
             page.Labels.push(subtitle);
         }
+        //Get the first system, first staffline skybottomcalculator
+        const topStaffline: StaffLine = page.MusicSystems[0].StaffLines[0];
+        const skyBottomLineCalculator: SkyBottomLineCalculator = topStaffline.SkyBottomLineCalculator;
         const composer: GraphicalLabel = this.graphicalMusicSheet.Composer;
         if (composer) {
             composer.PositionAndShape.Parent = page.PositionAndShape; // if using pageWidth. (which can currently be too wide) TODO fix pageWidth (#578)
@@ -1848,12 +1863,27 @@ export abstract class MusicSheetCalculator {
             //  firstStaffLineEndX); // awkward with 2-bar score
             relative.x = this.graphicalMusicSheet.ParentMusicSheet.pageWidth - this.rules.PageRightMargin;
             //relative.x = firstStaffLine.PositionAndShape.Size.width;
-            relative.y = firstSystemAbsoluteTopMargin - this.rules.SystemComposerDistance;
+            //when this is less, goes higher.
+            //So 0 is top of the sheet, 22 or so is touching the music system margin
+            relative.y = firstSystemAbsoluteTopMargin;
             //relative.y = - this.rules.SystemComposerDistance;
             //relative.y = -firstStaffLine.PositionAndShape.Size.height;
             // TODO only add measure label height if rendering labels and composer measure has label
             // TODO y-align with lyricist? which is harder if they have different bbox parents (page and firstStaffLine).
             // when the pageWidth gets fixed, we could use page as parent again.
+            if (!composer.TextLines || composer.TextLines?.length === 1) {
+                //Don't want to affect existing behavior
+                relative.y -= this.rules.SystemComposerDistance;
+            } else {
+                //Sufficient for now to just use the longest composer entry instead of bottom.
+                //Otherwise we need to construct a 'bottom line' for the text block
+                const endX: number = topStaffline.PositionAndShape.BorderMarginRight;
+                const startX: number = endX - composer.PositionAndShape.Size.width;
+
+                const currentMin: number = skyBottomLineCalculator.getSkyLineMinInRange(startX, endX);
+                relative.y += currentMin - composer.PositionAndShape.BorderBottom;
+                skyBottomLineCalculator.updateSkyLineInRange(startX, endX, currentMin - composer.PositionAndShape.MarginSize.height);
+            }
             composer.PositionAndShape.RelativePosition = relative;
             page.Labels.push(composer);
         }
@@ -1863,7 +1893,17 @@ export abstract class MusicSheetCalculator {
             lyricist.setLabelPositionAndShapeBorders();
             const relative: PointF2D = new PointF2D();
             relative.x = this.rules.PageLeftMargin;
-            relative.y = firstSystemAbsoluteTopMargin - this.rules.SystemComposerDistance;
+            relative.y = firstSystemAbsoluteTopMargin;
+            if (!lyricist.TextLines || lyricist.TextLines?.length === 1) {
+                relative.y -= this.rules.SystemComposerDistance;
+            } else {
+                const startX: number = topStaffline.PositionAndShape.BorderMarginLeft - relative.x;
+                const endX: number = startX + lyricist.PositionAndShape.Size.width;
+
+                const currentMin: number = skyBottomLineCalculator.getSkyLineMinInRange(startX, endX);
+                relative.y += currentMin - lyricist.PositionAndShape.BorderBottom;
+                skyBottomLineCalculator.updateSkyLineInRange(startX, endX, currentMin - lyricist.PositionAndShape.MarginSize.height);
+            }
             //relative.y = Math.max(relative.y, composer.PositionAndShape.RelativePosition.y);
             lyricist.PositionAndShape.RelativePosition = relative;
             page.Labels.push(lyricist);
@@ -1948,6 +1988,9 @@ export abstract class MusicSheetCalculator {
         for (let i: number = 0; i < this.graphicalMusicSheet.MeasureList.length; i++) {
             for (let j: number = 0; j < numberOfEntries; j++) {
                 const measure: GraphicalMeasure = this.graphicalMusicSheet.MeasureList[i][j];
+                if (!measure) {
+                    continue;
+                }
                 for (let idx: number = 0, len: number = measure.staffEntries.length; idx < len; ++idx) {
                     const graphicalStaffEntry: GraphicalStaffEntry = measure.staffEntries[idx];
                     const verticalContainer: VerticalGraphicalStaffEntryContainer =
@@ -1998,6 +2041,8 @@ export abstract class MusicSheetCalculator {
         if (activeClefs[staffIndex].ClefType === ClefEnum.TAB) {
             staff.isTab = true;
             measure = MusicSheetCalculator.symbolFactory.createTabStaffMeasure(sourceMeasure, staff);
+        } else if (sourceMeasure.multipleRestMeasures && this.rules.RenderMultipleRestMeasures) {
+            measure = MusicSheetCalculator.symbolFactory.createMultiRestMeasure(sourceMeasure, staff);
         } else {
             measure = MusicSheetCalculator.symbolFactory.createGraphicalMeasure(sourceMeasure, staff);
         }
@@ -2186,6 +2231,9 @@ export abstract class MusicSheetCalculator {
             const measures: GraphicalMeasure[] = this.graphicalMusicSheet.MeasureList[idx];
             for (let idx2: number = 0, len2: number = measures.length; idx2 < len2; ++idx2) {
                 const measure: GraphicalMeasure = measures[idx2];
+                if (!measure) {
+                    continue;
+                }
                 //This property is active...
                 if (this.rules.PercussionOneLineCutoff !== undefined && this.rules.PercussionOneLineCutoff !== 0) {
                     //We have a percussion clef, check to see if this property applies...
@@ -2686,6 +2734,10 @@ export abstract class MusicSheetCalculator {
         for (let i: number = minIndex; i <= maxIndex; i++) {
             const sourceMeasure: SourceMeasure = this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures[i];
             for (let j: number = 0; j < sourceMeasure.StaffLinkedExpressions.length; j++) {
+                if (!this.graphicalMusicSheet.MeasureList[i] || !this.graphicalMusicSheet.MeasureList[i][j]) {
+                    continue;
+                }
+
                 if (this.graphicalMusicSheet.MeasureList[i][j].ParentStaff.ParentInstrument.Visible) {
                     for (let k: number = 0; k < sourceMeasure.StaffLinkedExpressions[j].length; k++) {
                         if (sourceMeasure.StaffLinkedExpressions[j][k].InstantaneousDynamic !== undefined ||
@@ -2705,6 +2757,9 @@ export abstract class MusicSheetCalculator {
         for (let i: number = 0; i < this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures.length; i++) {
             const sourceMeasure: SourceMeasure = this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures[i];
             for (let j: number = 0; j < sourceMeasure.StaffLinkedExpressions.length; j++) {
+                if (!this.graphicalMusicSheet.MeasureList[i] || !this.graphicalMusicSheet.MeasureList[i][j]) {
+                    continue;
+                }
                 if (this.graphicalMusicSheet.MeasureList[i][j].ParentStaff.ParentInstrument.Visible) {
                     for (let k: number = 0; k < sourceMeasure.StaffLinkedExpressions[j].length; k++) {
                         if ((sourceMeasure.StaffLinkedExpressions[j][k].OctaveShiftStart)) {
@@ -2777,6 +2832,9 @@ export abstract class MusicSheetCalculator {
         for (let i: number = 0; i < this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures.length; i++) {
             const sourceMeasure: SourceMeasure = this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures[i];
             for (let j: number = 0; j < sourceMeasure.StaffLinkedExpressions.length; j++) {
+                if (!this.graphicalMusicSheet.MeasureList[i] || !this.graphicalMusicSheet.MeasureList[i][j]) {
+                    continue;
+                }
                 if (this.graphicalMusicSheet.MeasureList[i][j].ParentStaff.ParentInstrument.Visible) {
                     for (let k: number = 0; k < sourceMeasure.StaffLinkedExpressions[j].length; k++) {
                         if ((sourceMeasure.StaffLinkedExpressions[j][k].MoodList.length > 0) ||

+ 7 - 4
src/MusicalScore/Graphical/MusicSheetDrawer.ts

@@ -58,6 +58,7 @@ export abstract class MusicSheetDrawer {
         this.textMeasurer = textMeasurer;
         this.splitScreenLineColor = -1;
         this.drawingParameters = drawingParameters;
+        this.rules = drawingParameters.Rules;
     }
 
     public set Mode(value: PhonicScoreModes) {
@@ -147,10 +148,11 @@ export abstract class MusicSheetDrawer {
             return;
         }
         const screenPosition: PointF2D = this.applyScreenTransformation(graphicalLabel.PositionAndShape.AbsolutePosition);
-        const heightInPixel: number = this.calculatePixelDistance(label.fontHeight);
-        const widthInPixel: number = heightInPixel * this.textMeasurer.computeTextWidthToHeightRatio(label.text, label.font, label.fontStyle);
+        const fontHeightInPixel: number = this.calculatePixelDistance(label.fontHeight);
+        const widthInPixel: number = this.calculatePixelDistance(graphicalLabel.PositionAndShape.Size.width);
         const bitmapWidth: number = Math.ceil(widthInPixel);
-        const bitmapHeight: number = Math.ceil(heightInPixel * 1.2);
+        const bitmapHeight: number = Math.ceil(fontHeightInPixel * (0.2 + graphicalLabel.TextLines.length));
+
         switch (label.textAlignment) {
             // Adjust the OSMD-calculated positions to rendering coordinates
             // These have to match the Border settings in GraphicalLabel.setLabelPositionAndShapeBorders()
@@ -188,7 +190,8 @@ export abstract class MusicSheetDrawer {
             default:
                 throw new ArgumentOutOfRangeException("");
         }
-        this.renderLabel(graphicalLabel, layer, bitmapWidth, bitmapHeight, heightInPixel, screenPosition);
+
+        this.renderLabel(graphicalLabel, layer, bitmapWidth, bitmapHeight, fontHeightInPixel, screenPosition);
     }
 
     protected applyScreenTransformation(point: PointF2D): PointF2D {

+ 69 - 17
src/MusicalScore/Graphical/MusicSystemBuilder.ts

@@ -62,16 +62,21 @@ export class MusicSystemBuilder {
         // the first System - create also its Labels
         this.currentSystemParams.currentSystem = this.initMusicSystem();
 
-        let numberOfMeasures: number = 0;
-        for (let idx: number = 0, len: number = this.measureList.length; idx < len; ++idx) {
-            if (this.measureList[idx].length > 0) {
-                numberOfMeasures++;
-            }
-        }
+        // let numberOfMeasures: number = 0;
+        // for (let idx: number = 0, len: number = this.measureList.length; idx < len; ++idx) {
+        //     if (this.measureList[idx].length > 0) {
+        //         numberOfMeasures++;
+        //     }
+        // }
+        // console.log(`numberOfMeasures: ${numberOfMeasures}`);
 
         // go through measures and add to system until system gets too long -> finish system and start next system [line break, new system].
-        while (this.measureListIndex < numberOfMeasures) {
+        while (this.measureListIndex < this.measureList.length) {
             const graphicalMeasures: GraphicalMeasure[] = this.measureList[this.measureListIndex];
+            if (!graphicalMeasures || !graphicalMeasures[0]) {
+                this.measureListIndex++;
+                continue; // previous measure was probably multi-rest, skip this one
+            }
             for (let idx: number = 0, len: number = graphicalMeasures.length; idx < len; ++idx) {
                 graphicalMeasures[idx].resetLayout();
             }
@@ -114,12 +119,16 @@ export class MusicSystemBuilder {
             let nextSourceMeasure: SourceMeasure = undefined;
             if (this.measureListIndex + 1 < this.measureList.length) {
                 const nextGraphicalMeasures: GraphicalMeasure[] = this.measureList[this.measureListIndex + 1];
-                nextSourceMeasure = nextGraphicalMeasures[0].parentSourceMeasure;
-                if (nextSourceMeasure.hasBeginInstructions()) {
+                // TODO: consider multirest. then the next graphical measure may not exist. but there shouldn't be hidden changes here.
+                nextSourceMeasure = nextGraphicalMeasures[0]?.parentSourceMeasure;
+                if (nextSourceMeasure?.hasBeginInstructions()) {
                     nextMeasureBeginInstructionWidth += this.addBeginInstructions(nextGraphicalMeasures, false, false);
                 }
             }
-            const totalMeasureWidth: number = currentMeasureBeginInstructionsWidth + currentMeasureEndInstructionsWidth + currentMeasureVarWidth;
+            let totalMeasureWidth: number = currentMeasureBeginInstructionsWidth + currentMeasureEndInstructionsWidth + currentMeasureVarWidth;
+            if (graphicalMeasures[0]?.parentSourceMeasure?.multipleRestMeasures) {
+                totalMeasureWidth = this.rules.MultipleRestMeasureDefaultWidth; // default 4 (12 seems too large)
+            }
             const measureFitsInSystem: boolean = this.currentSystemParams.currentWidth + totalMeasureWidth + nextMeasureBeginInstructionWidth < systemMaxWidth;
             const doXmlPageBreak: boolean = this.rules.NewPageAtXMLNewPageAttribute && sourceMeasure.printNewPageXml;
             const doXmlLineBreak: boolean = doXmlPageBreak || // also create new system if doing page break
@@ -665,6 +674,16 @@ export class MusicSystemBuilder {
         let sourceMeasure: SourceMeasure = undefined;
         try {
             sourceMeasure = this.measureList[this.measureListIndex][0].parentSourceMeasure;
+            if (this.rules.RenderMultipleRestMeasures && sourceMeasure.multipleRestMeasures > 1) {
+                const newIndex: number = Math.min(
+                    this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures.length - 1, // safety check
+                    sourceMeasure.measureListIndex + sourceMeasure.multipleRestMeasures - 1
+                    // check the bar line of the last sourcemeasure in the multiple measure rest sequence
+                );
+                sourceMeasure = this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures[newIndex];
+                // sourceMeasure = this.measureList[this.measureListIndex + sourceMeasure.multipleRestMeasures - 1][0].parentSourceMeasure;
+                //    this will be possible when the other GraphicalMeasures in the measureList aren't undefined anymore
+            }
         } finally {
             // do nothing
         }
@@ -816,7 +835,7 @@ export class MusicSystemBuilder {
     protected getNextMeasureKeyInstruction(): KeyInstruction {
         if (this.measureListIndex < this.measureList.length - 1) {
             for (let visIndex: number = 0; visIndex < this.measureList[this.measureListIndex].length; visIndex++) {
-                const sourceMeasure: SourceMeasure = this.measureList[this.measureListIndex + 1][visIndex].parentSourceMeasure;
+                const sourceMeasure: SourceMeasure = this.measureList[this.measureListIndex + 1][visIndex]?.parentSourceMeasure;
                 if (!sourceMeasure) {
                     return undefined;
                 }
@@ -1018,11 +1037,44 @@ export class MusicSystemBuilder {
                     currentYPosition = this.rules.PageTopMargin;
                 }
 
-                // if it is the first System on the FIRST page: Add Title height and gap-distance
-                if (this.graphicalMusicSheet.MusicPages.length === 1 &&
-                    this.rules.RenderTitle) {
-                    currentYPosition +=   this.rules.TitleTopDistance + this.rules.SheetTitleHeight +
+                if (this.graphicalMusicSheet.MusicPages.length === 1) {
+                    /*
+                    Only need this in the event that lyricist or composer text intersects with title,
+                    which seems exceedingly rare.
+                    Leaving here just in case for future needs.
+                    Prefer to use skyline calculator in MusicSheetCalculator.calculatePageLabels
+
+                    let maxLineCount: number = this.graphicalMusicSheet.Composer?.TextLines?.length;
+                    let maxFontHeight: number = this.graphicalMusicSheet.Composer?.Label?.fontHeight;
+                    let lyricistLineCount: number = this.graphicalMusicSheet.Lyricist?.TextLines?.length;
+                    let lyricistFontHeight: number = this.graphicalMusicSheet.Lyricist?.Label?.fontHeight;
+
+                    maxLineCount = maxLineCount ? maxLineCount : 0;
+                    maxFontHeight = maxFontHeight ? maxFontHeight : 0;
+                    lyricistLineCount = lyricistLineCount ? lyricistLineCount : 0;
+                    lyricistFontHeight = lyricistFontHeight ? lyricistFontHeight : 0;
+
+                    let maxHeight: number = maxLineCount * maxFontHeight;
+                    const totalLyricist: number = lyricistLineCount * lyricistFontHeight;
+
+                    if (totalLyricist > maxHeight) {
+                        maxLineCount = lyricistLineCount;
+                        maxFontHeight = lyricistFontHeight;
+                        maxHeight = totalLyricist;
+                    } */
+
+                    if (this.rules.RenderTitle) {
+                    // if it is the first System on the FIRST page: Add Title height and gap-distance
+                    currentYPosition += this.rules.TitleTopDistance + this.rules.SheetTitleHeight +
                                             this.rules.TitleBottomDistance;
+                    }
+
+                    /*
+                    see comment above - only needed for rare case of composer/lyricist being
+                    wide enough to be below the title (or title wide enough to be above)
+                    if (maxLineCount > 2) {
+                        currentYPosition += maxFontHeight * (maxLineCount - 2);
+                    }*/
                 }
                 // now add the border-top: everything that stands out above the staffline:
                 currentYPosition += -currentSystem.PositionAndShape.BorderTop;
@@ -1047,7 +1099,7 @@ export class MusicSystemBuilder {
                 const previousSystem: MusicSystem = this.musicSystems[i - 1];
                 const prevSystemLastStaffline: StaffLine = previousSystem.StaffLines[previousSystem.StaffLines.length - 1];
                 const prevSystemLastStaffLineBB: BoundingBox = prevSystemLastStaffline.PositionAndShape;
-                let distance: number =  this.findReqiredDistanceWithSkyBottomLine(previousSystem, currentSystem);
+                let distance: number =  this.findRequiredDistanceWithSkyBottomLine(previousSystem, currentSystem);
 
                 // make sure the optical distance is the user-defined min distance:
                 distance += this.rules.MinSkyBottomDistBetweenSystems;
@@ -1090,7 +1142,7 @@ export class MusicSystemBuilder {
      * @param upperSystem
      * @param lowerSystem
      */
-    private findReqiredDistanceWithSkyBottomLine(upperSystem: MusicSystem, lowerSystem: MusicSystem): number {
+    private findRequiredDistanceWithSkyBottomLine(upperSystem: MusicSystem, lowerSystem: MusicSystem): number {
         const upperSystemLastStaffLine: StaffLine = upperSystem.StaffLines[upperSystem.StaffLines.length - 1];
         const lowerSystemFirstStaffLine: StaffLine = lowerSystem.StaffLines[0];
         const upperBottomLineArray: number[] = upperSystemLastStaffLine.BottomLine;

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

@@ -64,7 +64,7 @@ export class SkyBottomLineCalculator {
             const oldMeasureWidth: number = vsStaff.getWidth();
             // We need to tell the VexFlow stave about the canvas width. This looks
             // redundant because it should know the canvas but somehow it doesn't.
-            // Maybe I am overlooking something but for no this does the trick
+            // Maybe I am overlooking something but for now this does the trick
             vsStaff.setWidth(width);
             measure.format();
             vsStaff.setWidth(oldMeasureWidth);

+ 11 - 0
src/MusicalScore/Graphical/VexFlow/VexFlowGraphicalSymbolFactory.ts

@@ -28,6 +28,7 @@ import { VexFlowConverter } from "./VexFlowConverter";
 import { VexFlowTabMeasure } from "./VexFlowTabMeasure";
 import { VexFlowStaffLine } from "./VexFlowStaffLine";
 import { KeyInstruction } from "../../VoiceData/Instructions/KeyInstruction";
+import { VexFlowMultiRestMeasure } from "./VexFlowMultiRestMeasure";
 
 export class VexFlowGraphicalSymbolFactory implements IGraphicalSymbolFactory {
     /**
@@ -62,6 +63,16 @@ export class VexFlowGraphicalSymbolFactory implements IGraphicalSymbolFactory {
     }
 
     /**
+     * Construct a MultiRestMeasure from the given source measure and staff.
+     * @param sourceMeasure
+     * @param staff
+     * @returns {VexFlowMultiRestMeasure}
+     */
+    public createMultiRestMeasure(sourceMeasure: SourceMeasure, staff: Staff, staffLine?: StaffLine): GraphicalMeasure {
+        return new VexFlowMultiRestMeasure(staff, sourceMeasure, staffLine);
+    }
+
+    /**
      * Construct an empty Tab staffMeasure from the given source measure and staff.
      * @param sourceMeasure
      * @param staff

+ 1 - 1
src/MusicalScore/Graphical/VexFlow/VexFlowMeasure.ts

@@ -375,7 +375,7 @@ export class VexFlowMeasure extends GraphicalMeasure {
         this.addVolta(repetitionInstruction);
     }
 
-    private addVolta(repetitionInstruction: RepetitionInstruction): void {
+    protected addVolta(repetitionInstruction: RepetitionInstruction): void {
         let voltaType: number = Vex.Flow.Volta.type.BEGIN;
         if (repetitionInstruction.type === RepetitionInstructionEnum.Ending) {
             switch (repetitionInstruction.alignment) {

+ 166 - 0
src/MusicalScore/Graphical/VexFlow/VexFlowMultiRestMeasure.ts

@@ -0,0 +1,166 @@
+import Vex from "vexflow";
+import {SourceMeasure} from "../../VoiceData/SourceMeasure";
+import {Staff} from "../../VoiceData/Staff";
+import {StaffLine} from "../StaffLine";
+import {Beam} from "../../VoiceData/Beam";
+import {GraphicalNote} from "../GraphicalNote";
+import {GraphicalStaffEntry} from "../GraphicalStaffEntry";
+import {Tuplet} from "../../VoiceData/Tuplet";
+import {GraphicalVoiceEntry} from "../GraphicalVoiceEntry";
+import {Voice} from "../../VoiceData/Voice";
+import {VexFlowMeasure} from "./VexFlowMeasure";
+
+// type StemmableNote = Vex.Flow.StemmableNote;
+
+/** A GraphicalMeasure drawing a multiple-rest measure in Vexflow.
+ *  Mostly copied from VexFlowMeasure.
+ *  Even though most of those functions aren't needed, apparently you can't remove the layoutStaffEntry function.
+ */
+export class VexFlowMultiRestMeasure extends VexFlowMeasure {
+    private multiRestElement: any; // VexFlow: Element
+
+    constructor(staff: Staff, sourceMeasure: SourceMeasure = undefined, staffLine: StaffLine = undefined) {
+        super(staff, sourceMeasure, staffLine);
+        this.minimumStaffEntriesWidth = -1;
+
+        /*
+         * There is no case in which `staffLine === undefined && sourceMeasure === undefined` holds.
+         * Hence, it is not necessary to specify an `else` case.
+         * One can verify this through a usage search for this constructor.
+         */
+        if (staffLine) {
+            this.rules = staffLine.ParentMusicSystem.rules;
+        } else if (sourceMeasure) {
+            this.rules = sourceMeasure.Rules;
+        }
+
+        this.resetLayout();
+
+        // type note: Vex.Flow.MultiMeasureRest is not in the DefinitelyTyped definitions yet.
+        this.multiRestElement = new Vex.Flow.MultiMeasureRest(sourceMeasure.multipleRestMeasures, {
+            // number_line: 3
+        });
+    }
+
+
+    /**
+     * This method is called after the StaffEntriesScaleFactor has been set.
+     * Here the final x-positions of the staff entries have to be set.
+     * (multiply the minimal positions with the scaling factor, considering the BeginInstructionsWidth)
+     */
+    public layoutSymbols(): void {
+        // vexflow does the x-layout
+    }
+
+    /**
+     * Draw this measure on a VexFlow CanvasContext
+     * @param ctx
+     */
+    public draw(ctx: Vex.IRenderContext): void {
+        // Draw stave lines
+        this.stave.setContext(ctx).draw();
+
+        this.multiRestElement.setStave(this.stave);
+        this.multiRestElement.setContext(ctx);
+        this.multiRestElement.draw();
+
+        // Draw vertical lines
+        for (const connector of this.connectors) {
+            connector.setContext(ctx).draw();
+        }
+    }
+
+    public format(): void {
+        // return
+    }
+
+    /**
+     * Returns all the voices that are present in this measure
+     */
+    public getVoicesWithinMeasure(): Voice[] {
+        return [];
+    }
+
+    /**
+     * Returns all the graphicalVoiceEntries of a given Voice.
+     * @param voice the voice for which the graphicalVoiceEntries shall be returned.
+     */
+    public getGraphicalVoiceEntriesPerVoice(voice: Voice): GraphicalVoiceEntry[] {
+        return [];
+    }
+
+    /**
+     * Finds the gaps between the existing notes within a measure.
+     * Problem here is, that the graphicalVoiceEntry does not exist yet and
+     * that Tied notes are not present in the normal voiceEntries.
+     * To handle this, calculation with absolute timestamps is needed.
+     * And the graphical notes have to be analysed directly (and not the voiceEntries, as it actually should be -> needs refactoring)
+     * @param voice the voice for which the ghost notes shall be searched.
+     */
+    protected getRestFilledVexFlowStaveNotesPerVoice(voice: Voice): GraphicalVoiceEntry[] {
+        return [];
+    }
+
+    /**
+     * Add a note to a beam
+     * @param graphicalNote
+     * @param beam
+     */
+    public handleBeam(graphicalNote: GraphicalNote, beam: Beam): void {
+        return;
+    }
+
+    public handleTuplet(graphicalNote: GraphicalNote, tuplet: Tuplet): void {
+        return;
+    }
+
+    /**
+     * Complete the creation of VexFlow Beams in this measure
+     */
+    public finalizeBeams(): void {
+        return;
+    }
+
+    /**
+     * Complete the creation of VexFlow Tuplets in this measure
+     */
+    public finalizeTuplets(): void {
+        return;
+    }
+
+    // this needs to exist, for some reason, or it won't be found, even though i can't find the usage.
+    public layoutStaffEntry(graphicalStaffEntry: GraphicalStaffEntry): void {
+        return;
+    }
+
+    public graphicalMeasureCreatedCalculations(): void {
+        return;
+    }
+
+
+    /**
+     * Create the articulations for all notes of the current staff entry
+     */
+    protected createArticulations(): void {
+        return;
+    }
+
+    /**
+     * Create the ornaments for all notes of the current staff entry
+     */
+    protected createOrnaments(): void {
+        return;
+    }
+
+    protected createFingerings(voiceEntry: GraphicalVoiceEntry): void {
+        return;
+    }
+
+    /**
+     * Return the VexFlow Stave corresponding to this graphicalMeasure
+     * @returns {Vex.Flow.Stave}
+     */
+    public getVFStave(): Vex.Flow.Stave {
+        return this.stave;
+    }
+}

+ 21 - 2
src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetCalculator.ts

@@ -61,6 +61,12 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
     MusicSheetCalculator.symbolFactory = new VexFlowGraphicalSymbolFactory();
     MusicSheetCalculator.TextMeasurer = new VexFlowTextMeasurer(this.rules);
     MusicSheetCalculator.stafflineNoteCalculator = new VexflowStafflineNoteCalculator(this.rules);
+
+    // prepare Vexflow font (doesn't affect Vexflow 1.x). It seems like this has to be done here for now, otherwise it's too slow for the generateImages script.
+    //   (first image will have the non-updated font, in this case the Vexflow default Bravura, while we want Gonville here)
+    if (this.rules.DefaultVexFlowNoteFont === "gonville") {
+      (Vex.Flow as any).DEFAULT_FONT_STACK = [(Vex.Flow as any).Fonts?.Gonville, (Vex.Flow as any).Fonts?.Bravura, (Vex.Flow as any).Fonts?.Custom];
+    } // else keep new vexflow default Bravura (more cursive, bold)
   }
 
   protected clearRecreatedObjects(): void {
@@ -68,7 +74,7 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
     MusicSheetCalculator.stafflineNoteCalculator = new VexflowStafflineNoteCalculator(this.rules);
     for (const graphicalMeasures of this.graphicalMusicSheet.MeasureList) {
       for (const graphicalMeasure of graphicalMeasures) {
-        (<VexFlowMeasure>graphicalMeasure).clean();
+        (<VexFlowMeasure>graphicalMeasure)?.clean();
       }
     }
   }
@@ -76,6 +82,9 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
   protected formatMeasures(): void {
     // let totalFinalizeBeamsTime: number = 0;
     for (const verticalMeasureList of this.graphicalMusicSheet.MeasureList) {
+      if (!verticalMeasureList || !verticalMeasureList[0]) {
+        continue;
+      }
       const firstMeasure: VexFlowMeasure = verticalMeasureList[0] as VexFlowMeasure;
       // first measure has formatting method as lambda function object, but formats all measures. TODO this could be refactored
       firstMeasure.format();
@@ -120,9 +129,13 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
 
     // Format the voices
     const allVoices: Vex.Flow.Voice[] = [];
-    const formatter: Vex.Flow.Formatter = new Vex.Flow.Formatter();
+    // TODO: remove the any when the new DefinitelyTyped PR is through and update released
+    const formatter: Vex.Flow.Formatter = new (Vex.Flow as any).Formatter({softmaxFactor: this.rules.SoftmaxFactorVexFlow});
 
     for (const measure of measures) {
+      if (!measure) {
+        continue;
+      }
       const mvoices: { [voiceID: number]: Vex.Flow.Voice; } = (measure as VexFlowMeasure).vfVoices;
       const voices: Vex.Flow.Voice[] = [];
       for (const voiceID in mvoices) {
@@ -220,6 +233,9 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
     }
 
     for (const graphicalMeasure of measures) {
+      if (!graphicalMeasure) {
+        continue;
+      }
       for (const staffEntry of graphicalMeasure.staffEntries) {
         // here the measure modifiers are not yet set, therefore the begin instruction width will be empty
         (<VexFlowStaffEntry>staffEntry).calculateXPosition();
@@ -247,6 +263,9 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
     }
 
     for (const measure of measuresVertical) {
+      if (!measure) {
+        continue;
+      }
       const lastLyricEntryDict: LyricEntryDict = {}; // holds info about last lyrics entries for all verses j
 
       // for all staffEntries i, each containing the lyric entry for all verses at that timestamp in the measure

+ 13 - 3
src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetDrawer.ts

@@ -51,6 +51,11 @@ export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
     }
 
     public drawSheet(graphicalMusicSheet: GraphicalMusicSheet): void {
+        // vexflow 3.x: change default font
+        if (this.rules.DefaultVexFlowNoteFont === "gonville") {
+            (Vex.Flow as any).DEFAULT_FONT_STACK = [(Vex.Flow as any).Fonts?.Gonville, (Vex.Flow as any).Fonts?.Bravura, (Vex.Flow as any).Fonts?.Custom];
+        } // else keep new vexflow default Bravura (more cursive, bold).
+
         this.pageIdx = 0;
         for (const graphicalMusicPage of graphicalMusicSheet.MusicPages) {
             const backend: VexFlowBackend = this.backends[this.pageIdx];
@@ -386,12 +391,12 @@ export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
      * @param screenPosition the position of the lower left corner of the text in screen coordinates
      */
     protected renderLabel(graphicalLabel: GraphicalLabel, layer: number, bitmapWidth: number,
-                          bitmapHeight: number, heightInPixel: number, screenPosition: PointF2D): void {
+                          bitmapHeight: number, fontHeightInPixel: number, screenPosition: PointF2D): void {
         if (!graphicalLabel.Label.print) {
             return;
         }
         const height: number = graphicalLabel.Label.fontHeight * unitInPixels;
-        const { font, text } = graphicalLabel.Label;
+        const { font } = graphicalLabel.Label;
         let color: string;
         if (this.rules.ColoringEnabled) {
             color = graphicalLabel.Label.colorDefault;
@@ -406,7 +411,12 @@ export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
         if (!fontFamily) {
             fontFamily = this.rules.DefaultFontFamily;
         }
-        this.backend.renderText(height, fontStyle, font, text, heightInPixel, screenPosition, color, graphicalLabel.Label.fontFamily);
+
+        for (let i: number = 0; i < graphicalLabel.TextLines?.length; i++) {
+            const currLine: string = graphicalLabel.TextLines[i];
+            this.backend.renderText(height, fontStyle, font, currLine, fontHeightInPixel, screenPosition, color, graphicalLabel.Label.fontFamily);
+            screenPosition.y = screenPosition.y + fontHeightInPixel;
+        }
         // font currently unused, replaced by fontFamily
     }
 

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

@@ -25,6 +25,8 @@ export interface IGraphicalSymbolFactory {
 
     createGraphicalMeasure(sourceMeasure: SourceMeasure, staff: Staff): GraphicalMeasure;
 
+    createMultiRestMeasure(sourceMeasure: SourceMeasure, staff: Staff): GraphicalMeasure;
+
     createTabStaffMeasure(sourceMeasure: SourceMeasure, staff: Staff): GraphicalMeasure;
 
     createExtraGraphicalMeasure(staffLine: StaffLine): GraphicalMeasure;

+ 29 - 2
src/MusicalScore/ScoreIO/InstrumentReader.ts

@@ -26,7 +26,7 @@ import {SlurReader} from "./MusicSymbolModules/SlurReader";
 import {StemDirectionType} from "../VoiceData/VoiceEntry";
 import {NoteType, NoteTypeHandler} from "../VoiceData";
 import {SystemLinesEnumHelper} from "../Graphical";
-//import Dictionary from "typescript-collections/dist/lib/Dictionary";
+// import {Dictionary} from "typescript-collections";
 
 // FIXME: The following classes are missing
 //type ChordSymbolContainer = any;
@@ -411,7 +411,6 @@ export class InstrumentReader {
                 throw new MusicSheetReadingException(errorMsg + this.instrument.Name);
               }
             }
-
           }
           if (
             !xmlNode.element("divisions") &&
@@ -447,6 +446,34 @@ export class InstrumentReader {
               this.instrument.Staves[staffNumber - 1].StafflineCount = parseInt(staffLinesNode.value, 10);
             }
           }
+          // check multi measure rest
+          const measureStyle: IXmlElement = xmlNode.element("measure-style");
+          if (measureStyle) {
+            const multipleRest: IXmlElement = measureStyle.element("multiple-rest");
+            if (multipleRest) {
+              // TODO: save multirest per staff info a dictionary, to display a partial multirest if multirest values across staffs differ.
+              //   this makes the code bulkier though, and for now we only draw multirests if the staffs have the same multirest lengths.
+              // if (!currentMeasure.multipleRestMeasuresPerStaff) {
+              //   currentMeasure.multipleRestMeasuresPerStaff = new Dictionary<number, number>();
+              // }
+              const multipleRestValueXml: string = multipleRest.value;
+              let multipleRestNumber: number = 0;
+              try {
+                multipleRestNumber = Number.parseInt(multipleRestValueXml, 10);
+                if (currentMeasure.multipleRestMeasures !== undefined && multipleRestNumber !== currentMeasure.multipleRestMeasures) {
+                  // different multi-rest values in same measure for different staffs
+                  currentMeasure.multipleRestMeasures = 0; // for now, ignore multirest here. TODO: take minimum
+                  // currentMeasure.multipleRestMeasuresPerStaff.setValue(this.currentStaff?.Id, multipleRestNumber);
+                  //   issue: currentStaff can be undefined for first measure
+                } else {
+                  currentMeasure.multipleRestMeasures = multipleRestNumber;
+                }
+              } catch (e) {
+                console.log("multirest parse error: " + e);
+              }
+            }
+          }
+
         } else if (xmlNode.name === "forward") {
           const forFraction: number = parseInt(xmlNode.element("duration").value, 10);
           currentFraction.Add(new Fraction(forFraction, 4 * this.divisions));

+ 2 - 0
src/MusicalScore/VoiceData/SourceMeasure.ts

@@ -55,6 +55,8 @@ export class SourceMeasure {
     public printNewPageXml: boolean = false;
 
     private measureNumber: number;
+    public multipleRestMeasures: number; // usually undefined (0), unless "multiple-rest" given in XML (e.g. 4 measure rest)
+    // public multipleRestMeasuresPerStaff: Dictionary<number, number>; // key: staffId. value: how many rest measures
     private absoluteTimestamp: Fraction;
     private completeNumberOfStaves: number;
     private duration: Fraction;

+ 7 - 0
src/OpenSheetMusicDisplay/OSMDOptions.ts

@@ -194,6 +194,13 @@ export interface IOSMDOptions {
      *  IF this value is -1, it will render all percussion clef voices on the single line.
      */
     percussionForceVoicesOneLineCutoff?: number;
+    /** The softmaxFactor for Vexflow's formatter. Default is 5, default in Vexflow is 100 (voice.js).
+     *  Lowering this factor makes the spacing between individual notes smaller (especially from one half note to the next).
+     *  So, to get more compact scores, try lowering this value (or set osmd.zoom, which simply scales),
+     *  or try 100 for a more expansive layout.
+     *  Setting this is the same as setting osmd.EngravingRules.SoftmaxFactorVexFlow.
+     */
+    spacingFactorSoftmax?: number;
 }
 
 export enum AlignRestOption {

+ 3 - 0
src/OpenSheetMusicDisplay/OpenSheetMusicDisplay.ts

@@ -518,6 +518,9 @@ export class OpenSheetMusicDisplay {
         if (options.renderSingleHorizontalStaffline !== undefined) {
             this.rules.RenderSingleHorizontalStaffline = options.renderSingleHorizontalStaffline;
         }
+        if (options.spacingFactorSoftmax !== undefined) {
+            this.rules.SoftmaxFactorVexFlow = options.spacingFactorSoftmax;
+        }
     }
 
     public setColoringMode(options: IOSMDOptions): void {

+ 686 - 0
test/data/OSMD_Function_Test_Labels.musicxml

@@ -0,0 +1,686 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 3.1 Partwise//EN" "http://www.musicxml.org/dtds/partwise.dtd">
+<score-partwise version="3.1">
+  <work>
+    <work-title>OSMD Function Test - Labels
+    NewLine in Title</work-title>
+    </work>
+  <identification>
+    <creator type="composer">Composer 1
+    Composer 2
+    Composer Longer Name Test
+    Composer 4
+    Composer 5</creator>
+    <creator type="lyricist">Lyricist 1
+    Lyricist Longer Name Test
+    Lyricist 3</creator>
+    <rights>2020
+    2021</rights>
+    <encoding>
+      <software>MuseScore 3.4.2</software>
+      <encoding-date>2020-06-17</encoding-date>
+      <supports element="accidental" type="yes"/>
+      <supports element="beam" type="yes"/>
+      <supports element="print" attribute="new-page" type="yes" value="yes"/>
+      <supports element="print" attribute="new-system" type="yes" value="yes"/>
+      <supports element="stem" type="yes"/>
+      </encoding>
+    </identification>
+  <defaults>
+    <scaling>
+      <millimeters>7.05556</millimeters>
+      <tenths>40</tenths>
+      </scaling>
+    <page-layout>
+      <page-height>1683.36</page-height>
+      <page-width>1190.88</page-width>
+      <page-margins type="even">
+        <left-margin>56.6929</left-margin>
+        <right-margin>56.6929</right-margin>
+        <top-margin>56.6929</top-margin>
+        <bottom-margin>113.386</bottom-margin>
+        </page-margins>
+      <page-margins type="odd">
+        <left-margin>56.6929</left-margin>
+        <right-margin>56.6929</right-margin>
+        <top-margin>56.6929</top-margin>
+        <bottom-margin>113.386</bottom-margin>
+        </page-margins>
+      </page-layout>
+    <word-font font-family="FreeSerif" font-size="10"/>
+    <lyric-font font-family="FreeSerif" font-size="11"/>
+    </defaults>
+  <credit page="1">
+    <credit-words default-x="595.44" default-y="1626.67" justify="center" valign="top" font-size="24">OSMD Function Test - Labels</credit-words>
+    </credit>
+  <credit page="1">
+    <credit-words default-x="595.44" default-y="1569.97" justify="center" valign="top" font-size="14">Subtitle</credit-words>
+    </credit>
+  <credit page="1">
+    <credit-words default-x="1134.19" default-y="1526.67" justify="right" valign="bottom" font-size="12">Composer 1</credit-words>
+    </credit>
+  <credit page="1">
+    <credit-words default-x="56.6929" default-y="1526.67" justify="left" valign="bottom" font-size="12">Lyricist 1</credit-words>
+    </credit>
+  <credit page="1">
+    <credit-words default-x="595.44" default-y="113.386" justify="center" valign="bottom" font-size="8">2020</credit-words>
+    </credit>
+  <part-list>
+    <score-part id="P1">
+      <part-name>Violin</part-name>
+      <part-abbreviation>Vln.</part-abbreviation>
+      <score-instrument id="P1-I1">
+        <instrument-name>Violin</instrument-name>
+        </score-instrument>
+      <midi-device id="P1-I1" port="1"></midi-device>
+      <midi-instrument id="P1-I1">
+        <midi-channel>1</midi-channel>
+        <midi-program>41</midi-program>
+        <volume>78.7402</volume>
+        <pan>0</pan>
+        </midi-instrument>
+      </score-part>
+    </part-list>
+  <part id="P1">
+    <measure number="1" width="198.01">
+      <print>
+        <system-layout>
+          <system-margins>
+            <left-margin>0.00</left-margin>
+            <right-margin>0.00</right-margin>
+            </system-margins>
+          <top-system-distance>170.00</top-system-distance>
+          </system-layout>
+        </print>
+      <attributes>
+        <divisions>1</divisions>
+        <key>
+          <fifths>2</fifths>
+          </key>
+        <time>
+          <beats>4</beats>
+          <beat-type>4</beat-type>
+          </time>
+        <clef>
+          <sign>G</sign>
+          <line>2</line>
+          </clef>
+        </attributes>
+      <note default-x="110.28" default-y="-35.00">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>up</stem>
+        </note>
+      <note default-x="131.27" default-y="-25.00">
+        <pitch>
+          <step>A</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>up</stem>
+        </note>
+      <note default-x="152.25" default-y="-15.00">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>5</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        </note>
+      <note default-x="173.24" default-y="-5.00">
+        <pitch>
+          <step>E</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        </note>
+      </measure>
+    <measure number="2" width="97.72">
+      <note default-x="10.00" default-y="5.00">
+        <pitch>
+          <step>G</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        </note>
+      <note default-x="30.99" default-y="-5.00">
+        <pitch>
+          <step>E</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        </note>
+      <note default-x="51.97" default-y="-15.00">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>5</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        </note>
+      <note default-x="72.96" default-y="-25.00">
+        <pitch>
+          <step>A</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>up</stem>
+        </note>
+      </measure>
+    <measure number="3" width="97.72">
+      <note default-x="10.00" default-y="-35.00">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>up</stem>
+        </note>
+      <note default-x="30.99" default-y="-25.00">
+        <pitch>
+          <step>A</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>up</stem>
+        </note>
+      <note default-x="51.97" default-y="-15.00">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>5</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        </note>
+      <note default-x="72.96" default-y="-5.00">
+        <pitch>
+          <step>E</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        </note>
+      </measure>
+    <measure number="4" width="97.72">
+      <note default-x="10.00" default-y="-15.00">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>5</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        </note>
+      <note default-x="30.99" default-y="-25.00">
+        <pitch>
+          <step>A</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>up</stem>
+        </note>
+      <note default-x="51.97" default-y="-35.00">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>up</stem>
+        </note>
+      <note default-x="72.96" default-y="-30.00">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>up</stem>
+        </note>
+      </measure>
+    <measure number="5" width="97.72">
+      <note default-x="10.00" default-y="-20.00">
+        <pitch>
+          <step>B</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        </note>
+      <note default-x="30.99" default-y="-10.00">
+        <pitch>
+          <step>D</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        </note>
+      <note default-x="51.97" default-y="0.00">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>5</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        </note>
+      <note default-x="72.96" default-y="10.00">
+        <pitch>
+          <step>A</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        </note>
+      </measure>
+    <measure number="6" width="97.72">
+      <note default-x="10.00" default-y="0.00">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>5</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        </note>
+      <note default-x="30.99" default-y="-10.00">
+        <pitch>
+          <step>D</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        </note>
+      <note default-x="51.97" default-y="-20.00">
+        <pitch>
+          <step>B</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        </note>
+      <note default-x="72.96" default-y="-30.00">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>up</stem>
+        </note>
+      </measure>
+    <measure number="7" width="97.72">
+      <note default-x="10.00" default-y="-25.00">
+        <pitch>
+          <step>A</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>up</stem>
+        </note>
+      <note default-x="30.99" default-y="-30.00">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>up</stem>
+        </note>
+      <note default-x="51.97" default-y="-35.00">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>up</stem>
+        </note>
+      <note default-x="72.96" default-y="-40.00">
+        <pitch>
+          <step>E</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>up</stem>
+        </note>
+      </measure>
+    <measure number="8" width="97.72">
+      <note default-x="10.00" default-y="-35.00">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>up</stem>
+        </note>
+      <note default-x="30.99" default-y="-30.00">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>up</stem>
+        </note>
+      <note default-x="51.97" default-y="-35.00">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>up</stem>
+        </note>
+      <note default-x="72.96" default-y="-25.00">
+        <pitch>
+          <step>A</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>up</stem>
+        </note>
+      </measure>
+    <measure number="9" width="97.72">
+      <note default-x="10.00" default-y="-30.00">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>up</stem>
+        </note>
+      <note default-x="30.99" default-y="-20.00">
+        <pitch>
+          <step>B</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        </note>
+      <note default-x="51.97" default-y="-25.00">
+        <pitch>
+          <step>A</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>up</stem>
+        </note>
+      <note default-x="72.96" default-y="-15.00">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>5</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        </note>
+      </measure>
+    <measure number="10" width="97.72">
+      <note default-x="10.00" default-y="-20.00">
+        <pitch>
+          <step>B</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        </note>
+      <note default-x="30.99" default-y="-15.00">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>5</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        </note>
+      <note default-x="51.97" default-y="-10.00">
+        <pitch>
+          <step>D</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        </note>
+      <note default-x="72.96" default-y="-5.00">
+        <pitch>
+          <step>E</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        </note>
+      </measure>
+    <measure number="11" width="240.66">
+      <print new-system="yes">
+        <system-layout>
+          <system-margins>
+            <left-margin>0.00</left-margin>
+            <right-margin>-0.00</right-margin>
+            </system-margins>
+          <system-distance>150.00</system-distance>
+          </system-layout>
+        </print>
+      <note default-x="89.19" default-y="-10.00">
+        <pitch>
+          <step>D</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>down</stem>
+        </note>
+      <note default-x="163.95" default-y="-15.00">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>5</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>down</stem>
+        </note>
+      </measure>
+    <measure number="12" width="161.83">
+      <note default-x="10.36" default-y="5.00">
+        <pitch>
+          <step>G</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>down</stem>
+        </note>
+      <note default-x="85.12" default-y="-15.00">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>5</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>down</stem>
+        </note>
+      </measure>
+    <measure number="13" width="161.90">
+      <note default-x="10.00" default-y="-25.00">
+        <pitch>
+          <step>A</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>up</stem>
+        </note>
+      <note default-x="84.97" default-y="-35.00">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>up</stem>
+        </note>
+      </measure>
+    <measure number="14" width="166.46">
+      <note default-x="10.00" default-y="-45.00">
+        <pitch>
+          <step>D</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>up</stem>
+        </note>
+      <note default-x="87.25" default-y="-55.00">
+        <pitch>
+          <step>B</step>
+          <octave>3</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>up</stem>
+        </note>
+      </measure>
+    <measure number="15" width="174.82">
+      <note default-x="13.80" default-y="-65.00">
+        <pitch>
+          <step>G</step>
+          <octave>3</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>up</stem>
+        </note>
+      <note default-x="93.33" default-y="-60.00">
+        <pitch>
+          <step>A</step>
+          <octave>3</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>up</stem>
+        </note>
+      </measure>
+    <measure number="16" width="171.81">
+      <note default-x="10.00" default-y="-45.00">
+        <pitch>
+          <step>D</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>up</stem>
+        </note>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>half</type>
+        </note>
+      <barline location="right">
+        <bar-style>light-heavy</bar-style>
+        </barline>
+      </measure>
+    </part>
+  </score-partwise>

+ 1183 - 0
test/data/OSMD_function_test_multiple_rest_measures.musicxml

@@ -0,0 +1,1183 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 3.1 Partwise//EN" "http://www.musicxml.org/dtds/partwise.dtd">
+<score-partwise version="3.1">
+  <work>
+    <work-title>OSMD Function Test - Multiple Rest Measures</work-title>
+    </work>
+  <identification>
+    <encoding>
+      <software>MuseScore 3.4.2</software>
+      <encoding-date>2020-06-30</encoding-date>
+      <supports element="accidental" type="yes"/>
+      <supports element="beam" type="yes"/>
+      <supports element="print" attribute="new-page" type="yes" value="yes"/>
+      <supports element="print" attribute="new-system" type="yes" value="yes"/>
+      <supports element="stem" type="yes"/>
+      </encoding>
+    </identification>
+  <defaults>
+    <scaling>
+      <millimeters>7.05556</millimeters>
+      <tenths>40</tenths>
+      </scaling>
+    <page-layout>
+      <page-height>1683.36</page-height>
+      <page-width>1190.88</page-width>
+      <page-margins type="even">
+        <left-margin>56.6929</left-margin>
+        <right-margin>56.6929</right-margin>
+        <top-margin>56.6929</top-margin>
+        <bottom-margin>113.386</bottom-margin>
+        </page-margins>
+      <page-margins type="odd">
+        <left-margin>56.6929</left-margin>
+        <right-margin>56.6929</right-margin>
+        <top-margin>56.6929</top-margin>
+        <bottom-margin>113.386</bottom-margin>
+        </page-margins>
+      </page-layout>
+    <word-font font-family="FreeSerif" font-size="10"/>
+    <lyric-font font-family="FreeSerif" font-size="11"/>
+    </defaults>
+  <credit page="1">
+    <credit-words default-x="595.44" default-y="1626.67" justify="center" valign="top" font-size="24">OSMD Function Test - Multiple Rest Measures</credit-words>
+    </credit>
+  <part-list>
+    <score-part id="P1">
+      <part-name>Piano</part-name>
+      <part-abbreviation>Pno.</part-abbreviation>
+      <score-instrument id="P1-I1">
+        <instrument-name>Piano</instrument-name>
+        </score-instrument>
+      <midi-device id="P1-I1" port="1"></midi-device>
+      <midi-instrument id="P1-I1">
+        <midi-channel>1</midi-channel>
+        <midi-program>1</midi-program>
+        <volume>78.7402</volume>
+        <pan>0</pan>
+        </midi-instrument>
+      </score-part>
+    <score-part id="P2">
+      <part-name>Violin</part-name>
+      <part-abbreviation>Vln.</part-abbreviation>
+      <score-instrument id="P2-I1">
+        <instrument-name>Violin</instrument-name>
+        </score-instrument>
+      <midi-device id="P2-I1" port="1"></midi-device>
+      <midi-instrument id="P2-I1">
+        <midi-channel>2</midi-channel>
+        <midi-program>41</midi-program>
+        <volume>78.7402</volume>
+        <pan>0</pan>
+        </midi-instrument>
+      </score-part>
+    <score-part id="P3">
+      <part-name>Violoncello</part-name>
+      <part-abbreviation>Vc.</part-abbreviation>
+      <score-instrument id="P3-I1">
+        <instrument-name>Violoncello</instrument-name>
+        </score-instrument>
+      <midi-device id="P3-I1" port="1"></midi-device>
+      <midi-instrument id="P3-I1">
+        <midi-channel>5</midi-channel>
+        <midi-program>43</midi-program>
+        <volume>78.7402</volume>
+        <pan>0</pan>
+        </midi-instrument>
+      </score-part>
+    </part-list>
+  <part id="P1">
+    <measure number="1" width="133.28">
+      <print>
+        <system-layout>
+          <system-margins>
+            <left-margin>140.59</left-margin>
+            <right-margin>0.00</right-margin>
+            </system-margins>
+          <top-system-distance>170.00</top-system-distance>
+          </system-layout>
+        <staff-layout number="2">
+          <staff-distance>65.00</staff-distance>
+          </staff-layout>
+        </print>
+      <attributes>
+        <divisions>1</divisions>
+        <key>
+          <fifths>1</fifths>
+          </key>
+        <time>
+          <beats>2</beats>
+          <beat-type>4</beat-type>
+          </time>
+        <staves>2</staves>
+        <clef number="1">
+          <sign>G</sign>
+          <line>2</line>
+          </clef>
+        <clef number="2">
+          <sign>F</sign>
+          <line>4</line>
+          </clef>
+        <measure-style>
+          <multiple-rest>2</multiple-rest>
+          </measure-style>
+        </attributes>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>2</duration>
+        </backup>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>5</voice>
+        <staff>2</staff>
+        </note>
+      </measure>
+    <measure number="2" width="53.20">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>2</duration>
+        </backup>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>5</voice>
+        <staff>2</staff>
+        </note>
+      </measure>
+    <measure number="3" width="54.05">
+      <note default-x="13.80" default-y="-50.00">
+        <pitch>
+          <step>C</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>up</stem>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>2</duration>
+        </backup>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>5</voice>
+        <staff>2</staff>
+        </note>
+      </measure>
+    <measure number="4" width="54.05">
+      <note default-x="10.00" default-y="-45.00">
+        <pitch>
+          <step>D</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>up</stem>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>2</duration>
+        </backup>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>5</voice>
+        <staff>2</staff>
+        </note>
+      </measure>
+    <measure number="5" width="54.05">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>2</duration>
+        </backup>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>5</voice>
+        <staff>2</staff>
+        </note>
+      </measure>
+    <measure number="6" width="53.20">
+      <attributes>
+        <measure-style>
+          <multiple-rest>3</multiple-rest>
+          </measure-style>
+        </attributes>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>2</duration>
+        </backup>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>5</voice>
+        <staff>2</staff>
+        </note>
+      </measure>
+    <measure number="7" width="53.20">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>2</duration>
+        </backup>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>5</voice>
+        <staff>2</staff>
+        </note>
+      </measure>
+    <measure number="8" width="53.20">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>2</duration>
+        </backup>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>5</voice>
+        <staff>2</staff>
+        </note>
+      </measure>
+    <measure number="9" width="54.05">
+      <note default-x="10.36" default-y="-25.00">
+        <pitch>
+          <step>A</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>up</stem>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>2</duration>
+        </backup>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>5</voice>
+        <staff>2</staff>
+        </note>
+      </measure>
+    <measure number="10" width="53.20">
+      <attributes>
+        <measure-style>
+          <multiple-rest>3</multiple-rest>
+          </measure-style>
+        </attributes>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>2</duration>
+        </backup>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>5</voice>
+        <staff>2</staff>
+        </note>
+      </measure>
+    <measure number="11" width="53.20">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>2</duration>
+        </backup>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>5</voice>
+        <staff>2</staff>
+        </note>
+      </measure>
+    <measure number="12" width="53.20">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>2</duration>
+        </backup>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>5</voice>
+        <staff>2</staff>
+        </note>
+      </measure>
+    <measure number="13" width="56.89">
+      <note default-x="10.00" default-y="-30.00">
+        <pitch>
+          <step>G</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>up</stem>
+        <staff>1</staff>
+        </note>
+      <note>
+        <rest/>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>2</duration>
+        </backup>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>5</voice>
+        <staff>2</staff>
+        </note>
+      </measure>
+    <measure number="14" width="56.89">
+      <note default-x="10.00" default-y="-35.00">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>up</stem>
+        <staff>1</staff>
+        </note>
+      <note>
+        <rest/>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>2</duration>
+        </backup>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>5</voice>
+        <staff>2</staff>
+        </note>
+      </measure>
+    <measure number="15" width="53.20">
+      <attributes>
+        <measure-style>
+          <multiple-rest>2</multiple-rest>
+          </measure-style>
+        </attributes>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>2</duration>
+        </backup>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>5</voice>
+        <staff>2</staff>
+        </note>
+      </measure>
+    <measure number="16" width="53.20">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>2</duration>
+        </backup>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>5</voice>
+        <staff>2</staff>
+        </note>
+      </measure>
+    <measure number="17" width="56.89">
+      <note default-x="10.00" default-y="-25.00">
+        <pitch>
+          <step>A</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>up</stem>
+        <staff>1</staff>
+        </note>
+      <note>
+        <rest/>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>2</duration>
+        </backup>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>5</voice>
+        <staff>2</staff>
+        </note>
+      </measure>
+    <measure number="18" width="56.89">
+      <note default-x="10.00" default-y="-20.00">
+        <pitch>
+          <step>B</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        <staff>1</staff>
+        </note>
+      <note>
+        <rest/>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>2</duration>
+        </backup>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>5</voice>
+        <staff>2</staff>
+        </note>
+      </measure>
+    <measure number="19" width="92.43">
+      <attributes>
+        <measure-style>
+          <multiple-rest>5</multiple-rest>
+          </measure-style>
+        </attributes>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>2</duration>
+        </backup>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>5</voice>
+        <staff>2</staff>
+        </note>
+      </measure>
+    <measure number="20" width="92.43">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>2</duration>
+        </backup>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>5</voice>
+        <staff>2</staff>
+        </note>
+      </measure>
+    <measure number="21" width="92.43">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>2</duration>
+        </backup>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>5</voice>
+        <staff>2</staff>
+        </note>
+      </measure>
+    <measure number="22" width="92.43">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>2</duration>
+        </backup>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>5</voice>
+        <staff>2</staff>
+        </note>
+      </measure>
+    <measure number="23" width="92.43">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>2</duration>
+        </backup>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>5</voice>
+        <staff>2</staff>
+        </note>
+      </measure>
+    <measure number="24" width="56.89">
+      <note default-x="10.00" default-y="-15.00">
+        <pitch>
+          <step>C</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        <staff>1</staff>
+        </note>
+      <note>
+        <rest/>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>2</duration>
+        </backup>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>5</voice>
+        <staff>2</staff>
+        </note>
+      </measure>
+    <measure number="25" width="92.43">
+      <print new-system="yes">
+        <system-layout>
+          <system-margins>
+            <left-margin>72.31</left-margin>
+            <right-margin>636.58</right-margin>
+            </system-margins>
+          <system-distance>150.00</system-distance>
+          </system-layout>
+        <staff-layout number="2">
+          <staff-distance>65.00</staff-distance>
+          </staff-layout>
+        </print>
+      <attributes>
+        <measure-style>
+          <multiple-rest>2</multiple-rest>
+          </measure-style>
+        </attributes>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>2</duration>
+        </backup>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>5</voice>
+        <staff>2</staff>
+        </note>
+      </measure>
+    <measure number="26" width="92.43">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        <staff>1</staff>
+        </note>
+      <backup>
+        <duration>2</duration>
+        </backup>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>5</voice>
+        <staff>2</staff>
+        </note>
+      <barline location="right">
+        <bar-style>light-heavy</bar-style>
+        </barline>
+      </measure>
+    </part>
+  <part id="P2">
+    <measure number="1" width="133.28">
+      <print>
+        <staff-layout number="1">
+          <staff-distance>65.00</staff-distance>
+          </staff-layout>
+        </print>
+      <attributes>
+        <divisions>1</divisions>
+        <key>
+          <fifths>1</fifths>
+          </key>
+        <time>
+          <beats>2</beats>
+          <beat-type>4</beat-type>
+          </time>
+        <clef>
+          <sign>G</sign>
+          <line>2</line>
+          </clef>
+        <measure-style>
+          <multiple-rest>2</multiple-rest>
+          </measure-style>
+        </attributes>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="2" width="53.20">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="3" width="54.05">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="4" width="54.05">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="5" width="54.05">
+      <note default-x="10.36" default-y="-250.00">
+        <pitch>
+          <step>E</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>up</stem>
+        </note>
+      </measure>
+    <measure number="6" width="53.20">
+      <attributes>
+        <measure-style>
+          <multiple-rest>3</multiple-rest>
+          </measure-style>
+        </attributes>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="7" width="53.20">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="8" width="53.20">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="9" width="54.05">
+      <note default-x="10.36" default-y="-245.00">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>4</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>up</stem>
+        </note>
+      </measure>
+    <measure number="10" width="53.20">
+      <attributes>
+        <measure-style>
+          <multiple-rest>3</multiple-rest>
+          </measure-style>
+        </attributes>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="11" width="53.20">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="12" width="53.20">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="13" width="56.89">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="14" width="56.89">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="15" width="53.20">
+      <attributes>
+        <measure-style>
+          <multiple-rest>2</multiple-rest>
+          </measure-style>
+        </attributes>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="16" width="53.20">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="17" width="56.89">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="18" width="56.89">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="19" width="92.43">
+      <attributes>
+        <measure-style>
+          <multiple-rest>5</multiple-rest>
+          </measure-style>
+        </attributes>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="20" width="92.43">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="21" width="92.43">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="22" width="92.43">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="23" width="92.43">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="24" width="56.89">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="25" width="92.43">
+      <print new-system="yes">
+        <staff-layout number="1">
+          <staff-distance>65.00</staff-distance>
+          </staff-layout>
+        </print>
+      <attributes>
+        <measure-style>
+          <multiple-rest>2</multiple-rest>
+          </measure-style>
+        </attributes>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="26" width="92.43">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      <barline location="right">
+        <bar-style>light-heavy</bar-style>
+        </barline>
+      </measure>
+    </part>
+  <part id="P3">
+    <measure number="1" width="133.28">
+      <print>
+        <staff-layout number="1">
+          <staff-distance>65.00</staff-distance>
+          </staff-layout>
+        </print>
+      <attributes>
+        <divisions>1</divisions>
+        <key>
+          <fifths>1</fifths>
+          </key>
+        <time>
+          <beats>2</beats>
+          <beat-type>4</beat-type>
+          </time>
+        <clef>
+          <sign>F</sign>
+          <line>4</line>
+          </clef>
+        <measure-style>
+          <multiple-rest>2</multiple-rest>
+          </measure-style>
+        </attributes>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="2" width="53.20">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="3" width="54.05">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="4" width="54.05">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="5" width="54.05">
+      <note default-x="10.36" default-y="-320.00">
+        <pitch>
+          <step>G</step>
+          <octave>3</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>down</stem>
+        </note>
+      </measure>
+    <measure number="6" width="53.20">
+      <attributes>
+        <measure-style>
+          <multiple-rest>3</multiple-rest>
+          </measure-style>
+        </attributes>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="7" width="53.20">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="8" width="53.20">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="9" width="54.05">
+      <note default-x="10.36" default-y="-325.00">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>3</octave>
+          </pitch>
+        <duration>2</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>down</stem>
+        </note>
+      </measure>
+    <measure number="10" width="53.20">
+      <attributes>
+        <measure-style>
+          <multiple-rest>3</multiple-rest>
+          </measure-style>
+        </attributes>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="11" width="53.20">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="12" width="53.20">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="13" width="56.89">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="14" width="56.89">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="15" width="53.20">
+      <attributes>
+        <measure-style>
+          <multiple-rest>2</multiple-rest>
+          </measure-style>
+        </attributes>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="16" width="53.20">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="17" width="56.89">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="18" width="56.89">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="19" width="92.43">
+      <attributes>
+        <measure-style>
+          <multiple-rest>5</multiple-rest>
+          </measure-style>
+        </attributes>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="20" width="92.43">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="21" width="92.43">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="22" width="92.43">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="23" width="92.43">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="24" width="56.89">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="25" width="92.43">
+      <print new-system="yes">
+        <staff-layout number="1">
+          <staff-distance>65.00</staff-distance>
+          </staff-layout>
+        </print>
+      <attributes>
+        <measure-style>
+          <multiple-rest>2</multiple-rest>
+          </measure-style>
+        </attributes>
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      </measure>
+    <measure number="26" width="92.43">
+      <note>
+        <rest/>
+        <duration>2</duration>
+        <voice>1</voice>
+        </note>
+      <barline location="right">
+        <bar-style>light-heavy</bar-style>
+        </barline>
+      </measure>
+    </part>
+  </score-partwise>