Parcourir la source

Merge branch 'develop'

sschmid il y a 5 ans
Parent
commit
322498c472

+ 1 - 3
CHANGELOG.md

@@ -3,9 +3,7 @@
 
 ### Bug Fixes
 
-* **Fingering:** fix all TechnicalInstructions counted as fingering, fix fingering for tabs (don't display on tab clef) ([ee80e91](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/ee80e911c85e33bb1c96daf14be9af4420e59c22)), closes [#711](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/711)
 * **Slurs:** fix undefined slur error in beethoven moonlight sonata ([#679](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/679)) ([d23581f](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/d23581fa6f80ad2da9fcbfef248cb19d2f0f9932))
-* **Ties:** prevent undefined tieNotes from creating a Vexflow error and crashing rendering ([6209cd3](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/6209cd32257fb0639e3801e2a87e9f47cf0f9efa))
 * **Beams:** fix beams retaining old slope after zooming ([#655](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/655))  ([447c4f9](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/447c4f9a22be4348356761498c50a1f0a916894b))
 * **Tuplets:** fix rendering for half note tuplets (were displayed as whole notes) ([#700](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/700) ([9512c3a](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/9512c3aaf013034370545f47748c4d156e144b58))
 
@@ -19,7 +17,7 @@
 
 * **BackendSelection:** can now create and remove canvas backend (again), backend option only changed when given, improve backend creation code ([#662](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/662)) ([c0a522c](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/c0a522cf68398c8e346fd204d6f4d1a43fa59733))
 * **CanvasBackend:** limit canvas dimensions to browser limitation of 32767, for now ([#678](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/678)) ([55ef164](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/55ef164e33166dde865d97724ffe7ea352378f46))
-* **Clefs:** fix clef not detected when exported with invalid clef number (sibelius) ([#635](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/635)) ([3250842](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/325084285373c3b0203d5eee031d3689853e5538))
+* **Clefs:** fix clef not detected when exported with invalid clef number (Sibelius) ([#635](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/635)) ([3250842](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/325084285373c3b0203d5eee031d3689853e5538))
 * **Color:** fix defaultColorRest and defaultColorNotehead not applied ([7f5e1c9](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/7f5e1c9872733e2f2fd9fe3c1d81c1dd0f5f4c65))
 * **Color:** fix EngravingRules.ColorStemsLikeNoteheads, ColorBeams not respected for false ([9a6ac74](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/9a6ac74bae7b3d8fdc55db4af5d218a03ce38010))
 * **Demo:** fix optional zoom controls not shown, improve hiding/unhiding of control elements ([#661](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/661)) ([9783204](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/97832042f462b5b890711ea4d4a78fd8a59dcaf1))

+ 15 - 2
demo/index.js

@@ -33,9 +33,10 @@ import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMus
             "OSMD Function Test - Expressions Overlap": "OSMD_function_test_expressions_overlap.musicxml",
             "OSMD Function Test - Grace Notes": "OSMD_function_test_GraceNotes.xml",
             "OSMD Function Test - Invisible Notes": "OSMD_function_test_invisible_notes.musicxml",
-            "OSMD Function Test - Selecting Measures To Draw": "OSMD_function_test_measuresToDraw_Beethoven_AnDieFerneGeliebte.xml",
             "OSMD Function Test - Notehead Shapes": "OSMD_function_test_noteheadShapes.musicxml",
             "OSMD Function Test - Ornaments": "OSMD_function_test_Ornaments.xml",
+            "OSMD Function Test - Selecting Measures To Draw": "OSMD_function_test_measuresToDraw_Beethoven_AnDieFerneGeliebte.xml",
+            "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",
             "Schubert, F. - An Die Musik": "Schubert_An_die_Musik.xml",
@@ -88,6 +89,9 @@ import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMus
     var drawPartNamesOptionStashedValue = true;
     var drawPartAbbreviationsStashedValue = true;
     var drawPartNamesOptionNeedsReset = false;
+    var pageBreaksOptionStashedValue = false;
+    var pageBreaksOptionNeedsReset = false;
+    var systemBreaksOptionStashedValue = false; // reset handled by pageBreaksOptionNeedsReset
 
     var showControls = true;
     var showExportPdfControl = false;
@@ -634,7 +638,6 @@ import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMus
                 coloringMode: 2, // custom coloring set. 0 would be XML, 1 autocoloring
                 coloringSetCustom: ["#d82c6b", "#F89D15", "#FFE21A", "#4dbd5c", "#009D96", "#43469d", "#76429c", "#ff0000"],
                 // last color value of coloringSetCustom is for rest notes
-
                 colorStemsLikeNoteheads: true
             });
         } else if (autoCustomColoringOptionNeedsReset) {
@@ -653,6 +656,16 @@ import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMus
             openSheetMusicDisplay.setOptions({ autoBeam: autobeamOptionStashedValue });
             autobeamOptionNeedsReset = false;
         }
+        if (!isCustom && str.startsWith('OSMD_Function_Test_System_and_Page_Breaks')) {
+            pageBreaksOptionStashedValue = openSheetMusicDisplay.EngravingRules.NewPageAtXMLNewPageAttribute;
+            systemBreaksOptionStashedValue = openSheetMusicDisplay.EngravingRules.NewSystemAtXMLNewSystemAttribute;
+            pageBreaksOptionNeedsReset = true;
+            openSheetMusicDisplay.setOptions({ newPageFromXML: true, newSystemFromXML: true });
+        }
+        else if (pageBreaksOptionNeedsReset) {
+            openSheetMusicDisplay.setOptions({ newPageFromXML: pageBreaksOptionStashedValue, newSystemFromXML: systemBreaksOptionStashedValue });
+            pageBreaksOptionNeedsReset = false;
+        }
         if (!isCustom && str.includes("Schubert_An_die_Musik")) { // TODO weird layout bug here with part names. but shouldn't be in score anyways
             drawPartNamesOptionStashedValue = openSheetMusicDisplay.EngravingRules.RenderPartNames;
             drawPartAbbreviationsStashedValue = openSheetMusicDisplay.EngravingRules.RenderPartAbbreviations;

+ 3 - 3
package.json

@@ -25,10 +25,10 @@
     "generatePNG:paged": "node ./test/Util/generateImages_browserless.js ../../build ./test/data ./export 210 297 allSmall",
     "generatePNG:paged:debug": "node ./test/Util/generateImages_browserless.js ../../build ./test/data ./export 210 297 all --debug 5000",
     "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",
-    "generate:current:debug": "node ./test/Util/generateImages_browserless.js ../../build ./test/data ./visual_regression/current 0 0 allSmall --debug",
+    "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.*",
-    "generate:blessed": "node ./test/Util/generateImages_browserless.js ../../build ./test/data ./visual_regression/blessed 0 0 allSmall",
+    "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",
     "fix-memory-limit": "cross-env NODE_OPTIONS=--max_old_space_size=4096"

+ 14 - 0
src/MusicalScore/Graphical/DrawingParameters.ts

@@ -10,6 +10,7 @@ export enum ColoringModes {
 export enum DrawingParametersEnum {
     allon = "allon",
     compact = "compact",
+    compacttight = "compacttight",
     default = "default",
     leadsheet = "leadsheet",
     preview = "preview",
@@ -61,6 +62,9 @@ export class DrawingParameters {
             case DrawingParametersEnum.compact:
                 this.setForCompactMode();
                 break;
+            case DrawingParametersEnum.compacttight:
+                this.setForCompactTightMode();
+                break;
             case DrawingParametersEnum.default:
             default:
                 this.setForDefault();
@@ -117,6 +121,16 @@ export class DrawingParameters {
         this.drawHiddenNotes = false;
     }
 
+    public setForCompactTightMode(): void {
+        this.rules.CompactMode = true;
+        this.DrawCredits = false;
+        this.DrawPartNames = false;
+        this.drawHiddenNotes = false;
+        // this.BetweenStaffDistance = 2.5 // etc needs to be set in OSMD.rules
+        // this.StaffDistance = 3.5
+        // this.MinimumDistanceBetweenSystems = 1
+    }
+
     public setForLeadsheet(): void {
         this.drawHighlights = false;
         this.drawErrors = false;

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

@@ -213,6 +213,8 @@ export class EngravingRules {
     /** Position of fingering label in relation to corresponding note (left, right supported, above, below experimental) */
     private fingeringPosition: PlacementEnum;
     private fingeringInsideStafflines: boolean;
+    private newSystemAtXMLNewSystemAttribute: boolean;
+    private newPageAtXMLNewPageAttribute: boolean;
     private pageFormat: PageFormat;
     private pageBackgroundColor: string; // vexflow-color-string (#FFFFFF). Default undefined/transparent.
     private renderSingleHorizontalStaffline: boolean;
@@ -441,6 +443,8 @@ export class EngravingRules {
         this.renderLyrics = true;
         this.fingeringPosition = PlacementEnum.Left; // easier to get bounding box, and safer for vertical layout
         this.fingeringInsideStafflines = false;
+        this.newSystemAtXMLNewSystemAttribute = false;
+        this.newPageAtXMLNewPageAttribute = false;
 
         EngravingRules.FixStafflineBoundingBox = false; // TODO temporary workaround
 
@@ -1561,6 +1565,18 @@ export class EngravingRules {
     public set FingeringInsideStafflines(value: boolean) {
         this.fingeringInsideStafflines = value;
     }
+    public get NewSystemAtXMLNewSystemAttribute(): boolean {
+        return this.newSystemAtXMLNewSystemAttribute;
+    }
+    public set NewSystemAtXMLNewSystemAttribute(value: boolean) {
+        this.newSystemAtXMLNewSystemAttribute = value;
+    }
+    public get NewPageAtXMLNewPageAttribute(): boolean {
+        return this.newPageAtXMLNewPageAttribute;
+    }
+    public set NewPageAtXMLNewPageAttribute(value: boolean) {
+        this.newPageAtXMLNewPageAttribute = value;
+    }
     public get PageFormat(): PageFormat {
         return this.pageFormat;
     }

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

@@ -44,6 +44,7 @@ export abstract class MusicSystem extends GraphicalObject {
     protected graphicalMarkedAreas: GraphicalMarkedArea[] = [];
     protected graphicalComments: GraphicalComment[] = [];
     protected systemLines: SystemLine[] = [];
+    public breaksPage: boolean = false;
 
     constructor(id: number) {
         super();

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

@@ -104,17 +104,20 @@ export class MusicSystemBuilder {
             let nextMeasureBeginInstructionWidth: number = this.rules.MeasureLeftMargin;
 
             // Check if there are key or rhythm change instructions within the next measure:
+            let nextSourceMeasure: SourceMeasure = undefined;
             if (this.measureListIndex + 1 < this.measureList.length) {
                 const nextGraphicalMeasures: GraphicalMeasure[] = this.measureList[this.measureListIndex + 1];
-                const nextSourceMeasure: SourceMeasure = nextGraphicalMeasures[0].parentSourceMeasure;
+                nextSourceMeasure = nextGraphicalMeasures[0].parentSourceMeasure;
                 if (nextSourceMeasure.hasBeginInstructions()) {
                     nextMeasureBeginInstructionWidth += this.addBeginInstructions(nextGraphicalMeasures, false, false);
                 }
             }
             const totalMeasureWidth: number = currentMeasureBeginInstructionsWidth + currentMeasureEndInstructionsWidth + currentMeasureVarWidth;
             const measureFitsInSystem: boolean = this.currentSystemParams.currentWidth + totalMeasureWidth + nextMeasureBeginInstructionWidth < systemMaxWidth;
-            //if (true) // prevent line break at all costs, squeezes measures and breaks lyrics spacing
-            if (isSystemStartMeasure || measureFitsInSystem) {
+            const doXmlPageBreak: boolean = this.rules.NewPageAtXMLNewPageAttribute && sourceMeasure.printNewPageXml;
+            const doXmlLineBreak: boolean = doXmlPageBreak || // also create new system if doing page break
+                (this.rules.NewSystemAtXMLNewSystemAttribute && sourceMeasure.printNewSystemXml);
+            if (isSystemStartMeasure || (measureFitsInSystem && !doXmlLineBreak)) {
                 this.addMeasureToSystem(
                     graphicalMeasures, measureStartLine, measureEndLine, totalMeasureWidth,
                     currentMeasureBeginInstructionsWidth, currentMeasureVarWidth, currentMeasureEndInstructionsWidth
@@ -123,12 +126,12 @@ export class MusicSystemBuilder {
                 this.measureListIndex++;
             } else {
                 // finalize current system and prepare a new one
-                this.finalizeCurrentAndCreateNewSystem(graphicalMeasures, previousMeasureEndsSystem);
+                this.finalizeCurrentAndCreateNewSystem(graphicalMeasures, previousMeasureEndsSystem, doXmlPageBreak);
                 // don't increase measure index to check this measure now again
             }
             previousMeasureEndsSystem = sourceMeasureEndsSystem;
         }
-        this.finalizeCurrentAndCreateNewSystem(this.measureList[this.measureList.length - 1], true);
+        this.finalizeCurrentAndCreateNewSystem(this.measureList[this.measureList.length - 1], true, false);
         return this.musicSystems;
     }
 
@@ -171,7 +174,9 @@ export class MusicSystemBuilder {
      * @param measures
      * @param isPartEndingSystem
      */
-    private finalizeCurrentAndCreateNewSystem(measures: GraphicalMeasure[], isPartEndingSystem: boolean = false): void {
+    private finalizeCurrentAndCreateNewSystem(measures: GraphicalMeasure[],
+                                              isPartEndingSystem: boolean = false, startNewPage: boolean = false): void {
+        this.currentSystemParams.currentSystem.breaksPage = startNewPage;
         this.adaptRepetitionLineWithIfNeeded();
         if (!isPartEndingSystem) {
             this.checkAndCreateExtraInstructionMeasure(measures);
@@ -1017,8 +1022,9 @@ export class MusicSystemBuilder {
                                         (previousStaffLineBB.RelativePosition.y + previousStaffLineBB.BorderBottom);
                 distance = Math.max(this.rules.MinimumDistanceBetweenSystems, distance);
                 const neededHeight: number = distance - currentSystem.PositionAndShape.BorderTop + currentSystem.PositionAndShape.BorderBottom;
-                if (currentYPosition + neededHeight <
-                    this.rules.PageHeight - this.rules.PageBottomMargin) {
+                const doXmlPageBreak: boolean = this.rules.NewPageAtXMLNewPageAttribute && previousSystem.breaksPage;
+                if (!doXmlPageBreak &&
+                    (currentYPosition + neededHeight < this.rules.PageHeight - this.rules.PageBottomMargin)) {
                     // enough space on this page:
                     this.addSystemToPage(currentPage, currentSystem);
                     const relativePosition: PointF2D = new PointF2D(this.rules.PageLeftMargin + this.rules.SystemLeftMargin,

+ 16 - 4
src/MusicalScore/ScoreIO/InstrumentReader.ts

@@ -135,7 +135,16 @@ export class InstrumentReader {
     try {
       const xmlMeasureListArr: IXmlElement[] = this.xmlMeasureList[this.currentXmlMeasureIndex].elements();
       for (const xmlNode of xmlMeasureListArr) {
-        if (xmlNode.name === "note") {
+        if (xmlNode.name === "print") {
+          const newSystemAttr: IXmlAttribute = xmlNode.attribute("new-system");
+          if (newSystemAttr?.value === "yes") {
+            currentMeasure.printNewSystemXml = true;
+          }
+          const newPageAttr: IXmlAttribute = xmlNode.attribute("new-page");
+          if (newPageAttr?.value === "yes") {
+            currentMeasure.printNewPageXml = true;
+          }
+        } else if (xmlNode.name === "note") {
           let printObject: boolean = true;
           if (xmlNode.hasAttributes && xmlNode.attribute("print-object") &&
               xmlNode.attribute("print-object").value === "no") {
@@ -481,9 +490,12 @@ export class InstrumentReader {
           }
           const location: IXmlAttribute = xmlNode.attribute("location");
           if (location && location.value === "right") {
-            const stringValue: string = xmlNode.element("bar-style").value;
-            this.currentMeasure.endingBarStyleXml = stringValue;
-            this.currentMeasure.endingBarStyleEnum = SystemLinesEnumHelper.xmlBarlineStyleToSystemLinesEnum(stringValue);
+            const stringValue: string = xmlNode.element("bar-style")?.value;
+            // TODO apparently we didn't anticipate bar-style not existing (the ? above was missing). how to handle?
+            if (stringValue) {
+              this.currentMeasure.endingBarStyleXml = stringValue;
+              this.currentMeasure.endingBarStyleEnum = SystemLinesEnumHelper.xmlBarlineStyleToSystemLinesEnum(stringValue);
+            }
           }
           // TODO do we need to process bars with left location too?
         } else if (xmlNode.name === "sound") {

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

@@ -54,6 +54,10 @@ export class SourceMeasure {
      */
     public endingBarStyleXml: string;
     public endingBarStyleEnum: SystemLinesEnum;
+    /** Whether the MusicXML says to print a new system (line break). See OSMDOptions.newSystemFromXML */
+    public printNewSystemXml: boolean = false;
+    /** Whether the MusicXML says to print a new page (page break). See OSMDOptions.newPageFromXML */
+    public printNewPageXml: boolean = false;
 
     private measureNumber: number;
     private absoluteTimestamp: Fraction;

+ 13 - 3
src/OpenSheetMusicDisplay/OSMDOptions.ts

@@ -18,7 +18,7 @@ export interface IOSMDOptions {
     autoBeamOptions?: AutoBeamOptions;
     /** Automatically resize score with canvas size. Default is true. */
     autoResize?: boolean;
-    /** Render Backend, will be SVG if given undefined, SVG or svg, otherwise Canvas. */
+    /** Render Backend, will be SVG if given undefined, "SVG" or "svg", otherwise Canvas. */
     backend?: string;
     /** Defines the mode that is used for coloring: XML (0), Boomwhacker(1), CustomColorSet (2). Default XML.
      *  If coloringMode.CustomColorSet (2) is chosen, a coloringSetCustom parameter must be added.
@@ -28,7 +28,7 @@ export interface IOSMDOptions {
     coloringSetCustom?: string[];
     /** Whether to enable coloring noteheads and stems, depending on coloringMode. */
     coloringEnabled?: boolean;
-    /** Whether to color the stems of notes the same as their noteheads */
+    /** Whether to color the stems of notes the same as their noteheads. Default false. */
     colorStemsLikeNoteheads?: boolean;
     /** Default color for a note head (without stem). Default black (undefined).
      * Only considered before loading a sample, not before render.
@@ -118,11 +118,21 @@ export interface IOSMDOptions {
      *  Example: "#FFFFFF" = white. "#12345600" = transparent.
      *  This can be useful when you want to export an image with e.g. white background color instead of transparent,
      *  from a CanvasBackend.
-     *  Note: Using a background color will prevent the cursor from being visible.
+     *  Note: Using a background color will prevent the cursor from being visible for now (will be fixed at some point).
      */
     pageBackgroundColor?: string;
     /** This makes OSMD render on one single horizontal (staff-)line. */
     renderSingleHorizontalStaffline?: boolean;
+    /** Whether to begin a new system ("line break") when given in XML ('new-system="yes"').
+     *  Default false, because OSMD does its own layout that will do line breaks interactively
+     *  at different measures. So this option may result in a system break after a single measure in a system.
+     */
+    newSystemFromXML?: boolean;
+    /** Whether to begin a new page ("page break") when given in XML ('new-page="yes"').
+     *  Default false, because OSMD does its own layout that will do page breaks interactively (when given a PageFormat)
+     *  at different measures. So this option may result in a page break after a single measure on a page.
+     */
+    newPageFromXML?: boolean;
 }
 
 export enum AlignRestOption {

+ 18 - 1
src/OpenSheetMusicDisplay/OpenSheetMusicDisplay.ts

@@ -29,7 +29,7 @@ import svg2pdf = require("svg2pdf.js/dist/svg2pdf.min");
  * After the constructor, use load() and render() to load and render a MusicXML file.
  */
 export class OpenSheetMusicDisplay {
-    private version: string = "0.7.5-release"; // getter: this.Version
+    private version: string = "0.7.5-dev"; // getter: this.Version
     // at release, bump version and change to -release, afterwards to -dev again
 
     /**
@@ -335,6 +335,17 @@ export class OpenSheetMusicDisplay {
         if (options.drawingParameters) {
             this.drawingParameters.DrawingParametersEnum =
                 (<any>DrawingParametersEnum)[options.drawingParameters.toLowerCase()];
+            if (this.drawingParameters.DrawingParametersEnum === DrawingParametersEnum.compacttight) {
+                // tight rendering mode, lower margins and safety distances between systems, staffs etc. may cause overlap.
+                // these options can afterwards be finetuned by setting osmd.rules.BetweenStaffDistance for example
+                this.rules.BetweenStaffDistance = 2.5;
+                this.rules.StaffDistance = 3.5;
+                this.rules.MinimumDistanceBetweenSystems = 1;
+                // this.rules.PageTopMargin = 0.0; // see this.rules.PageTopMarginNarrow used in compact mode
+                this.rules.PageBottomMargin = 1.0;
+                this.rules.PageLeftMargin = 2.0;
+                this.rules.PageRightMargin = 2.0;
+            }
         }
 
         const backendNotInitialized: boolean = !this.drawer || !this.drawer.Backends || this.drawer.Backends.length < 1;
@@ -429,6 +440,12 @@ export class OpenSheetMusicDisplay {
         if (options.fingeringInsideStafflines !== undefined) {
             this.rules.FingeringInsideStafflines = options.fingeringInsideStafflines;
         }
+        if (options.newSystemFromXML !== undefined) {
+            this.rules.NewSystemAtXMLNewSystemAttribute = options.newSystemFromXML;
+        }
+        if (options.newPageFromXML !== undefined) {
+            this.rules.NewPageAtXMLNewPageAttribute = options.newPageFromXML;
+        }
         if (options.fillEmptyMeasuresWithWholeRest !== undefined) {
             this.rules.FillEmptyMeasuresWithWholeRest = options.fillEmptyMeasuresWithWholeRest;
         }

+ 30 - 11
test/Util/generateImages_browserless.js

@@ -25,17 +25,18 @@ function sleep (ms) {
 async function init () {
     console.log('[OSMD.generate] init')
 
-    let [osmdBuildDir, sampleDir, imageDir, pageWidth, pageHeight, filterRegex, debugFlag, debugSleepTimeString] = process.argv.slice(2, 10)
+    let [osmdBuildDir, sampleDir, imageDir, pageWidth, pageHeight, filterRegex, mode, debugSleepTimeString] = process.argv.slice(2, 10)
     if (!osmdBuildDir || !sampleDir || !imageDir) {
         console.log('usage: ' +
-            'node test/Util/generateImages_browserless.js osmdBuildDir sampleDirectory imageDirectory [width|0] [height|0] [filterRegex|all|allSmall] [--debug] [debugSleepTime]')
-        console.log('  (use "all" to skip filterRegex parameter. "allSmall" skips two huge OSMD samples that take forever to render)')
+            'node test/Util/generateImages_browserless.js osmdBuildDir sampleDirectory imageDirectory [width|0] [height|0] [filterRegex|all|allSmall] [--debug|--osmdtesting] [debugSleepTime]')
+        console.log('  (use "all" to skip filterRegex parameter. "allSmall" with --osmdtesting skips two huge OSMD samples that take forever to render)')
         console.log('example: node test/Util/generateImages_browserless.js ../../build ./test/data/ ./export 210 297 allSmall --debug 5000')
-        console.log('Error: need sampleDir and imageDir. Exiting.')
+        console.log('Error: need osmdBuildDir, sampleDir and imageDir. Exiting.')
         process.exit(1)
     }
 
-    const DEBUG = debugFlag === '--debug'
+    const osmdTestingMode = mode.includes('osmdtesting') // can also be --debugosmdtesting
+    const DEBUG = mode.startsWith('--debug')
     // const debugSleepTime = Number.parseInt(process.env.GENERATE_DEBUG_SLEEP_TIME) || 0; // 5000 works for me [sschmidTU]
     if (DEBUG) {
         console.log(' (note that --debug slows down the script by about 0.3s per file, through logging)')
@@ -142,7 +143,7 @@ async function init () {
     const sampleDirFilenames = fs.readdirSync(sampleDir)
     let samplesToProcess = [] // samples we want to process/generate pngs of, excluding the filtered out files/filenames
     for (const sampleFilename of sampleDirFilenames) {
-        if (filterRegex === 'allSmall') {
+        if (osmdTestingMode && filterRegex === 'allSmall') {
             if (sampleFilename.match('^(Actor)|(Gounod)')) { // TODO maybe filter by file size instead
                 debug('filtering big file: ' + sampleFilename, DEBUG)
                 continue
@@ -158,7 +159,7 @@ async function init () {
     }
 
     // filter samples to process by regex if given
-    if (filterRegex && filterRegex !== '' && filterRegex !== 'all' && filterRegex !== 'allSmall') {
+    if (filterRegex && filterRegex !== '' && filterRegex !== 'all' && !(osmdTestingMode && filterRegex === 'allSmall')) {
         debug('filtering samples for regex: ' + filterRegex, DEBUG)
         samplesToProcess = samplesToProcess.filter((filename) => filename.match(filterRegex))
         debug(`found ${samplesToProcess.length} matches: `, DEBUG)
@@ -179,12 +180,12 @@ async function init () {
 
     // you can set finer-grained rendering/engraving settings in EngravingRules:
     osmdInstance.EngravingRules.TitleTopDistance = 5.0 // 9.0 is default
-    osmdInstance.EngravingRules.PageTopMargin = 5.0 // 5 is default
-    osmdInstance.EngravingRules.PageBottomMargin = 5.0 // 5 is default. <5 can cut off scores that extend in the last staffline
+    // osmdInstance.EngravingRules.PageTopMargin = 5.0 // 5 is default
+    // osmdInstance.EngravingRules.PageBottomMargin = 5.0 // 5 is default. <5 can cut off scores that extend in the last staffline
     // note that for now the png and canvas will still have the height given in the script argument,
     //   so even with a margin of 0 the image will be filled to the full height.
-    osmdInstance.EngravingRules.PageLeftMargin = 5.0 // 5 is default
-    osmdInstance.EngravingRules.PageRightMargin = 5.0 // 5 is default
+    // osmdInstance.EngravingRules.PageLeftMargin = 5.0 // 5 is default
+    // osmdInstance.EngravingRules.PageRightMargin = 5.0 // 5 is default
     // osmdInstance.EngravingRules.MetronomeMarkXShift = -8; // -6 is default
     // osmdInstance.EngravingRules.DistanceBetweenVerticalSystemLines = 0.15; // 0.35 is default
     // for more options check EngravingRules.ts (though not all of these are meant and fully supported to be changed at will)
@@ -213,6 +214,24 @@ async function init () {
         // console.log('loadParameter: ' + loadParameter)
         // console.log('typeof loadParameter: ' + typeof loadParameter)
 
+        // set sample-specific options for OSMD visual regression testing
+        if (osmdTestingMode) {
+            const isFunctionTestAutobeam = sampleFilename.startsWith('OSMD_function_test_autobeam')
+            const isFunctionTestAutoColoring = sampleFilename.startsWith('OSMD_function_test_auto-custom-coloring')
+            const isFunctionTestSystemAndPageBreaks = sampleFilename.startsWith('OSMD_Function_Test_System_and_Page_Breaks')
+            const isFunctionTestDrawingRange = sampleFilename.startsWith('OSMD_function_test_measuresToDraw_')
+            osmdInstance.setOptions({
+                autoBeam: isFunctionTestAutobeam, // only set to true for function test autobeam
+                coloringMode: isFunctionTestAutoColoring ? 2 : 0,
+                coloringSetCustom: isFunctionTestAutoColoring ? ['#d82c6b', '#F89D15', '#FFE21A', '#4dbd5c', '#009D96', '#43469d', '#76429c', '#ff0000'] : undefined,
+                colorStemsLikeNoteheads: isFunctionTestAutoColoring,
+                drawFromMeasureNumber: isFunctionTestDrawingRange ? 9 : 1,
+                drawUpToMeasureNumber: isFunctionTestDrawingRange ? 12 : Number.MAX_SAFE_INTEGER,
+                newSystemFromXML: isFunctionTestSystemAndPageBreaks,
+                newPageFromXML: isFunctionTestSystemAndPageBreaks
+            })
+        }
+
         await osmdInstance.load(loadParameter).then(function () {
             debug('xml loaded', DEBUG)
             try {

BIN
test/data/OSMD_Function_Test_System_and_Page_Breaks_4_pages.mxl