Преглед изворни кода

feat(Layout): support XML page breaks optionally. new option newPageFromXML. #702

close #702
sschmid пре 5 година
родитељ
комит
2bcec40e70

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

@@ -214,6 +214,7 @@ export class EngravingRules {
     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;
@@ -443,6 +444,7 @@ export class EngravingRules {
         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
 
@@ -1569,6 +1571,12 @@ export class EngravingRules {
     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();

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

@@ -104,17 +104,19 @@ 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
-            const doXmlLineBreak: boolean = this.rules.NewSystemAtXMLNewSystemAttribute && sourceMeasure.printNewSystemXml;
+            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,
@@ -124,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;
     }
 
@@ -172,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);
@@ -1018,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,

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

@@ -140,6 +140,10 @@ export class InstrumentReader {
           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") &&

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

@@ -56,6 +56,8 @@ export class SourceMeasure {
     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;

+ 5 - 0
src/OpenSheetMusicDisplay/OSMDOptions.ts

@@ -128,6 +128,11 @@ export interface IOSMDOptions {
      *  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 {

+ 3 - 0
src/OpenSheetMusicDisplay/OpenSheetMusicDisplay.ts

@@ -443,6 +443,9 @@ export class OpenSheetMusicDisplay {
         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;
         }